0
votes

This is the code in the frontend sending the request - notice the order of the params:

params = {ticket_guid: "XXX-XXX", user_name: "David", quantity: 2}
$.get('/init_stripe_transaction', params, function(data) {

This is the "Bad Request" data in the dev console on the browser showing the order of the params:

http://localhost:4000/init_stripe_transaction?ticket_guid=XXX-XXX&user_name=David&quantity=2

This is the error response in the terminal(server side), i.e phoenix elixir def logs. NOTICE - how the order of the params has now been changed for some reason:

[info] GET /init_stripe_transaction
[debug] Processing with DiceWeb.TransactionController.create_stripe_session/2

Parameters: %{"quantity" => "2", "ticket_guid" => "XXX-XXX", "user_name" => "David"}
Pipelines: [:browser]
[info] Sent 400 in 357ms
[debug] ** (Phoenix.ActionClauseError) no function clause matching in DiceWeb.TransactionController.create_stripe_session/2 

This is how I am pattern matching in the controller:

def create_stripe_session(
  conn, 
  %{ticket_guid: ticket_guid, user_name: user_name, quantity: quantity}
) do ...

EXTRA FYI: I am very new to elixir/pattern matching. So decided stack overflow was better than creating an issue on the phoenix repo. Is it OK to pattern match in this way?

2

2 Answers

5
votes

1) Just like in ruby, the map:

%{a: 1, b: 2} 

is short hand for the map:

%{:a => 1, :b => 2}

In iex:

iex(1)> %{a: 1, b: 2} == %{:a => 1, :b => 2}
true

2) In Elixir, atoms are not garbage collected, so there is a hard limit on the size of the atom table, which is where atoms are stored. Phoenix protects you against someone sending a request with 100 million name/value pairs, which if handed over to your app as atom/value pairs would flood the atom table. Instead, the actions in your controllers receive maps with string/value pairs, which you can then pattern match to pick out the string/value pairs that you are interested in. The remaining string/value pairs will be garbage collected. By convention, you then convert the key/value pairs that you pattern matched to atom/value pairs for internal use, for instance:

def world(conn, %{"name" => name}) do 
    render(conn, "world.html", name: name)
end 

3) A string is not equivalent to an atom:

iex(1)> "a" == :a
false

So if a parameter variable for a function specifies a map with atoms for keys, that function will not pattern match against a map whose keys are strings:

defmodule My do
  def go(%{:a => x}) do
    IO.puts x
  end
end

My.go(%{"a" => 10})

At the command line:

~/elixir_programs$ elixir a.exs
** (FunctionClauseError) no function clause matching in My.go/1    

    The following arguments were given to My.go/1:

        # 1
        %{"a" => 10}

    a.exs:2: My.go/1
    (elixir) lib/code.ex:767: Code.require_file/2

4) Eventually, you'll run across something like this:

def go(%User{}) do
   ...
end

%User{} matches any User struct.

0
votes

ah wow - clearly still thinking in Ruby.

  1. The order doesn't matter.
  2. : != => when using maps to pattern match like this. Simple replacing the symbol style with fat arrow style solved my problem. Regardless of what order the params are in - way more sense.