defmodule Object do
defstruct []
defmacro __using__(fields) do
quote do
require Object
Object.inherit from: Object, fields: unquote(fields)
end
end
defmacro inherit([from: module, fields: fields]) do
mod = Macro.expand(module, __CALLER__)
functions =
mod.__info__(:functions)
|> Enum.reject(&(Atom.to_string(elem(&1, 0)) =~ "__"))
|> Enum.map(fn({name, arity}) -> {name, build_args(arity)} end)
fields = mod.__info__(:struct)
|> Enum.map(&({&1.field, &1.default}))
|> Keyword.merge(fields)
delegate_calls = Enum.map(functions, fn {name, args} ->
quote location: :keep do
defdelegate(unquote({name, [], args}), to: unquote(__MODULE__))
end
end)
overridable_list = Enum.map(functions, fn {name, args} -> {name, length(args)} end)
quote location: :keep do
defmacro __using__(fields) do
quote do
require Object
Object.inherit from: unquote(__MODULE__), fields: unquote(fields)
end
end
defstruct unquote(fields)
unquote_splicing(delegate_calls)
defoverridable unquote(overridable_list)
end
end
defp build_args(arity) do
Enum.map(1..arity, &({:"var_#{&1}", [], Elixir}))
end
def foo(bar, baz) do
bar + baz
end
def bar(a), do: a + 2
end
{:module, Object, <<70, 79, 82, 49, 0, 0, 26, ...>>, {:bar, 1}}
[__using__: 1, inherit: 1]
defmodule Foo do
use Object, [
a: 1
]
end
{:module, Foo, <<70, 79, 82, 49, 0, 0, 14, ...>>, :ok}
defmodule Bar do
use Foo, [
b: 2
]
end
{:module, Bar, <<70, 79, 82, 49, 0, 0, 14, ...>>, :ok}
bar = %Bar{}
Bar.foo(bar.a, bar.b)
3