How to pass env variable value to macros?

I have a macro that spins up RabbitMQ consumers and I want to pass in the number of consumer instances based on different environments.

Let’s say 2 instances for integration and 10 for production.

@instance_number Application.compile_env(:consumers, :address_consumer_instances)

use Consumers.Projection,
  entities: ["address", "customer"],
  instances: @instance_number

This doesn’t work however, and I get the following error:

** (ArgumentError) could not call Module.get_attribute/2 because the module Rabbit.Consumer is already compiled. Use the Module.__info__/1 callback or Code.fetch_docs/1 instead

The macro looks like this:

  defmacro __using__(opts) do
    instances = Macro.expand(Keyword.get(opts, :instances), __ENV__)

    quote location: :keep do
      @behaviour unquote(__MODULE__)

      use Module.Rabbit.Consumer,
        event: Consumers.Projection.Event,
        instances: unquote(instances)

I tried multiple ways to fix this like:

  • using Macro.expand
  • Module.put_attribute

Thanks in advance for your comments and answers

instance_number = Application.compile_env(:consumers, :address_consumer_instances)

use Consumers.Projection,
  entities: ["address", "customer"],
  instances: instance_number

you want this:

instances = Macro.expand(Keyword.get(opts, :instances), __CALLER__)

__ENV__ is the environment in the macro execution, but you’re pulling the @ macro from the caller’s context.

That said I’d be super concerned that this is macro overuse, especially seeing this:

@behaviour unquote(__MODULE__)