Skip to content

Commit

Permalink
Refactor hooks into hook :before/:after.
Browse files Browse the repository at this point in the history
  • Loading branch information
mtwilliams committed Apr 30, 2016
1 parent d950366 commit 70d7c1f
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 17 deletions.
51 changes: 35 additions & 16 deletions lib/blazon/serializable.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,40 @@ defmodule Blazon.Serializable do
quote do
import Blazon.Serializable

# This is helps us differentiate between representers and structs. We
# could use `Module.defines?(__MODULE__, :__struct__)`, but this seems
# cleaner because it's not tied to Elixir's internals. It also allows us
# to detect modules that are neither.
# This helps us differentiate between representers and structs. We could
# use `Module.defines?(__MODULE__, :__struct__)`, but this seems cleaner
# because it's not tied to Elixir's internals. It also allows us to
# detect modules that are neither (and inform the user of their incompetence).
def __blazon__, do: true

# Our field/3, link/3, and embed/3 macros simply build up an agnostic
# definition of how to serialize an object thats used by Blazon.Serializer
# definition of how to serialize an object that's used by Blazon.Serializer
# implementations.
@before_compile Blazon.Serializable
Module.register_attribute __MODULE__, :__serialize__, accumulate: true, persist: false

# Allow users to massage their model prior to serialization.
defp __before_serialize__(model), do: model
defoverridable [__before_serialize__: 1]

# Also allow users to massage their model after serialization. I can't
# think of case where this makes sense (in production). However, it can
# be useful for profiling.
defp __after_serialize__(model), do: model
defoverridable [__after_serialize__: 1]
end
end

@doc false
defmacro __before_compile__(_opts) do
quote do
def serialize(serializer, model, opts \\ []) do
model = __before_serialize__(model)
# We ensure __field__ is defined at least once so `extract` (in
# `serialize/3` below) compiles even if a user doesn't declare a single
# field, link, or embed to serialize.
defp __field__(nil, _), do: nil

filtered = case {Keyword.get(opts, :only), Keyword.get(opts, :except)} do
def serialize(serializer, model, opts \\ []) do
fields = case {Keyword.get(opts, :only), Keyword.get(opts, :except)} do
{nil, nil} ->
@__serialize__
{nil, leave} ->
Expand All @@ -42,9 +51,15 @@ defmodule Blazon.Serializable do
Enum.filter(@__serialize__, fn field -> field in keep end)
end

filtered
|> Enum.map(fn serialize -> {serialize, __field__(serialize, model)} end)
extract = fn model ->
Enum.map(fields, fn field -> {field, __field__(field, model)} end)
end

model
|> __before_serialize__
|> extract.()
|> serializer.serialize(opts)
|> __after_serialize__
end
end
end
Expand All @@ -57,9 +72,13 @@ defmodule Blazon.Serializable do
end
end

defmacro before(do: hook) do
@hooks ~w(before after)a

defmacro hook(hook, do: body) when hook in @hooks do
quote do
defp __before_serialize__(var!(model)), [do: unquote(hook)]
defp unquote(:"__#{hook}_serialize__")(var!(model)) do
unquote(body)
end
end
end

Expand All @@ -68,14 +87,14 @@ defmodule Blazon.Serializable do
{:fn, _, _} = generator ->
quote do
@__serialize__ unquote(name)
def __field__(unquote(name), model) do
defp __field__(unquote(name), model) do
unquote(generator).(model)
end
end
_ ->
quote do
@__serialize__ unquote(name)
def __field__(unquote(name), model) do
defp __field__(unquote(name), model) do
Map.get(model, unquote(name))
end
end
Expand All @@ -94,7 +113,7 @@ defmodule Blazon.Serializable do
expand = expander(representer_or_type, opts)
quote do
@__serialize__ unquote(name)
def __field__(unquote(name), model) do
defp __field__(unquote(name), model) do
Enum.map(Map.get(model, unquote(name)), unquote(expand))
end
end
Expand All @@ -104,7 +123,7 @@ defmodule Blazon.Serializable do
expand = expander(representer_or_type, opts)
quote do
@__serialize__ unquote(name)
def __field__(unquote(name), model) do
defp __field__(unquote(name), model) do
unquote(expand).(Map.get(model, unquote(name)))
end
end
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defmodule Blazon.Mixfile do
defp documentation_url, do: "https://github.com/mtwilliams/blazon"

defp version do
"0.1.3"
"0.1.4"
end

defp elixirc_paths(:test), do: ~w(test/support) ++ elixirc_paths
Expand Down
19 changes: 19 additions & 0 deletions test/blazon_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,23 @@ defmodule Blazon.Tests do
encoded = BasicsSerializer.serialize(Blazon.Serializers.JSON, @basics)
assert encoded == Poison.encode!(@basics)
end

defmodule HooksSerializer do
use Blazon.Serializable

hook :before do
%{before: true}
end

hook :after do
Map.merge(model, %{after: true})
end

field :before
field :after
end

test "hooks" do
assert HooksSerializer.serialize(Blazon.Serializers.Map, %{}) == %{before: true, after: true}
end
end

0 comments on commit 70d7c1f

Please sign in to comment.