Metaprogramming to create a wrapping/delegating module

I want to use metaprogramming to “wrap” another module’s functions. For example:

defmodule Foo do
  def one(x) do
    ...
  end
  def two(x, y) do
    ...
  end
end

Then some metaprogramming magic that produces the following module:

defmodule Bar do
  def one(x) do
    Logger.info("one called")
    Foo.one(x)
  end
  def two(x, y) do
    Logger.info("two called")
    Foo.two(x, y)
  end
end

How to do this?

I’m talking real simple, as in this is just a one off, I don’t need a function to do this multiple times. Here’s where I’m stuck at:

defmodule Bar
  Enum.each Foo.__info__(:functions), fn {name, arity} ->
    # What goes here?
  end
end

Thanks for the help!

That would require me to type out all the function definitions. The reason why I want to do this is to save me from having to type out dozens of functions. Also, if the source modules changes, my metaprogramming generated module would get the changes without me having to do anything.

Works up to 26 arguments per function. If you need more you can adjust accordingly.

defmodule Bar do
  require Foo
  require Logger

  arg_names = ~w[a b c d e f g h i j k l m n o p q r s t u v w x y z]a

  for {name, arity} <- Foo.__info__(:functions) do
    args     = for name <- Enum.take(arg_names, arity), do: {name, [], Elixir}
    function = {name, [], args}
    log_text = "#{name} called"

    def unquote(function) do
      Logger.info(unquote(log_text))

      Foo.unquote(function)
    end
  end
end
1 Like

Or just dynamically generate it. Look at my MLElixir project to see how to do all of these as I do a substantial amount of code generation there, with no argument limits.

I can’t imagine needing anywhere close to 26 arguments. I think the simplicity of the static list wins out in this case. If you’re going to publish as a package then sure, a smarter dynamic method should be used.

It happens in generated code. ^.^