[
  {
    "path": ".gitignore",
    "content": "/_build\n/cover\n/deps\nerl_crash.dump\n*.ez\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: elixir\nsudo: false\nelixir:\n- 1.3.4\notp_release:\n- 18.2\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\"> <br><img src=\"logo/logotype_horizontal.png?raw=true\" alt=\"oop\" width=\"256\"> <br>\n\n# OOP\n\n[![Build Status](https://travis-ci.org/wojtekmach/oop.svg?branch=master)](https://travis-ci.org/wojtekmach/oop)\n\nAre you tired of all of that modules, processes and functions nonsense? Do you want to just use classes, objects and methods? If so, use OOP [1] library in Elixir [2]!\n\n## Demo\n\n[![Lightning Talks - Wojtek Mach (ElixirConfEU 2016)](https://img.youtube.com/vi/5EtV2JUU0Z4/0.jpg)](https://www.youtube.com/watch?v=5EtV2JUU0Z4)\n\n## Example\n\n```elixir\nimport OOP\n\nclass Person do\n  var :name\n\n  def say_hello_to(who) do\n    what = \"Hello #{who.name}\"\n    IO.puts(\"#{this.name}: #{what}\")\n  end\nend\n\njoe = Person.new(name: \"Joe\")\nmike = Person.new(name: \"Mike\")\nrobert = Person.new(name: \"Robert\")\n\njoe.say_hello_to(mike)    # Joe: Hello Mike\nmike.say_hello_to(joe)    # Mike: Hello Joe\nmike.say_hello_to(robert) # Mike: Hello Robert\nrobert.say_hello_to(mike) # Robert: Hello Mike\n\njoe.set_name(\"Hipster Joe\")\njoe.name # => Hipster Joe\n```\n\nAn OOP library wouldn't be complete without inheritance:\n\n```elixir\nclass Animal do\n  var :name\nend\n\nclass Dog < Animal do\n  var :breed\nend\n\nsnuffles = Dog.new(name: \"Snuffles\", breed: \"Shih Tzu\")\nsnuffles.name # => \"Snuffles\"\nsnuffles.breed # => \"Shih Tzu\"\n```\n\n... or multiple inheritance:\n\n```elixir\nclass Human do\n  var :name\nend\n\nclass Horse do\n  var :horseshoes_on?\nend\n\nclass Centaur < [Human, Horse] do\nend\n\njohn = Centaur.new(name: \"John\", horseshoes_on?: true)\njohn.name # => \"John\"\njohn.horseshoes_on? # => true\n```\n\nSee more usage in the [test suite](test/oop_test.exs).\n\n## Installation\n\nAdd `oop` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [{:oop, \"~> 0.1.0\"}]\nend\n```\n\n[1] According to Alan Kay, the inventor of OOP, \"objects\" is the lesser idea; the big idea is \"messaging\". In that sense, I can't agree more with Joe Armstrong's quote that Erlang is \"possibly the only object-oriented language\".\n\n[2] Please don't. You've been warned.\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2015 Wojciech Mach\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "lib/oop.ex",
    "content": "defmodule OOP.Registry do\n  def start_link do\n    Agent.start_link(fn -> %{} end, name: __MODULE__)\n  end\n\n  def register(pid, class) do\n    Agent.update(__MODULE__, &Map.put(&1, pid, class))\n  end\n\n  def get(pid) do\n    Agent.get(__MODULE__, &Map.get(&1, pid, nil))\n  end\nend\n\ndefmodule OOP.Application do\n  use Application\n\n  def start(_type, _args) do\n    OOP.Registry.start_link()\n  end\nend\n\ndefmodule OOP.Builder do\n  def create_class(class, superclasses, block, opts) do\n    quote do\n      defmodule unquote(class) do\n        OOP.Builder.ensure_can_be_subclassed(unquote(superclasses))\n\n        @final Keyword.get(unquote(opts), :final, false)\n\n        def __final__? do\n          @final\n        end\n\n        def new(data \\\\ [], descendant? \\\\ false) do\n          OOP.Builder.ensure_can_be_instantiated(unquote(class), descendant?, unquote(opts))\n\n\n          object = :\"#{unquote(class)}#{:erlang.unique_integer()}\"\n\n          defmodule object do\n            use GenServer\n\n            def start_link(data) do\n              GenServer.start_link(__MODULE__, data, name: __MODULE__)\n            end\n\n            def class do\n              unquote(class)\n            end\n\n            def methods do\n              built_ins = [\n                code_change: 3, handle_call: 3, handle_cast: 2, handle_info: 2,\n                init: 1, start_link: 1, terminate: 2,\n                class: 0, methods: 0,\n              ]\n\n              __MODULE__.__info__(:functions) -- built_ins\n            end\n\n            import Kernel, except: [def: 2]\n\n            Module.register_attribute(__MODULE__, :friends, accumulate: true)\n\n            unquote(block)\n\n            Enum.each(unquote(superclasses), fn superclass ->\n              parent = superclass.new(data, true)\n\n              for {method, arity} <- parent.methods do\n                Code.eval_quoted(OOP.Builder.inherit_method(method, arity, parent), [], __ENV__)\n              end\n            end)\n          end\n\n          {:ok, pid} = object.start_link(Enum.into(data, %{}))\n          OOP.Registry.register(pid, unquote(class))\n\n          object\n        end\n      end\n    end\n  end\n\n  def ensure_can_be_subclassed(superclasses) do\n    Enum.each(superclasses, fn s ->\n      if s.__final__?, do: raise \"cannot subclass final class #{s}\"\n    end)\n  end\n\n  def ensure_can_be_instantiated(class, descendant?, opts) do\n    abstract? = Keyword.get(opts, :abstract, false)\n\n    if !descendant? and abstract? do\n      raise \"cannot instantiate abstract class #{class}\"\n    end\n  end\n\n  def create_method(call, expr) do\n    # HACK: this is a really gross way of checking if the function is using `this`.\n    #       if so, we let it leak: `var!(this) = data`.\n    #       We do this so that we don't get the \"unused variable this\" warning when\n    #       we don't use `this`.\n    using_this? = String.match?(Macro.to_string(expr), ~r\"\\bthis\\.\")\n\n    {method, args} = Macro.decompose_call(call)\n\n    handle_call_quoted =\n      quote do\n        try do\n          [do: value] = unquote(expr)\n          {:reply, {:ok, value}, data}\n        rescue\n          e in [RuntimeError] ->\n            {:reply, {:error, e}, data}\n        end\n      end\n\n    quote do\n      def unquote(call) do\n        case GenServer.call(__MODULE__, {:call, unquote(method), unquote(args)}) do\n          {:ok, value} -> value\n          {:error, e} -> raise e\n        end\n      end\n\n      if unquote(using_this?) do\n        def handle_call({:call, unquote(method), unquote(args)}, _from, data) do\n          var!(this) = data\n          unquote(handle_call_quoted)\n        end\n      else\n        def handle_call({:call, unquote(method), unquote(args)}, _from, data) do\n          unquote(handle_call_quoted)\n        end\n      end\n    end\n  end\n\n  def inherit_method(method, arity, parent) do\n    args = (0..arity) |> Enum.drop(1) |> Enum.map(fn i -> {:\"arg#{i}\", [], OOP} end)\n\n    {:defdelegate, [context: OOP, import: Kernel],\n      [{method, [], args}, [to: parent]]}\n  end\n\n  def create_var(field, opts) do\n    private? = Keyword.get(opts, :private, false)\n\n    quote do\n      def unquote(field)() do\n        case GenServer.call(__MODULE__, {:get, unquote(field)}) do\n          {:ok, value} -> value\n          {:error, :private} -> raise \"Cannot access private var #{unquote(field)}\"\n        end\n      end\n\n      def unquote(:\"set_#{field}\")(value) do\n        GenServer.call(__MODULE__, {:set, unquote(field), value})\n      end\n\n      def handle_call({:get, unquote(field)}, {pid, _ref}, data) do\n        classes = [class() | @friends]\n        if unquote(private?) and ! OOP.Registry.get(pid) in classes do\n          {:reply, {:error, :private}, data}\n        else\n          {:reply, {:ok, Map.get(data, unquote(field))}, data}\n        end\n      end\n\n      def handle_call({:set, unquote(field), value}, _from, data) do\n        {:reply, value, Map.put(data, unquote(field), value)}\n      end\n    end\n  end\nend\n\ndefmodule OOP do\n  defmacro class(class_expr, block, opts \\\\ []) do\n    {class, superclasses} =\n      case class_expr do\n        {:<, _, [class, superclasses]} when is_list(superclasses) ->\n          {class, superclasses}\n\n        {:<, _, [class, superclass]} ->\n          {class, [superclass]}\n\n        class ->\n          {class, []}\n      end\n\n    OOP.Builder.create_class(class, superclasses, block, opts)\n  end\n\n  defmacro abstract(class_expr, block) do\n    {:class, _, [class]} = class_expr\n\n    quote do\n      OOP.class(unquote(class), unquote(block), abstract: true)\n    end\n  end\n\n  defmacro final(class_expr, block) do\n    {:class, _, [class]} = class_expr\n\n    quote do\n      OOP.class(unquote(class), unquote(block), final: true)\n    end\n  end\n\n  defmacro def(call, expr \\\\ nil) do\n    OOP.Builder.create_method(call, expr)\n  end\n\n  defmacro var(field, opts \\\\ []) do\n    OOP.Builder.create_var(field, opts)\n  end\n\n  defmacro private_var(field) do\n    quote do\n      var(unquote(field), private: true)\n    end\n  end\n\n  defmacro friend(class) do\n    quote do\n      @friends unquote(class)\n    end\n  end\nend\n"
  },
  {
    "path": "mix.exs",
    "content": "defmodule OOP.Mixfile do\n  use Mix.Project\n\n  def project do\n    [app: :oop,\n     version: \"0.1.1\",\n     description: \"OOP in Elixir!\",\n     package: package(),\n     elixir: \"~> 1.0\",\n     build_embedded: Mix.env == :prod,\n     start_permanent: Mix.env == :prod,\n     deps: deps()]\n  end\n\n  def application do\n    [mod: {OOP.Application, []}]\n  end\n\n  defp deps do\n    []\n  end\n\n  defp package do\n    [\n      maintainers: [\"Wojtek Mach\"],\n      licenses: [\"MIT\"],\n      links: %{\"GitHub\" => \"https://github.com/wojtekmach/oop\"},\n    ]\n  end\nend\n"
  },
  {
    "path": "test/oop_test.exs",
    "content": "defmodule OOPTest do\n  use ExUnit.Case\n  import OOP\n\n  test \"define empty class\" do\n    c = class Person do\n    end\n\n    assert c\n    purge Person\n  end\n\n  test \"instantiate empty object\" do\n    class Person do\n    end\n\n    alice = Person.new\n    assert alice.class == Person\n    purge Person\n  end\n\n  test \"define methods on objects\" do\n    class Person do\n      def zero do\n        0\n      end\n\n      def sum1(a) do\n        a\n      end\n\n      def sum2(a, b) do\n        a + b\n      end\n    end\n\n    alice = Person.new\n    assert alice.zero() == 0\n    assert alice.sum1(1) == 1\n    assert alice.sum2(1, 2) == 3\n    purge Person\n  end\n\n  test \"define fields\" do\n    class Person do\n      var :name\n\n      def title(prefix) do\n        \"#{prefix} #{this.name}\"\n      end\n    end\n\n    alice = Person.new\n    assert alice.name == nil\n\n    bob = Person.new(name: \"Bob\")\n    assert bob.name == \"Bob\"\n    bob.set_name(\"Hipster Bob\")\n    assert bob.name == \"Hipster Bob\"\n    assert bob.title(\"Mr.\") == \"Mr. Hipster Bob\"\n\n    assert alice.name == nil\n\n    purge Person\n  end\n\n  test \"define private fields\" do\n    class AppleInc do\n      private_var :registered_devices\n\n      def registered_devices_count do\n        length(this.registered_devices)\n      end\n    end\n\n    apple = AppleInc.new(registered_devices: [\"Alice's iPhone\", \"Bob's iPhone\"])\n\n    assert_raise RuntimeError, \"Cannot access private var registered_devices\", fn ->\n      apple.registered_devices\n    end\n\n    assert apple.registered_devices_count == 2\n\n    purge AppleInc\n  end\n\n  test \"define friend class\" do\n    class NSA do\n      def get_data(company) do\n        company.registered_devices\n      end\n    end\n\n    class Thief do\n      def get_data(company) do\n        company.registered_devices\n      end\n    end\n\n    class AppleInc do\n      friend NSA\n      private_var :registered_devices\n    end\n\n    apple = AppleInc.new(registered_devices: [\"Alice's iPhone\", \"Bob's iPhone\"])\n    thief = Thief.new\n    nsa = NSA.new\n\n    assert_raise RuntimeError, \"Cannot access private var registered_devices\", fn ->\n      thief.get_data(apple)\n    end\n\n    assert nsa.get_data(apple) == [\"Alice's iPhone\", \"Bob's iPhone\"]\n\n    purge [AppleInc, Thief, NSA]\n  end\n\n  test \"inheritance\" do\n    class Animal do\n      var :name\n\n      def title(prefix) do\n        \"#{prefix} #{this.name}\"\n      end\n    end\n\n    class Dog < Animal do\n      var :breed\n    end\n\n    snuffles = Dog.new(name: \"Snuffles\", breed: \"Shih Tzu\")\n    assert snuffles.name == \"Snuffles\"\n    assert snuffles.breed == \"Shih Tzu\"\n    assert snuffles.title(\"Mr.\") == \"Mr. Snuffles\"\n\n    purge [Animal, Dog]\n  end\n\n  test \"multiple inheritance\" do\n    class Human do\n      var :name\n    end\n\n    class Horse do\n      var :horseshoes_on?\n    end\n\n    class Centaur < [Human, Horse] do\n    end\n\n    john = Centaur.new(name: \"John\", horseshoes_on?: true)\n    assert john.name == \"John\"\n    assert john.horseshoes_on? == true\n\n    purge [Human, Horse, Centaur]\n  end\n\n  test \"define abstract class\" do\n    abstract class ActiveRecord.Base do\n    end\n\n    assert_raise RuntimeError, \"cannot instantiate abstract class #{ActiveRecord.Base}\", fn ->\n      ActiveRecord.Base.new\n    end\n\n    class Post < ActiveRecord.Base do\n      var :title\n    end\n\n    assert Post.new(title: \"Post 1\").title == \"Post 1\"\n    purge [ActiveRecord.Base, Post]\n  end\n\n  test \"abstract class inheriting from abstract class\" do\n    abstract class ActiveRecord.Base do\n    end\n\n    abstract class ApplicationRecord < ActiveRecord.Base do\n    end\n\n    assert_raise RuntimeError, \"cannot instantiate abstract class #{ActiveRecord.Base}\", fn ->\n      ActiveRecord.Base.new\n    end\n\n    assert_raise RuntimeError, \"cannot instantiate abstract class #{ApplicationRecord}\", fn ->\n      ApplicationRecord.new\n    end\n\n    class Post < ApplicationRecord do\n      var :title\n    end\n\n    assert Post.new(title: \"Post 1\").title == \"Post 1\"\n    purge [ActiveRecord.Base, ApplicationRecord, Post]\n  end\n\n  test \"define final class\" do\n    final class FriezaFourthForm do\n    end\n\n    assert FriezaFourthForm.new\n\n    assert_raise RuntimeError, \"cannot subclass final class #{FriezaFourthForm}\", fn ->\n      class FriezaFifthForm < FriezaFourthForm do\n      end\n    end\n\n    purge [FriezaFourthForm, FriezaFifthForm]\n  end\n\n  defp purge(module) when is_atom(module) do\n    :code.delete(module)\n    :code.purge(module)\n  end\n  defp purge(modules) when is_list(modules) do\n    Enum.each(modules, &purge/1)\n  end\nend\n"
  },
  {
    "path": "test/test_helper.exs",
    "content": "ExUnit.start()\n"
  }
]