Current section

Files

Jump to
defused lib module.ex
Raw

lib/module.ex

defmodule Defused.Module do
@moduledoc """
Use `Defused.Module` in a module to defuse all or some of its def:s
## Examples
defmodule Module do
use Defused.Module, fuse: :fuse_name, only: [foo: 2]
def foo(a, b) do
# defused
end
def bar() do
# not defused
end
end
## Options
* `:fuse` - required, the name of the fuse
* `:only` - the defs that should be defused
* `:except` - the defs that should not be defused
Only one of `:only` and `:except` can be used. If none of them are specified, all def:s will be defused
"""
@doc false
defmacro __using__(opts) do
quote do
fuse = Keyword.fetch!(unquote(opts), :fuse)
only = case Keyword.get(unquote(opts), :only, nil) do
nil -> nil
x -> MapSet.new(x)
end
except = case Keyword.get(unquote(opts), :except, nil) do
nil -> nil
x -> MapSet.new(x)
end
if only && except do
throw "Cannot specify both `only` and `except`"
end
use Defused
import Kernel, except: [def: 2]
import Defused.Module
@defused_only only
@defused_except except
@defused_fuse fuse
end
end
defp meta(call) do
case call do
{:when, _, [call | _]} -> meta(call)
{name, _, args} ->
arity = case args do
nil -> 0
args -> length(args)
end
{name, arity}
end
end
@doc false
defmacro def(call, expr \\ nil) do
quote do
{name, arity} = unquote(meta(call))
cond do
{name, arity} == {:blown_error, 2} -> Kernel.def(unquote(call), unquote(expr))
@defused_only != nil ->
case MapSet.member?(@defused_only, {name, arity}) do
false -> Kernel.def(unquote(call), unquote(expr))
true -> defused @defused_fuse, unquote(call), unquote(expr)
end
@defused_except != nil ->
case MapSet.member?(@defused_except, {name, arity}) do
true -> Kernel.def(unquote(call), unquote(expr))
false -> defused @defused_fuse, unquote(call), unquote(expr)
end
true -> defused @defused_fuse, unquote(call), unquote(expr)
end
end
end
end