Routex.Branching (Routex v1.2.2)

View Source

Provides a function to build branched variants of macro's

Summary

Functions

Takes a list of match patterns and creates the AST for branching variants for all arities of macro in module by wrapping them in a case statement.

Functions

branch_macro(patterns, match_binding, module, macro, opts \\ [])

Takes a list of match patterns and creates the AST for branching variants for all arities of macro in module by wrapping them in a case statement.

Args

  • patterns: the match patterns to be used as case clauses
  • match_binding: ast inserted as case [match_binding] do
  • module: the module name
  • macro: the macro name
  • opts: list of options

Options

  • arities: a list of arities to transform, default: all arities
  • as: name of the branching variant. default: the macro name
  • arg_post: function to calculate the replaced argument position. default: 0

The clauses and arguments can be transformed by providing MFA's. The transformers receive as arguments the pattern, the banched arg and any other argument provided in a.

  • clause_transformer: {m,f,a}. transforms a pattern; used as case clause in the macro body.
  • arg_transformer: {m,f,a}. transforms a branched argument; used in the macro body.

Example

We want to create a branching variant of the url macro in Phoenix.VerifiedRoutes module. The original macro generates code that simply prints the given path argument, but we want to it to write multiple clauses and prefix the given argument based on the clause.

defmacro url(path, opts \ []) do -> quote do IO.puts(path) end

Given this code:

defmodule MyMod do
  def transform_arg(pattern, arg, extra), do: "/" <> extra <> "/europe/" <> pattern <> "/" <> arg end
end

patterns = ["en", "nl"]
match_binding = var!(external_var)
arg_pos = fn arity -> arity - 1 end)
arg_transformer = {MyMod, transform_arg, ["my_extra"]}
opts = [as: :url, orig: :url_original, arg_pos: arg_pos, arg_transformer: arg_transformer]

branch_macro(patterns, match_binding, OriginalModule, :url, opts)

A new macro is build which outputs the AST of the original macro, wrapped in a case clause given transformed arguments.

defmacro url(path, opts \ []) do
    quote do
      case external_var do
         "en" -> Original.Module.url( "/" <> "my_extra" <> "/europe/en/" <> path, opts)
         "nl" -> Original.Module.url("/" <> "my_extra" <> "/europe/nl/" <> path, opts)
     end
   end
 end

For more examples, please see the test module Routex.BranchingTest.