Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mattvonrocketstein/fb70e89b323fca61e9ea38f69bd5c509 to your computer and use it in GitHub Desktop.
Save mattvonrocketstein/fb70e89b323fca61e9ea38f69bd5c509 to your computer and use it in GitHub Desktop.
Elixir module inheritance
defmodule Extension do
defmacro extends(module) do
# As above...
end
defmacro implements(module, protocol: protocol) do
quote do
defimpl unquote(protocol), for: unquote(module) do
import Extension
extends unquote(module)
end
end
end
end
Code.require_file "../test_helper.exs", __FILE__
import Extension
defprotocol Foo do
def direct(this)
def wrapper(this, arg)
end
defmodule Bar do
@behaviour Foo
def direct(_this) do
:foo
end
def wrapper(this, arg) do
[ Foo.direct(this), arg ]
end
end
implements Bar, protocol: Foo
defmodule Baz do
@behaviour Foo
import Extension
extends Bar
def direct(_this) do
:bar
end
end
implements Baz, protocol: Foo
defmodule ExtensionTest do
use ExUnit.Case, async: true
test "foo" do
assert Foo.direct({Bar}) == :foo
assert Foo.wrapper({Bar}, 0) == [ :foo, 0 ]
end
test "bar" do
assert Foo.direct({Baz}) == :bar
assert Foo.wrapper({Baz}, 0) == [ :bar, 0 ]
end
end
defmodule Extension do
defmacro extends(module) do
module = Macro.expand(module, __CALLER__)
functions = module.__info__(:functions)
signatures = Enum.map functions, fn { name, arity } ->
args = if arity == 0 do
[]
else
Enum.map 1 .. arity, fn(i) ->
{ binary_to_atom(<< ?x, ?A + i - 1 >>), [], nil }
end
end
{ name, [], args }
end
quote do
defdelegate unquote(signatures), to: unquote(module)
defoverridable unquote(functions)
end
end
end
Code.require_file "../test_helper.exs", __FILE__
defmodule Foo do
def direct do
:foo
end
def wrapper do
direct
end
end
defmodule Bar do
import Extension
extends Foo
def direct do
:bar
end
end
defmodule Baz do
import Extension
extends Bar
def direct do
:baz
end
def wrapper do
direct
end
end
defmodule TodoxTest do
use ExUnit.Case, async: true
test "foo" do
assert Foo.direct == :foo
assert Foo.wrapper == :foo
end
test "bar" do
assert Bar.direct == :bar
assert Bar.wrapper == :bar # Actually returns :foo
end
test "baz" do
assert Baz.direct == :baz
assert Baz.wrapper == :baz
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment