Rob Lacey

Brighton, UK - contact@robl.me

Software Engineer specialising in Ruby on Rails. I do occasional freelance work, I love working for SARD JV Ltd, they are awesome and give me my pocket money every month. By all means dangle a carrot.


Best way to learn, buy an out of date book

There is nothing worse than working through a programming textbook and everything working first time. Sometimes the best way to learn is to see backtraces, and broken stuff all over the place. I’ve been working full time as a programmer for over 10 years and while I’m experienced, if there is a possibility for something to break I seem to find it without fail.

I also systematically go through eBay and pick up older Pragmatic Programmers books because I just love the presentation, they’ve nailed it and every read is just a comfortable. However, my £4 Programming Phoenix book means that I’m about 5 years out of date and so everything doesn’t work first time. But that’s ok, I’m learning and finding out stuff by breaking stuff.

Today it’s working out Ecto changeset. Firstly the cast function was missing, so I needed to add import Ecto.Changeset

defmodule Dungeon.Room do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  schema "rooms" do
    field :name, :string
    field :x, :integer
    field :y, :integer
  end

  def changeset(model, params \\ :empty) do
    model
    |> cast(params, ~w[name x y], [])
    |> validate_length(:name, min: 1, max: 30)
  end
end

And now….

Ecto.CastError at GET /rooms/new
expected params to be a :map, got: `:empty`

It appears that cast takes a :map and we’ve defaulted to :empty and cast is now kicking off :S

I can’t replace it with…

def changeset(model, params \\ %{}) do   model
  |> cast(params, ~w[name x y], [])
  |> validate_length(:name, min: 1, max: 30)
end

…since cast is expecting name, x, or y. Oh right. Ok the fourth argument to cast/4 is optional keys. This will be pretty lame since every empty Room struct will have no name, x, or y. So there’s got to be something else missing here :S

def changeset(model, params \\ %{}) do
  model
  |> cast(params, [], ~w[name x y])
  |> validate_length(:name, min: 1, max: 30)
end

Hmmzzz….

It would appear that my name, x, and y are all empty.

def changeset(model, params \\ %{}) do
  model
  |> cast(params, ~w[name description x y]a, [])
  |> validate_length(:name, min: 1, max: 30)
end

Seems we need a list of atoms rather than strings so.

~w[name x y]a

Ok, queen….I’ll check the Ecto documentation. I can read that too right?

defmodule User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :name
    field :email
    field :age, :integer
  end

  def changeset(user, params \\ %{}) do
    user
    |> cast(params, [:name, :email, :age])
    |> validate_required([:name, :email])
    |> validate_format(:email, ~r/@/)
    |> validate_inclusion(:age, 18..100)
    |> unique_constraint(:email)
  end
end

OK, looks like this \\ :empty isn’t the right thing to do.