How to update a module attribute in my macro module

I have a macro module that generates ids for my database.

defmodule Haberdash.Inventory.Id do
  defmacro __using__(_) do
    quote do
      use Ecto.Type
      @prefix "inv"
      def type, do: :id
      def cast(<< _a1, _a2, _a3, _a4, _a5, _a6, _a7, _a8, ?-,
      _b1, _b2, _b3, _b4, ?-,
      _c1, _c2, _c3, _c4, ?-,
      _d1, _d2, _d3, _d4, ?-,
      _e1, _e2, _e3, _e4, _e5, _e6, _e7, _e8, _e9, _e10, _e11, _e12 >> = id), do: {:ok, @prefix <> "_" <> id}
      #.....
      def generate(), do: @prefix <> "_" <> Ecto.UUID.generate |> String.replace("-", "")
      def autogenerate(), do: generate()
      #.....

    end
  end
end

How do i override the prefix module attribute to use another variable, similar to foreign_key_type in Ecto.Schema?

Instead of a module attribute, I think I would more likely use a parameter to the macro and invoke it with use Haberdash.Inventory.ID, prefix: “my_prefix”.

1 Like

Thanks but for future reference, how would i override a module attribute values?

From my understanding, you cannot because __using__/1 will overwrite your prefix on each run.

If you want to make it overridable, you could write this in the following way:

#!/usr/bin/env elixir

defmodule Generator do
  defmacro __using__(_opts) do
    quote do
      if !Module.has_attribute?(__MODULE__, :prefix) do
        @prefix "test-"
      end

      def generate(), do: @prefix <> generate_string()

      defp generate_string() do
        :crypto.strong_rand_bytes(16)
        |> Base.encode16(case: :lower)
      end
    end
  end
end

defmodule Test do
  @prefix "custom-" # NOTE: The order is important!

  use Generator
end

Test.generate()
|> IO.puts()
# custom-730f02dae4932dcf3d9f866fa37a3547

Alternatively, you could register this attribute as accumulative and always take first value. But I don’t recommend both approaches. It’s much better use to a parameter.