Last active
September 12, 2024 04:08
-
-
Save orenbenkiki/5174435 to your computer and use it in GitHub Desktop.
Elixir module inheritance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
Thanks for this! I was able to edit the Extension module so it did not throw a warning in newer versions of Elixir like so: