Today I encountered an weird behavior when I tried to change the accumulate
option for a module attribute.
I would like to know if this behavior is intended since the documentation does not mention it.
In summary the steps are:
- Create a module attribute with the
accumulate: true
option - Delete the module attribute
- Recreate it with the
accumulate: false
option - Try to assign values, they will still be accumulated
Here is an example :
defmodule ModuleDeleteTest do
# 1/ Define a module attribute with accumulate: true
Module.register_attribute(__MODULE__, :attribut, accumulate: true)
# 2/ Then delete and recreate the module attribute but with accumulate: false
Module.delete_attribute(__MODULE__, :attribut)
Module.register_attribute(__MODULE__, :attribut, accumulate: false)
# Unfortunately, the accumulate: true flag is still present
@attribut :foo
@attribut :bar
# > CURRENT attribut value: [:bar, :foo]
IO.inspect(@attribut, label: "CURRENT attribut value")
end
On this example I was expecting@attribut
to be equals to :bar
at the end.
After examining the Elixir source code, I noticed that delete_attribute/2 with accumulate: true does remove the attribute from the bag but not from the set. As a result, the set still contains the accumulate: true
flag.
Here here a quick fix:
defmodule ModuleFix do
def delete_attribute(module, key) do
{set, _bag} = :elixir_module.data_tables(module)
# Here bag is clean by Module.delete_attribute/2
Module.delete_attribute(module, key)
# But you also need to clean the set or the module attribute
# still have the :accumulate flag set
:ets.delete(set, key)
end
end
defmodule ModuleDeleteTestFix do
# 1/ Define a module attribute with accumulate: true
Module.register_attribute(__MODULE__, :attribut, accumulate: true)
# 2/ Then delete and recreate the module attribute but with accumulate: false
ModuleFix.delete_attribute(__MODULE__, :attribut)
Module.register_attribute(__MODULE__, :attribut, accumulate: false)
# Now, module attribute is properly reset
@attribut :foo
@attribut :bar
# > FIXED field value: :bar
IO.inspect(@attribut, label: "FIXED attribut value")
end
As said in the introduction, I don’t know if this behavior is intended and if it is, I think it should be mentioned in the doc If it’s not, I’d be happy to open an issue on Github and propose a PR.