Skip to content

Commit

Permalink
mix format the code base
Browse files Browse the repository at this point in the history
  • Loading branch information
Arjan Scherpenisse committed Sep 29, 2018
1 parent 6dbebcf commit b165cd9
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 118 deletions.
4 changes: 4 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
104 changes: 62 additions & 42 deletions lib/decorators/decorate.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
defmodule Decorator.Decorate do

defmodule Context do
defstruct name: nil, arity: nil, module: nil, args: nil
end
Expand All @@ -19,84 +18,100 @@ defmodule Decorator.Decorate do

decorated
|> filter_undecorated(decorated_functions)
|> Enum.reduce({nil, []}, fn(d, acc) ->
|> Enum.reduce({nil, []}, fn d, acc ->
decorate(env, d, decorated_functions, acc)
end)
|> elem(1)
|> Enum.reverse
|> Enum.reverse()
end

defp decorated_functions(all) do
Enum.group_by(all,
fn({_kind, fun, args, _guard, _body, _decorators}) ->
Enum.group_by(
all,
fn {_kind, fun, args, _guard, _body, _decorators} ->
{fun, Enum.count(args)}
end,
fn({_kind, _fun, _args, _guard, _body, decorators}) ->
fn {_kind, _fun, _args, _guard, _body, decorators} ->
decorators
end)
|> Enum.filter(fn({_k, decorators_list}) ->
end
)
|> Enum.filter(fn {_k, decorators_list} ->
List.flatten(decorators_list) != []
end)
|> Enum.into(%{})
end

# Remove all defs which are not decorated -- these doesn't need to be redefined.
defp filter_undecorated(all, decorated_functions) do
all |> Enum.filter(
fn({_kind, fun, args, _guard, _body, _decorators}) ->
Map.has_key?(decorated_functions, {fun, Enum.count(args)})
end)
all
|> Enum.filter(fn {_kind, fun, args, _guard, _body, _decorators} ->
Map.has_key?(decorated_functions, {fun, Enum.count(args)})
end)
end

defp implied_arities(args) do
arity = Enum.count(args)

default_count =
args
|> Enum.filter(fn({:\\, _, _}) -> true; (_) -> false end)
|> Enum.count
|> Enum.filter(fn
{:\\, _, _} -> true
_ -> false
end)
|> Enum.count()

:lists.seq(arity, arity - default_count, -1)
end

defp decorate(env, {kind, fun, args, guard, body, decorators},
decorated_functions, {prev_fun, all}) do
defp decorate(
env,
{kind, fun, args, guard, body, decorators},
decorated_functions,
{prev_fun, all}
) do
override_clause =
implied_arities(args)
|> Enum.map(&(quote do
defoverridable [{unquote(fun), unquote(&1)}]
end))
|> Enum.map(
&quote do
defoverridable [{unquote(fun), unquote(&1)}]
end
)

arity = Enum.count(args || [])
context = %Context{
name: fun,
arity: arity,
args: args,
module: env.module}

applicable_decorators = case decorators do
[] -> Map.get(decorated_functions, {fun, arity}) |> hd()
_ -> decorators
end
context = %Context{name: fun, arity: arity, args: args, module: env.module}

body = applicable_decorators
|> Enum.reverse
|> Enum.reduce(body, fn(decorator, body) ->
apply_decorator(context, decorator, body)
end)
|> ensure_do()
applicable_decorators =
case decorators do
[] -> Map.get(decorated_functions, {fun, arity}) |> hd()
_ -> decorators
end

body =
applicable_decorators
|> Enum.reverse()
|> Enum.reduce(body, fn decorator, body ->
apply_decorator(context, decorator, body)
end)
|> ensure_do()

def_clause =
case guard do
[] ->
quote do
Kernel.unquote(kind)(unquote(fun)(unquote_splicing(args)), unquote(body))
end

_ ->
quote do
Kernel.unquote(kind)(unquote(fun)(unquote_splicing(args)) when unquote_splicing(guard), unquote(body))
Kernel.unquote(kind)(
unquote(fun)(unquote_splicing(args)) when unquote_splicing(guard),
unquote(body)
)
end
end

arity = Enum.count(args)

if {fun, arity} != prev_fun do
{{fun, arity}, [def_clause, override_clause | all]}
else
Expand All @@ -107,31 +122,36 @@ defmodule Decorator.Decorate do
defp ensure_do([{:do, _} | _] = body), do: body
defp ensure_do(body), do: [do: body]

defp apply_decorator(context, mfa, [do: body]) do
defp apply_decorator(context, mfa, do: body) do
[do: apply_decorator(context, mfa, body)]
end
defp apply_decorator(context, mfa, [do: body, rescue: rescue_block]) do
[do: apply_decorator(context, mfa, body), rescue: apply_decorator_to_rescue(context, mfa, rescue_block)]

defp apply_decorator(context, mfa, do: body, rescue: rescue_block) do
[
do: apply_decorator(context, mfa, body),
rescue: apply_decorator_to_rescue(context, mfa, rescue_block)
]
end

defp apply_decorator(context, {module, fun, args}, body) do
if Enum.member?(module.__info__(:functions), {fun, Enum.count(args) + 2}) do
Kernel.apply(module, fun, (args || []) ++ [body, context])
else
raise ArgumentError, "Unknown decorator function: #{fun}/#{Enum.count(args)}"
end
end

defp apply_decorator(_context, decorator, _body) do
raise ArgumentError, "Invalid decorator: #{inspect decorator}"
raise ArgumentError, "Invalid decorator: #{inspect(decorator)}"
end

defp apply_decorator_to_rescue(context, mfa, rescue_block) do
rescue_block
|> Enum.map(fn({:->, meta, [match, body]}) ->
|> Enum.map(fn {:->, meta, [match, body]} ->
{:->, meta, [match, apply_decorator(context, mfa, body)]}
end)
end

def generate_args(0, _caller), do: []
def generate_args(n, caller), do: for(i <- 1..n, do: Macro.var(:"var#{i}", caller))

end
40 changes: 18 additions & 22 deletions lib/decorators/define.ex
Original file line number Diff line number Diff line change
@@ -1,50 +1,46 @@
defmodule Decorator.Define do

defmacro __using__(decorators) do
decorator_module = __CALLER__.module

decorator_macros = for {decorator, arity} <- decorators do
arglist = Decorator.Decorate.generate_args(arity, decorator_module)
quote do
defmacro unquote(decorator)(unquote_splicing(arglist)) do
if Module.get_attribute(__CALLER__.module, :decorate) != nil do
raise ArgumentError, "Decorator #{unquote(decorator)} used without @decorate"
decorator_macros =
for {decorator, arity} <- decorators do
arglist = Decorator.Decorate.generate_args(arity, decorator_module)

quote do
defmacro unquote(decorator)(unquote_splicing(arglist)) do
if Module.get_attribute(__CALLER__.module, :decorate) != nil do
raise ArgumentError, "Decorator #{unquote(decorator)} used without @decorate"
end

Macro.escape({unquote(decorator_module), unquote(decorator), unquote(arglist)})
end
Macro.escape({unquote(decorator_module), unquote(decorator), unquote(arglist)})
end
end
end

quote do

@decorator_module unquote(decorator_module)
@decorator_defs unquote(decorators)

unquote_splicing(decorator_macros)

defmacro __using__(_) do

imports = for {decorator, arity} <- @decorator_defs do
quote do
{unquote(decorator), unquote(arity)}
imports =
for {decorator, arity} <- @decorator_defs do
quote do
{unquote(decorator), unquote(arity)}
end
end
end

quote do

import unquote(@decorator_module), only: unquote(imports)

Module.register_attribute __MODULE__, :decorate, accumulate: true
Module.register_attribute __MODULE__, :decorated, accumulate: true
Module.register_attribute(__MODULE__, :decorate, accumulate: true)
Module.register_attribute(__MODULE__, :decorated, accumulate: true)

@on_definition {Decorator.Decorate, :on_definition}
@before_compile {Decorator.Decorate, :before_compile}

end

end
end

end

end
31 changes: 17 additions & 14 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,32 @@ defmodule Decorator.Mixfile do
use Mix.Project

def project do
[app: :decorator,
version: "1.2.3",
elixir: "~> 1.3",
elixirc_options: [warnings_as_errors: true],
description: description(),
package: package(),
source_url: "https://github.com/arjan/decorator",
homepage_url: "https://github.com/arjan/decorator",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
[
app: :decorator,
version: "1.2.3",
elixir: "~> 1.3",
elixirc_options: [warnings_as_errors: true],
description: description(),
package: package(),
source_url: "https://github.com/arjan/decorator",
homepage_url: "https://github.com/arjan/decorator",
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

defp description do
"Function decorators for Elixir"
end

defp package do
%{files: ["lib", "mix.exs",
"*.md", "LICENSE"],
%{
files: ["lib", "mix.exs", "*.md", "LICENSE"],
maintainers: ["Arjan Scherpenisse"],
licenses: ["MIT"],
links: %{"GitHub" => "https://github.com/arjan/decorator"}}
links: %{"GitHub" => "https://github.com/arjan/decorator"}
}
end

def application do
Expand Down
8 changes: 4 additions & 4 deletions test/basic_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defmodule DecoratorTest.Fixture.MyDecorator do
use Decorator.Define, [some_decorator: 0]
use Decorator.Define, some_decorator: 0

def some_decorator(body, _context) do
body
Expand Down Expand Up @@ -34,11 +34,11 @@ defmodule DecoratorTest.Basic do
end

test "decorate function with no argument list" do
assert 24 == MyModule.answer
assert 24 == MyModule.answer()
end

test "normal module attributes should still work" do
assert 123 == MyModule.value123
assert 666 == MyModule.value666
assert 123 == MyModule.value123()
assert 666 == MyModule.value666()
end
end
4 changes: 1 addition & 3 deletions test/default_argument_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defmodule DecoratorTest.Fixture.OptionalArgsTestDecorator do
use Decorator.Define, [test: 0]
use Decorator.Define, test: 0

def test(body, _context) do
body
Expand All @@ -13,7 +13,6 @@ defmodule DecoratorTest.Fixture.OptionalArgsTestModule do
def result(_aa, arg \\ nil) do
{:ok, arg}
end

end

defmodule DecoratorTest.DefaultArguments do
Expand All @@ -24,5 +23,4 @@ defmodule DecoratorTest.DefaultArguments do
assert {:ok, nil} == OptionalArgsTestModule.result(1)
assert {:ok, 1} == OptionalArgsTestModule.result(2, 1)
end

end
4 changes: 2 additions & 2 deletions test/exception_test.exs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# two arguments
defmodule DecoratorTest.Fixture.ExceptionDecorator do
use Decorator.Define, [test: 0]
use Decorator.Define, test: 0

def test(body, _context) do
{:ok, body}
end

end

defmodule DecoratorTest.Fixture.ExceptionTestModule do
Expand All @@ -16,6 +15,7 @@ defmodule DecoratorTest.Fixture.ExceptionTestModule do
if a == :throw do
raise RuntimeError, "text"
end

a
rescue
_ in RuntimeError ->
Expand Down
Loading

0 comments on commit b165cd9

Please sign in to comment.