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

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

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
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews