Indifferent Access with Elixir/Phoenix App

Ly Channa
2 min readApr 30, 2019

--

Phoenix is a great framework, however, there is some limitation as it is relatively news.

One of the problems I found is the params passing to the controller and the params we create in our test.

Indifferent access in Map Elixir

Let’s look at the example:

In your test or anywhere else it is more convenient to use the atom as keys:

valid_attrs = %{
title: "Prison Break",
duration: 40,
released_date: "2017-01-01T12:23:34Z",
original_lang: "en-US",
original_title: "Prison Break",
backdrop_url: file_upload_fixture(),
poster_url: file_upload_fixture(),
file_url: file_upload_fixture()
}

However, you get the data from the controller with Map with a string key

movie_params = %{
"movie" => % {
"title"=> " "Prison Break",
"duration" => 40,
"released_date" => "2017-01-01T12:23:34Z",
"original_lang" => "en-US",
"original_title" => "Prison Break",
...
}}

We need to handle the key for example generate the slug from the title field, copy the file to store in the disk from the file_url field attributes which is a file upload field.

How are you going to pass the field if they can be either atom or string?

At the moment I could not find any libs to do it. I decided to write my own as a purpose of learning too.

Solution

As Elixir does not allow to have either atom or string key in the same Map, I can securely assume if one of the keys is an atom there are all atoms, otherwise, they will be all strings.

# field_key is the field name can be either string or atom
def sanitize_key_type_in_attrs(attrs, field_key) do
key = List.first(Map.keys(attrs))
case is_atom(key) do
true ->
:"#{field_key}"
_ ->
"#{field_key}"
end
end

Now let say we want to check values in the fields

def put_file_input_field(attrs, file_key) do
file_key = sanitize_key_type_in_attrs(attrs, file_key)
result = Map.has_key?(attrs, file_key)

case result do
true ->
upload = attrs[file_key]
file_name = "#{upload.filename}"
root = File.cwd!
location = "#{root}/media/#{file_name}"
File.cp!(upload.path, location)
Map.put(attrs, file_key, file_name)
_ ->
attrs
end
end

Conclusion

Elixir Map is awesome but in some case scenarios, the input attributes can come in different Map formats. By having a function that can handle the key passed in the function at runtime it will be helpful. However, this approach is not a good practice. The better way is to try to have a common format as much as possible — rolling on your own means you spend more time on doing things besides your business goal.

--

--

Ly Channa
Ly Channa

Written by Ly Channa

Highly skilled: REST API, OAuth2, OpenIDConnect, SSO, TDD, RubyOnRails, CI/CD, Infrastruct as Code, AWS.

No responses yet