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.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance










