2
votes

I'm learning the Phoenix framework from the book Programming Phoenix and am stuck with an error I can't seem to understand. Basically, the book is currently at the point of adding a form for creating new users. So when I point the browser to http://localhost:4000/users/new I get the below page (please expand the image):

enter image description here

In the command line output the server says:

[info] GET /users/new
[debug] Processing by Rumbl.UserController.new/2
  Parameters: %{}
  Pipelines: [:browser]
[info] Sent 500 in 9ms
[error] #PID<0.543.0> running Rumbl.Endpoint terminated
Server: localhost:4000 (http)
Request: GET /users/new
** (exit) an exception was raised:
    ** (ArgumentError) argument error
        :erlang.binary_to_existing_atom("name,", :utf8)
        (ecto) lib/ecto/changeset.ex:402: Ecto.Changeset.process_empty_fields/2
        (elixir) lib/enum.ex:1184: Enum."-map/2-lists^map/1-0-"/2
        (ecto) lib/ecto/changeset.ex:373: Ecto.Changeset.do_cast/7
        (rumbl) web/models/user.ex:15: Rumbl.User.changeset/2
        (rumbl) web/controllers/user_controller.ex:17: Rumbl.UserController.new/2
        (rumbl) web/controllers/user_controller.ex:1: Rumbl.UserController.action/2
        (rumbl) web/controllers/user_controller.ex:1: Rumbl.UserController.phoenix_controller_pipeline/2
        (rumbl) lib/rumbl/endpoint.ex:1: Rumbl.Endpoint.instrument/4
        (rumbl) lib/phoenix/router.ex:261: Rumbl.Router.dispatch/2
        (rumbl) web/router.ex:1: Rumbl.Router.do_call/2
        (rumbl) lib/rumbl/endpoint.ex:1: Rumbl.Endpoint.phoenix_pipeline/1
        (rumbl) lib/plug/debugger.ex:122: Rumbl.Endpoint."call (overridable 3)"/2
        (rumbl) lib/rumbl/endpoint.ex:1: Rumbl.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

Since the error says something like binary_to_existing_atom I thought maybe I had mistyped the parameters or something, but it doesn't seem to be the case.

My model file:

defmodule Rumbl.User do
    use Rumbl.Web, :model

    schema "users" do
        field :name, :string
        field :username, :string
        field :password, :string, virtual: true
        field :password_hash, :string

        timestamps
    end

    def changeset(model, params \\ :invalid) do
        model
        |> cast(params, ~w(name, username), [])
        |> validate_length(:username, min: 1, max: 20)
    end 
end

My migration file:

defmodule Rumbl.Repo.Migrations.CreateUser do
    use Ecto.Migration

    def change do
        create table(:users) do
            add :name, :string
            add :username, :string, null: false
            add :password_hash, :string

            timestamps
        end

        create unique_index(:users, [:username])
    end
end

The controller:

defmodule Rumbl.UserController do
    use Rumbl.Web, :controller

    def index(conn, _params) do
        users = Repo.all(Rumbl.User)
        render conn, "index.html", users: users
    end

    def show(conn, %{"id" => id}) do
        user = Repo.get(Rumbl.User, id)
        render conn, "show.html", user: user
    end

    alias Rumbl.User

    def new(conn, _params) do
        changeset = User.changeset(%User{})
        render conn, "new.html", changeset: changeset 
    end
end

And finally, the template:

<h1>New User</h1>

<%= form_for @changeset, user_path(@conn, :create), fn f -> %>
    <div class="form-group">
        <%= text_input f, :name, placeholder: "Username", class: "form-control" %>        
    </div>
    <div class="form-group">
        <%= password_input f, :password, placeholder: "Password", class: "form-control" %>        
    </div>
    <%= submit "Create User", class: "btn btn-primary" %>
<% end %>

Let me know if some other code is also needed. And thanks in advance!

1
Try to remove the comma after the name in cast(params, ~w(name, username), []) so it would be like this: cast(params, ~w(name username), []) - NoDisplayName
@JustMichael Whoa, this worked! Please explain why and add it as your answer. :-) - ankush981

1 Answers

6
votes

The ~w sigil does not require commas after each element but when you add those, they get concatenated to the list items so in your case

~w(name, username)

generated the following list:

["name,", "username"]

which is obviously not what you wanted.

So you should just remove the comma and use this instead:

~w(name username)

which generates exactly what you are expecting:

["name", "username"]