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.