halostatue

halostatue

How do I make this work in a defenum macro?

  defenum EmailStatus, :email_status, @email_states

Right now, I’m getting:

warning: undefined module attribute @email_states, please remove access to @email_states or explicitly set it before access

It’s making no difference whether I’m using Macro.expand/2 or not:

values: {:@, [line: 122], [{:email_states, [line: 122], nil}]}
values-expanded: {{:., [],
  [
    {:__aliases__, [counter: {MyApp.Enum, 9}, alias: false],
     [:Module]},
    :get_attribute
  ]}, [],
 [
   {:__MODULE__, [counter: {MyApp.Enum, 9}], Kernel},
   :email_states,
   122
 ]}

I actually want to pass in a value that’s closer to:

  @a ~w(a b c d)
  @b ~w(e f g h)
  @c ~w(I j k l)
  defenum EmailStatus, :email_status, @a ++ @b ++ @c

The macro in question is complex, and may eventually be released as a fork of an already existing package. But for the purposes of this test, I believe that this will do:

  defmacro defenum(module, type, values) do
    quote location: :keep do
      defmodule unquote(module) do
        @enum_type unquote(type)
        @values Enum.map(unquote(values), &to_string/1)

        def type, do: @enum_type
        def values, do: @values
      end
    end
  end

Ideas?

-a

Showing Posts 1 to 6

Eiji

Eiji

@halostatue First of all I do not recommend creating an module (except nested ones) inside macro for example:

# instead of
import Example
@values [:values, :list]
defenum(MyApp.MyEnum, :type, @values)

# you could do
defmodule MyApp.MyEnum do
  import Example
  @values [:values, :list]
  defenum(:type, @values)
  # you can now access here all attributes you specify
end

Here is how usually I’m working with such macros:

defmodule Example do
  defmacro defenum(type, values) do
    quote bind_quoted: [type: type, values: values], unquote: false do
      @enum_type type
      @values Enum.map(values, &to_string/1)

      def type, do: @enum_type
      def values, do: @values
    end
  end
end

You can take a look at code of my last project:

Feel free in case of any questions.

halostatue

halostatue OP

Looks interesting and has some of the same features that I was building in myself, although organized differently and with some features that (a) I wouldn’t implement myself (your type conversion SQL) and (b) I hadn’t thought of (generating the Absinthe enum type module directly). One feature that I’m specifically building in what I’m doing is that the Absinthe enum type may have extra fields/values that aren’t part of the database enum, so I have ways of setting that, and that I put @desc attributes on each Absinthe enum.

I’m not in a position to change the enumeration code we use right now, even if your library were yet available, but there are some things I may have adapt from your concepts later.

I’ve put your answer as the solution to the problem, because it does solve the explicit example given…but it doesn’t answer the ultimate problem: it doesn’t appear possible to properly pass module attributes to macros where there’s any complexity to the module attribute (as I gave above), even when using Module.expand/2.

Eiji

Eiji

Not sure if you noticed it (I would need to add more documentation), but my project supports such extra absinthe options. Instead of :values option you need to call value/2 macro in do … end block. :values option is only for defining values in shortest possible way. Also it’s possible to pass :absinthe extra options to enum module.

For more information please see:

If you would need some features then please let me know. I will at least comment each proposition.

Sorry, I did not get what do you mean by that.

I just tried my suggested code i.e.:

defmodule Example do
  defmacro defenum(type, values) do
    quote bind_quoted: [type: type, values: values], unquote: false do
      @enum_type type
      @values Enum.map(values, &to_string/1)

      def type, do: @enum_type
      def values, do: @values
    end
  end
end

and used it with multiple attributes without any problem:

defmodule MyEnum do                  
  @a ~w(a b c d)                       
  @b ~w(e f g h)                       
  @c ~w(I j k l)                       
  import Example                                    
  defenum :email_status, @a ++ @b ++ @c
end

and here is a result of MyEnum.values():

["a", "b", "c", "d", "e", "f", "g", "h", "I", "j", "k", "l"]

Did I missed something?

halostatue

halostatue OP

I’ll try the bind_quoted solution when I have a bit of time; that’s the one thing that I haven’t tried. I’m still not in a position to switch from generating modules (which are nested inside my enum namespace, but I understand the difference of what you’re talking about).

I did. Part of what I can do (although not as nicely as what you’ve got) is have something like:

defmodule MyApp.Enums do
  import EnumMacros, only: [defenum: 3]

  defenum Region, :region, ~w(east central west)
end

defmodule MyApp.Schema do
  # Absinthe preamble…
  import EnumMacros, only: [defgraphqlenum: 2]

  defgraphqlenum :regions, from: MyApp.Region do
    @desc "View all regions"
    value :all    
  end
end

It’s not that nice, right now, but that’s the idea. :all is not a permitted value in the database, but it is permitted in the GraphQL because the GraphQL enum can be used as a filter.

Eiji

Eiji

Not sure what permissions stops you from doing something like:

defmodule MyApp.Enums do
  import EnumMacros, only: [defenum: 3]

  defmodule Region do
    defenum :region, ~w(east central west)
  end
end

If it’s really a problem then nothing really stops you from having it + add an extra macro just to generate module like:

defmodule Example do
  defmacro defenum(type, values) do
    quote bind_quoted: [type: type, values: values], unquote: false do
      @enum_type type
      @values Enum.map(values, &to_string/1)

      def type, do: @enum_type
      def values, do: @values
    end
  end

  defmacro defenum(module, type, values) do
    # …
  end
end

This option is really useful. I recommend to read documentation:

That’s a nice argument! I would probably do it in one of such ways:

  1. Add :only option to value/2 macro with supported values: [:raw, :absinthe]
defmodule MyApp.MyEnum do
  import Enumex

  defenum do
    value(:first)
    value(:second, only: :raw)
    value(:third, only: :absinthe)
  end
end
  1. Add an extra enum macro to Enumex.Absinthe. This would be called inside normal Enumex.enum macro, but also it would optionally support be called separately. For example:
defmodule MyApp.MyEnum do
  import Enumex

  enum(values:  [:first, :second, :third])
end

defmodule MyApp.MyAbsintheEnum do
  import Enumex.Absinthe

  enum(values:  [:first, :second, :third])
end

In that example MyApp.MyEnum.Absinthe would have exactly same absinthe type as a MyApp.MyAbsintheEnum.

Added to my private TODO list, thanks :smiley:

halostatue

halostatue OP

It’s time available, because it’s a relatively large change to how my enums are currently created (it’s derived from ecto_enum but primarily switches to a string representation for the enums for a number of reasons). I like your overall approach better, but it’s going to take time that I don’t actually have to change how I manage the ~20 different enums used in our app.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews