Skip to content

Instantly share code, notes, and snippets.

@toraritte
Last active July 7, 2017 12:28
Show Gist options
  • Save toraritte/ba33753d9476200b6fe8ee56dce679c9 to your computer and use it in GitHub Desktop.
Save toraritte/ba33753d9476200b6fe8ee56dce679c9 to your computer and use it in GitHub Desktop.
# Partial EEx template application
#
# As OvermindDL1 and Jose' pointed out, this doesn't cover for
# templates like "<%= a + b %>" and why not just simply collect
# the assigns, and apply them when we have them all.
# https://groups.google.com/forum/#!topic/elixir-lang-core/pYSfP3x7vwc
# I still think that there would be a use case for partial application
# and it was a nice mind exercise:)
defmodule Partial do
# Starting out with `collect_assigns/{1,2}` and 'get_default_bindings/1`
# where the latter was useful just to harvest bindings at will if there
# is a need for it
def collect_assigns({:ok, tokens}), do: collect_assigns(tokens, [])
def collect_assigns([{:expr,_,_,chars} | rest], acc) do
assign =
chars
|> String.Chars.to_string()
|> String.trim()
|> String.to_atom()
default =
'<%=' ++ chars ++ '%>'
|> String.Chars.to_string()
collect_assigns(rest, [{assign, default}|acc])
end
def collect_assigns([_|rest], acc), do: collect_assigns(rest, acc)
def collect_assigns([], acc), do: acc
def get_default_bindings(str) do
str
|> EEx.Tokenizer.tokenize(1)
|> collect_assigns()
end
# Next, make a wrapper around `eval_*`s and `function_from_*`s
def partial_eval_string(source, bindings \\ []) do
new_bindings =
source
|> get_default_bindings()
|> Keyword.merge(bindings)
EEx.eval_string(source, new_bindings)
end
# I didn't need to use the `function_from_*` macros in my project but
# the solution should be similar but doing the keyword merges before the `quote`
# and create the functions with default values.
# At this point it gets murky for me but this seems to be a good place to
# start figuring things out:
# https://medium.com/@mustafaturan/define-dynamic-functions-with-dynamic-arguments-arity-using-elixir-macros-a28241d4f119
defmacro function_from_string(kind, name, source, args \\ [], options \\ []) do
#
# new_args = args |> get_default_bindings() |> Keyword.merge(args)
# + other stuff
#
quote bind_quoted: binding() do
info = Keyword.merge [file: __ENV__.file, line: __ENV__.line], options
args = Enum.map args, fn arg -> {arg, [line: info[:line]], nil} end
compiled = EEx.compile_string(source, info)
case kind do
:def -> def(unquote(name)(unquote_splicing(args)), do: unquote(compiled))
:defp -> defp(unquote(name)(unquote_splicing(args)), do: unquote(compiled))
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment