ngeraedts
Conditional import fails to compile
I’m trying to import a helper module in my application that is only available in under :dev and :test mix environments.
I’ve got the following in my mix.exs to prevent the helper module from being compiled under :prod:
defp elixirc_paths(:test), do: ["lib", "test/fixtures", "test/support"]
defp elixirc_paths(:dev), do: ["lib", "test/fixtures"]
defp elixirc_paths(_), do: ["lib"]
and then the following in the body of one of my modules:
def MyAppWeb.ProxyController do
...
# Insert controller actions to serve fixture data in development and testing
if Mix.env() in [:dev, :test] do
import MyApp.Fixtures, only: [mock_proxy: 1]
mock_proxy(:the_thing)
end
...
end
The mock_proxy/1 macro injects some function clauses to the module. This works great under :dev or :test environments, but fails in :prod with the following error?
== Compilation error in file lib/my_app_web/controllers/proxy_controller.ex ==
** (CompileError) lib/my_app_web/controllers/proxy_controller.ex:18: module MyApp.Fixtures is not loaded and could not be found
(stdlib 3.14.2) lists.erl:1358: :lists.mapfoldl/3
(stdlib 3.14.2) lists.erl:1359: :lists.mapfoldl/3
(elixir 1.12.1) expanding macro: Kernel.if/2
I thought this wouldn’t happen because of the if statement at the module level, but clearly something else is going on. I can “fix” this by compiling this path in all environments, but I’d like to avoid that if I can.
Any thoughts on what I’m missing here?
Marked As Solved
aziz
The code needs to be put in a macro which can return something depending on a condition. I was able to make the following work:
defmodule Compile do
defmacro only_in(envs, body) do
if Mix.env() == envs or Mix.env() in envs do
body
end
end
end
def MyAppWeb.ProxyController do
require Compile
Compile.only_in [:dev, :test] do
import MyApp.Fixtures, only: [mock_proxy: 1]
mock_proxy(:the_thing)
end
end
This is just a suggestion. Maybe there’s a better way but this should help you to make progress for now. 
Also Liked
aziz
Here is an improved version that allows an optional else-body:
defmodule Compile do
defmacro only_in(envs, body) do
if Mix.env() in List.wrap(envs) do
body[:do]
else
body[:else]
end
end
end
def MyAppWeb.ProxyController do
require Compile
Compile.only_in [:dev, :test] do
import MyApp.Fixtures, only: [mock_proxy: 1]
mock_proxy(:the_thing)
else
IO.puts("No mock for prod.")
end
end
josevalim
Correct! The code in “if” won’t be executed but it still has to be expanded, hence the failure.







