Protocols are powered by behaviours behind the scenes
This commit is contained in:
@@ -59,11 +59,10 @@ defmodule Protocol do
|
||||
Protocol.assert_protocol(protocol)
|
||||
|
||||
defmodule name do
|
||||
def __impl__, do: unquote(protocol)
|
||||
@behaviour unquote(protocol)
|
||||
unquote(block)
|
||||
def __impl__, do: unquote(protocol)
|
||||
end
|
||||
|
||||
Protocol.assert_impl(name, protocol)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -85,28 +84,14 @@ defmodule Protocol do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Check if the given `impl` is a valid impl for `protocol`.
|
||||
Raises an error if not.
|
||||
|
||||
"""
|
||||
def assert_impl(impl, protocol) do
|
||||
remaining = protocol.__protocol__(:functions) -- impl.__info__(:functions)
|
||||
|
||||
if remaining != [] do
|
||||
pp = Enum.map_join remaining, ", ", fn {x,y} -> "#{x}/#{y}" end
|
||||
raise ArgumentError,
|
||||
message: "#{inspect impl} did not implement #{inspect protocol}, missing: #{pp}"
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Defines meta information about the protocol and internal callbacks.
|
||||
"""
|
||||
def meta(env, functions, fallback) do
|
||||
contents = quote do
|
||||
def __protocol__(:name), do: __MODULE__
|
||||
def __protocol__(:functions), do: unquote(:lists.sort(functions))
|
||||
def __protocol__(:name), do: __MODULE__
|
||||
def __protocol__(:functions), do: unquote(:lists.sort(functions))
|
||||
def behaviour_info(:callbacks), do: [{ :__impl__, 0 }|__protocol__(:functions)]
|
||||
|
||||
def __impl_for__(arg) do
|
||||
case __raw_impl__(arg) do
|
||||
|
||||
@@ -89,8 +89,9 @@ handle_file_warning(_, _File, { _Line, v3_kernel, bad_call }) -> [];
|
||||
|
||||
%% Rewrite
|
||||
handle_file_warning(_, File, {Line,erl_lint,{undefined_behaviour_func,{Fun,Arity},Module}}) ->
|
||||
Raw = "undefined callback function ~s/~B (behaviour ~s)",
|
||||
Message = io_lib:format(Raw, [Fun,Arity,inspect(Module)]),
|
||||
Kind = protocol_or_behaviour(Module),
|
||||
Raw = "undefined ~s function ~s/~B (for ~s ~s)",
|
||||
Message = io_lib:format(Raw, [Kind, Fun, Arity, Kind, inspect(Module)]),
|
||||
io:format(file_format(Line, File, Message));
|
||||
|
||||
handle_file_warning(_, File, {Line,erl_lint,{undefined_behaviour,Module}}) ->
|
||||
@@ -153,4 +154,21 @@ format_error([], Desc) ->
|
||||
io_lib:format("~p", [Desc]);
|
||||
|
||||
format_error(Module, Desc) ->
|
||||
Module:format_error(Desc).
|
||||
Module:format_error(Desc).
|
||||
|
||||
protocol_or_behaviour(Module) ->
|
||||
case is_protocol(Module) of
|
||||
true -> protocol;
|
||||
false -> behaviour
|
||||
end.
|
||||
|
||||
is_protocol(Module) ->
|
||||
case code:ensure_loaded(Module) of
|
||||
{ ok, _ } ->
|
||||
case erlang:function_exported(Module, '__protocol__', 1) of
|
||||
true -> Module:'__protocol__'(name) == Module;
|
||||
false -> false
|
||||
end;
|
||||
{ error, _ } ->
|
||||
false
|
||||
end.
|
||||
Reference in New Issue
Block a user