Validations using WITH

Hello!

I’m trying to validate my entries using with, but it is not clearly for me how to use it properly:

with {formatted_price, _} <- Float.parse price,
       {:ok, formatted_sale_time} = Ecto.DateTime.cast(sale_time)
do
       send_resp(conn, 201, "Cool!")
else #How do I send a response back for each error?
      :error ->  send_resp(conn, 400, "Price is invalid!")
      :error ->  send_resp(conn, 400, "Sale time is invalid!")
end

Thanks in advance!!

1 Like

You have to match on something more than just :error atom

3 Likes

Like @andre1sk said, you need to match on different clauses, if you have the same :error clause twice it will match the first one. Here is a slightly modified version which works:

with {formatted_price, ""} <- Float.parse price,
{:ok, formatted_sale_time} = Ecto.DateTime.cast(sale_time)
do
  send_resp(conn, 201, "Cool!")
else
  {_, str} when is_binary(str) and byte_size(str) > 0 -> send_resp(conn, 400, "Price is invalid!")
  :error ->  send_resp(conn, 400, "Sale time is invalid!")
end
3 Likes

Thanks guys for the explanation !! I didn’t worked, but I’ve moved the validation trough the changeset model, like this:

sale = Momsfood.Sale.changeset(%Momsfood.Sale{}, conn.params)

if sale.valid? do
  Momsfood.Repo.insert(sale)
send_resp(conn, 201, "")
else
  send_resp(conn, 400, sale.errors)
end

But now I’m having trouble to send the errors back to the client :frowning2:

send_resp(conn, 400, sale.errors)

** (ArgumentError) argument error
     stacktrace:
       :erlang.iolist_to_binary([sale_time: "is invalid"]) 

How to parse the errors properly to send it back?

Thanks guys!!

Change that to send_resp(conn, 400, inspect(sale.errors)) send_resp accepts an iolist as the third argument. inspect changes your errors object into a string so that should work.

Yay! It’s working now!

I was trying to encode into JSON using Poison, but I’m struggling with this error:

 ** (Poison.EncodeError) unable to encode value: {:sale_time, "is invalid"}

Here is what I was trying to do:

send_resp(conn, 400, Poison.encode!(sale.errors, []))

I was reading some issues related to that problem, but I didn’t manage to find a properly way solve that, even adding the @derive directive to my model:

@derive {Poison.Encoder, only: [:price, :sale_time]}
    schema "sales" do
      field :price, :float
      field :sale_time, Ecto.DateTime
      timestamps
end

Hey guys, I’ve manage to make it works!!

if sale.valid? do
   Momsfood.Repo.insert(sale)

   send_resp(conn, 201, "")
else
    errors = Enum.into(sale.errors, %{})

    send_resp(conn, 400, Poison.encode!(errors))
end

I’ve ended up converting my errors to a map, which is the correctly format expected by Poison.

Thanks for helping guys!!

1 Like