Map key is a atom or string

Hi,

I am new to elixir and here is part of the code I dont like

slider =  case Map.has_key?(attrs, :slider_id) do
  true -> get_slider!(attrs.slider_id)
  false -> get_slider!(attrs["slider_id"])
end

Reason for this is because when I do attrs = Map.put(attrs, :slider_id, slider.id) in sliders_test map key is atom

But when I do the same thing in slider_controller_test map key is string.

1 Like

You could…

slider = Map.get(attrs, :slider_id) || Map.get(attrs, "slider_id")

As a rule of thumb, expect data coming from outside to be “key”, and after cast to be :key.

3 Likes

You would be best served treating all incoming params as having string keys. If you must accept either atom or string keys, then using Map.get(“key”) || Map.get(:key) would be clearer than a case.

2 Likes

Thanks guys for your help, I agree that this is much cleaner syntax.

1 Like

To elaborate a bit, the reason why map keys from external sources (like user input) are strings, is that atoms are not garbage collected and there is a set maximum amount of atoms available in the system. If you exceed that amount, the VM will crash. So that’s why you shouldn’t create atoms from user input for example.

3 Likes

But why is phx gen generating tests maps with keys?
I tried to put slider_id as string but I got this:
expected params to be a map with atoms or string keys, got a map with mixed keys

So I should convert all pregenerated keys from atoms to string?

Example:
@valid_attrs %{description: "some description"}
to
@valid_attrs %{"description" "some description"}

1 Like

The test maps should be automatically serialised to json by the phoenix test helpers, then deserialised to string-keyed maps in the Plug.Parsers plug declared in your endpoint module.

2 Likes

They are only in controllers tests…

1 Like

Params in contollers have string keys because anybody can send you any param and creating an atom for those would be a attack vector to crash the vm. It’s still generally adviced to work with atom keys in your business part of the app and to sanitize and atomize params before passing things forward into it.

5 Likes

This is interesting. In test that are generated by phx.gen.html the controller tests will have a fixture function that creates a test record with Ecto. But there is no changing of the atom keys to string keys. So if you do anything inside the context modules where you expect a string key from the controller your tests fail but the website works. Hmm, not great.

If you are getting anything other than string keyed params in Controllers then there is something wrong with the test.

I still do not understand why is phx.gen generating atoms as @valid_attrs. I do not have any Plug.Parsers nor serialised func in my test helpers.

I could easily be missing something tho… For now, i’m writing :
@valid_attrs %{“stats” => “some stats”, “name” => “some name”}

1 Like

Is this entire issue/convention documented or explained fullly anywhere? As a newbie to Elixir I was caught off guard.

1 Like

Which one are you talking about? There are a few things mentioned in this topic.