Dynamic modules / module "aliasing"

Hey,

I know there are other ways to do this, but I went down a deep rabbit hole last night trying to figure this one out :slight_smile: … this is purely a though experiment.

Say you have 2 almost identical dependencies, but you only want to use one of them at a time. Maybe the choice of dependency is geographic based?

Both dependencies have an example module hierarchy like:

 M0
  |
  M0.M1 - M0.M2 - M0.M3
            |
         M0.M2.M4

Now of course a simple solution to ensure the code in the rest of the application selects the correct dependency would be something like this in all the modules that use it:

  config = Application.compile_env(:my_app, SpecialDependency, [])
  @dependency Keyword.get(config, :special_dep)

  # And code would do:
  @dependency.foo(a, b, c)

Seems a bit hokey…what would be nice is to create a new (set) of stub modules that, for want of a better word “alias” the correct dependency. All the application code would call the stub which would get mapped onto the correct target dependency.

For example (yes, I can also use defdelegate)

defmodule SpecialDependencyStub do

  config = Application.compile_env(:my_app, SpecialDependency, [])
  @dependency Keyword.get(config, :special_dep)

 # Generate the same functions as the real dependency
  Enum.each(@dependency.__info__(:functions), fn({fun, arity}) ->
     args = Macro.generate_arguments(arity, __MODULE__)
     def unquote(fun)(unquote_splicing(args)), do: unquote(@dependency).unquote(fun)(unquote_splicing(args))
  end)
end

Now that works fine…where I am stumped is how to handle all the modules under the SpecialDependency. I want those modules to retain the same name as the real dependency, and I’d rather not have to repeat the above for every module. Something like

Enum.each(modules, fn(mod) -> WizzardsBeHere end)

Hopefully that all makes sense

1 Like

Why would you want to write Stub.foo(bar) instead of @stub.foo(bar)?

I am slowly working on a core rewrite of the Double library for this purpose, so it can be used as a drop-in for Mox and allow folks the tools to slowly introduce behaviours and mocks

The code is dense and in the middle of a refactor, but if you’re interested, I’m taking a somewhat similar approach, at least in the defmock side.

I’ll take a look…thanks

If these modules have the same interface, couldn’t you use a macro that would alias the correct module and you’d use that alias in your code?