mikl
September 15, 2019, 11:33pm
1
I find myself unable to figure out why this pattern match isn’t working in my controller:
From organisation_controller.ex:
def switch(conn, %{switch: %{org: org_id}}) do
Logger.warn("MATCHED ORG_ID")
conn |> do_something()
end
def switch(conn, _params) do
Logger.warn("FALLBACK")
conn
|> put_status(:bad_request)
|> put_view(Pult.ErrorView)
|> render("400.html")
end
From router.ex
post "/organisations/switch", OrganisationController, :switch
The server logs then have the following:
info] POST /organisations/switch
[debug] Processing with Pult.OrganisationController.switch/2
Parameters: %{"_csrf_token" => "[snipped]", "_utf8" => "✓", "switch" => %{"org" => "3"}}
Pipelines: [:browser, :protected]
[warn] FALLBACK
[info] Sent 400 in 6ms
So the [warn] FALLBACK
indicate the first match/2 is not being matched, although the parameters should match %{switch: %{org: org_id}}
?
If I run the code in iex, it appears to match correctly:
iex(4)> switch(Phoenix.ConnTest.build_conn(), %{"switch": %{"org": "8"}})
[warn] MATCHED ORG_ID
%Plug.Conn{...}
What am I missing here?
Shouldn’t the matching in the controller match on string keys rather than atoms?
I.e.
%{"switch" => %{"org" => org_id}}
In your test example you create a map using keywords. Even if they are string the key get converted to an atom.
iex> %{"hello": "world"}
%{hello: "world"}
vs
iex> %{"hello" => "world"}
%{"hello" => "world"}
3 Likes
mikl
September 16, 2019, 12:06am
3
I’ve tried that, makes no difference, but the elixir compiler yields the following warning:
found quoted keyword "org" but the quotes are not required. Note that keywords are always atoms, even when quoted. Similar to atoms, keywords made exclusively of Unicode letters, numbers, underscore, and @ do not require quotes
So as far as I can tell, %{"hello": "world"}
is interpreted exactly the same as %{hello: "world"}
by Elixir.
In %{"hello": "world"}
- :"hello"
is still an atom.
%{"hello" : "world"}
is syntax sugar for %{:"hello" => "world"}
.
In %{"hello" => "world"}
- "hello"
is a string - the =>
with the lack of the leading colon on the key makes all the difference.
https://elixir-lang.org/getting-started/keywords-and-maps.html#maps
When all the keys in a map are atoms , you can use the keyword syntax for convenience :
1 Like
mikl
September 16, 2019, 12:45am
5
Ah, right, %{switch: %{org: org_id}}
is a keyword list. If I change it to %{"switch" => %{"org" => org_id}}
, everything works as expected, thanks to both of you.
1 Like
NobbZ
September 16, 2019, 5:01am
6
No, its a map with atom keys.
A keyword list is a list of two-tuples where the first element is an atom.
4 Likes
A map using the keyword syntax
%{:switch => %{:org => org_id}}
Is still a map with atom keys - but it doesn’t use the keyword syntax .
[{:a, 1}, {:b, 2}]
- a keyword list
[a: 1, b: 2]
- the same keyword list in the special keyword syntax
2 Likes