Do I need a macro to do this, or can I go away with something else?

Hi all,

I have many components (about ~50) that have 3 common attributes, now I am going to add a couple more, and before I do that I am seeking some advice.

attr :class, :string, default: ""
attr :id, :string, default: nil
attr :rest, :global 

Currently, I have repeated those ~50 times.

  • is there a way to avoid repeating those all the times?
  • is a macro the tool I want (I know nothing about macros)?
  • I would love to keep the class and id attrs separated from the rest for documentation purposes

Thank you
Cheers!

I would avoid going this path, as the benefit you gain (1 line of code vs 3 lines of code) is not worth it, because it will make the code much less readable.

1 Like

It will be 5, but got your point. Thank you

It’s a better idea to setup your editor with a custom snippet so you can insert those lines with a quick keyboard shortcut or a command.

Macro here would be using a shotgun to kill a mosquito.

As I mentioned, I am in the process of adding 2 more attributes, are you suggesting to add snippets every time I need some sort of common attribute to all my components?
(that would be reasonable, I am just asking :))

In general, it’s still difficult for me to think at how to implement a “Parent Component” with common functionality (I would use a Parent class in the previous language I used, which comes with its own downsides).
Probably a false issue, but ~50 components are a lot when doing manual edits, it become tedious quickly.

I am not seriously suggesting it, I am saying it’s one of the potentially viable solutions just to illustrate that it would be too much to use macros here.

That would be a good candidate for a macro. Have that parent have defmacro __using__(opts \\ []) macro and inject code through it.

That way you can have all your 50+ components only have this line at the top:

use Parent, option_1: true, option_2: ["hello", "there"], option_3: :duh

…and the rest will be just their specific parts.


Or, if you are REALLY convinced that this is a strict parent-children relationship and that this will not change – i.e. you will never need multiple parents per one child – then you can just use Elixir’s protocols and apply a little violence.

1 Like

Interesting, will think about that.

I’ve got bitten too many times in the past by the OOP dog. I would just day NO, I am not convinced :slight_smile:

Thank you

Actually I would avoid using here __using__ as it is not necessary. I would something like this:

defmodule Attributes do
  defmacro generic_attributes() do
    quote do
      attr :class, :string, default: ""
      attr :id, :string, default: nil
      attr :rest, :global 
    end
  end
end

And using it inside of components by:

require Attributes

Attributes.generic_attributes()

The advantages are:

  1. Much more readability, as you can document the macro and have a clear understanding where you use it, opposed to use;
  2. Macro defined in this way can only inject code, to avoid people coming later and adding their “smart” ideas.
1 Like

I don’t see much difference to be honest, you can just have a module named e.g. Parent.GenericAttributes and then use that. :person_shrugging:

OK that could be a good point but you gotta draw the line somewhere. Put some docs in the module that defines the __using__() or generic_attributes() and say “This should only be used to inject common attributes, please contact @dev_author to discuss other usages before modifying this code”. We’re not talking about some 50+ devs team after all.

I think generally speaking for someone who doesn’t write macros, it is easier to understand the variant of a macro vs use, as at the end of the day it does the same thing.

I think that limiting temptation is the best thing to avoid someone over-using macros, at least that is what I saw from my own experience.

Ah, no doubt, that’s what my first reply here alluded to as well. Though after OP elaborated it seems like their need could genuinely be served well with a macro.

IIUC if I put the common attributes in the __using__() I can’t have a module with many function components having their own common attributes,
while this is doable with a generic_attributes() one.

Correct me if I am wrong

You are not wrong but I would be completely okay if one module’s only job was to inject code in others. Having huge modules doing many things with many code-injection macros is not exactly a win for readability and maintainability.

In the end it’s your call, and the difference can be easily handled.

1 Like

Gotcha, thanks.