By Developers, For Developers

Historical errata for Programming Phoenix

PDF PgPaper PgTypeDescriptionFixed onComments
1SUGGEST

This may be the wrong forum for this, but I tried to upload the epub file to my google books account and it threw an error saying that it couldn’t process the epub file.

Anyone had this same issue?

— This is a Google Play Reader bug.

2015-10-02
16TYPO

Regarding this sentence: “If you’re using a database, in Phoenix you want to be using Ecto, the persistence layer.”

The first comma looks like a typo.

2015-10-31
16TYPO

“In Phoenix, whenever its possible…”

should be “it’s”

2015-10-31
42ERROR

“Our two new routes use the new UsersController, which doesn’t yet exist” Should be “UserController”

2015-10-28
15ERROR

In the graph the functions are:

connection |> endpoint |> router |> pipelines |> controller

but in the description below is:

connection |> endpoint |> pipelines |> router |> controller

2015-10-28
19SUGGEST

When running:
mix ecto.create

if the user hasn’t got a postgresql database with a user ‘postgres’ and password ‘postgres’ setup there will be an error:

  • (Mix) The database for Hello.Repo couldn’t be created, reason given: “psql: FATAL: password authentication failed for user \\”postgres\\“\
    FATAL: password authentication failed for user \\”postgres\\“\

so maybe a mention that these need to be in place would be a good idea.

2015-12-28
34SUGGEST

“The basic flow of traditional applications is endpoint, pipeline, controller”

Shouldn’t the router be included too?

2015-10-28
71ERROR

Under the heading “Creating Users” the text discusses using the “registration_changeset” in the create action but the `User.changeset` is still shown in the example.

2015-10-28
15ERROR

You have:
connection
|> endpoint
|> router
|> pipelines
|> controller

and must be

connection
|> endpoint
|> pipelines
|> router
|> controller

2015-10-28
4642TYPO

sends them to the UsersController, calling the index action -> sends them to the UserController, calling the index action

—Deans Charbal

2015-10-28
90ERROR

Quotes are unbalanced so SQLi would fail in some instances(0). When you attack using SQL you tend to use SQL comments at the end of your attack so you don’t need to worry about such things. ie:

bob’; drop database production; —

Also, Bravo. I’m just inhaling this book it’s so well done thus far.

2015-12-22
78TYPO

“right after use Rumble.Web” should read “right after use Rumbl.Web”

2015-10-28
62TYPO

“How does Phoenix knows which data to show in the form?”
How does Phoenix know what data to show in the form?

2015-10-28
71ERROR

```
def create(conn, %{“user” => user_params}) do
changeset = User.changeset(%User{}, user_params)
case Repo.insert(changeset) do
#…
end
end
```
Should be `changeset = User.registration_changeset(%User{}, user_params) `

2015-10-28
15ERROR

Layers of Phoenix:

Diagram and textual description differ in ordering: (pipelines <-> router)
Diagram shows:
connection
|> endpoint
|> router
|> pipelines
|> controller

Text states: “A request comes into an endpoint. From there, a request goes into pipelines. As you might expect, a pipeline groups functions together to handle common tasks. You might have a pipeline for browser requests, and another for JSON requests. From there, requests go into our router layer, which directs a request into the appropriate controller.”

ie:
endpoint
|> pipelines
|> router
|> controller

2015-10-28
16SUGGEST

I found the paragraph in this page that says “If you’re a diehard MVC person…” to be a bit unclear. I came in as a “diehard MVC” person (or I prefer to think of myself as an open-minded person with more than passing familiarity with MVC). I didn’t quite understand how Phoenix’s approach to MVC differs from “diehard MVC”. I think the ambiguity comes with the phrase “we process data in the model”. What does it mean to “process data”? Perhaps an illustration that makes the different approaches more clear?

2015-10-31
18ERROR

When installing Phoenix, the command line which begins mix archive.install… has the first line in the “type this” purplish color, but the line below that (starts with download/v1.0.3) is a continuation of the command. I was thrown by the fact that the entire command was not in that purple color.

2015-10-28
41SUGGEST

We know that Jose likes Elixir :-) so his password in this page is: “<3<3elixir”
but on the previous page (40) he has only set it as “elixir”.

2015-10-28
27SUGGEST

In the ‘Jose says’: section.

I’m not sure I fully understand why the distinction Jose is making with the two. A code-reload of a ‘long running process’ still maintains its state so I don’t really see that as a difference per se. Am I missing something else?

2016-01-02Long running processes are not simply reloaded or maintain state. They need to be explicitly asked to reload both code and update its state. Reloading the code without going through those steps will actually kill the long running process because it will at some point get outdated. It is too early to make this distinction in the book though.
17ERROR

Quoted minimum erlang version is 17. Pretty sure I saw on the mailing list that 18 or 18.1 will be required on the next elixir bump.

Probably something you’ll double-check before you drop out of beta anyways but just wanted to notate it.

2015-12-3117 will still be compatible
44SUGGEST

I found the book around pages 43, 44, and 45 to be a particularly rocky road. I was trying to understand the method that I wrote from the view and how EEX worked when I was suddenly told that the template I just created was “so fast” because it is a function (?!?) and that it uses helpers which appear magically out of nowhere which I can call myself…

Having looked at Phoenix (and Rails) in the past I was able to keep up with the information here, but I really think a reader new to Phoenix and/or new to Elixir might really be baffled by so many different concepts all together in such a short span. It is my opinion that they’re a bit jumbled here. Some more explanation might help.

2016-01-02
45SUGGEST

With all due respect, It seems that this paragraph was written by someone for whom English was a second language (José?):

Phoenix.HTML is responsible for all HTML functionality, from generating links to working with forms. That module also introduces the notion of HTML safety: our applications are by default safe from cross-site scripting (XSS) attacks because Phoenix takes care of annotating only the markup it generates as safe. That’s why calling link returned a tuple, with the first element being the :safe atom.

The sentence structure and wording choices seem a bit out-of-order. I had to read the paragraph a couple of times to get the gist of it (of course… the problem might also be the fact that it’s rather late in the day here). I might reword it as follows:

Phoenix.HTML is responsible for the HTML functionality in views, from generating links, to working with forms. Reliance on this module is a big part of Phoenix’s approach to HTML safety. By default, applications are safe from cross-site scripting (XSS) attacks because care is taken to ensure that the markup generated by this module is safe. In fact, that’s why the link helper function returns a tuple. The first element of the tuple, the :safe atom indicates that the markup in the second element came from Phoenix.HTML and is known to be safe content.

2015-12-31
49SUGGEST

“Layouts also are just templates.”

If layouts are just templates, why is there a separate concept? Its not clear to me here.

After pouring over this section a couple of times, I think I see the distinction. Layouts are templates that are “owned” by controllers. They provide an common HTML structure for the controller, and the HTML generated by the actions (with their corresponding views) are substituted into that structure.

What I think is missing somewhere in the book up to this point, is a section describing the relationship between controllers, actions, layouts, and views from a conceptual level (with big square block diagrams and responsibilities for each block spelled out. perhaps right before the ‘building a controller’ section.

2015-12-31
51SUGGEST

The Understanding Ecto section sets of by describing Ecto in terms of other persistence frameworks (Hibernate, Active Record, LINQ) but doesn’t ACTUALLY tell you what Ecto is or why it exists.

To draw an analogy I might tell you that a Ukulele is a musical instrument that combines features of the Braguesa and the Rajão. Unless you already know what those two instruments are however describing the Ukulele in those terms is meaningless.

I would expand this section to talk about what problem Ecto is intended to solve, on it’s own terms, and then draw comparison to other libraries that solve those same problems for folks that are familiar with them.

2015-12-31
54SUGGEST

“Now that we have our Repo and our model configured, we need to add a migration to create our ”users" table with the schema for our User model."

What’s a migration? Why do I need one?

Even if you don’t want to explain it fully in this book, some handy pointers on where to learn more might be helpful.

2016-01-02
25TYPO

That means that this group of routes will attempt to match all routes beginning with /.

Redundant “that”?

2015-10-31
57SUGGEST

Adding the alias for Rumbl.User at this point (as the first code example in “Building Forms”) seems… weird. I think it should be in an extra step after we’ve completed this section, or done before the index + show actions were defined.

2015-10-28
52ERROR

When restoring the Ecto.Repo back to working order, there’s a step missing.

The user also needs to uncomment the line “worker(Rumbl.Repo, []),” in lib/rumbl.ex

2015-10-28
19TYPO

Part of the output from “mix phoenix.new hello” (from “We are all set!” to “iex -S mix phoenix.server”) has been set as normal text, rather than as monospaced command output. This makes it look like the instructions are in the book, rather than the mix output. The commands work (assuming you have postgres configured as it expects), but it makes things a bit confusing when, a couple of paragraphs later, it says “We’ll skip the instructions related to ecto since our application won’t use a database for now.”

2015-12-28
42ERROR

On page 41, the text indicates:

In the file
lib/rumbl.ex, comment out the line that starts the repository:

  1. Start the Ecto repository
  2. worker(Rumbl.Repo, []),

On page 42, when the text indicates:
If you point your browser to [http]://localhost:4000/users…

Expected: The error message described in the text
What happened: Received a different error message about ecto.
How to fix: CTRL-C, CTRL-C, re-run mix phoenix.server

If a change in /lib requires a full-reload, it would be helpful to indicate in the text.

Really enjoying the book so far, keep up the good work.

2015-10-28
77TYPO

“Specifically, we don’t want to users to access…”

It looks like the word “allow” is missing after the first “to”.

2015-10-31
82TYPO

<%= text_input f, :username, placeholder: “Name”, class: “form-control” %>

Placeholder should be “Username”

2015-10-28
31ERROR

“The browser pipeline accepts only HTML” – I might be wrong (and/or pedantic), but isn’t it more that it only services requests that have stated in their accepts header that they can handle HTML responses?

2015-12-22
9592ERROR

In the j_users query, “%j” is used in the ilike which won’t match any users. It’s probably meant to be ^“j%” whic also matches the [debug] line in the Repo.one query using j_users

2015-10-28
93ERROR

The pipe syntax query at the top of the page doesn’t match the [debug] output below it. The query includes “ilike(u.name, ^”c“)” and the debug output only has the j ILIKE

2015-12-22
96ERROR

On page 96 the process of moving authenticate/2 to authenticate_user/2 (and moving it from user_controller.ex to auth.ex) there is one piece missing that will cause a compile error when trying to do the “mix ecto.migrate” on page 98. The plug call in user_controller.ex needs to be updated from “plug :authenticate” to “plug :authenticate_user”

2015-12-29
54SUGGEST

After typing in the CreateUsers migration I was left with several questions.

What types of fields can I add in a database (besides :string)?
What does the null:false mean (presumably that the field cannot be empty)?

I wanted to go into iex and look up the documentation for “add” but I ran into two problems. First, I have no idea in what module the add macro is defined. Presumably it would go in Ecto.Migrations, so that’s where I started. The second problem I ran into, however was an error from iex

Ecto.Migration was not compiled with docs

Well shoot.

Perhaps a sidebar box somewhere that explains how to compile dependencies with documentation and some pointers about where to find more information on ‘add’ and friends?

2016-01-02
15ERROR

Section: The Layers of Phoenix
It seems to me that the diagram and the text do not match. Which goes after endpoint in the diagram, router or pipelines?

2015-10-28
19ERROR

It is never mentioned that we need a running PostgreSQL server

2015-12-28
27ERROR

There’s a figure that shows: connection |> endpoint |> router |> pipelines |> controller

The text that follows says: “A request comes into an endpoint. From there, a request goes into pipelines. … From there, requests go into our router layer.”

2015-10-28
2319ERROR

When first starting up hello world there isn’t any guide or mention of Postgres needing to be installed. A google led me to the docs which resolved the error.

The line `mix ecto.create` makes it blow up even though a database isn’t used yet. Perhaps passing the `—no-ecto` flag to phoenix.new would help resolve this?

(Mix) The database for Hello.Repo couldn’t be created, reason given: “psql: could not connect to server: Connection refused\
\\tIs the server running on host \\”localhost\\" (::1) and accepting\
\\tTCP/IP connections on port 5432?\
could not connect to server: Connection refused\
\\tIs the server running on host \\“localhost\\” (127.0.0.1) and accepting\
\\tTCP/IP connections on port 5432?\
".

2015-12-28
71ERROR

The example code intending to show how to use the registration_changeset doesn’t actually make use of the function.

I believe the line

changeset = User.changeset(%User{}, user_params)

should read

changeset = User.registration_changeset(%User{}, user_params)

2015-10-28
72SUGGEST

You say “In the endpoint module” with the assumption that people know where this is. I would recommend linking to it again, probably in brackets:

“In the endpoint module (lib/rumbl/endpoint.ex)…”

2015-12-29
71TYPO

Under “Creating Users”, the code example doesn’t actually use the new “registration_changeset” function (like the text says), but incorrectly still uses the original “changeset” function.

(Great book so far!)

2015-10-28
84SUGGEST

The explanation of the new layout code (handling logged in/logged out users) could be clearer… It says “If you’re watching closely, you can see that this link uses the session_path twice”, but there isn’t a link that uses session_path twice; then, “Breaking it down” uses a link with the text “signout”, which isn’t one of the links given.

2015-12-29
42SUGGEST

Ecto.Queryable not implemented for Rumbl.User

Similar to another comment for page 41 this is the error that you get if you do not restart the server after making the changes to repo and UserController.

2015-10-28
43TYPO

we’ll create user directory

should be

we’ll create the user directory

2015-10-28
93ERROR

from(u in User,
where: fragment(“lower(username) = ?”,
^String.downcase(uname)))

I think you mean from(u in User,
where: fragment(“lower(username) = ?”,
^String.downcase(u.name)))

2015-10-28
114TYPO

The second shell command on the page should be
`mix run priv/repo/seeds.exs` to simulate what the paragraph is saying, which is to run the previous command twice.

2015-12-29
111ERROR

In the Video controller update action, you reference a watch path. That should be video_path

|> redirect(to: watch_path(conn, :show, video))
vs
|> redirect(to: video_path(conn, :show, video))

2015-12-22
113ERROR

Before we move on, let’s check to see what happens
if someone runs the seeds file twice:
$ mix phoenix.gen.model Category categories name:string

(Ecto.ConstraintError) constraint error when attempting to insert model:
* unique: categories_name_index

The code should be
$ mix run priv/repo/seeds.exs
Since you’re running the seed file twice

2015-12-22
114TYPO

First long paragraph has the typo “databased” at the end of the second line.

Should read “database”

2015-10-31
118ERROR

You need to import Ecto.Query in order for this to work

video = Repo.one(from v in Video, limit: 1)

2015-10-28
44SUGGEST

Should it be mentioned that in order to use the link helper in iex, iex should be started with iex -S mix instead of only iex?

iex(1)> Phoenix.HTML.Link.link(“Home”, to: “/”)

(UndefinedFunctionError) undefined function: Phoenix.HTML.Link.link/2 (module Phoenix.HTML.Link is not available)
Phoenix.HTML.Link.link(“Home”, [to: “/”]

2015-10-28
80ERROR

When talking about the routes that get generated with `resources “/sessions”, etc.` it mentions the “GET new_session” URL being created - this is incorrect, the URL generated is “sessions/new”. (verified with mix phoenix.routes)

2015-10-28
80ERROR

Following on from previous erratum, `POST session` and `DELETE session/:id` are also incorrect - they should be `sessions, `plural.

2015-10-28
82SUGGEST

I think it’s worth pointing out here that this authentication code won’t work for any of the sample José, Chris or Bruce users - their password hashes are still the old plaintext values. You’ll get something like the following:

Request: POST /sessions

(exit) an exception was raised:
(MatchError) no match of right hand side value: [‘<3<3elixir’]
(comeonin) lib/comeonin/bcrypt.ex:113: Comeonin.Bcrypt.hashpw/2

2015-12-29
59SUGGEST

The book doesn’t contain functionality for editing or deleting users, so should we be adding routes for those actions with `resources “/users”, UserController` ?

2015-10-28
101ERROR

In the summary:
“We created a video model and used it to issue some queries across two models.”

The chapter doesn’t cover this - we generate the video scaffold, hook it up in the router, then look through the generated code. We don’t actually do any manual querying with it at all.

2015-12-22
108TYPO

When returning a tuple of users and videos from Ecto:

[{%Rumble.User{…}, %Rumbl.Video{…}}]

Typo - Rumble should be Rumbl

2015-12-22
107ERROR

Inserting a video with just a title of “hi” leads to errors down the track. By default, the model requires a url, title and description, and this skips those validations and inserts invalid data into the database.

Problem only comes to light later on on page 118 when it comes time to update the first video (which turns out to be this “hi” video) with a category ID - it doesn’t return {:ok, %Rumbl.Video{…}}, it returns {:error, %Ecto.Changeset{…}}.

2016-01-02
17SUGGEST

Maybe it would be cute to continue the piping paradigm for the installation of the framework and its dependencies:

machine
|> Erlang (~> 17)
|> Elixir
|> Phoenix

(or even one-line it: machine |> Erlang |> Elixir |> Phoenix and use it as the title), especially since the section titles are a bit inconsistent in breaking the flow: “Elixir needs Erlang” followed by “Phoenix needs Elixir”.

2015-12-22
1915TYPO

I think the Layers Of Phoenix figure has the position of the router and pipelines functions reversed? Based on the text, it seems like the connection would be passed to pipelines first, then router.

2015-10-28
71ERROR

Listing for user_controller.ex should use the new function registration_changeset like indicated in the lead in. However it still uses the old changeset function.

2015-10-28
120ERROR

In the set of iex commands:

changeset = foreign_key_constraint(changeset, …)

won’t work without first “import Ecto.Changeset” as foreign_key_constraint/3 will be undefined in iex

2015-10-28
74TYPO

It should be “User.registration_changeset” instead of User.changeset inside code example “authentication/rumbl/web/controllers/user_controller.ex”

2015-10-28
82ERROR

Because the users created earlier in the book have their password_hash set to plaintext passwords, I dropped my Ecto tables (mix ecto drop) and created a seeds file to recreate those users:

alias Rumbl.Repo
alias Rumbl.User

Repo.insert!(User.registration_changeset(User{},{name: “Jose”, username: “josevalim”, password: “<3<3elixir”}))
Repo.insert!(User.registration_changeset(User{},{name: “Bruce”, username: “redrapids”, password: “7langs”}))
Repo.insert!(User.registration_changeset(User{},{name: “Chris”, username: “chrismccord”, password: “PheonixRocks”}))

Upon running the seeds file I was able to login as the test users.

2015-12-29
82ERROR

Using the code as printed in the book, I got a deprecation warning:

the :name option in form_for/inputs_for is deprecated, please use :as instead
(phoenix_html) lib/phoenix_html/form_data.ex:47: Phoenix.HTML.FormData.Plug.Conn.to_form/2
(phoenix_html) lib/phoenix_html/form.ex:226: Phoenix.HTML.Form.form_for/4
(rumbl) web/templates/session/new.html.eex:2: Rumbl.SessionView.“new.html”/1
(phoenix) lib/phoenix/view.ex:197: Phoenix.View.render_within/3

I changed the “form_for” line to read:

<%= form_for conn, session_path(conn, :create), [as: :session], fn f -> %>

(note change from [name: :session] to [as: :session])

and the deprecation warning went away.

2015-10-28
89SUGGEST

The figure that shows “how it all fits together.” is not helping me.

I am so used to an MVC model where the “Model” and the Repo are very closely tied together that seeing them separated is somewhat confusing.

I’m not sure what the “1, 2, 3” labels on the diagram are meant to show, nor is it clear what the colors and shapes are meant to convey.

Perhaps another diagram showing how the “traditional” MVC mechanism works and THEN a comparison showing how the Phoenix model of MVC is different would help?

Is it the model/repo that is the break between the pure and impure? (so the model and repo are together in traditional MVC, but in Phoenix’s MVC the repo become part of the controller because it is impure and has DB side effects)?

I know this diagram and associated discussion are meant to convey something important but I fear I may just not be getting it :-)

2015-12-31
89SUGGEST

Also in the diagram on Page 89 there is a circle labelled “Mail”. What is that? Where did it come from?

2015-12-31
17TYPO

CHANGE:
In Elixir, We might
TO:
In Elixir, we might

2015-10-31
120ERROR

This line:
iex> Repo.delete category

Won’t work, since category was built without the foreign_key_constraint.

Should be:
iex> Repo.delete changeset

2016-02-14José? How would you prefer us to address this one?
89ERROR

Several times throughout the book it is mentioned that the model functions should be pure.

However in the authentication chapter, the encrypted_password (generated by the comeonin library) is put into a changeset:

put_change(changeset, :password_hash, Comeonin.Bcrypt.hashpwsalt(pass))

There are two issues with this:

1) Using an external library in a changeset when we don’t know if the function is pure
2) The hashpwsalt function will generate a new password each time - so the model function is not pure.

The solutions are either:

* Generate the encrypted password somewhere where impure functions are encouraged (controller?)
* Add a note so that the reader is aware of the issue
* Add a function to ecto for example `put_change_fn(changeset, :password_hash, fn ->> Comeonin.Bcrypt.hashpwsalt(pass) end)

2016-01-01
61SUGGEST

After making the changes in the controller, model and view, if we try to go to:

localhost:4000/users/new
we will get an error unless we stop and start the server. Should that be mentioned?

error] #PID<0.354.0> running Rumbl.Endpoint terminated
Server: localhost:4000 (http)
Request: GET /users/new

(exit) an exception was raised:
(Ecto.CastError) deps/ecto/lib/ecto/repo/queryable.ex:188: value `“new”` in `where` cannot be cast to type :id in query:

2015-10-28
65ERROR

Would it be a good idea to also include delete and update for the user?

2015-10-28
19SUGGEST

When starting up the hello app on Windows for the first time I am prompted with ‘Could not find “rebar”, which is needed to build dependency :fs I can install a local copy which is just used by Mix Shall I install rebar? [Yn]’ There isn’t any indication from the text in the book that this might happen. I responded with Y and installed it and it appears to be working but it was a little surprising to see this without being prepared by the text.

2015-12-28
68SUGGEST

comeonin is now on version 1.2

defp deps do
[…,
{:comeonin, “~> 1.2”}]
end

2015-10-28
15TYPO

In the Layers of Phoenix section you go over the layers of a request and mention that pipelines come before the router. In the diagram it is the other way around. “A request comes into an
endpoint. From there, a request goes into pipelines. As you might expect, a
pipeline groups functions together to handle common tasks. You might have
a pipeline for browser requests, and another for JSON requests. From there,
requests go into our router layer, which directs a request into the appropriate
controller.”

2015-10-28
15TYPO

In the blue box of functions you have endpoint |> router |> pipelines |> controller but the text directly underneath says endpoint |> pipelines |> router |> controller.

2015-10-28
74ERROR

My user_controller.ex file matches the linked one but it doesn’t validate password length and it’s not saving anything in the password_hash column of the database. There’s no mention of the registration_changeset in the code example or the linked controller file. I tried replacing the first mention of the User.changeset with User.registration_changeset and got an exception. I’m running Elixir 1.0.5 and Phoenix 1.0.3 and Erlang/OTP 18 [erts-7.0.3] [source] [64-bit].

2015-10-28
76SUGGEST

Should
word.fetch! will raise if the given key does not exist,…

be
word.fetch! will raise an error if the given key does not exist,…

2015-12-22
46TYPO

Filename of the first code snippet on the page `controllers_views_templates/listings/rumbl/web/controllers/user_controller.change1.ex` should not contain the term `change1` ?

That confused me for a bit, as I thought it was some kind of weird shorcut name for the file.

2015-10-28
90SUGGEST

In the “Dangerous, insecure code!” section, the `table_name` variable doesn’t seem to be used.

2015-12-22
8790TYPO

“We’ve have fetched data…” should be “We have fetched data…” or “We’ve fetched data…”

2015-10-31
15ERROR

The order in the pipeline “diagram” for “The Layers of Phoenix” isn’t the same as described in the text below (endpoint -> router -> pipelines vs. endpoint -> pipelines -> router).

2015-10-28
119ERROR

In the ‘Validating Foreign Keys’ section, inside ‘iex’, the user has to first ‘import Ecto.Query’ or the ‘video = Repo.one(from v in Video, limit: 1’ line will return ‘undefined function from/2’.

2015-10-28
32TYPO

In the sentence, “All of the routes after pipe_through :browser, which is all…” the word pipe_through is not in the monospace teletype font like the word :browser is even though this is a reference to a line in the previous code listing.

2015-10-28
114TYPO

After running priv/repo/seeds.exs, we run it again to watch it fail. The command given this second time is incorrect. Rather than just running `mix run priv/repo/seeds.exs` again, it says to run `mix phoenix.gen.model Category categories name:string`. (This happens on ~ line 6).

2015-12-22
117ERROR

In order for the sample code in iex to work, you need to also import Ecto.Query. Otherwise, the line(from the next page) `Repo.one(from v in Video, limit: 1)` throws this error: ‘protocol Enumerable not implemented for Rumbl.Video’

2015-12-22
81ERROR

In Rumbl.Auth.login_by_username_and_pass, the third clause of the cond statement (true -> …) seems like it’ll never happen, since “user -> …” is a catch-all, no? Nor should it, probably; we better not reveal whether a username exists or not, to someone trying to log in.

2015-12-22`user` is a variable that we bound a few lines up, so it's not like case where it serves as a catch-all match. thanks for the report!
81SUGGEST

In Rumbl.Auth.login_by_username_and_pass, as a general good practice, one might
make use of dummy_checkpw() for nonexistent users in order to avoid user
enumeration by timing attacks:

import Comeonin.Bcrypt, only: [checkpw: 2, dummy_checkpw: 0]

# …

def login_by_username_and_pass(conn, username, given_pass, opts) do
repo = Keyword.fetch!(opts, :repo)
user = repo.get_by(Rumbl.User, username: username)

case verify_password(given_pass, user) do
true ->
{:ok, login(conn, user)}
false ->
{:error, :unauthorized, conn}
end
end

def verify_password(given_pass, %Rumbl.User{password_hash: pw_hash}) do
checkpw(given_pass, pw_hash)
end
def verify_password(_given_pass, nil) do
dummy_checkpw()
end

2015-12-29You are completely right. dummy_checkpw() will be in next beta. Thanks!
127TYPO

“Tight your seatbelt” – isn’t the usual phrase “tighten your seatbelt”?

2015-10-31
127TYPO

“… we’ll create a new controller explicitly for watching videos, along with it’s view and template” – should be “its template”.

2015-12-22
75ERROR

In Ch. 5, the first code example has an error I believe. The code for file “authentication/rumbl/web/controllers/user_controller.ex” has the following:

```
changeset = User.changeset(%User{}, user_params)
```

Should be:

```
changeset = User.changeset(%User.registration_changeset(%User{}, params)
```

With the way it’s currently written, no changes are made to “user_controller.ex” and the passwords are not encrypted.

2015-10-28
125TYPO

Then, we’ll create a new controller explicitly for watching videos, along with it’s view and template.

“it’s” should be “its”.

2015-10-31
80SUGGEST

Shouldn’t the following:

changeset = User.registration_changeset(%User{}, user_params)

be

changeset = User.changeset(%User{}, user_params)

2015-10-28
131TYPO

“Next, we implement a `onYouTubeReady` function” – the function seems to be actually called `onIframeReady`.

2015-10-28
133ERROR

We generate a migration but there’s no reference to telling the user to run it. Should probably do that after filling in the contents, or adding the slug field to the model.

2015-12-29
6764ERROR

In the code listing for `echo/listings/rumbl/web/templates/user/new.change1.html.eex` file, there is a missing `<% end %>` that should close out the anonymous method passed to form_for as the last parameter.

2015-12-29
80SUGGEST

Regarding my previous suggestion for the same page (above?), the User. registration_changeset is actually the correct one, as otherwise the password_hash is not saved and there is an error when trying to use sessions/new.

2015-10-28
81TYPO

Typo: “Then, we call a as-yet undefined helper…”
Fix: “Then, we call an as-yet undefined helper…”

2015-10-31
61TYPO

“If we inspect the HTML that was generated by visiting https[link] in our browser, we’ll see the following markup” - why is this https?

(link omitted because ‘hyperlinks are not allowed in errata’)

2015-10-28
87TYPO

what query compositions looks like
should be
what query compositions look like

2015-10-31
133SUGGEST

Video.changeset suddenly uses assoc_constraint instead of foreign_key_constraint; it would probably be helpful to avoid confusion if one or the other was used consistently, OR the change was explained to be for some particular reason.

2015-12-29
75ERROR

Line 2 of the source listing is changeset = User.changeset(%User{}, user_params)

Surely this should be changeset = User.registration_changeset(%User{}, user_params) to actually use the new function.

2015-10-28
133SUGGEST

The form of combined ‘if’ conditional and assignment used in Video.slugify_title doesn’t (to me at least) feel very explicit; the assignment in ‘if’ can easily be mistaken for a comparison, and it’s less clear what the actual return value is when it doesn’t find a :title in the changeset. I much prefer using ‘case’, as below:

case get_change(changeset, :title) do
nil -> changeset
title -> put_change(changeset, :slug, slugify(title))
end

2015-12-22
90SUGGEST

If we are following from previous examples that we have used the alias for the Repo.User, shouldn’t we use:
from u in User
instead of:
from u in Rumbl.User

2015-12-22They won't always be aliased in the User's session, so we either show explicit aliasing, or we use the full aliases. Thanks!
133DEFER

Perhaps the migration should also include handling existing data, to make sure we get slugs generated? Something like:

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

def up do
alter table(:videos) do
add :slug, :string
end

execute(“update videos set slug = regexp_replace(lower(title), ‘[^\\\\w]+’, ’’)”)
end

def down do
alter table(:videos) do
remove :slug
end
end
end

2016-04-05
39ERROR

Not sure about page, it’s from reader software. The paragraph is called “The Layers Of Phoenix” and it shows a pipeline of a request, however the text description swaps the order, from endpoint |> router |> pipelines to endpoint |> pipelines |> router

2015-10-28
115TYPO

Typo: “Ecto allows developers to enjoy many of the guarantees database offers in terms of data integrity”

Should be (if referring to databases in general): “Ecto allows developers to enjoy many of the guarantees databases offer in terms of data integrity”.
Should be (if referring to a database in general): “Ecto allows developers to enjoy many of the guarantees a database offers in terms of data integrity”.

2015-10-31
147ERROR

In the paragraph:

“And we’re all set. Head over to your browser and visit localhost:4000/manage/videos. You’ll see an empty list of videos:”

the extra path element, “manage”, is wrong and should be removed. The paragraph should then read:

“And we’re all set. Head over to your browser and visit localhost:4000/videos. You’ll see an empty list of videos:”

I had to remove the “https…” in the above expressions because of your spam prevention.

btw, really enjoying the book; you present Phoenix in an eye-opening way. I’m beginning to suspect that Phoenix is a viable alternative for Rails for ALL web work, not just complex projects.

2015-12-22
117ERROR

You have a missing “iex -S mix” at the beginning of the “Validating Foreign Keys” section as follows:

In addition to the steps here:

alias Rumbl.Category
alias Rumbl.Video
alias Rumbl.Repo

you also need to do

import Ecto.Query

If this is omitted, the the following instruction you suggest:

video = Repo.one(from v in Video, limit: 1)

fails w/ the error

(CompileError) iex:5: undefined function from/2

HTH

2015-12-22
98ERROR

In the paragraph:

“And we’re all set. Head over to your browser and visit localhost:4000/manage/videos. You’ll see an empty list of videos:”

the extra path element, “manage”, is wrong and should be removed. The paragraph should then read:

“And we’re all set. Head over to your browser and visit localhost:4000/videos. You’ll see an empty list of videos:”

I had to remove the “https…” in the above expressions because of your spam prevention.

btw, really enjoying the book; you present Phoenix in an eye-opening way. I’m beginning to suspect that Phoenix is a viable alternative for Rails for ALL web work, not just complex projects.—Scott Smith

2015-12-22"/manage" prefix is correct as we use it in the router scope, `scope "/manage"`. Thanks!
118ERROR

In the definition of the function “load_categories”, the expression

Repo.all from(c in Rumbl.Category,
order by: c.name,
select: {c.name, c.id})

must not have the open and close parenthesis; i.e. should read

Repo.all from c in Rumbl.Category,
order by: c.name,
select: {c.name, c.id}

Otherwise when you do “mix phoenix.server” before starting the Delete section on page 119, the console will complain:

Compilation error on file web/controllers/video_controller.ex

(SyntaxError) web/controllers/video_controller.ex:84: unexpected parentheses. If you are making a function call, do not insert spaces between the function name and the opening parentheses. Syntax error before: ‘(’

2015-12-22
70ERROR

“Then, we use Comeonin to encrypt our password”.

You do hash the password, not encrypt it.

Search for 4948322 on stackoverflow for a description of the difference.

2015-10-28
71ERROR

`changeset = User.changeset(%User{}, user_params)`

Should probably be:

`changeset = User.registration_changeset(%User{}, user_params)`

Though I notice that you do things differently in the dowloadable source code. There it’s:

`changeset = User.changeset(%User{}, :registration, user_params)`

2015-10-28
71ERROR

The paragraphs says:

“The create action in the user controller must now use our new registration_changeset, like this:”

and the code is:

changeset = User.changeset(%User{}, user_params)

but according to what is written, should’t the code be like ?

changeset = User.registration_changeset(%User{}, user_params)

2015-10-28
110SUGGEST

Should we leave an empty line on the show action between getting the video and rendering the view as in the above example for index?

2015-12-22No new line/space. Thanks!
111TYPO

create an unique index for it
should be
create a unique index fort it

2015-10-31
117TYPO

at the end of this page, I think it’s missing import Ecto.Query . Or video = Repo.one(from v in Video, limit: 1) will raise (CompileError) iex:5: undefined function from/1

2016-01-01Sounds like you were on an older version of phoenix than (1.0.4) we import from/1 and from/2 in web.ex :controller for you
81SUGGEST

The text

“That create action picks of the inbound arguments for username as user and password as pass”

is a little confusing. Should it perhaps read

“That create action picks off the inbound arguments for username as user and password as pass”

2015-10-31
81TYPO

In the text “…inbound arguments for username as user and password as pass.” The words username, user, and password are all in the monospace type used for code but the word “pass” is not. The word “pass” should be monospace type to maintain consistency.

2015-12-29
101TYPO

The sentence that reads “Finally, we created categories for our videos.” is incorrect. We didn’t create any categories for videos in this chapter.

2015-12-22
130TYPO

we’ll know the exactly when an annotation is added.
should be
we’ll know exactly when an annotation is added.

2015-12-22
15ERROR

Pipeline diagram says

connection
|> endpoint
|> router
|> pipelines
|> controller

but text below suggest

connection
|> endpoint
|> pipelines
|> router
|> controller

2015-12-28
69ERROR

In the listing for new.change1.html.eex, you’re missing a <% end %> (for the if)

2015-12-29
83ERROR

the authenticate function needs to be modified to take a second parameter (_options?) in order to operate as a function plug.

2015-12-22
268TYPO

In user_socket.ex, “Repo.get!(Rumbl.User, user_id)” should be “Rumbl.Repo.get!(Rumbl.User, user_id)”

and ‘def id(socket), do: “users_socket:” <> socket.assigns.user_id’ should be ‘def id(socket), do: “users_socket:#{socket.assigns.current_user.id}”’

2015-12-30
273TYPO

“…Phoenix provides a 3-tuple join signature to both join the channel and sent a join response at the same time.”

should be:

“…Phoenix provides a 3-tuple join signature to both join the channel and send a join response at the same time.”

2015-12-22
279ERROR

“Finally, we wired up a data-seek attribute on our renderAnnotation template, but we haven’t done anything with it yet.”

This was never wired up in any previous sep - need to change renderAnnotation to:

let template = document.createElement(“div”)
template.setAttribute(‘data-seek’, at)
template.innerHTML = `[${this.formatTime(at)}] ${user.username}: ${body}`

2015-12-30
145ERROR

Line numbering in the JavaScript code listing makes it impossible to copy-paste in a sane way; the line numbers and dashes are copied together with the code.

2015-12-22Unfortunately there is no sane way to copy from the pdf. Instead we include links to the online sources above code snippets where you can copy appropriately. Thanks!
157ERROR

user = Repo.get!(Rumbl.User, user_id)

should be:

user = Rumbl.Repo.get!(Rumbl.User, user_id)

2015-12-30
157ERROR

The last line of user_socket.ex has a couple of problems.

def id(socket), do: “users_socket:” <> socket.assigns.user_id

1. The user_id is an Integer, which is not a valid argument to <>.

2. We never assigned the user_id in the first place. It should be:

def id(socket), do: “users_socket:%{socket.assigns.current_user.id}”

2015-12-22
96ERROR

in this example:

iex> j_users = from u in users_count, where: ilike(u.name, “%j”) #Ecto.Query<from u in Rumbl.User, where: ilike(u.name,“%j”), select: count(“*”)>

you have the ilike string backwards. It should be “j%”, not %j

2015-12-22
157ERROR

To get the code working I had to change two lines in connect in user_socket.ex to this:
user = Rumbl.Repo.get!(Rumbl.User, user_id)
{:ok, assign(socket, :user_id, Integer.to_string(user.id))}
(the lines are the :ok case).

I got error when trying to store user in assigns, so changed to user_id.

2015-12-30
78TYPO

There’s a missing line of code, right after:

> We need only add an options variable, which we’ll ignore

I believe the missing line should add `_opts` as the second argument to `authenticate`:

` defp authenticate(conn, _opts) do `

2015-12-22
76TYPO

In the 2nd paragraph after the code snippet, the following sentence sounds odd:

If one exists, we lookup it up and assign the result in the connection.

Maybe “lookup” should just be “look”. For example:

If one exists, we look it up and assign the result in the connection.

2015-12-29
78TYPO

Missing code snippet after this sentence:

We can then change our index action back to it’s previous state, like this:

2015-12-22
78TYPO

“We need only add an options variable, which we’ll ignore:..” is missing the appropriate code text. Similarly “…We can then change our index action back to it’s previous state, like this:…” is missing the code suggestion. I also could not find the appropriate code changes in the source code, yet I am receiving an error message.

2015-12-22
79TYPO

Missing code snippet after the following sentence:

Let’s go back to the Rumbl.UserController.create action and change it to call the login function after we insert the user in the database:

2015-12-22
78ERROR

In this part, there is missing code

we can satisfy that contract. We need only add an options variable, which
we’ll ignore:

**Missing code in this place

Needs something like this:

defp authenticate(conn, _opts) do

Now we can plug it in our controller, right after use Rumbl.Web:
plug :authenticate when action in [:index, :show]

2015-12-22
162SUGGEST

I ran into an error where the scheduleMessages function would fail when it called Player.getCurrentTime() because the underlying implementation called this.player.getCurrentTime() on the iframe. That function wasn’t defined for some reason because i was running adblock— even though there were no ads on the video I was embedding. If you see an unrelated console error about not being able to connect to doubleclick.net, it’s actually related! Anyway, might want to warn the reader about adblock in the context of embedding videos. I hadn’t run into that particular trouble with the youtube player before.

2016-01-01
17ERROR

Footnote 3 has an extra http: at the beginning.

2015-12-28
19ERROR

The formatting makes it look like the standard output of “mix phoenix.new” is actually body text of the book. (Formatting error existed back to beta 1 version of the book.) At the top of the page the “$ mix phoenix.new hello” line is in color, indicating something I should type. However, all the text down through “$ iex -S mix phoenix.server” should be indented, black and white, and in a monospaced font, indicating that it is all output from the one command.

2015-12-28
146TYPO

Repetition of “Said another way”:

“Said another way, for each request, a new conn would flow through all of the pipelines and then die.”

and then a couple of lines later:

Said another way, your socket is your ongoing conversation.

2015-12-29
156TYPO

Unclear about what the second part of this sentence means: “Just before our app.js script, we render a script tag that attaches a userToken variable to the window from our conn.assigns, along with the entire Plug.conn struct.”

window.userToken = “<%= assigns[:user_token] %>”

We only seem to be doing the first part?

2016-02-14
156TYPO

Based on the code that follows this paragraph, “socket.assigns” should probably be “conn.assigns”:

Next, we need to add the :user_token to socket.assigns whenever we have a current user. We already have code to assign the current user in Rumbl.Auth, so let’s handle this there:

2015-12-22
156TYPO

Shouldn’t “token = Phoenix.Token.sign(conn, ”user socket“, user.id)” be “token = Phoenix.Token.sign(conn, ”user.socket“, user.id)” (missing dot between user and socket)?

2015-12-22it takes a string, so "user socket" is fine in this case. Thanks!
29ERROR

When referencing the config/config.exs the actual config code that is shown differs from what is generated in version 1.0.3 of Phoenix.

Old code:
config :hello, Hello.Endpoint,
url: [host: “localhost”],
root: Path.expand(“..”, DIR),
secret_key_base: “QNU… …Oo/eLnw”,
debug_errors: false,
pubsub: [name: Hello.PubSub,
adapter: Phoenix.PubSub.PG2]

New Code:
config :hello, Hello.Endpoint,
url: [host: “localhost”],
root: Path.dirname(DIR),
secret_key_base: “7ol…10/tNtvx”,
render_errors: [accepts: ~w(html json)],
pubsub: [name: Hello.PubSub,
adapter: Phoenix.PubSub.PG2]

2015-12-28
127SUGGEST

The youtu.be regex has a few issues. It’s not core to the book of course, but still, it could distract the reader if they notice the same things I did. This also simplifies the regex, giving the reader a bit less to process.

Some thoughts:

- could escape the period in the URL so it doesn’t match any character (good habit)

- good idea to use + instead of * if you expect the ID to be non-empty

- could use something like ~“” to avoid having to escape a bunch of slashes

- no need to escape & and ? within the square brackets
- no need to anchor/bound the regex with “^.*” before or “.*” after, given current usage

Before:

~r/^../

After:

~r“(?:youtu\\.be/|v/|e/|u/\\w+/|embed/|v=)(?[^#&?]+)”

2015-11-18
92ERROR

^“%j” matches only users ending in “j”
^“j” would match users with “j” in their name, as intended

2015-12-22
162TYPO

In the description below the code, the paragraph about `renderAtTime` (talking about `messagesToRender`, `this.messageQueue` and using map) doesn’t seem to relate to the actual code, which uses a simple filter.

2016-01-01
94ERROR

iex> name = 123

iex> Repo.all(from u in Rumbl.User, where: u.name ^name) ** (Ecto.CastError) iex:4: value `123` in `where` cannot be cast to type :string in query: from u in Rumbl.User, where: u.name ^123

Should we not be interpolating name i.e. ^name instead of ^123 here to show that an integer will fail since the query expects a string.

2015-12-22We are interpolating `name`, but Elixir knows it is a literal `123`, so that's how it is injected in the query. Thanks!
24SUGGEST

“External data is unsafe, so we explicitly match on the string keys”

Would be good to clarify how strings/atoms relate to safety. I assume it is something like atoms not being GCed? Would be good to be explicit.

2015-12-28
176ERROR

“making sure sensitive fields like role cannot be set externally”

Reference to “role” when we haven’t talked about it yet. I’m assuming this is a reference to something introduced later in the book.

2015-12-30
146SUGGEST

I think it would also be good to mention what happens in the browser console after making the changes in app.change1.js. For example, in Firefox I get ‘Unable to join Object { reason=“unmatched topic”}’. I presume this is because we haven’t created the socket named “video:#{videoId}” on the server yet. Just worth a mention I think.

2015-12-29
147TYPO

The generated Rumbl.UserSocket file is different in our apps - lots of commenting (which I can assume was left out for brevity), but the :longpoll transport was also commented out.

2015-12-22The comments were left out for brevity, but I will fix the long poll comment. We leave it disabled by default since several versions ago. Thanks!
144SUGGEST

In the “Chris Says Why ES6” box, I think it would be great to have links to some resources on ES6, eg. detailing new features, syntax changes, etc. As someone reasonably familiar with “normal” JavaScript, a lot of this ES6 code looks completely foreign.

2015-12-29
148TYPO

In first paragraph under “Creating the Channel”, the topic has an identifier of something like “video:#{video_id}”, not “video:video_id”. Gotta put the actual video ID in there.

2015-12-29
155ERROR

Even without forwarding the raw payload, we have a large security hole - the current implementation does not escape HTML submitted by the user.

2015-12-29
158ERROR

You miss instructing the user to actually run the migration created by generating the Annotation model.

2015-12-30
158TYPO

“so add the has_many relationship to both your User and Video schema blocks in web/models/annotation.ex and web/models/user.ex”

should this be “web/models/user.ex and web/models/video.ex” ?

2015-12-30
162ERROR

Similar to @musoumusic I was also getting a problem with this.player.getCurrentTime not being a function. I presume this is a timing thing and is being called before the player gets properly loaded - if I change the setTimeout call to 2000 instead of 1000 it appears to load correctly (but then of course you only get updates every two seconds). I think this code needs to be reworked a bit to only try things when the player is loaded.

2016-01-01
163ERROR

The JavaScript code given to jump to the exact time the annotation was made only works if you don’t click on the displayed username - the username is in a `b` tag and therefore doesn’t have the `data-seek` attribute set.

2016-01-01
82TYPO

Last paragraph says “will always have the value of in the field”, which would seem to be missing something between “of” and “in”.

2015-12-29
157ERROR

in “channels/listings/rumbl/web/channels/user_socket.change2.ex”

This line fails:
user = Repo.get!(Rumbl.User, user_id)

Because in the current “user_socket.ex” file, `Repo` was not imported.

Unless I missed something in the current version book, there is no import/use/alias for `Rumbl.Repo`

I wonder if `use Rumbl.Web, :channel` should be added on the top of the file?

2015-12-30
99TYPO

in the “Examining Our Changes to Generated Code” section, second paragraph, first line, the code says `belongs_to :user, Rumble.User`.

Using Rumble instead of Rumbl

2015-12-22
114ERROR

"We set up some aliases and then traverse a list of category names, writing
them to the database. Let’s run the seeds file with mix run:

$ mix run priv/repo/seeds.exs

Presto! We have categories. Before we move on, let’s check to see what happens
if someone runs the seeds file twice:

$ mix phoenix.gen.model Category categories name:string

(Ecto.ConstraintError) constraint error when attempting to insert
model:

  • unique: categories_name_index"

The second command is not the seed command, but the output does show what the seed command produces if you run it a second time.

2015-12-22
154TYPO

Look a the function head.
should be
Look at the function head.

2015-12-22
78TYPO

In the first paragraph, after “We need only add an options variable, which we’ll ignore:” there should be example but it’s missing in my version of the ebook (PDF).

2015-12-22
85SUGGEST

When implementing log out functionality I was trying to figure out why my flash message wasn’t showing up after logging out. I figured out that `configure_session(conn, drop: true)` has to be changed to `delete_session(conn, :user_id)` if you want the flash message to appear. I think this could be made more clear.

2016-02-15Chris, what do you think? Do you think we need to make a prose change?
100TYPO

Under the wrapping up section of Chapter 6 Ecto Queries, it states:

“In this chapter, we started with some Ecto queries in the console. We then generated a Video model with a relationship to User. Finally, we created categories for our videos. Along the way, we gained some insights.”

The sentence about creating categories for our videos is incorrect. That was never covered in the material in chapter 6.

2015-12-22
101TYPO

The final bullet under the Wrapping Up section of chapter 6, it says:

“We created a video model and used it to issue some queries across two models.”

This is incorrect. No such exercise was conducted in this chapter.

2015-12-22
60SUGGEST

Not the highest of priorities, but I know you can do better than this: “With our database ready, we can begin to use our database.”

;)

2015-12-29
5853TYPO

Second paragraph of “Defining the User Schema”, second sentence:
“The schema and fields macros”
It should be:
“The schema and field macros”
(Non-pluralised “field” macro name.)

2015-12-28
92TYPO

In Chapter 6, while exploring the Ecto’s query interface there is an example showing query composition. The `j_users` query seems to have a typo:

~
j_users = from u ​in​ users_count, ​where:​ ilike(u.name, ^​“​​%j”​)
~

this will return all users with names ending in ‘j’.

I think that the correct query should be:

~
j_users = from u ​in​ users_count, ​where:​ ilike(u.name, ^​“​​j%”​)
~

2015-12-22
77ERROR

On the code snippet user_controller.change2.ex there is one parameter missing that will result in an error (Elixir says there isn’t a defined function authenticate of arity 2). In the code for the next chapter this is corrected passing a second parameter _opts.

2015-12-22
159ERROR

When I try to add a new annotation after we have added persistence for them I get the error:

(Ecto.ChangeError) value `“2”` for `Rumbl.Annotation.video_id` in `insert` does not match type :id

The id is being passed as a string instead of an integer, which it expects for type :id.

I fixed it by changing our join function in “video_channel.ex”

FROM:

def join(“videos:” <> video_id, _params, socket) do
{:ok, assign(socket, :video_id, video_id)}
end

TO:

def join(“videos:” <> video_id, _params, socket) do
case Integer.parse(video_id) do
{int, _} ->
{:ok, assign(socket, :video_id, int)}

_ ->
{:error}
end
end

2015-12-30
161TYPO

“We composed a response by rendering an annotation.html view for every annotation in our list. Instead of building the list by hand”

Should say “annotation.json” rather than “annotation.html”

2015-12-30
162ERROR

Getting the error to the console:

* this.player.getCurrentTime is not a function*

Most likely caused because the getCurrentTime function in the Timeout loop in the “scheduleMessages” function is fired before the YouTube iFrame is ready.

Fixed it by adding a conditional check in the Timeout loop to check if the iFrame is ready:

scheduleMessages(msgContainer, annotations){
setTimeout(() => {
if (window.onYouTubeIframeAPIReady) {
let ctime = Player.getCurrentTime()
let remaining = this.renderAtTime(annotations, ctime, msgContainer)
this.scheduleMessages(msgContainer, remaining)
}
}, 1000)
},

2016-01-01
184ERROR

I was completely unable to get this to work with :httpc.request

It kept returning some kind of intermediate ad page or something. I ended up going with HTTPoison. Also, wolfram can be slow to respond so the timeouts had to be lengthened

Additionally, wolfram doesn’t always return a Result pod, the examples to use in iex return a Definition pod. I ended up restructuring a bit to grab Result, or Definitions if result was nil. gist(dot)github(dot)com/55b0c4df0f1679ff6536

2016-01-10
185ERROR

URI.encode should be URI.encode_www_form

2015-12-22
118ERROR

On page 118 we try to update a video with both an existing and non-existing category_id.

This will be failing with the following message for readers that just followed the book (without adding more videos or tweaking the input data):

{:error,
Ecto.Changeset{action: :update, changes:{category_id: 2},
constraints: [%{constraint: “videos_category_id_fkey”, field: :category_id,
message: “does not exist”, type: :foreign_key}],
errors: [url: “can’t be blank”, description: “can’t be blank”], filters: %{},
model: %Rumbl.Video{meta: #Ecto.Schema.Metadata<:loaded>,
category: #Ecto.Association.NotLoaded<association :category is not loaded>,
category_id: nil, description: nil, id: 1,
inserted_at: #Ecto.DateTime<2015-11-22T09:59:21Z>, title: “hi”,
updated_at: #Ecto.DateTime<2015-11-22T09:59:21Z>, url: nil,
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1}, optional: [:category_id], opts: [source: :changeset],
params: %{“category_id” => 2}, repo: Rumbl.Repo,
required: [:url, :title, :description],
types: %{category_id: :id, description: :string, id: :id,
inserted_at: Ecto.DateTime, title: :string, updated_at: Ecto.DateTime,
url: :string, user_id: :id}, valid?: false, validations: []}}

As you can see it’s failing because when we created our first video in page 107 we didn’t add any description or url, both of which are required. This we directly inserted that model in the database without using changesets it went through without any issues.

2016-01-02
136SUGGEST

In “Creating slugs” (starts page 132 in B4) we create a migration but never tell the reader to run that migration. This might be useful as I imagine people will run into issues because of that.

2015-12-29
8277SUGGEST

I implemented the authenticate function in Chapter 5 like this:

defp authenticate(conn, _opts) do
if !conn.assigns.current_user do
conn
|> put_flash(:error, “You must be logged in to access that page.”)
|> redirect(to: page_path(conn, :index))
|> halt()
end
conn
end

This formulation of the function makes it clearer (to me at least) that we return the connection object from the function regardless of whether the authentication was a success.

2015-12-22
8378TYPO

On page 78 and 79 of Chapter 5 there are three places where code is implied but not actually listed.

I know the book is in early release but there are no visible indications that would remind the authors to return to these sections and fill them in. So, here’s a list of places that need code listings on these pages:

Page 78:

We need only add an options variable, which we’ll ignore:

We can then change our index action back to it’s previous state, like this:

Page 79:

Let’s go back to the Rumbl.UserController.create action and change it to call the login function after we insert the user in the database:

Hope this helps! Love the book so far.

2015-12-22
151TYPO

2nd paragraph

“Each channel module will have three ways receive events.”

should probably be

“Each channel module will have three ways to receive events.”

2015-12-22
160ERROR

`{:ok, resp, assign(socket, :video_id, video_id)}` assigns a string to :video_id, but the ` build(:annotations, video_id: socket.assigns.video_id)` line, in `handle_in/3` just below it, expects :video_id to be an integer. Changing the line to use `video.id` fixes the issue:

{:ok, resp, assign(socket, :video_id, video.id)}

2015-12-30
96ERROR

“Let’s recap that now:” sounds like it’s supposed to have a code fragment after it.

2015-12-22
84TYPO

Between
Let’s go back to the Rumbl.UserController.create action and change it to call the login function after we insert the user in the database:

And
Register a new user and try to access the pages we have previously restricted. You’ll see the user can finally access them.

No code appears.

I changed my Rumbl.UserController.create action to add this line :
|> Rumbl.Auth.login(user)

2015-12-22
83SUGGEST

First paragraph ends with “We need only add an options variable, which we’ll ignore:”
but the code isn’t shown.

2015-12-22
93TYPO

Double “at”:

“This feature will probably seem a little tedious to you at at first, but it is the only way to guarantee your application has predictable performance when the amount of data grows.”

Excerpt From: Chris McCord, Bruce Tate, and José Valim. “Programming Phoenix (for Travis Graham).” iBooks.

2015-12-22
182TYPO

We’ve built a generic [module module] to spawn compu- tations for queries.

should be

We’ve built a generic [module] to spawn compu- tations for queries.

2015-12-30
105TYPO

Possibly left ‘our model to handle’ when adding ‘our controller and model to handle’ as an updated thought.

“Now that we’ve updated our model to handle our controller and model to handle new users, we need to add the new action to our router.”

Excerpt From: Chris McCord, Bruce Tate, and José Valim. “Programming Phoenix (for Travis Graham).” iBooks.

2015-12-22
113TYPO

Missing “<% end %>” for complete context in the code snipit:

“​<​ form_for changeset, user_path(conn, ​:create​), ​fn​ f -> ​>​”

Excerpt From: Chris McCord, Bruce Tate, and José Valim. “Programming Phoenix (for Travis Graham).” iBooks.

2015-12-22
157ERROR

The Repo in def connect is not aliased.
Either you need to add alias Rumbl.Repo or you have to call it explicitly.

2015-12-30
59SUGGEST

I don’t understand what this means:

> Now that we’ve updated our model to handle our controller and model to handle new users…

2015-12-29
26SUGGEST

In the section “Building a Feature”, the code section file path is started with “getting_started/listings/”, which probably makes sense if you are using the code downloads, but if you are following along the book example in the console, that doesn’t make a whole lot of sense. It would just be “hello/

2015-12-22
184ERROR

It seems that what’s returned by the Wolfram API has changed.
The xpath query on page 184 should be changed to:

xpath(~x“/queryresult/pod[contains(@title, ‘Definitions’)]/subpod/plaintext/text()”)

Otherwise you no longer get any results on page 186, when trying in the IEx console.

2016-01-10
39TYPO

`usernmae` should be `username`.

2015-12-28
7772TYPO

Section: The Anatomy of a Plug
Paragraph: 1
Sentence: 2

The following
“There are two kinds of plugs: module plugs and functions plugs”
Should be:
“There are two kinds of plugs: module plugs and function plugs”
(de-pluralise “function”)

2015-12-29
8075TYPO

Section: Writing an Authentication Plug
Paragraph: 1
Sentence: 1

The following:
“The authentication process work in two stages.”
Should be:
“The authentication process works in two stages.”
(pluralise “work”)

2015-12-29
8378ERROR

Missing code block for the sentence:
“We can then change our index action back to it’s previous state, like this:”

The missing code block should be the original `index` function:
```
def index(conn, _params) do
users = Repo.all(User)
render conn, “index.html”, users: users
end
```

2015-12-22
155TYPO

“Look a the function head.” => “Look at the function head”

2015-12-22
161ERROR

html => json, as in:

“We composed a response by rendering an annotation.html view for every annotation in our list.”

=>

“We composed a response by rendering an annotation.json view for every annotation in our list.”

2015-12-30
130TYPO

in the popup titled “Why brunch”, at the of the text its written:

about the video so we’ll know the exactly when an annotation is added.

The word “the” should not be there !?

2015-12-22
78ERROR

It looks like there is a code snippet missing. Between “We need only add an options variable, which we’ll ignore:”

and

“Now we can plug it in our controller, right after use Rumbl.Web:”

The colon indicates that there should be a code example but there is nothing. I guess it is about adding the opts parameter to the authenticate function.

2015-12-22
167TYPO

“It’s only job is to spawn a process and return {:ok, pid} …” – should be “its only job …”.

2015-12-22
78TYPO

Seems to be a section missing after the text …

“We can then change our index action back to it’s previous state, like this:”

2015-12-22
78OK

The function plug stuff on this page is confusing. I tried looking at the source code to see if i could figure out what was going on but got more confused as I can’t find the code in the source at that bit actually being used anywhere.

I think i know what the intention is but I feel it is missing a section of code - or a sample fragment - that would clear up how this could be used as a function plug.

2016-01-02I *think* you're talking about the macro expansion. In that case, I clarified the prose a little bit more. I think the clarifications will help.
183ERROR

The text says that Phoenix generates a file called config/prod.secrets.exs. The actual filename is config/prod.secret.exs. This singular/plural discrepancy is spread all over this section.

I realize that this doesn’t actually matter, since you can call it whatever you want, but it’s sloppy.

2015-12-22
184TYPO

“with our hard-coded score of 90%.”

The hard-coded score is 95

2015-12-22
157ERROR

In Rumbl.UserSocket.id/1, the binary concatenation fails; Suggest interpolation, using the current_user as

def id(socket), do: “users_socket:#{socket.assigns.current_user.id}”

Gary Fleshman

2015-12-30
79ERROR

Let’s go back to the Rumbl.UserController.create action and change it to call the login function after we insert the user in the database:

——- NO CODE IS PROVIDED

2015-12-22
182ERROR

run the backend_seeds.exs with the proper file extension, ‘exs’

2015-12-22
189ERROR

agree that the solution described by Steve Domin (79233) is necessary for a query “what is elixir?”

unfortunately, the response for a query “what is the meaning of life?” lacks a “Definitions” title, but does contain a “Results” title

one solution would expand the xpath spec as follows:

|> xpath(~x“/queryresult/pod[contains(title, 'Definitions') or contains(title, ‘Result’)]
/subpod/plaintext/text()”)

nevertheless, this approach would not scale well to a larger number of expected titles

2015-12-22
190ERROR

with a timeout value of 5_000 (millisec), the wolfram API consistently fails to return an answer for this query: Rumbl.InfoSys.compute(“what is elixir?”)

raising the timeout to 8_000 consistently succeeds for the same query

2016-01-10
157ERROR

def id(socket), do: “users_socket:” <> socket.assigns.user_id

should be

def id(socket), do: “users_socket:#{socket.assigns.current_user.id}”

Since we only assigned `current_user` to socket, not `user_id`.

2015-12-22
192ERROR

def handle_in(“new_annotation”, params, user, socket) do

Having `user` as a param is odd (and also broken), since that param is never actually passed in. This change seems to have come out of nowhere, since the user is pulled from the socket in all prior examples.

2015-12-30
127TYPO

This line:
$ `mix test test/controllers/page_controller_test.exs:4

Should be:
$ mix test test/controllers/page_controller_test.exs:4

2015-12-22
116TYPO

“The error message has everything we need to do move on” -> “The error message has everything we need to do in order to move on”

2015-12-22
212TYPO

The sentence that read “Make the following changes to you web/channels/video_channel.ex.” should say “Make the following changes to your web/channels/video_channel.ex.” or possibly even “Make the following changes to web/channels/video_channel.ex.” to eliminate the pronoun altogether.

2015-12-22
212TYPO

The sentence that reads “…in our channel so our users gets their annotation broadcast…” should read “…in our channel so our users get their annotation broadcast…”

2015-12-22
126ERROR

In the codeblock for test/support/conn_case.ex you already include the `import Rumbl.TestHelpers` line, which threw me at first because I didn’t have this line in my file and wondered why.

2016-01-01
131ERROR

Missing the definition for `put_current_user/2`. Should show:

```elixir
defp put_current_user(conn, user) do
token = Phoenix.Token.sign(conn, “user socket”, user.id)

conn
|> assign(:current_user, user)
|> assign(:user_token, token)
end
```

2015-12-22
132TYPO

In the setup block the context of `config` is missing. After some debugging I found out that the arguments in the setup block are wrong, which should be something like `setup context do` and using `context[:login_as]` on the third line.

2015-12-22
132ERROR

`setup %{conn: conn} do` -> `setup config do` on line 1 of the code example.

2015-12-22
135TYPO

Create a web/controllers/auth_test.exs

should be

Create a test/controllers/auth_test.exs

2015-12-22
126ERROR

The document code sample shows:

setup tags do
unless tags[:async] do
Ecto.Adapters.SQL.restart_test_transaction(Rumbl.Repo, [])
end
{:ok, conn: Phoenix.ConnTest.conn()} end

However, my generated code only returns :ok and the conn is established in page_controller_test.exs where page 127 says we don’t have to define it.

Confusing. I am not sure the advantage or how I have something different.

2015-12-22The generate code changes a bit around here in later versions of Phoenix. The book will be updated in the beta after next to reflect these changes. Thanks!
45TYPO

visit Phoenix.HMTL1. should read visit Phoenix.HTML

2015-12-28
155TYPO

The value of this expression appears to be wrong:

iex> video = %Rumbl.Video{id: 1, slug: “hello”}
“/”

When I run it in my REPL I get the following:

iex> video = %Rumbl.Video{id: 1, slug: “hello”}
%Rumbl.Video{meta: #Ecto.Schema.Metadata<:built>

2015-12-22
131ERROR

When you run ‘mix test’ later in the chapter, both instances of put_current_user(conn, user) from the code listing cause a compilation error: fuction put_current_user/2 undefined. Either add ‘alias Rumbl.TestHelpers’ in the module, or make the calls to Rumbl.TestHelpers.put_current_user() explicitly.

2015-12-22
78TYPO

Says “We need only add an options variable, which
we’ll ignore:
Now we can plug it in our controller, right after use Rumbl.Web”

The code snippet is missing. It should be: " defp authenticate(conn, _options) do…."

2015-12-22
131SUGGEST

The implementation of the bypass mechanism on auth.ex is using the private function put_current_user which was not defined until now

2015-12-22
136ERROR

The error I got before using `bypass_through` was:

1) test authenticate_user halts when no current_user exists (Rumbl.AuthTest)
test/controllers/auth_test.exs:6
(KeyError) key :current_user not found in: %{}
stacktrace:
(rumbl) web/controllers/auth.ex:26: Rumbl.Auth.authenticate_user/2
test/controllers/auth_test.exs:7

and not:

1) test authenticate_user halts when no current_user exists (Rumbl.AuthTest)
test/controllers/auth_test.exs:6
(ArgumentError) flash not fetched, call fetch_flash/2
stacktrace:
(phoenix) lib/phoenix/controller.ex:990: Phoenix.Controller.get_flash/1
(phoenix) lib/phoenix/controller.ex:975: Phoenix.Controller.put_flash/3
(rumbl) web/controllers/auth.ex:63: Rumbl.Auth.authenticate_user/2
test/controllers/auth_test.exs:7

I might have just typo’d something but it worked as soon as I added the `setup` function as per the book.

2015-12-22
137TYPO

“Now we can we re-run” -> “Now we can re-run”

2015-12-29
139TYPO

“Now let’s run out tests” -> “Now let’s run our tests” (last paragraph)

2015-12-22
201TYPO

Let’s break that down down

should be

Let’s break that down

2015-12-22
201ERROR

We’ve built a generic module module to spawn …

should be

We’ve built a generic module to spawn …

2015-12-22
78ERROR

After “We can then change our index action back to it’s previous state, like this: ”
Code should appear, I guess.

2015-12-22
78TYPO

It seems that the example code for adding the optional options parameter to the authenticate function is missing.

2015-12-22
92SUGGEST

For this code example, ```Repo.one from u in j_users, limit: 1```. It was said in a previous page (p. 88) that

“Repo.one does not mean ‘return the first result.’ It means ‘one result is expected, so if there’s more, fail.’”

So it is not clear why this code sample includes “limit: 1”. Wouldn’t it fail if there is not just one record? If so, why specify a limit?

2015-12-22`limit: 1` will always return at most one result, so Repo.one is find here.
17ERROR

The version of Elixir used on this page is 1.0.4, but the latest is 1.1.0. (very soon to be 1.2.0)

2015-12-28
18ERROR

The version of Node is an older version, particularly v0.12.2. This should be changed to v4.2.3, or v5.3.0 (depending on LTS support or not). Personally, I’m using Node 5.3.0 and it’s working fine for me.

2015-12-28
20SUGGEST

“Building a Feature” doesn’t explain why we want to tie the pieces together. It just says:

“First things first. Let’s tie a URL to a controller, a controller to a function, and a function to a view.”

I think this should be:

“We’re going to show you how easy it is to create a new page for this application. To make that happen, we’re going to add a brand new route, a new controller with a new function, and a view. Once we’ve done that, we’ll have this: show a picture

2016-01-02
20OK

The description of .ex files seems a bit disjointed if not paired with an explanation of .exs files:

“The .ex extension is for compiled Elixir files.”

I recommend changing this to:

“The .ex extension is for compiled Elixir files, while .exs is for Elixir scripts, which are not compiled.”

2016-01-02
21SUGGEST

Show a picture of what happens after adding the “hello” route, and then accessing localhost:4000. While you explain you’ll get an error page, it’d be more helpful to a newbie to see that error page so that they know that they’re seeing the same thing you’re seeing.

2015-12-28
27SUGGEST

“long-running [services|processes]” are mentioned twice on this page, but no example is given. An example might be helpful to really drive the point home.

2016-01-02
33SUGGEST

“The browser pipeline will protect from forgery attacks, honor Accept headers, and fetch the session.”

What kind of forgery attacks, specifically? (I suspect CSRF)

2015-12-28
83TYPO

The link “signout” was changed to “Log out” but has not been updated everywhere.
In particular:
link “signout”,
to: session_path(conn, :delete, current_user),
method: “delete”
Should read “Log out”

and
• with the text signout
should read:

• with the text Log out.

2015-12-29
79SUGGEST

As others have mentioned for the missing: “Let’s go back to the Rumbl.UserController.create action and change it to call the login function after we insert the user in the database”.
|> Rumbl.Auth.login(user) worked for me.

2015-12-22
131TYPO

“Update your lib/controllers/auth.ex”, I think that should read “Update your web/controllers/auth.ex” In other parts of the book, the Auth module was put in the web/ folder.

2015-12-22
49ERROR

Directory/link is absent in the sentence:

“Just replace the layout with the one you’ll find at …”

2015-12-31Yes, it is not ready yet. We will do before final. :)
178ERROR

On the video_channel.ex he is assigning video_id to the socket as a string. Later when persisting to the database I’m getting the following error:

(Ecto.ChangeError) value `“3”` for `Rumbl.Annotation.video_id` in `insert` does not match type :id

I’m casting the video_id to int before assigning it to the socket and it works fine

2015-12-30
176SUGGEST

The tests are broken because the authentication bypass mechanism added on the integration tests chapter was removed

user = conn.assigns[:current_user] -> put_current_user(conn, user)

2015-12-22
8983ERROR

Since I upgrade phoenix to 1.1 according to Phoenix 1.0.x to 1.1.0 upgrade instructions
@current_user will not be found.

Thanks!

2015-12-22Phoenix 1.1 changes will be addressed in the beta due out at the beginner of Jan. Thanks!
128SUGGEST

While going through the wrap up of Chapter 6, I’ve read “Finally, we created categories for our videos.” but can’t see any reference to categorizing the videos in the chapter.

I might be misunderstanding something here, but shouldn’t there a be a section talking about this?

Thanks,

2015-12-22
177TYPO

In UserSocket.connect/2 function we ‘assign(socket, :current_user, user)’, however
in the UserSocket.id/1 we try to get value by :user_id key from assigns map, it produces quite nasty error. I hope I’ve helped.

2015-12-30
92TYPO

j_users = from u in users_count, where: ilike(u.name, ^“%j”)

Should be

j_users = from u in users_count, where: ilike(u.name, ^“j%”)

2015-12-22
62OK

(bottom of pg62) Pick one of “frontend” or “front end” or “front-end” and stick with it. Same with backend.

2016-01-02We're going to let our editor make that call at copy edit... let's keep erratum related to technical book issues.
79TYPO

Missing code example after this paragraph:

“Let’s go back to the Rumbl.UserController.create action and change it to call the login function after we insert the user in the database:”

2015-12-29
109ERROR

build(:videos)

Should be

Repo.build(:videos)

2016-01-01
8TYPO

In the change history for B6.0 the link for the Umbrellas chapter links to the Testing MVC chapter

2015-12-28
0TYPO

“generated by visting” should of course read ‘visiting’ (in chapter “Building Forms”)

2015-12-28
131ERROR

The changes to “auth.ex” reference a put_current_user method that hasn’t been defined yet.

2015-12-29
178ERROR

You don’t actually show the running of the `mix ecto.migrate` command here (although it’s pretty much implied).

2015-12-30
77SUGGEST

At the top of this page, show a picture of the “You must be logged in to access that page”, along with the login fields. It will help readers solidify what they’re trying to accomplish.

2016-02-12
98SUGGEST

So, we just went and pulled out the authenticate function from the user controller, and renamed it and popped it in to the Auth plug.

The way I follow is we are deleting the code from the user controller, but we don’t rename the usage of plug at the top of the user controller

2015-12-29
101SUGGEST

Why did we spend nearly the whole chapter talking about the ins and outs of querying Repo and then just generating a form.

Feels like the teaching got ahead of the objective in this chapter

2016-01-02
110ERROR

relationships/listings/rumbl/web/controllers/video_controller.change1.ex
defp user_videos(user) do
assoc(user, :videos)
end

Ecto.Model.assoc isn’t being imported so this code fails for me. Is there a missed instruction?

2016-01-01Eco.Model is imported in web.ex :controller (in 1.0.4), but will be changing to `Eco.Schema` in phoenix 1.1. In either case assoc should be available
mobi?SUGGEST

In Ch.6 when we create the video, it gets no user_id. I thought there was a mistake (mine or the book’s) and tried to fix it myself until I got to Ch. 7 and saw the solution there. I suggest adding a reassuring statement: “Fill out the form, and click “Submit”. You’ll see a list of your new videos, just as you should. (The user_id isn’t populated yet - we’ll take care of that in the next chapter.)"

2016-01-01
130SUGGEST

It is not clear if we are to delete all the content in video_controller_test or just add in content.

2015-12-29
138SUGGEST

This is the first page send_resp is mentioned. It isn’t explained at all how it works.

2015-12-29
159ERROR

Earlier in the book (pg 117) you use foreign_key_constraint(:category_id). On pg 159 this has changed to assoc_constraint(:category) with no explanation. pg 159 seems to be the first place this is mentioned.

2015-12-30
161SUGGEST

It is not clear where to put the implementation code beginning with:

defimpl Phoenix.Param, for: Rumbl.Video do

2015-12-30
161TYPO

Near the end of this page it says “all helpers accept it as argument.” Seems like there is a word missing between “as” and “argument”. Perhaps the word is “an”?

2015-12-30
163SUGGEST

While mentioned on 162 that the file path is lib/rumbl/permalink.ex, I think it should be shown in the code listing too.

2015-12-30
164SUGGEST

It is not clear which file the code shown near the bottom of this page should go. A little bit of thinking tells me that it goes in the video model, but it would be nice for this to be made clear in the text.

2015-12-30
131ERROR

new auth in auth plug

where did put_current_user come from? (I see it in the example code online, but I’ve lost it along the way)

2015-12-29
170SUGGEST

The “Chris says…” box on this page talks about ES6, but this was first mentioned a few pages ago; specifically Page 155. I suggest moving this “Chris says…” box up to that first mention of ES6.

2015-12-31
182SUGGEST

It is not clear here why the call and login functions from Rumbl.Auth are mentioned. There are no differences between these functions to what I already have. You might be able to get away without mentioning them here.

2015-12-30
184ERROR

“so add the has_many relationship to both your User and Video schema blocks in web/models/annotation.ex and web/models/user.ex, like this:”

This is pointing to the wrong files. It should be web/models/user.ex, and web/models/video.ex.

2015-12-30
131ERROR

I can’t find a reference to put_current_user/2 before page 131 while it was actually added in page 182 as part of the “Socket Authentication”

This confuses the reader and causes the enhanced test suggested on page 131 for “Preparing for Logged In Users” to fail.

2015-12-29
185183ERROR

def id(socket), do: “users_socket:#{socket.assigns.user_id}”

^ Above line should really be:
def id(socket), do: “users_socket:#{socket.assigns.current_user.id}”

Otherwise you get “ErlangError” with reason “%KeyError” because there is no “user_id” on socket.

2015-12-30
186184TYPO

“Next, we need to wire up our new relationships to our User and Video models. Both users and videos will have annotations, so add the has_many relationship to both your User and Video schema blocks in web/models/annotation.ex and web/models/user.ex, like this:”

^ Above paragraph refers to web/models/annotation.ex, but should be web/models/video.ex instead.

screencast.com/t/dCUV1TzDZ

2015-12-30
186184ERROR

In “join” function, I think you will have to cast the video_id to integer before assigning it on “socket”, like -> {video_id, _} = Integer.parse(video_id)

Otherwise, in “handle_in” function, Ecto seems to throw an error when checking changesets i.e. Rumbl.Annotation.changeset(params), if video_id is a string.

2015-12-30
188186TYPO

“Next, we did something new. We composed a response by rendering an annotation.html view for every annotation in our list.”

^ I think it should be “annotation.json”, not “annotation.html”?

2015-12-30
163SUGGEST

Not an error,
Please specify the defimpl code fragment should be in the lib directory file,
I accidentally put in the the controller and it did not give any error and neither worked.

2015-12-30
33TYPO

At the bottom of the page, I think you meant “router” instead of “browser”:

connection
|> endpoint
|> browser (should this be router?)
|> HelloController.world
|> HelloView.render(

2015-12-28In this case browser is references the browser pipeline of the router
42ERROR

For the “user_controller.ex” listing, I think two “aliases” are missing:
defmodule Rumbl.UserController do
use Rumbl.Web, :controller
alias Rumbl.Repo
alias Rumbl.User

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

2015-12-28
4235TYPO

In the last sentence of the page, it is said “each user will record and play back videos that must be saved and served quickly […].”

I believe that what was meant was “play back comments

2015-12-28
144TYPO

assert {:password, {“should be at least %{count} characters”, count: 6}}
- in changeset.errors

Should be

assert {:password, {“should be at least %{count} character(s)”, count: 6}}
- in changeset.errors

2015-12-29
211SUGGEST

Rumbl.InfoSys.compute(“what is elixir?”) doesn’t work because the response doesn’t contain a pod with a title attribute of “Result”.

2016-01-10
52ERROR

When re-enabling ecto, no mention is made of uncommenting the supervisor start line in rumbl.ex that we commented out on page 41. I’m pretty sure this is required to continue with the exercise.

2015-12-30
226TYPO

“rumbrella/apps/info_sys/mix.exe” should be reumbrella/apps/infosys/mix.exs

2015-12-30
228TYPO

On step 6 <- should not be html escaped.

2015-12-31
196SUGGEST

When you mention code upgrading and introspection using an observer, it might be good to link to places where we could read more about these.

2015-12-31The introspection is done in the next chapter, I will make that clear. Code upgrading is another story... we will look for some getting started stories in Elixir but we may not have this material yet.
203TYPO

“Erlang’s OTP has been around from the beginning,”

From the beginning of what?

2015-12-31
135130ERROR

The book doesn’t lead you to make the change to the `setup` in conn_case.ex that provides `conn` to the tests. As a result, attempting to run the newly-added video_controller_test.exs code results in an initially confusing

(FunctionClauseError) no function clause matching in Rumbl.VideoControllerTest.“test requires user authentication on all actions”/1

because there is no arity of 1 without the setup addition.

The code on the site at /titles/phoenix/code/testing_mvc/listings/rumbl/test/support/conn_case.change1.ex includes that change but it isn’t mentioned specifically in the book.

2015-12-30This is provided by phoenix 1.0.4 (and 1.1), which the latest beta assumes is the code you are working with. Thanks!
209ERROR

There doesn’t appear to be a reason for the `import Ecto.Query, only: [from: 2]` line to exist within wolfram.ex.

2015-12-30
209ERROR

The pod that I get back does not have a title attribute with “Result” it is:

Perhaps look for the one where “primary” is true?

2016-01-10
226ERROR

The path is shown as rumbrella/apps/info_sys/mix.exe, but that should be rumbrella/apps/info_sys/mix.exs

2015-12-30
226SUGGEST

It looks like you’re missing the step to copy the configuration for wolfram over from Rumbl to InfoSys.

2015-12-31We don't need to, specially with Elixir v1.2, where the configuration is shared. There is nothing stopping you from moving it though. :) Thank you!
228ERROR

The left-arrow for Step #6 (“Change the function”) is escaped to <

2015-12-31
228ERROR

I think this config should be config :info_sys, :wolfram, :app_id.

2015-12-31
136ERROR

When running the tests for the auth plug I get the following error:

1) test authenticate_user halts when no current_user exists (Rumbl.AuthTest)
test/controllers/auth_test.exs:6
(KeyError) key :current_user not found in: %{}
stacktrace:
(rumbl) web/controllers/auth.ex:51: Rumbl.Auth.authenticate_user/2
test/controllers/auth_test.exs:7

This is because on page 97 we defined authenticate_user like this:

def authenticate_user(conn, _opts) do
if conn.assigns.current_user do
[…]
else
[…]
end
end

Changing it to the below fixes the issue

def authenticate_user(conn, _opts) do
if conn.assigns[:current_user] do
[…]
else
[…]
end
end

2016-01-01
141TYPO

In the 3rd and 4th paragraphs the line numbers don’t match with the actual assigns mentioned. It should be on line 6 and 18 rather than 5 and 17.

2016-01-01
130SUGGEST

The authentication test example is missing a test for the `new` action. I suggest adding the following to the list:

```
get(conn, video_path(conn, :new))
```

2016-01-01
127ERROR

The following excerpt from page 127, the testing chapter, is misleading:

> Also notice the setup block. It sets up database transactions and places a base `conn` into our test metadata. This is where the `%{conn: conn}` map comes from in our `page_controller_test`.

Except that no base `conn` is being set in the `setup` block, as shown below:

``` setup tags do
unless tags[:async] do
Ecto.Adapters.SQL.restart_test_transaction(Rumbl.Repo, [])
end

:ok
end
```

2016-01-01
148143ERROR

This page says we’ll see how `errors_on` comes into play with changeset tests, but we don’t actually use that function in any of the examples in this revision of the text.

2015-12-31
43SUGGEST

If you forgot to restart the server and/or did the iex examples in a new shell you will end up getting the error “protocol Ecto.Queryable not implemented for Rumbl.User,…” as the lib is not hotreloaded after our change on page 41. See also orums.pragprog.com/forums/393/topics/13800
Maybe a notice in the book would help.

2016-01-02
184ERROR

This might just be me, but I had to cast `socket.assigns.video_id` to an integer before Ecto would accept it as an id in the build function.

2016-01-01
136ERROR

It seems that when introducing the Auth plug tests, the reported exception

“”"

(ArgumentError) flash not fetched, call fetch_flash/2
“”"

on page 136 doesn’t show up initially and instead we get the following error:

“”"

(KeyError) key :current_user not found in: %{}
“”"

Tracing this back to page 97 in the `authenticate_user` function, the “if” condition seems to expect the :current_user key to exist. Testing for the existence of the :current_user key fixes this issue:

“”"
if conn.assigns[:current_user] do
“”"

2016-02-14
193TYPO

Third paragraph from the top “Our val function on the hand” -> “Our val function on the other hand”

2016-01-10
211TYPO

We’ve hardcoded the score to 95 and yet the example shows it returning a score of 90.

```
iex> Rumbl.InfoSys.compute(“what is elixir?”) iex> flush()
[{:results, #Reference<0.0.1.9227>,
[%Rumbl.InfoSys.Result{backend: “wolfram”, score: 90,
text: “1 | noun | a sweet flavored liquid (usually containing …”,
url: nil}]}]
```

The explanation code below also mentions that it should be 95.

“A query like ”what is elixir?" indeed reports a list of Rumbl.InfoSys.Result{}’s, with our hard-coded score of 95."

2016-01-02
110SUGGEST

“Next, we’ll use the new function in the index and show actions” is ambiguous. It could mean either the `new` function in the controller or the newly created function `user_videos`.

2016-02-14
64ERROR

The validation error output code needs to be changed for the message from
<%= message %>
to
<%= translate_error message %>
otherwise an exception is thrown for ecto validation error messages that are tuples (e.g. length mismatch).

2016-01-10You generated an app with Phoenix 1.1, but the book was targeting Phoenix 1.0.4. The next beta will use the new translate_errors
viiTYPO

In the release notes for Beta 6.0 the first link is in reference to “Chapter 12: Observer and Umbrellas” but is actually a link to Chapter 8.

2016-02-08
70ERROR

With Phoenix 1.1, <%= message %> doesn’t work when displaying error messages.
Chris pointed me to the right way which was <%= translate_error(message) %>.

2016-01-10Chris McCord says: You generated an app with Phoenix 1.1, but the book was targeting Phoenix 1.0.4. The next beta will use the new translate_errors
42TYPO

Middle of the page, in the paragraph “You have seen the get macro before”:
`UsersController` should be `UserController`.

2016-01-10
135SUGGEST

The “This is changing” box only says which assert type to use with phoenix 1.1, but not how the tests should look like, which would be extremely helpful. I’m using phoenix 1.1.1 and the assert_raise tests are green; not sure if this is intended or not.

2016-01-10
139TYPO

The test “login with a valid email and pass” actually calls Auth.login_by_username_and_pass, the test description should be called “login with a valid username and password”.

2016-01-10
157ERROR

As the #79185 by Joseph DiVita report, but his solution was not working for me. What I ddi was to change the getCurrentTime on player.js to

getCurrentTime(){ return this.player.getCurrentTime ? Math.floor(this.player.getCurrentTime() * 1000) : 0},

2016-01-10
142SUGGEST

This is similar to #78964. The code that is responsible for testing the password is hashed is in explicitly put in the side effect free tests file. However, hashing a password is not a pure function. You can validate this by having an assertion on the result of the password hash and running the tests twice. This hash will be different.

2016-01-10
316ERROR

In Phoenix 1.1.1, the Rumbl.Repo app is launched as a supervisor, and not a worker. I believe this also changed PDF pg 323 where the Rumbl.Repo is expected to log that it is restarting, after the Counter worker dies under the :one_for_all strategy. It doesn’t show up in the logs.

2016-01-10
47TYPO

2nd paragraph of `Nesting Templates`:
`Then, whenever we build tables or listings or users…` should be `…of users…`

2016-01-21
52TYPO

Bottom of the page:
“Now, it’s time to create a real database-backed Ecto repository. When you type the following commands…”
Since there is a single command to type, it should be “the following command”

2016-01-21
53ERROR

The code snippet of web/web.ex and the sentence after that seems to be outdated, it references Ecto.Model, which is deprecated. The current code uses Ecto.Schema, and it has one more import as well.

2016-01-10
41TYPO

First code snippet:

  1. Start the Ecto repository
  2. worker(Rumbl.Repo, []),

In the generated code this line is different:

  1. supervisor(Rumbl.Repo, []),

The same issue is present on page 55.

2016-01-10
49SUGGEST

@inner in web/templates/layout/app.html.eex has been changed in Phoenix 1.1.0 in favor of a more explicit call to render. This might be worth a footnote (or a link to the Pheonix changelog for v1.1.0 from 2015-12-16) although the Phoenix error message points you to exactly this change. :)

2016-01-10
224TYPO

In the section ‘Using Umbrellas’
in the text below the
“mix​​ ​​new​​ ​​rumbrella​​ ​​—umbrella” command
‘config.exs’ is written as
‘config.exe’

2016-01-10
64ERROR

The following code snippet:

```
<%= for {attr, message} <- f.errors do %>

  • <%= humanize(attr) %> <%= message %>

  • ```

    leads to an exception: `protocol Phoenix.HTML.Safe not implemented for {“should be at least %{count} character(s)”, [count: 1]}`

    (I’m running Elixir 1.2.0, Phoenix 1.1.2, and Ecto 1.1.1; I’m guessing it’s a version compatibility issue.)

    I was able to fix it by replacing `message` with `translate_error(message)`.

    2016-01-21It sounds like you generated your project with the Phoenix 1.0.x project generator, where B7 uses Phoenix 1.1.x, which includes different form output than provided.
    119TYPO

    In the middle of the page: Change “IDS” to “IDs”

    2016-01-21
    144TYPO

    In the test code block, line 5, the error message should have parens around the s in characters. like: `“should be at least %{count} character(s)”`

    2016-02-14
    133ERROR

    In Chapter 8, under the section “Testing Logged Out Users,” the tests do not pass…

    (ArgumentError) cannot retrieve association :videos for empty list
    stacktrace:
    (ecto) lib/ecto.ex:494: Ecto.assoc/2
    (rumbl) web/controllers/video_controller.ex:59: Rumbl.VideoController.edit/3
    (rumbl) web/controllers/video_controller.ex:1: Rumbl.VideoController.action/2
    (rumbl) web/controllers/video_controller.ex:1: Rumbl.VideoController.phoenix_controller_pipeline/2
    (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/phoenix/endpoint/render_errors.ex:34: Rumbl.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:194: Phoenix.ConnTest.dispatch/5
    test/controllers/video_controller_test.exs:48

    I initially thought this was an oversight/error with Phoenix after upgrading from 1.0.4 to 1.1.2 prior to the testing chapter. Before the chapter on testing, I was also using the previous version of the book B6.

    2016-02-14
    77ERROR

    When hashing existing users’ passwords via iex, user chrismccord comes with a too-short password: phx.

    So when we execute

    iex>
    for u <- Repo.all(User) do
    Repo.update!(User.registration_changeset(u, %{password: u.password_hash}))
    end

    registration_changeset rightly complains.

    Changing chrismccords password to phxphx for instance will fix that.

    Btw: good idea to add this password_hash snippet there!

    2016-01-21
    103TYPO

    The sentence reads: “Once Ecto fetched the association for us, and we can then reference the user.videos as needed.” I think there is a stray “and” in the middle there, and possibly also a comma. If I’m reading it right I think you might have wanted: “Once Ecto fetched the association for us we can then reference the user.videos as needed.”

    2016-02-12
    1SUGGEST

    It looks like the images for the mobi version aren’t showing up.

    2016-03-29
    203ERROR

    “Let’s look at that listen function on line 7” should be “.. line 20”.

    2016-02-14
    151ERROR

    `assert username: {“should be at most %{count} character(s)”, [count: 20]} in errors_on(%User{}, attrs)` doesn’t test the production code. The keyword list is interpreted as the sole argument of the `assert` macro. This line of code is the equivalent of `assert(true) in errors_on(%User{}, attrs)`, or really just `assert(true)` since the return value isn’t used. errors_on/2 isn’t part of the assertion, so it passes no matter what the function returns. (False positives like this are a good argument for a test-first approach.)

    The line should read: `assert {:username, {“should be at most %{count} character(s)”, [count: 20]}} in errors_on(%User{}, attrs)`.

    2016-02-14
    220ERROR

    “To start our module, we import the functions we’ll need. SweetXml will help us
    parse the XML we need, Ecto.Query will let us fetch the credentials we’ll need,
    and Result has the struct for the results we will use.”

    We did not Import Ecto.Query in wolfram.ex and there is no Ecto.Query in the code?

    2016-01-21
    221ERROR

    The wofram API got smarter in the meantime and delivers an answer for “a query” so that the statement that we get empty list back is not true anymore:
    ```
    iex(1)> Rumbl.InfoSys.compute(“a query”)
    [{#PID<0.362.0>, #Reference<0.0.1.993>}]
    iex(2)> flush
    {:results, #Reference<0.0.1.993>,
    [%Rumbl.InfoSys.Result{backend: “wolfram”, score: 95,
    text: “1 | noun | an instance of questioning\
    2 | verb | pose a question”,
    url: nil}]}
    :ok
    ```

    2016-02-15
    104ERROR

    Halfway down page 104, the book states that Ecto.build_assoc will return an Ecto.Query, when in fact it returns a struct/Model object. If you try to pass that return into Repo.all, as instructed to by the book, you’ll get “(Protocol.UndefinedError) protocol Ecto.Queryable not implemented” error.

    From the book: "Other times we just want to fetch the videos associated to a user, without storing them in the user struct, like this:
    iex> query = Ecto.build_assoc(user, :videos)
    #Ecto.Query<…>
    iex> Repo.all(query)
    [%Rumbl.Video{…}]

    build_assoc is another convenient function from Ecto that returns an Ecto.Query with all videos scoped to the given user, or to a list of users. We convert this query into data by calling Repo.all. "

    I believe the book should be referring to Ecto.assoc, not Ecto.build_assoc.

    The earlier references to build_assoc at the top of page 104 seem fine.

    2016-02-12
    228ERROR

    You write “On line 10, we call into our Information System,…” the code snipped above that comment has no line numbering and the call is made in line 2.

    2016-02-15
    23ERROR

    There is a reference to Rumbl.repo, but this is in the Hello World example, I suspect it should be Hello.repo.

    2016-01-21
    22ERROR

    Update instructions to pull down phoenix 1.1.

    mix archive.install github.com/phoenixframework/phoenix/releases/download/v1.1.2/phoenix_new-1.1.2.ez

    2016-01-21
    49TYPO

    or any other tempalte engine. Should be template

    2016-01-21
    24TYPO

    We’ll tie a URL to a a function

    2016-01-21
    42TYPO

    for our users controller —> user’s

    2016-01-21
    45TYPO

    start a interactive Elixir —> start an interactive Elixir session

    2016-01-21
    209TYPO

    “:transient - the child is restarted only if it terminates abnormally, with an exit reason than :normal…” - probably should say “other than :normal…”

    2016-02-15
    49TYPO

    tempalte

    2016-01-21
    212ERROR

    Repo is shown started as a worker, where later versions of Ecto specify starting it as a supervisor: /elixir-lang/ecto/blob/master/lib/ecto.ex#L52-L66

    2016-01-21
    220TYPO

    Ecto.Query is in the summary of the code example, but is not actually present in the code.

    2016-01-21
    95TYPO

    The sentence “and then update your repository by running migrations:” is colored in green.

    2016-04-05
    95ERROR

    Running mix ecto.migrate triggers a compilation of the new generated files, which fails with the following error:

    Compiled web/models/video.ex

    Compilation error on file web/controllers/video_controller.ex

    (CompileError) web/controllers/video_controller.ex:25: undefined function video_path/2
    (stdlib) lists.erl:1337: :lists.foreach/2
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8

    2016-01-21Sounds like you skipped the instructions to add the video path to your router? \n \n"It is time to follow up on the remaining instructions printed by the generator. First, we’ll need to add the route to web/router.ex: \n resources "/videos", VideoController" \n
    54TYPO

    That’s why we need the view in first place.
    -> “the”

    2016-01-21
    127TYPO

    The second `delete` statement should be

    ```
    iex> Repo.delete changeset
    {:error, changeset}
    ```

    2016-02-14
    anyERROR

    All code examples in the mobi version on a kindle 3g are unreadable (one letter wide boxes) See: www.dropbox.com/s/gin3xl1rz9qysuw/2016-01-21%2021.14.21.jpg?dl=0

    2016-02-08
    6909ERROR

    In Chapter 10 there’s a technical error in the video_channel.ex code in all downloads and references.

    In it the following section is provided:

    {:ok, resp, assign(socket, :video_id, video_id)}

    This SHOULD be:

    {:ok, resp, assign(socket, :video_id, video.id)}

    If you deploy this code as provided, when you add persistence later, your inserts will fail with the following error.

    [error] GenServer #PID<0.366.0> terminating

    (Ecto.ChangeError) value `“6”` for `Rumbl.Annotation.video_id` in `insert` does not match type :id
    (ecto) lib/ecto/query/planner.ex:33: anonymous fn/6 in Ecto.Query.Planner.fields/4
    (stdlib) lists.erl:1262: :lists.foldl/3
    (ecto) lib/ecto/query/planner.ex:21: Ecto.Query.Planner.fields/4
    (ecto) lib/ecto/repo/schema.ex:449: Ecto.Repo.Schema.dump_changes/5
    (ecto) lib/ecto/repo/schema.ex:77: anonymous fn/11 in Ecto.Repo.Schema.do_insert/4
    (ecto) lib/ecto/repo/schema.ex:477: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/9
    (ecto) lib/ecto/pool.ex:292: Ecto.Pool.with_rollback/3
    (ecto) lib/ecto/adapters/sql.ex:582: Ecto.Adapters.SQL.transaction/8
    (ecto) lib/ecto/pool.ex:244: Ecto.Pool.outer_transaction/6
    (ecto) lib/ecto/adapters/sql.ex:551: Ecto.Adapters.SQL.transaction/3
    (rumbl) web/channels/video_channel.ex:27: Rumbl.VideoChannel.handle_in/3
    (phoenix) lib/phoenix/channel/server.ex:229: Phoenix.Channel.Server.handle_info/2
    (stdlib) gen_server.erl:615: :gen_server.try_dispatch/4
    (stdlib) gen_server.erl:681: :gen_server.handle_msg/5
    (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
    Last message: Phoenix.Socket.Message{event: “new_annotation”, payload:{“at” => 0, “body” => “a”}, ref: “166”, topic: “videos:6”}
    State: Phoenix.Socket{assigns:{user_id: 6, video_id: “6”}, channel: Rumbl.VideoChannel, channel_pid: #PID<0.366.0>, endpoint: Rumbl.Endpoint, handler: Rumbl.UserSocket, id: “users_socket:6”, joined: true, pubsub_server: Rumbl.PubSub, ref: nil, serializer: Phoenix.Transports.WebSocketSerializer, topic: “videos:6”, transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.354.0>}

    2016-02-15
    167ERROR

    “Our new migration uses an alter_table function, one …”; should this be “alter macro” instead?

    2016-02-14
    21SUGGEST

    Similar to Steven Nunez’s input, prior to publishing, update pages to show latest software versions:

    $ elixir -v
    Elixir 1.2.1

    $ mix local.hex

    • creating ~/.mix/archives/hex-0.10.1.ez

    And on page 22:

    $ psql —version
    psql (PostgreSQL) 9.5.0

    $ node —version
    v5.5.0

    2016-02-08
    31TYPO

    Typo in following paragraph:

    If you receive database errors when running mix ecto.create, double check your Rumbl.Repo username and password values in config/dev.exs and match your system settings where necessary.

    We create the Project with phoenix.new hello.
    The Application name should be Hello and not Rumbl.

    2016-02-08
    65TYPO

    logging —> login (at least that’s why I think we wanna do at Facebook :) )

    “Imagine that your boss lays down the requirement of logging into your application through Facebook.”

    2016-03-28
    203SUGGEST

    It says “This unique reference is just a byte that’s guaranteed to be globally unique”. Aren’t references 8 bytes?

    2016-02-15
    106TYPO

    At the top of page 106 (the function to look up all the videos for the user it is stated “We used the build_assoc function, also imported from Ecto,” when the code example simply says “assoc” - so either the code example or the description are wrong (or I don’t get it) :)

    defp user_videos(user) do
    assoc(user, :videos)
    end

    2016-02-12
    26TYPO

    The screenshot of the error is incorrect… it refers to the Rumbl application, rather than the Hello throwaway app.

    2016-02-08
    106ERROR

    In defp user_videos(user), you are using assoc. But in the text describing that function (right below it) you are mentioning build_assoc.. Which one is correct?

    2016-02-12
    130123SUGGEST

    You say, “Relational databases deal with relationships between tables.”

    This seems to imply that the database is relational because some tables relate to others (through foreign keys). But that is not why it is relational. The tables themselves are the relations. The “relation” refers to the relation of the rows of a table to each other, not to rows in other tables. A relational database with only one table (relation) is still relational. See E. F. Codd’s original paper (sorry, won’t let me hyperlink).

    I understand that it’s not a book on databases, but hopefully you don’t want to misinform readers even on peripheral material.

    Response: Thanks for your comment. I think there’s a tension between writing in a more academic style and writing for a more general audience. I don’t think this sentence misinforms the readers in any meaningful way. Saying more at this point would bog the book down. I believe this paragraph captures the essence of what a relational database is without going into the original papers with relations, selections, projections, keys, candidate keys, etc. What’s important here is that where we specify relationships, we specify them through primary and foreign keys in data rather than hard wiring relationships in the schema, and I think we communicate this well enough.

    2016-04-05Bruce, this one's yours.
    152SUGGEST

    The password test says they test that a password is at least 6 chars long, but the test is conducted with a 2 character password. I know this book is not directly about testing but I think it’d be important to test with values surrounding the edge case, e.g. 5 for a failure and 6 for a success :)

    2016-02-15
    132125TYPO

    The screenshot shows an error message that includes a bulleted list, but in my code I get an error message below the field and a generic error up top.

    2016-02-14
    13TYPO

    and we’ll talk you through each layer of Phoenix

    should be:

    ….. walk you …..

    2016-02-08
    196ERROR

    I was getting an error inserting the annotations into the database.

    Changing this line:
    |> build_assoc(:annotations, video_id: socket.assigns.video_id)

    to this:
    |> build_assoc(:annotations, video_id: String.to_integer(socket.assigns.video_id))

    fixed the problem.

    2016-02-14
    65ERROR

    The resources macro `resources “/users”, UserController, only: [:index, :show, :new, :create]` on page 65 does not produce the routes shown on page 66 when the reader runs `$ mix phoenix.routes`

    Page 66 show the following:
    page_path GET
    user_path GET
    user_path GET
    user_path GET
    user_path GET
    user_path POST
    user_path PATCH /users/:id
    PUT /users/:id
    user_path DELETE /users/:id
    / Rumbl.PageController :index
    /users Rumbl.UserController :index
    /users/:id/edit Rumbl.UserController :edit
    /users/new
    /users/:id
    /users
    Rumbl.UserController :new
    Rumbl.UserController :show
    Rumbl.UserController :create
    Rumbl.UserController :update
    Rumbl.UserController :update
    Rumbl.UserController :delete

    But, ` [:index, :show, :new, :create]` would actually produce:
    user_path GET /users Rumbl.UserController :index
    user_path GET /users/new Rumbl.UserController :new
    user_path GET /users/:id Rumbl.UserController :show
    user_path POST /users Rumbl.UserController :create
    page_path GET / Rumbl.PageController :index

    2016-02-12
    118TYPO

    “Once we have molded the data to the shape our business model requirements”, I guess this should be “… to the shape OF our business model req.”.

    2016-02-14
    22SUGGEST

    The command for installing Phoenix is outdated and no longer working.
    v1.0.3/phoenix_new-1.0.3.ez

    npm also shows a warning about a missing Licence field in the deps.
    npm WARN EPACKAGEJSON hello No license field.

    It would be great to mention that the default user and pass for the Postgres install should be “postgres” because the windows installer doesn’t provide a default password.

    2016-02-08
    84ERROR

    Command was given as:
    iex>
    for u <- Repo.all(User) do
    Repo.update!(User.registration_changeset(u, %{password: u.password_hash}))
    end
    —-
    This erred out until I first added: alias Rumbl.Repo

    2016-02-12
    75SUGGEST

    In this page you say “Now let’s try it out:” but (as far as I can see) you don’t explicitly provide the URL to hit using the web browser. It is obvious to me but others without experience in frameworks such as rails may struggle!

    2016-02-12
    75ERROR

    The book says to enter this into iex:

    iex>
    for u <- Repo.all(User) do
    Repo.update!(User.registration_changeset(u, %{password: u.password_hash}))
    end

    However, in a new iex session there is no alias set for Rumbl.Repo and it fails with “module Repo is not available”

    I suggest adding ‘alias Rumbl.Repo’ as the first command for the iex session.

    2016-02-12
    111TYPO

    “We will explore such strategies later on this chapter when
    we talk about constraints.”

    Should change to “later on in this chapter” for clarity. (addition of the word “in”)

    2016-02-14
    25SUGGEST

    The screenshot of the controller not found error appears to be for the Rumbl app created in the next chapter instead of the Hello app being discussed.

    It’s minor but I though I would bring it up just in case.

    Great book so far!

    2016-02-08
    188TYPO

    In the Processes and Channels section, “with the simple guaranties” should read “with the simple guarantees”.

    2016-03-28
    122TYPO

    In the section entitled “Fast”, the first sentence of the second paragraph reads “Let’s compare Phoenix with some of the frameworks that are.” This is not a complete sentence. The intent could perhaps be better conveyed as follows: “Let’s compare Phoenix with some of the other popular frameworks out there.”

    2016-02-08
    102ERROR

    On page 102:

    iex> query = Ecto.build_assoc(user, :videos)

    #Ecto.Query<…>

    iex> Repo.all(query)

    [%Rumbl.Video{…}]

    The function call to Ecto.build_assoc/2 returns a %Rumbl.Video{…}, not the #Ecto.Query<…> you need to run Repo.all(query). The call needs to be to Ecto.assoc/2 (iex> query = Ecto.build_assoc(user, :videos)) to get the correct #Ecto.Query<…> returned.

    2016-02-12
    120ERROR

    Page 120 in Chapter 7 … This query:

    iex(19)> Repo.all from u in User,
    …(19)> join: v in assoc(u, :videos),
    …(19)> join: c in assoc(v, :categories),
    …(19)> on: c.name == “Comedy”,
    …(19)> select: {u, v}

    Should be:

    iex(19)> Repo.all from u in User,
    …(19)> join: v in assoc(u, :videos),
    …(19)> join: c in assoc(v, :category),
    …(19)> where: c.name == “Comedy”,
    …(19)> select: {u, v}

    2016-02-14
    186SUGGEST

    In the ES6 template at the top of page 186, ${at} is technically user supplied input, too. It is sent from the client to the server (pg 185, top), then broadcast unfiltered (pg 186, bottom) via the socket. This is a cross-site scripting vulnerability. I would recommend using ${this.esc(at)} in this case, too.

    2016-02-14
    56ERROR

    Is:
    “we briefly changed lib/rumbl/repo.ex to comment out the default Ecto repository”
    Should be:
    “we briefly changed lib/rumbl/repo.ex to remove the default Ecto repository”
    The repository was removed on PDF page 44:
    “let’s replace the default repository implementation in lib/rumbl/repo.ex with this shell” and a snippet without the “use Ecto.Repo, otp_app: :rumbl” line.

    2016-02-12
    146SUGGEST

    Is:
    “but we don’t need all of that security.”
    Should be:
    “but we don’t need all of that security here.”

    2016-02-14
    251TYPO

    Link to Bypass.
    Is: “hxxp://hexdocs.pm/ecto/Ecto.Migration.html”
    Should be: “hxxps://hex.pm/packages/bypass” (no docs on hexdocs.pm)

    2016-02-15
    260SUGGEST

    Add wrap up, as promised page before. :)

    2016-02-15
    102TYPO

    In following section, build_assoc should be assoc:

    Preload is great for bundling data together. Other times we just want to fetch the videos associated to a user, without storing them in the user struct, like this:

    iex> query = Ecto.build_assoc(user, :videos) #Ecto.Query<…>

    iex> Repo.all(query) [%Rumbl.Video{…}]

    build_assoc is another convenient function …

    2016-02-15
    104TYPO

    “We used the build_assoc function, also imported from Ecto …”

    should be

    “We used the assoc function, also imported from Ecto …”

    2016-02-15
    120TYPO

    in:

    join: v in assoc(u, :videos),
    join: c in assoc(v, :categories)

    :categories should have been :category

    and on: should should be where:

    2016-02-15
    124TYPO

    “after choosing the remove category” should be “after choosing the removed category”

    2016-02-15
    132ERROR

    The test method uses an old syntax:

    test “GET /” do
    conn = get conn(), “/”
    assert html_response(conn, 200) =~ “Welcome to Phoenix!”
    end

    should be

    test “GET /”, %{conn: conn} do
    conn = get conn, “/”
    assert html_response(conn, 200) =~ “Welcome to Phoenix!”
    end

    2016-03-29
    43TYPO

    In the code, there a multiple instances of the typo “usernmae” and “nmae” instead of "name.

    2016-03-28
    81ERROR

    In

    if conn.assigns.current_user do
    conn
    else

    current_user should be referenced as conn.assigns[:current_user]. Otherwise the first test on page 142

    test “authenticate_user halts when no current_user exists”, %{conn: conn} do
    conn = Auth.authenticate_user(conn, [])
    assert conn.halted
    end

    will fail with a KeyError instead of the expected error (due to fetch_flash etc):

    1) test authenticate_user halts when no current_user exists (Rumbl.AuthTest)
    test/controllers/auth_test.exs:5
    (KeyError) key :current_user not found in: %{}
    stacktrace:
    (rumbl) web/controllers/auth.ex:56: Rumbl.Auth.authenticate_user/2
    test/controllers/auth_test.exs:6

    2016-04-05
    167TYPO

    Add the follow snippet to the bottom

    should be

    Add the following snippet to the bottom

    2016-03-28
    252TYPO

    “application pushes messages to to a client,”

    repeated the word ‘to’

    2016-03-28
    250TYPO

    In ‘Jose Says’ section, “Wolphram” -> “Wolfram”

    2016-03-28
    29TYPO

    “and then our application boundaries like controllers and channels will convert them into atoms keys which we will rely on everywhere else inside Phoenix” - atom should be singular: ‘into atom keys’

    2016-03-29
    8TYPO

    “None of them come with the simple guaranties” guaranties should be guarantees

    2016-03-28
    250TYPO

    It sould be “Wolfram” not “Wolphram” in “Jose says …. Wolphram API” ;-)

    2016-03-28
    258254ERROR

    Apologies. I was not logged in when I posted my previous erratum, so I can’t edit it. I was on the wrong terminal tab. The tests do pass.

    That said, to get the tests to run on the book’s code, I did need to add the config/dev.exs file to get it to build (I added config/prod.exs, too, but don’t know if it would have built without it).

    Also, I had to fix a line of code in /rumbrella/apps/rumbl/web/channels/video_channel.ex:13 to change `_params` to `params`. Once I’d made those changes, it compiled and the tests passed.

    Again, sorry for the confusion.

    2016-04-05
    258254ERROR

    Damn. You’d better check that test. I have to retract my retraction. The errata should be editable if one is logged in!

    I will not attempt to stop confusing myself. Good luck with the book.

    2016-04-05
    257253ERROR

    I was able to get all tests to run by using 1%2B+1 instead of 1%20%201:

    So this:

    defmodule InfoSys.Test.HTTPClient do
    wolfram_xml File.read!("test/fixtures/wolfram.xml") def request(url) do url = to_string(url) cond do String.contains?(url, "1%20+%201") -> {:ok, {[], [], wolfram_xml}}
    true -> {:ok, {[], [], “”}}
    end
    end
    end

    ExUnit.start()

    becomes this:

    defmodule InfoSys.Test.HTTPClient do
    @wolfram_xml File.read!(“test/fixtures/wolfram.xml”)
    def request(url) do
    url = to_string(url)

    cond do
    String.contains?(url, “1+%2B+1”) -> {:ok, {[], [], @wolfram_xml}}
    true -> {:ok, {[], [], “”}}
    end
    end
    end

    ExUnit.start()

    I guess the URL encoder works differently. Hope this helps.

    2016-04-05
    43TYPO

    iex> alias Rumbl.User
    iex> user = %{usernmae: “jose”, password: “elixir”}

    username spelt incorrectly: usernmae

    2016-03-28
    43TYPO

    OK, too hasty! Noticed this was deliberate!

    2016-03-28
    0ERROR

    There is an issue with the .mobi version where the included images are not mapped to the correct locations in the text. That is, images which are displayed are usually not relevant to the surrounding text. A sanity pass over the PDF version indicates that images are mapped correctly in that format.

    2016-03-29
    248TYPO

    in the text it says the new config file should be rumbrella/apps/info_sys/config/text.exs whereas it must be rumbrella/apps/info_sys/config/test.exs

    2016-04-05
    32SUGGEST

    After doing phoenix.new to create a new application, you immediately list the 3 instructions:

    $ cd hello
    $ mix ecto.create
    $ mix phoenix.server

    Then a paragraph later you say:

    At the bottom of the mix phoenix.new output, you can see a few sentences that tell you what to do next. We’ll skip the instructions related to ecto since our application won’t use a database for now. We will want to start Phoenix, though. Change into the hello directory and run the Phoenix web server through mix, which will start looking for requests on port 4000, like this:
    $ cd hello
    hello $ mix phoenix.server

    2016-03-29
    27TYPO

    The : tells Phoenix to create a parameter called

    should be

    That : tells Phoenix to create a parameter called

    2016-03-28
    273TYPO

    I’m using iBooks so the page number is likely incorrect; however, “we hand it of to the repository” should be “we hand it off to the repository” (right after adding alphabetical function to the Category model).

    2016-03-28
    232ERROR

    The code listed in this reference (otp/listings/rumbl/web/channels/video_channel.change1.ex) has the join function as

    def join(“videos:” <> video_id, _params, socket) do

    which causes a compilation error, it should be

    def join(“videos:” <> video_id, params, socket) do

    instead

    2016-04-05
    54SUGGEST

    The first paragraph on page 54 is confusing.

    It says: “Rather than make you slog through a bunch of CSS and HTML, we’ll let you pull down the source code at your leisure.” Does pull down mean download or remove? If it means download, where’s the download link?

    Also, please make this statement clearer: “Just replace the layout you find at web/templates/layout/app.html.eex with the one you can find there.” Where is there?

    2016-04-05Good catch. We did not provide a CSS and revised HTML layout. Instead, we decided to focus on Phoenix. Maybe in the next edition.
    43TYPO

    Mistake in username

    user = %{usernmae: “jose”, password: “elixir”}

    2016-03-28
    50TYPO

    In the sentence: “To learn more about existing HTML helpers, visit the Phoenix.HMTL1 documenta- tion.”

    Letters are transposed in the reference to the Phoenix.HTML documentation.

    2016-03-28
    55OK

    “Ecto is a wrapper that’s primarily intended for relational databases, allowing developers to read and persist data to underlying storages, like PostgreSQL”

    The user of “storages” here seems strange. Maybe “storage systems”?

    2016-04-05Thanks for your comment. The copy editor revised to storage. I think storage works.
    187ERROR

    In the included server output for a channel having been joined, the ID on the JOIN line is not the same as that on the reply line.

    Diff of suggested change:

    [info] JOIN videos:2 to Rumbl.VideoChannel
    Transport: Phoenix.Transports.WebSocket
    Parameters: %{}
    - [info] Replied videos:4 :ok
    + [info] Replied videos:2 :ok

    2016-03-29
    483ERROR

    Not using PDF.

    Chapter 9, Creating Slugs : watching_videos/listings/rumbl/web/models/video.change1.ex
    Unicode title like “猫 cat” causes error in slugify() function. Instead of this line:
    |> String.replace(~r/[^\\w-]+/, “-”)

    Use a unicode regex:
    |> String.replace(~r/[^\\w-]+/u, “-”)

    2016-04-05
    129ERROR

    In the Wrapping Up section of Chapter 7, the following statement is wrong:

    We used two forms of queries, a map-based syntax and a pipe-based syntax.

    Instead it should say a keyword list-based syntax instead of map, since the Ecto query API takes a keyword list (important since it allows repeating keys!).

    2016-03-29
    487ERROR

    Not using PDF. iBooks portrait page 487/552.

    Chapter 12, Using Umbrellas

    info_sys/lib/info_sys.ex is modified to use InfoSys.Supervisor.start_link().
    This modification is destroyed by later copying rumbl/lib/rumbl/info_sys.ex to info_sys/lib/info_sys.ex. The file should be copied and then the supervisor should be added.

    1. Add the following after copying the file.
      use Application

    def start(_type, _args) do
    InfoSys.Supervisor.start_link()
    end

    2016-04-05
    202ERROR

    Regarding the code for “Handling Disconnects”, VideoChannel should send the `last_seen_id` upon join instead of relying on video.js on calculating the `last_seen_id` based on the annotations returned. Because upon reconnect from a disconnection, and there are no fresh annotations sent, the calculated `last_seen_id` on video.js will be nil. And the next reconnection will cause duplicate annotations which we’re working to avoid.

    2016-04-05
    253ERROR

    I needed to add to the presented fixture to pass the accompanying test on [printed] Page 254. I needed to add a containing (minimally):



    2</plaintext><br /> </subpod><br /> </pod></p> <p>Without it, the Wolfram module doesn’t get the answer it needs to match the test on [printed] Page 254.</p> </td><td>2016-04-05</td><td></td></tr> <tr><td>202</td><td></td><td>TYPO</td><td><p>This is elsewhere, too, but in video_channel.ex you have us sorting by desc instead of asc. If you sort by descending, then the comments will be out of order if you skip ahead in the video. Here’s the examples:</p> <hr /> <p>Code as in the book:</p> <p>When skipped ahead to 2 min results in:<br /> [00:00] cmccord: Test<br /> [00:00] cmccord: Hi<br /> [00:25] cmccord: CAM!<br /> [00:15] cmccord: May be my favorite.<br /> [00:08] cmccord: This is a really cool video</p> <hr /> <p>Suggested fix to the code:</p> <p>annotations = Repo.all(<br /> from a in assoc(video, :annotations),<br /> where: a.id &gt; ^last_seen_id,<br /> order_by: [asc: a.at],<br /> limit: 200,<br /> preload: [:user]<br /> )</p> <p>Results in:<br /> [00:00] cmccord: Test<br /> [00:00] cmccord: Hi<br /> [00:08] cmccord: This is a really cool video<br /> [00:15] cmccord: May be my favorite.<br /> [00:25] cmccord: CAM!</p> </td><td>2016-04-05</td><td></td></tr> <tr><td>150</td><td></td><td>TYPO</td><td><p>``` test “login with a valid email and pass”, %{conn: conn} do ```</p> <p>it should be “valid username and pass”.</p> </td><td>2016-03-29</td><td></td></tr> <tr><td>15</td><td></td><td>TYPO</td><td><p>Apparently all code snippets for the .mobi version are not indented, which is awful to read.</p> </td><td>2016-03-29</td><td></td></tr> <tr><td>12</td><td></td><td>SUGGEST</td><td><p>I’m starting chapter 4 now, and so far (I think ;) I understand everything – except that I still have no idea what “OTP” means. Since you/Phoenix use that abbreviation quiet a lot, maybe you could explain that here in a half sentence or so :)</p> <p>-Chris</p> </td><td>2016-04-05</td><td></td></tr> <tr><td>13</td><td></td><td>TYPO</td><td><p>Under Bruce Tate’s Acknowledgment it gives thanks to Paul Janowitz at icnamakeitbetter.com when, I believe, it should be Paul Janowitz at icanmakeitbetter.com</p> <p>Hope that helps, looking forward to the rest of the book!</p> <p>Joe Lane</p> <p>Lanejo01@luther.edu</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>31</td><td></td><td>TYPO</td><td><p>Whenever José’s name appears in a sidebar, it is spelled (incorrectly) as Jose.</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>38</td><td></td><td>TYPO</td><td><p>In the code listings, “connection” should not be indented.</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>XII</td><td></td><td>TYPO</td><td><p>In Bruce Tate section, there is a mention to icanmakeitbetter.com, but the link is wrongly typed.</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>-</td><td></td><td>OK</td><td><p>“We know that Phoenix is a bold name for a bold framework, but look. By now, you’ve likely seen enough web frameworks to know most of our ideas aren’t new.”</p> <p>“but look.” - doesn’t read very well before a full stop, perhaps a comma?</p> </td><td>2016-04-14</td><td> </td></tr> <tr><td>230</td><td></td><td>TYPO</td><td><p>:phonenix_ecto</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>150</td><td></td><td>ERROR</td><td><p>The paragraph talks about code that does not match the code printed just before.</p> <p>e.g.<br /> - line 5, we set up our required <code>videos assigns but there's no </code>videos in line 5.</p> <p>- On line 17, we again set up our necessary <code>video and </code>category assigns<br /> again no <code>video or </code>category on line 17</p> </td><td>2016-04-15</td><td>Chris: this is yours </td></tr> <tr><td>41</td><td></td><td>ERROR</td><td><p>technical typo in `user = %{usernmae: “jose”, password: “elixir”}`<br /> it should be username. And also the defstruct of the model is defined as username.</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>57</td><td></td><td>TYPO</td><td><p>“For example, field creates a new field” —&gt; “For example, add creates a new field”</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>62</td><td></td><td>TYPO</td><td><p>“carpel-tunnel” —&gt; “carpal tunnel”</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>96</td><td></td><td>TYPO</td><td><p>In the first paragraph of “Implementing Login and Logout” section, there is the following:</p> <p>[…] used this information to restrict user access. and then […]</p> <p>I believe the dot after “access” is a typo.</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>165</td><td></td><td>ERROR</td><td><p>The Rumbl app channel join doesn’t appear to work correctly in all environments / web browsers due to a YouTube Iframe API (javascript) error.</p> <p>OSX 10.11<br /> Safari 9.1 and Chrome 49.0.2623.110</p> <p>YouTube IFrame API stops working and causes constant JS retry error.</p> <p>(Possible the Javascript Engines of Chrome and Safari get bogged down, which causes Phoenix channels to stop communicating?)</p> <p>watching_videos/listings/rumbl/web/static/js/player.js<br /> line # 17:</p> <p>should add “https:”<br /> according to</p> <p>code[dot]google[dot]com/p/gdata-issues/issues/detail?id=5788</p> <p>Its not really Phoenix related, but had me stumped as to why I wasn’t seeing channel broadcasts, until I tried different browsers [OSX 10.11, Safari and Chrome did not work - but Firefox did]</p> <p>Further oddities occur when you don’t have any internet connection and attempt to load the YouTube Iframe API, which also prevents Phoenix channels from joining.</p> </td><td>2016-04-15</td><td>Chris, change this one if there's not much risk. </td></tr> <tr><td>18</td><td></td><td>TYPO</td><td><p>Last sentence before “Installing Your Development Environment”, “probably a eager” should read “probably eager”.</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>xii</td><td></td><td>TYPO</td><td><p>“Bruce Tate” section: icnamakeitbetter.com should be icanmakeitbetter.com</p> </td><td>2016-04-14</td><td></td></tr> <tr><td>204</td><td></td><td>TYPO</td><td><p>In the second sentence of the first paragraph of this page, the path should be: lib/rumbl/counter.ex</p> </td><td>2016-04-15</td><td></td></tr> <tr><td>251</td><td></td><td>ERROR</td><td><p>The Wolfram test was failing for me. I had to replace the URI-encoded “1 + 1” string from “1+%2B+1” to “1%20+%201”.</p> </td><td></td><td></td></tr> <tr><td>50</td><td></td><td>SUGGEST</td><td><p>Chapter 3 -&gt; Nesting Templates<br /> It should be</p> </td> <p>in another place in web/templates/user/index.html to have nice formatting on web page:</p> <p>Wrong:</p> <tr> <td> <p>&lt;%= render “user.html”, user: user %&gt;</p> </td> <td> <p>&lt;%= link “View”, to: user_path(@conn, :show, user.id) %&gt;</p> </td> </tr> <p>Right:</p> <tr> <td> <p>&lt;%= render “user.html”, user: user %&gt;</p> <td> <p>&lt;%= link “View”, to: user_path(@conn, :show, user.id) %&gt;</p> </td> </td> </tr> </td><td></td><td></td></tr> <tr><td>48</td><td></td><td>ERROR</td><td><p>Hi,</p> <p>I’me reasonably new to Elixir and phoenix…So, I’m not 100% sure this is a real issue or i may have done something wrong earlier in the exercise, however i discover the following issue with the following code.</p> <p>Cut and paste from the book:<br /> def show(conn, %{“id” =&gt; id}) do<br /> user = Repo.get(Rumbl.User, id)<br /> render conn, “show.html”, user: user<br /> end</p> <p>I could not get this to work without first converting “id” to an integer as below<br /> def show(conn, %{“id” =&gt; id}) do<br /> user = Repo.get(Rumbl.User, String.to_integer(id))<br /> render conn, “show.html”, user: user<br /> end</p> <p>Thanks<br /> Craig</p> </td><td></td><td></td></tr> <tr><td>33</td><td></td><td>TYPO</td><td><p>double slashes in it between ‘raw’ and ‘master’ in the archive.install URL</p> </td><td></td><td></td></tr> <tr><td>94</td><td></td><td>SUGGEST</td><td><p>There is a picture/diagram of the model Annotation and the relationship with User and Video. In this version this diagram is split in two pages. It would be good if the entire diagram is placed in one page.</p> </td><td></td><td></td></tr> <tr><td>101</td><td></td><td>TYPO</td><td><p>It looks like the paragraph between ecto.rollback and ecto.migrate examples is out of place. Maybe it needs some review.</p> </td><td></td><td></td></tr> <tr><td>54</td><td></td><td>TYPO</td><td><p>“For now, it only uses Ecto.Model and import functions to work with changesets and queries, but this function serves as an extension point that will let us explicitly alias, import or use the various libraries our models might need.”</p> <p>I believe this should be<br /> “For now, it only uses Ecto.Model and imports functions to work with changesets and queries, but this function serves as an extension point that will let us explicitly alias, import or use the various libraries our models might need.”</p> <p>Adding an ‘s’ to “import” makes the intention clear.</p> </td><td></td><td></td></tr> <tr><td>166</td><td></td><td>ERROR</td><td><p>The video.css should be set to a fixed height with scroll enabled, or the scroll-to-bottom code in Chapter 10 won’t work.</p> <p>#msg-container {<br /> min-height: 180px;<br /> max-height: 180px;<br /> overflow-y: scroll;<br /> }</p> </td><td></td><td></td></tr> <tr><td>41</td><td></td><td>TYPO</td><td><p>iex&gt; user = %{usernmae: “jose”, password: “elixir”}</p> <p>usernmae should be username</p> </td><td></td><td></td></tr> <tr><td>73</td><td></td><td>ERROR</td><td><p>The code to be entered into IEx has an error.</p> <p>for u &lt;- Rumbl.Repo.all(User) do<br /> Rumbl.Repo.update!(User.registration_changeset(u, %{<br /> password: u.password_hash || “temppass”<br /> }))<br /> end</p> <p>It needs either a line at the beginning “alias Rumbl.User”<br /> or<br /> add “Rumbl.” in front of all User references.</p> </td><td></td><td></td></tr> <tr><td>122</td><td></td><td>SUGGEST</td><td><p>The error message contains “constraint error when attempting to insert struct”, but in my setup, it reads: “constraint error when attempting to insert model”.</p> </td><td></td><td></td></tr> <tr><td>29</td><td></td><td>ERROR</td><td><p>Box “José says”: Photo with image of José is missing (it displays a red cross).<br /> Also on page 32, 64, 225, 249.</p> </td><td></td><td></td></tr> <tr><td>29</td><td></td><td>ERROR</td><td><p>José’s avatar is broken (a small red cross appears instead of his face).</p> </td><td></td><td></td></tr> <tr><td>26</td><td></td><td>TYPO</td><td><p>In the mobi edition at around location 776 there are blocks from<br /> the next chapter. The block that was atom keys vs strings in the<br /> pdf seems to have been replaced stuff from further up the input<br /> stream.</p> </td><td></td><td></td></tr> <tr><td></td><td>343</td><td>ERROR</td><td><p>In “rumbrella/apps/info_sys/test/backends/http_client.exs”</p> <p>I had to change</p> <p>@wolfram_xml File.read!(“test/fixtures/wolfram.xml”)</p> <p>to</p> <p>@wolfram_xml File.read!(<em>DIR</em> &lt;&gt; “/../fixtures/wolfram.xml”)</p> <p>in order for the rumbl tests to pass when running them from the rumbl dir.</p> <p>With this change the tests pass regardless of whether they are run from rumbrella or rumbl dirs.</p> </td><td></td><td></td></tr> <tr><td>18</td><td></td><td>TYPO</td><td><p>The original, with surrounding text for context, is “you’ll want to separate the code that calls another web server, or fetches code from a database, from the code that processes that data.”</p> <p>It probably should be “or the code that fetches from a database”, or “fetches data from a database” instead of “or fetches code from a database”.</p> </td><td></td><td></td></tr> <tr><td>198</td><td></td><td>TYPO</td><td><p>In section 4, “building forms”, right before “ecto/listings/rumbl/web/router.change1.ex”</p> <p>“You’ll see a good example of this policy segregation when your learn about authentication.”</p> <p>Should be “when you learn”</p> </td><td></td><td></td></tr> <tr><td>124</td><td></td><td>TYPO</td><td><p>“but suppose tried to update” -&gt; “but suppose WE tried to update”</p> </td><td></td><td></td></tr> <tr><td>73</td><td></td><td>TYPO</td><td><p>“You’ll see a good example of this policy segregation when your learn about authentication.”</p> <p>Should be “when <strong>you</strong> learn”.</p> </td><td></td><td></td></tr> <tr><td>126</td><td></td><td>ERROR</td><td><p>You write:<br /> “There are three approaches to solving this problem, and all have tradeoffs. First, you might decide to let the application (and the web framework) manage relationships for you. This approach, adopted by frameworks like Rails, leads to simpler code and database layers that are much more portable, but at a cost.”</p> <p>There is confusion as to what the subject, i.e. “the problem”, is. The second sentence cited above seems to be referring to managing foreign key integrity, but the preceding paragraph indicates that the discussion is of managing database constraints in general; if the latter is the case, it seems inaccurate to cite Rails as falling into this category (the one referenced by “first”), as it allows you to specify indices and other column based constraints in migrations that are enforced by the database.</p> <p>Thanks for the great book!</p> </td><td></td><td></td></tr> <tr><td>137</td><td></td><td>SUGGEST</td><td><p>Not all the dots from the first integration test are green, some are purple.</p> </td><td></td><td></td></tr> <tr><td>148</td><td></td><td>SUGGEST</td><td><p>“on line 5” and “on line 18” do not match the code above, should be 6 and 19.</p> </td><td></td><td></td></tr> <tr><td></td><td>21</td><td>ERROR</td><td><p>The complete output of “mix phoenix.new hello” should be formatted as output in the book. The output from “We’re all set!” to “$ iex -S mix phoenix.server” looks like the normal book text and it isn’t clear when the command output stops and the commentary resumes.</p> <p>The output of the command is also much different in 1.14, but that is far less confusing that the mixed up formatting.</p> </td><td></td><td></td></tr> <tr><td></td><td>24</td><td>TYPO</td><td><p>“We call the functions invoked by the router on our controller’s actions, but don’t get confused.” is confusing (ironically) and the grammar is wrong. This might be better: “Actions are what we call the functions invoked by the router on the controller, but don’t get confused.”</p> </td><td></td><td></td></tr> <tr><td></td><td>25-26</td><td>SUGGEST</td><td><p>Include the assignment of third to :bears in the first example: “. . . and it can do so by assigning first to :lions, second to :tigers, and third to :bears.” Because the assignment wasn’t included with the other two it made me wonder if I was misunderstanding something.</p> <p>You might also mention on the next page (26) that first and third still get assigned in the example where you are testing that the third element in the tuple is :bears.</p> </td><td></td><td></td></tr> <tr><td>72</td><td></td><td>SUGGEST</td><td><p>In this line of code:</p> <p>|&gt; cast(params, ~w(name username), [])</p> <p>it is requiring parameters. Further down, you are expected to navigate to “/users/new” without any parameters giving you an error. I couldn’t get it to render until I removed those parameters.</p> </td><td></td><td></td></tr> <tr><td>21-27</td><td></td><td>SUGGEST</td><td><p>I just try to install the phoenix framework following the book (mix phoenix.new hello; cd hello; mix ecto.create;), and I find a error on this last commad. This error doesn´t prevent to run the server (using mix phoenix.server), but I think it could causes problems on the future, with the connections to the database or something…<br /> Here is the problem</p> <p>(…more lines of info…)<br /> Consolidated List.Chars<br /> Consolidated String.Chars<br /> Consolidated Collectable<br /> Consolidated Enumerable<br /> Consolidated IEx.Info<br /> Consolidated Inspect</p> <p><strong></strong> (ArgumentError) argument error<br /> (stdlib) :io.put_chars(:standard_error, :unicode, [[[], &lt;&lt;42, 42, 32, 40, 77, 105, 120, 41, 32, 84, 104, 101, 32, 100, 97, 116, 97, 98, 97, 115, 101, 32, 102, 111, 114, 32, 72, 101, 108, 108, 111, 46, 82, 101, 112, 111, 32, 99, 111, 117, 108, 100, 110, 39, 116, 32, 98, 101, …&gt;&gt;], 10])<br /> (mix) lib/mix/cli.ex:67: Mix.CLI.run_task/2<br /> (elixir) lib/code.ex:363: Code.require_file/2</p> <p>The problem seems to be that the config/devs.exs database password is not setted up. It something that you don´t say on the book, but on the web. I hope you do not displease me tell, I only do for improve the next edition.</p> <p>There is another thing that it is not on the book but is on the web: when you make a new controller (using the router.ex), and a new template, it seems to be enough as to make the request to the new action… but it is not true (I think), we need to make a view handler (¿no?).</p> </td><td></td><td></td></tr> <tr><td></td><td>19</td><td>TYPO</td><td><p>“Good online resources3 exist, but we recommend the excellent book , by Dave Thomas, …”</p> <p>I believe the name of the book was left out. Or maybe a hyperlink? I’m not sure what was intended to go there. Notice that there is a space on both sides of the second comma.</p> </td><td></td><td></td></tr> <tr><td>NA</td><td></td><td>ERROR</td><td><p>I am reading the online version on safari. I can’t locate the page number on it. In chapter 6, “Managing Related Data”, after adding the video relationship to the user, the index action on page refresh generates an error because the logged in user does not have any videos associated. Don’t we have to handle that? Right now its giving me an error</p> </td><td></td><td></td></tr> <tr><td>46</td><td></td><td>ERROR</td><td><p>In the iex listing at the bottom of the page,`Phoenix.HTML.Link.link(“Delete” ….)` is called, but the example response shows the anchor text as `[x]`</p> </td><td></td><td></td></tr> <tr><td>161</td><td></td><td>TYPO</td><td><p>“That’s the file loaded by browsers at the end of web/templates/layout.html.eex when we call static_path(@conn, ”/js/app.js“).”</p> <p>should be</p> <p>“That’s the file loaded by browsers at the end of web/templates/layout/app.html.eex when we call static_path(@conn, ”/js/app.js“).”</p> </td><td></td><td></td></tr> <tr><td>167</td><td></td><td>SUGGEST</td><td><p>We are instructed to add the code snippet that implements the Phoenix.Param protocol for the Rumbl.Video struct to the bottom of web/models/video.ex</p> <p>I suggest it is made clearer that this needs to be added to the very bottom, outside the Rumbl.Video module’s “end”. At first I put it below the slugify function, but before the final “end”, and the server fails to start with the rather cryptic:</p> <p>“<strong></strong> (Mix) Could not start application rumbl: Rumbl.start(:normal, []) returned an error: shutdown: failed to start child: Rumbl.Endpoint”</p> </td><td></td><td></td></tr> <tr><td>119</td><td></td><td>TYPO</td><td><p>In the SQL fragment description “downcase(uname)” should be “downcase(username)” in both places where downcase is used as an example.</p> </td><td></td><td></td></tr> <tr><td>41</td><td></td><td>TYPO</td><td><p>Is:<br /> user = %{usernmae: “jose”, password: “elixir”}<br /> should_be:<br /> user = %{username: “jose”, password: “elixir”}</p> <p>typo in username</p> </td><td></td><td></td></tr> <tr><td>232</td><td></td><td>ERROR</td><td><p>On line 20 of video_channel.ex, it should either be Rumbl.AnnotationView, or you need to alias it first. Once I prefixed it with Rumbl, it worked great.</p> </td><td></td><td></td></tr> <tr><td></td><td>203</td><td>SUGGEST</td><td><p>1) The explanation of the code is a bit unclear, especially the “notice the _from in the function head” as I thought you were still referring to the GenServer.call on line 8, but you were actually referring to line 28.</p> <p>2) It would be good if you could explain the handle_call function in more detail. Even stating that each of the 3 arguments represent “Request, From, State”, and that in this case it returns “Reply, NewState” would be enough!<br /> (It’s slightly too different from lines 24-26 in the first version of counter.ex to be intuitive to grasp)</p> </td><td></td><td></td></tr> <tr><td>253</td><td></td><td>TYPO</td><td><p>The line 6 of second code sample should be<br /> ```<br /> String.contains?(url, “1%20<span class="underline">%201“) -&gt; {:ok, {[], [], @wolfram_xml}}<br /> ```<br /> instead of ”1</span>%2B+1”</p> </td><td></td><td></td></tr> <tr><td></td><td>247</td><td>ERROR</td><td><p>I had the same problem as Marc Linsangan in both print and PDF versions; the wolfram test fails. “1+%2B+1” should be “1%20+%201”</p> </td><td></td><td></td></tr> <tr><td>22</td><td></td><td>ERROR</td><td><p>2nd paragraph talks about .ex being a “extension for compiled elixir files”. IMHO that should be changed to “is the extension for elixir source files” — they’re getting compiled and are source files, not the compiled byte code files.</p> </td><td></td><td></td></tr> <tr><td>227</td><td>228</td><td>TYPO</td><td><p>info_sys.change1.ex:<br /> 18: await_results(opts)<br /> 31&amp;35: await_result (not plural).</p> <p>Typo?</p> </td><td></td><td></td></tr> <tr><td>60</td><td></td><td>SUGGEST</td><td><p>I ran into some error when following the code because the “name” field isn’t validated.</p> <p>This means that the code actually allows to insert a user with no name field.</p> <p>Maybe alter the code such that the “name” field is also validated?</p> </td><td></td><td></td></tr> <tr><td>97</td><td></td><td>ERROR</td><td><p>This text</p> <p>The VideoController, like any other controller, also has a pipeline, and the Phoenix<br /> generator plugs a function called scrub_params for the create and update actions:<br /> plug :scrub_params, “video” when action in [:create, :update]</p> <p>But I generated the code using the commands for html generator, and the code isn’t in the controller, maybe is the version of phoenix that i’m using.</p> <p>Dependencies:</p> <p>defp deps do<br /> [{:phoenix, “~&gt; 1.2.0”},<br /> {:phoenix_pubsub, “~&gt; 1.0”},<br /> {:phoenix_ecto, “~&gt; 3.0”},<br /> {:postgrex, “&gt;= 0.0.0”},<br /> {:phoenix_html, “~&gt; 2.6”},<br /> {:phoenix_live_reload, “~&gt; 1.0”, only: :dev},<br /> {:gettext, “~&gt; 0.11”},<br /> {:cowboy, “~&gt; 1.0”},<br /> {:comeonin, “~&gt; 2.0”}<br /> ]<br /> end</p> </td><td></td><td></td></tr> <tr><td>138</td><td></td><td>ERROR</td><td><p>The CLI prints a warning:<br /> …warning: using conn/0 to build a connection is deprecated. Use build_conn/0 instead.<br /> test/controllers/video_controller_test.exs:7: Rumbl.VideoControllerTest.__ex_unit_setup_1/1<br /> test/controllers/video_controller_test.exs:1: Rumbl.VideoControllerTest.<em>ex_unit</em>/2<br /> (ex_unit) lib/ex_unit/runner.ex:289: ExUnit.Runner.exec_test_setup/2<br /> (ex_unit) lib/ex_unit/runner.ex:247: anonymous fn/2 in ExUnit.Runner.spawn_test/3</p> <p>The book says we write this:<br /> setup do<br /> user = insert_user(username: “max”)<br /> conn = assign(conn(), :current_user, user)<br /> {:ok, conn: conn, user: user}<br /> end</p> <p>I changed conn() to: build_conn</p> <p>I’m using phoenix 1.2</p> </td><td></td><td></td></tr> <tr><td>150</td><td></td><td>ERROR</td><td><p>This test:<br /> test “changeset does not accept long usernames” do<br /> attrs = Map.put(@valid_attrs, :username, String.duplicate(“a”, 30))<br /> assert {:username, {“should be at most %{count} character(s)”, [count: 20]}} in<br /> errors_on(%User{}, attrs)<br /> end</p> <p>Throws this error when running mix test:</p> <p>.</p> <p>1) test changeset does not accept long usernames (Rumbl.UserTest)<br /> test/models/user_test.exs:18<br /> Assertion with in failed<br /> code: {:username, {“should be at most {count} character(s)“, [count: 20]}} in errors_on(%User{}, attrs)<br /> lhs: {:username, {”should be at most{count} character(s)”, [count: 20]}}<br /> rhs: [username: “should be at most 20 character(s)”]<br /> stacktrace:<br /> test/models/user_test.exs:20: (test)</p> <p>.</p> <p>Finished in 0.06 seconds<br /> 3 tests, 1 failure</p> <p>Randomized with seed 872579</p> <p>Phoenix version: 1.2</p> </td><td></td><td></td></tr> <tr><td>152</td><td></td><td>ERROR</td><td><p>When I run test user_repo_test with mix I getting error:<br /> test/models/user_repo_test.exs:8: undefined function insert_user/1</p> </td><td></td><td></td></tr> <tr><td>152</td><td></td><td>ERROR</td><td><p>Please ignore my previous reported error on page 152. I missed to add ‘import Rumbl.TestHelpers’ to the model_case.ex</p> <p>Ruslan</p> </td><td></td><td></td></tr> <tr><td></td><td>34</td><td>SUGGEST</td><td><p>The bullet list should have an entry for the router.</p> </td><td></td><td></td></tr> <tr><td></td><td>44</td><td>SUGGEST</td><td><p>The code for user_controller.ex shows a render call which (somehow) sets the value of <code>users for later use in a view. In Rails, </code>users would be an instance variable, but here it seems to be a bit of (unexplained) magic. Is it an attribute or what?</p> </td><td></td><td></td></tr> <tr><td></td><td>46</td><td>ERROR</td><td><p>The book explains that &lt;%= %&gt; tags inject results into templates, while &lt;% %&gt; tags do not. This seems like a side effect to me. It then says “We’ll try to use code without side effects in views whenever possible, so we’ll use mostly the &lt;%= %&gt; form.” Shouldn’t this be “… the &lt;% %&gt; form.”?</p> </td><td></td><td></td></tr> <tr><td></td><td>46</td><td>SUGGEST</td><td><p>“… within our index action.” should be “… within our index action, via the call to render/3.”</p> </td><td></td><td></td></tr> <tr><td></td><td>51</td><td>SUGGEST</td><td><p>In “… receives a couple of special assigns …”, “assigns” should either be clarified as a code term or changed to “assignments”.</p> </td><td></td><td></td></tr> <tr><td></td><td>56</td><td>SUGGEST</td><td><p>Somewhere around the migration example, I’d like to see an explanation of the specific changes that were made (eg, :passwd went away, :username added “null: false”).</p> </td><td></td><td></td></tr> <tr><td></td><td>64</td><td>SUGGEST</td><td><p>In the first sentence of the Creating Resources section, I would change “macro” to “macro call”, for clarity.</p> </td><td></td><td></td></tr> <tr><td></td><td>78</td><td>TYPO</td><td><p>“… downstream functions including …” should be “… downstream functions, including …”</p> </td><td></td><td></td></tr> <tr><td></td><td>78</td><td>SUGGEST</td><td><p>I’d like to see some discussion of the fact that the return value from init/1 magically gets handed in as the second argument to call/2.</p> </td><td></td><td></td></tr> <tr><td></td><td>85</td><td>SUGGEST</td><td><p>The sentence “This hardens our … application secure” seems a bit awkward. I would rewrite it for clarity.</p> </td><td></td><td></td></tr> <tr><td>138</td><td></td><td>ERROR</td><td><p>test lists all user’s videos on index (Rumbl.VideoControllerTest)</p> <p><strong></strong> (ArgumentError) cannot retrieve association :videos for empty list</p> </td><td></td><td></td></tr> <tr><td>41</td><td></td><td>TYPO</td><td><p>error in “username”</p> <p>iex&gt; user = %{usernmae: “jose”, password: “elixir”}</p> </td><td></td><td></td></tr> <tr><td>150</td><td></td><td>SUGGEST</td><td><p>The test “changeset does not accept long usernames” keeps failing because lhs &amp; rhs don’t match (one is a list and another is a tuple). I think I found a fix by copying what was done in the following test “registration_changeset password must be atleast 6 chars long” on the next page. Add the same changeset assignment using User.registration_changeset/2 before assert and replace error_on/2 with changeset.errors after the “in” keyword. The tests will pass this way.</p> </td><td></td><td></td></tr> <tr><td></td><td>60</td><td>SUGGEST</td><td><p>:empty atom is passed to the changeset (and the next page describes the reason).</p> <p>But now I get a warning<br /> warning: passing :empty to Ecto.Changeset.cast/3 is deprecated, please pass an empty map or :invalid instead<br /> (rumbl) web/models/user.ex:15: Rumbl.User.changeset/2<br /> (rumbl) web/controllers/user_controller.ex:24: Rumbl.UserController.new/2<br /> (rumbl) web/controllers/user_controller.ex:1: Rumbl.UserController.action/2<br /> (rumbl) web/controllers/user_controller.ex:1: Rumbl.UserController.phoenix_controller_pipeline/2</p> <p>Can this be clarified, pl.?</p> </td><td></td><td></td></tr> <tr><td>55</td><td></td><td>SUGGEST</td><td><p>There is a line that states: “import Ecto.Query, only: [from: 1, from: 2]”. As far as I know, we’re not instructed to actually write “, only: [from:1, from:2]”. My web.ex file just says “import Ecto.Query”.</p> <p>Unless I missed something while reading I suspect that this might be a change in Phoenix since the book was published.</p> </td><td></td><td></td></tr> <tr><td>46</td><td></td><td>SUGGEST</td><td><p>Coming from a non-Rails/ERB background, I was briefly stumped by this sentence.</p> <p>“Remember, we’ve already populated @users within our index action.”</p> <p>Where did the @ prefix come from? What does “populate” mean?</p> <p>Took some backtracking to discover that “populating” meant passing [keyword list] data to render/3.</p> <p>Even so, I couldn’t infer why populated data are prefixed with @. Is that EEX syntax like &lt;%= %&gt;?</p> <p>After discovering that templates are functions, I assume any arguments passed to render/3 are automagically turned into module attributes (I’m still not a 100% sure).</p> </td><td></td><td></td></tr> <tr><td></td><td>247</td><td>TYPO</td><td><p>The listing for http_client.exs has an error on line 6. The encoded version of “1 + 1” is shown as “1+%2B+1” but it should be “1%20+%201”.</p> <p>I double checked using URI.encode inside iex and with encodeURI in javascript.</p> </td><td></td><td></td></tr> <tr><td>150</td><td></td><td>ERROR</td><td><p>Ran into the same error someone else reported (#80513), and the test on page 150 line 18 passes if changed to the following:</p> <p>```<br /> test “changeset does not accept long usernames” do<br /> attrs = Map.put(@valid_attrs, :username, String.duplicate(“a”, 30))<br /> assert username: “should be at most 20 character(s)” in<br /> errors_on(%User{}, attrs)<br /> end<br /> ```</p> </td><td></td><td></td></tr> <tr><td>9</td><td></td><td>TYPO</td><td><p>emulate failures by crashing a database connections on purpose,…</p> <p>should be</p> <p>emulate failures by crashing a database connection on purpose,…</p> </td><td></td><td></td></tr> <tr><td>24</td><td></td><td>TYPO</td><td><p>… please define a clause for render/2 …</p> <p>should be</p> <p>… please define a matching clause for render/2 …</p> </td><td></td><td></td></tr> <tr><td>31</td><td></td><td>SUGGEST</td><td><p>the line:</p> <p>root: Path.dirname(<em>DIR</em>),</p> <p>does not appear in the generated config/config.exs from the latest phoenix release.</p> </td><td></td><td></td></tr> <tr><td>79</td><td></td><td>ERROR</td><td><p>iex&gt; changeset = Rumbl.User.changeset(%Rumbl.User{username: “eric”})</p> <p>returns a deprication error:<br /> warning: passing :empty to Ecto.Changeset.cast/3 is deprecated, please pass an empty map or :invalid instead</p> </td><td></td><td></td></tr> <tr><td>83</td><td></td><td>TYPO</td><td><p>|&gt; cast(params, ~w(password), []) in block:</p> <p>def registration_changeset(model, params) do model<br /> |&gt; changeset(params)<br /> |&gt; cast(params, ~w(password), [])<br /> |&gt; validate_length(:password, min: 6, max: 100) |&gt; put_pass_hash()<br /> end</p> <p>is missing closing parens.</p> </td><td></td><td></td></tr> <tr><td></td><td>247</td><td>ERROR</td><td><p>In the book testing_otp/listings/rumbrella/apps/info_sys/test/fixtures/wolfram.xml is incomplete.<br /> In media.pragprog.com/titles/phoenix/code/testing_otp/listings/rumbrella/apps/info_sys/test/fixtures/wolfram.xml the XML is actually complete.</p> <p>otp/listings/rumbl/lib/rumbl/info_sys/wolfram.ex will try to find the answer, which is 2, but it will not find it, since it’s been cut out from the XML.</p> <p>Especifically the text that should be in the XML in the book is <plaintext>2</plaintext>.</p> </td><td></td><td></td></tr> <tr><td>225</td><td></td><td>ERROR</td><td><p>The additional info box “What About Task.async/await?” slipped in between the following code-example lines, which shows only a single line on the previous page. Would be nicer if the whole code example would be rendered after this info box.</p> </td><td></td><td></td></tr> <tr><td>5562</td><td></td><td>ERROR</td><td><p>Im reading it on the kindle reader so no page number. Location 5652, code “watching_videos/listings/rumbl/web/views/watch_view.ex”</p> <p>The original regular expression:<br /> ~r{<sup>.*(​?:youtu\\.be/|\\w+/|v=)(​?<id>[</sup>​#&amp;?]*)}</p> <p>did not extract the id for me. I instead fixed it with this which is obviously not good but works for me for now:<br /> ~r{(?<url>watch\\?v=)(?<id>.*)}</p> <p>There is also a small formatting error where everything after [^ in the regex appears as italic.</p> <p>kind regards,<br /> stephan</p> </td><td></td><td></td></tr> <tr><td></td><td>99</td><td>ERROR</td><td><p>Chapter 6: Building relationships -</p> <p><code>required_fields ~w(url title description) </code>optional_fields ~w()</p> <p>Those module attributes do not appear in my generated code.</p> <p>Phoenix version 1.2.1, elixir version 1.2.6</p> </td><td></td><td></td></tr> <tr><td></td><td>237</td><td>TYPO</td><td><p>`mix test` should show 33 tests, rather than 37 tests.</p> <p>By the end of Chapter 8, there are 31 tests. At page 189, two tests are added automatically with `mix phoenix.gen.model`, bringing total up to 33 tests. No tests were written/added until page 237, but the sample test output now surprisingly shows 37 tests.</p> </td><td></td><td></td></tr> <tr><td>26</td><td></td><td>SUGGEST</td><td><p>Super wishlist… After assigning values to `austin`, it would be nice if you had showed how they can be used. Something like:</p> <p>iex(3)&gt; Place.city(austin)<br /> “Austin”<br /> iex(4)&gt; Place.texas?(austin)<br /> true</p> </td><td></td><td></td></tr> <tr><td></td><td>159</td><td>TYPO</td><td><p>The book states:<br /> “We extract the player ID from the video url field by a function aptly named player_id.”</p> <p>I think “aptly” is a typo.</p> </td><td></td><td></td></tr> <tr><td>29</td><td></td><td>ERROR</td><td><p>There is a missing image icon in the “José says” box indicating something did not render properly when the pdf was generated.</p> </td><td></td><td></td></tr> <tr><td>43</td><td></td><td>ERROR</td><td><p>In “Coding VIews” the example for index.html.eex should use “users_path” instead of “user_path”, otherwise it will not compile.<br /> BROKEN: &lt;%= link “View”, to: user_path(<code>conn, :show, user.id)%&gt; WORKS: &lt;%= link "View", to: users_path(</code>conn, :show, user.id)%&gt;</p> <p>I just got kindle edition today, 10/30/2016 - best guess at version/page, above.</p> </td><td></td><td></td></tr> <tr><td>70</td><td></td><td>ERROR</td><td><p><strong></strong> (KeyError) key :password_hash not found in: %Rumbl.User{id: nil, name: “Jose”, password: nil, username: “josevalim”}</p> <p>(stdlib) :maps.update(:password_hash, “&lt;3&lt;3elixir”, %Rumbl.User{id: nil, name: “Jose”, password: nil, username: “josevalim”})<br /> (rumbl) web/models/user.ex:2: anonymous fn/2 in Rumbl.User.<em>struct</em>/1<br /> (elixir) lib/enum.ex:1623: Enum.“<del>reduce/3-lists^foldl/2-0</del>”/3<br /> (rumbl) expanding struct: Rumbl.User.<em>struct</em>/1<br /> iex:22: (file)</p> </td><td></td><td></td></tr> <tr><td>74</td><td>62</td><td>ERROR</td><td><p>When adding the users as resources you need to specify the “only” methods in a different order.</p> <p>resources “/users”, UserController, only: [:index, :show, :new, :create]</p> <p>must be</p> <p>resources “/users”, UserController, only: [:index, :new, :show, :create]</p> <p>this is because “/users/new” matches to the :show path also with new as the user id.</p> </td><td></td><td></td></tr> <tr><td>145</td><td></td><td>SUGGEST</td><td><p>Using `Dict.merge/2` instead of `Map.merge/2`. Dict is deprecated.</p> </td><td></td><td></td></tr> <tr><td></td><td>162</td><td>SUGGEST</td><td><p>In the Javascript for `watching_videos/listings/rumbl/web/static/js/player.js`, in the `events` object, there is an extra space:</p> <p>“onReady”: (event =&gt; onReady(event) ),</p> <p>should be</p> <p>“onReady”: (event =&gt; onReady(event)),</p> </td><td></td><td></td></tr> <tr><td></td><td>159</td><td>SUGGEST</td><td><p>In `watching_videos/listings/rumbl/web/views/watch_view.ex` when creating the player_id function it uses `Kernel.get_in/2` to create a single pipe rather than assign a variable and use the square bracket notation to access the “id” key. `Kernel.get_in/2` a bit out of place here and is somewhat confusing and may lead people to keep using it this way, when it really should be used for nested structures.</p> <p>I suggest either assigning the result of `Regex.named_captures/1` to a variable and using the square bracket notation to reference it, or using `Access.get/3`</p> </td><td></td><td></td></tr> <tr><td></td><td>163</td><td>TYPO</td><td><p>“Our onYouTubeReady function needs…”</p> <p>`onYouTubeReady` was never created, or mentioned. I believe it’s supposed to be `onIframeReady`.</p> </td><td></td><td></td></tr> <tr><td></td><td>164</td><td>SUGGEST</td><td><p>In `watching_videos/listings/rumbl/web/static/css/video.css` it might be worth mentioning that you do not need to manually add this CSS file in your `app.html.eex`</p> </td><td></td><td></td></tr> <tr><td>46</td><td></td><td>ERROR</td><td><p>The <strong>link</strong> function seems to work differently in newer versions of Elixir.</p> <p>iex&gt; Phoenix.HTML.Link.link(“Home”, to: “/”)<br /> {:safe, [“<a href=\\"/\\">”, “Home”, “</a>”]}</p> <p>now outputs:</p> <p>iex&gt; Phoenix.HTML.Link.link(“Home”, to: “/”)<br /> {:safe, [60, “a”, " href=\\“/\\”“, 62, ”Home“, 60, 47, ”a", 62]}</p> <p>The same issue occurs with the following line.</p> </td><td></td><td></td></tr> <tr><td>25</td><td></td><td>SUGGEST</td><td><p>“Replace the first route in web/router.ex with this one:”</p> <p>I suggest to add a hint, that changing the route will result in an error, when sending a request WITHOUT a “name” param because in</p> <p>get “/hello/:name”, HelloController, :world</p> <p>:name is required and not optional. Calling localhost:4000/hello will result in</p> <p>Phoenix.Router.NoRouteError at GET /hello<br /> no route found for GET /hello (Hello.Router)</p> </td><td></td><td></td></tr> <tr><td></td><td>189</td><td>ERROR</td><td><p>Once annotations are added to the code, videos that are associated with annotations cannot be deleted. I see a constraint violation on annotations_video_id_fkey whenever I attempt to delete a video with annotations assigned.</p> <p>To fix this, the migration *_create_annotation.exs file should use the following definition:</p> <p>add :video_id, references(:videos, on_delete: :delete_all)</p> <p>This is as opposed to the current</p> <p>add :video_id, references(:videos),</p> <p>which is the default if you follow the instructions on pg. 189. (This is also what the code listing shows in the distribution)</p> <p>-Bill</p> </td><td></td><td></td></tr> <tr><td></td><td>167</td><td>ERROR</td><td><p>Once the Phoenix.Param.to_param is overridden for Rumbl.Video to include the slug as part of the id, the Rumbl.VideoControllerTest test “update user video and redirects when using valid data” no longer passes.</p> <p>This is what I did to fix it:</p> <p><code>tag login_as: "gilligan" test "update user video and redirects when using valid data", %{conn: conn, user: us er} do user_video = insert_user_video user conn = put conn, video_path(conn, :update, user_video), video: </code>valid_attrs</p> <p>user_video_mod = Repo.get_by!(Video, @valid_attrs)<br /> assert user_video.id user_video_mod.id assert redirected_to(conn) video_path(conn, :show, user_video_mod)<br /> end</p> <p>-Bill</p> </td><td></td><td></td></tr> <tr><td>212</td><td></td><td>TYPO</td><td><p>In the subsection “Choosing an Information Strategy”, there’s a full stop where there should be a comma - “It doesn’t make sense for us to retry the computation. because”.</p> </td><td></td><td></td></tr> <tr><td>60</td><td></td><td>SUGGEST</td><td><p>`:empty` atom in params in the changeset function is deprecated in newer versions of Phoenix. Suggest that you change it to `:invalid`.</p> </td><td></td><td></td></tr> <tr><td>19</td><td></td><td>ERROR</td><td><p>Problem:<br /> root@backdraft:~# mix local.hex</p> <p><strong></strong> (UndefinedFunctionError) undefined function :inets.stop/2 (module :inets is not available)<br /> :inets.stop(:httpc, :mix)<br /> (mix) lib/mix/utils.ex:355: Mix.Utils.read_httpc/1<br /> (mix) lib/mix/utils.ex:301: Mix.Utils.read_path/2<br /> (mix) lib/mix/local.ex:107: Mix.Local.read_path!/2<br /> (mix) lib/mix/local.ex:86: Mix.Local.find_matching_versions_from_signed_csv!/2<br /> (mix) lib/mix/tasks/local.hex.ex:30: Mix.Tasks.Local.Hex.run/1<br /> (mix) lib/mix/cli.ex:58: Mix.CLI.run_task/2</p> <p>Solution:<br /> sudo apt-get install erlang-ssl erlang-inets</p> </td><td></td><td></td></tr> <tr><td>19</td><td></td><td>SUGGEST</td><td><p>On page 19 you tell the user to check the postgresql version number, but do not tell them to actually create a user. Therefore, when mix ecto.create is run on page 21, it will fail. Even in the Ecto chapter, on page 54, you don’t clarify that the postgresql user will require createdb permission (even if the db is already created (bad Ecto!!)).</p> <p>You should tell the reader that they need a create user command like this:<br /> createuser —createdb —encrypted —login —pwprompt —no-createrole —no-superuser —no-replication wwalker</p> <p>Users may get this:</p> <p><strong></strong> (Mix) The database for Hello.Repo couldn’t be created: ERROR (insufficient_privilege): permission denied to create database</p> <p>I find it referenced in many places, like in the phoenix forums on “Is it necessary to create a postgresql role with superuser privilege” in Phoenix Talk in Groups.google :</p> <p>“Depending on how you are creating the database you may not even need the ”Create DB" attribute."</p> </td><td></td><td></td></tr> <tr><td>119</td><td></td><td>ERROR</td><td><p>Someone before me submitted it as a bug, because uname is not username. The real issue is that uname is not declared eg. add one line with uname = “Jose Valim” and then the current code.</p> </td><td></td><td></td></tr> <tr><td>139</td><td></td><td>ERROR</td><td><p>Not an error but Deprecation Notice.</p> <p>warning: using conn/0 to build a connection is deprecated. Use build_conn/0 instead.</p> <p>Changing<br /> conn = assign(conn(), :current_user, user)<br /> to<br /> conn = assign(build_conn(), :current_user, user)<br /> Fixed for me!</p> </td><td></td><td></td></tr> <tr><td>60</td><td></td><td>ERROR</td><td><p>Another deprecation error</p> <p>warning: `Ecto.Changeset.cast/4` is deprecated, please use `cast/3` + `validate_required/3` instead</p> <p>Changing</p> <p>model<br /> |&gt; cast(params, ~w(name username), [])<br /> |&gt; validate_length(:username, min: 1, max: 20)</p> <p>to</p> <p>model<br /> |&gt; cast(params, ~w(name username))<br /> |&gt; validate_required([:name, :username])<br /> |&gt; validate_length(:username, min: 1, max: 20)</p> <p>Fixed it for me.</p> </td><td></td><td></td></tr> <tr><td>71</td><td></td><td>ERROR</td><td><p>Another deprecation error</p> <p>warning: `Ecto.Changeset.cast/4` is deprecated, please use `cast/3` + `validate_required/3` instead</p> <p>Changing</p> <p>model<br /> |&gt; changeset(params)<br /> |&gt; cast(params, ~w(password), [])<br /> |&gt; validate_length(:password, min: 6, max: 100)<br /> |&gt; put_pass_hash()</p> <p>to</p> <p>model<br /> |&gt; changeset(params)<br /> |&gt; cast(params, ~w(password))<br /> |&gt; validate_required(:password)<br /> |&gt; validate_length(:password, min: 6, max: 100)<br /> |&gt; put_pass_hash()</p> <p>Fixed it for me.</p> </td><td></td><td></td></tr> <tr><td></td><td>191</td><td>TYPO</td><td><p>`resp = <span style="annotations: Phoenix.View.render_many(annotations, AnnotationView, &quot;annotation.json&quot;);">` should be `resp =</span>{annotations: Phoenix.View.render_many(annotations, Rumbl.AnnotationView, “annotation.json”)}`</p> </td><td></td><td></td></tr> <tr><td>41</td><td></td><td>TYPO</td><td><p>The map key “username” is spelled “usernmae”.</p> </td><td></td><td></td></tr> <tr><td>55</td><td></td><td>ERROR</td><td><p>It seems that Elixir now dislikes the timestamps macro in a migration file. The correct syntax is timestamps()</p> </td><td></td><td></td></tr> <tr><td>60</td><td></td><td>ERROR</td><td><p>Text:</p> <p>“alias Rumbl.User<br /> def new(conn, _params) do<br /> changeset = User.changeset(%User{})<br /> render conn, ”new.html“, changeset: changeset<br /> end<br /> Notice the User.changeset function. This function receives a struct and the controller<br /> parameter, and returns an Ecto.Changeset.”</p> <p>The source code only shows changeset accepting one parameter, the struct. Then on the same page:</p> <p>“def changeset(model, params \\\\ :empty) do<br /> model<br /> |&gt; cast(params, ~w(name username), [])<br /> |&gt; validate_length(:username, min: 1, max: 20)<br /> end<br /> Our changeset accepts a User struct and parameters”</p> <p>What? This definition of changeset() is different than what was mentioned at the top of the page!</p> </td><td></td><td></td></tr> <tr><td></td><td>62</td><td>ERROR</td><td><p>resources “/users”, UserController, only: [:index, :show, :new, :create]</p> <p>is wrong, it must be</p> <p>resources “/users”, UserController, only: [:index, :new, :show, :create]</p> <p>or else the “show” route will match /users/new</p> </td><td></td><td></td></tr> <tr><td></td><td>56</td><td>TYPO</td><td><p>My 2¢: These two sentences sound redundant.</p> <p>“The mix ecto.gen.migration creates a migration file for us with a special timestamp to ensure ordering of our database migrations. Note that your migration file- name is different from ours because Ecto prepends a timestamp to maintain the ordering of migrations.”</p> </td><td></td><td></td></tr> <tr><td></td><td>144</td><td>SUGGEST</td><td><p>Hey,</p> <p>On the tests with ``login / logout.</p> <p>I would highly suggest to add a ‘recycle(conn)’ before the new ‘get’.</p> <p>The new ‘get’ simulate a new connection going through the endpoint/router/:browser pipeline. But it isn’t in fact new.<br /> It took me so long to wrap my head around it.</p> <p>Finally when I understood how cookie are sent back by the browser, I just had to look and found that there is the ‘recycle(conn)’ function that would be such a great addition here. (Plus explanation of why it simulates what the browser do (send back cookie)).</p> <p>Thanks ;)</p> </td><td></td><td></td></tr> <tr><td></td><td>42</td><td>SUGGEST</td><td><p>I was a little confused by how the get_by function works. It is described in following sentence:<br /> “Let’s also add a couple of functions to get a user by id, or by a custom attribute”<br /> But the implementation shown processes a keyword list of custom attributes, so really it’s more accurate to say:<br /> “Let’s also add a couple of functions to get a user by id, or by custom attribute(s)”</p> </td><td></td><td></td></tr> <tr><td>102</td><td></td><td>ERROR</td><td><p>the signatures:<br /> ​def​ all(Rumbl.User) ​do​<br /> ​def​ all(_module), ​do​: []<br /> throw this compilation error now:<br /> lib/rumbl/repo.ex:14: def all/1 conflicts with defaults from def all/2</p> </td><td></td><td></td></tr> <tr><td>60</td><td></td><td>SUGGEST</td><td><p>This worked for me:</p> <p>def changeset(user, params \\\\ %{}) do<br /> user<br /> |&gt; cast(params, [:name, :username])<br /> |&gt; validate_required([:name, :username])<br /> |&gt; validate_length(:username, min: 1, max: 20)<br /> end</p> <p>From what I have read, there has been an upgrade to the ‘Ecto.Changeset’ module since the book was written. The recommendation in the current docs is to use a form of cast with a lower arity (cast/3) to include the piped-in user struct as param 1. It appears that the former way of doing things with the ~w(name username) word list parameter goes with the :empty atom for a blank form, but the current version uses the empty map %{} with cast/3 in concert with the validate_ functions.</p> </td><td></td><td></td></tr> <tr><td></td><td>27</td><td>ERROR</td><td><p>navigating to localhost:4000/hello/phoenix generates the error:</p> <p>Phoenix.Router.NoRouteError at GET /hello/phoenix</p> <p>but navigating to localhost:4000/hello?name=phoenix works as expected.</p> </td><td></td><td></td></tr> <tr><td>235</td><td></td><td>ERROR</td><td><p>The listing for the app.html.eex on the book is different from the file linked: /code/authentication/listings/rumbl/web/templates/layout/app.change1.html.eex</p> <p>Which make the app not behave like expected.</p> </td><td></td><td></td></tr> <tr><td>67</td><td></td><td>ERROR</td><td><p>The method:<br /> def first_name(%User{name: name}) do<br /> name<br /> |&gt; String.split(" ")<br /> |&gt; Enum.at(0)<br /> end</p> <p>Fails with the String.split(" "). this happened in the form of a new user when there is nothing type in the form.</p> </td><td></td><td></td></tr> <tr><td>27</td><td></td><td>ERROR</td><td><p>it is a parameter, not a different route. so the line says “point your browser to localhost:4000/hello/phoenix.” should say,<br /> “localhost:4000/hello?name=phoenix”</p> <p>this worked locally, if that is not correct, what did i miss? thanks.</p> </td><td></td><td></td></tr> <tr><td></td><td>43</td><td>ERROR</td><td><p>There is an error running the code up to here.</p> <p>Error when start iex :<br /> Rumpl.Repo.start_link/0 is undefined or private</p> <p>You need this snipped in Repo.ex</p> <p>def start_link do<br /> {:ok, self()}<br /> end</p> <p>see :<br /> stackoverflow (can send the link because your form does not allow it) Search for the error.</p> <p>(I do not know why (I am Beginner)</p> </td><td></td><td></td></tr> <tr><td>135</td><td></td><td>ERROR</td><td><p>When inserting test users, the line: “user#{Base.encode16(:crypto.rand_bytes(8))}” must be changed to “user#{Base.encode16(:crypto.strong_rand_bytes(8))}”, i.e. rand_bytes -&gt; strong_rand_bytes</p> </td><td></td><td></td></tr> <tr><td>72</td><td></td><td>SUGGEST</td><td><p>I am running Elixir 1.5.1, Erlang/OTP 20.<br /> When I ran this page code, I got error: “module Comeonin.Bcrypt is not available”.<br /> I thought to mention this for all the newbiees.</p> <p>Go to “mix.exs” and under “defp deps do” add {:bcrypt_elixir, “~&gt; 1.0”} and then run<br /> mix deps.get<br /> in terminal. The error should be fixed now.</p> <p>Cheers</p> </td><td></td><td></td></tr> <tr><td></td><td>145</td><td>TYPO</td><td><p>“[…] and finally make that no user_id is in the session”</p> <p>I think you missed a word.<br /> It could be “finally make sure that no…”<br /> or “finally assert that no…”</p> </td><td></td><td></td></tr> <tr><td></td><td>262</td><td>TYPO</td><td><p>Bottom of the page “we recommend that you to study…”.<br /> Remove the “to”.</p> </td><td></td><td></td></tr> <tr><td>140</td><td></td><td>ERROR</td><td><p>The first assert of the second test, namely “does not create video and renders errors when invalid”, assets a status code of 200, when the test itselfs says it fails, which means by definition cannot return a 2xx code.<br /> It should be 302 or something alike,</p> </td><td></td><td></td></tr> <tr><td>82</td><td></td><td>SUGGEST</td><td><p>In the most recent versions of Elixir and Comeonin, a few of the instructions here will not work, and need minor changes:</p> <p>1. As of Elixir 1.4, the :applications key in the mix.exs application method is inferred by Elixir based on the deps method. Unless you’ve added it yourself, your mix.exs file’s application method won’t have an :applications key, and you won’t need to add one for this to work. See the “Elixir v1.4 released” post on the Elixir blog for more details (the errata form blocks links to prevent spam; sorry to make you dig!).</p> <p>2. As of Comeonin 4, you need to add an additional entry to the list in the mix.exs deps method for whichever algorithm you want to use. This book uses Bcrypt, so you’ll need to add the following entry (version number may be different if you’re reading this after November 11, 2017): {:bcrypt_elixir, “~&gt; 1.0”}.</p> </td><td></td><td></td></tr> <tr><td>200</td><td></td><td>ERROR</td><td><p>On Erlang/OTP 20, Elixir 1.5.2</p> <p>Running the simple OTP Counter example, code is here gist.github.com/daya/865116d1fed01577f8764f5f1a691d85, causes ArgumentError on :erlang.send</p> <p>Interactive Elixir (1.5.2) - press Ctrl+C to exit (type h() ENTER for help)<br /> iex(1)&gt; alias Rumbl.Counter<br /> Rumbl.Counter<br /> iex(2)&gt; counter = Counter.start_link(0)<br /> listening….. initial value 0<br /> {:ok, #PID&lt;0.324.0&gt;}<br /> iex(3)&gt; Counter.val(counter)<br /> {:ok, #PID&lt;0.324.0&gt;}<br /> here<br /> #Reference&lt;0.1548235550.607911943.102579&gt;<br /> #PID&lt;0.321.0&gt;</p> <p><strong></strong> (ArgumentError) argument error<br /> :erlang.send({:ok, #PID&lt;0.324.0&gt;}, {:val, #PID&lt;0.321.0&gt;, #Reference&lt;0.1548235550.607911943.102579&gt;})<br /> (rumbl) lib/rumbl/counter.ex:13: Rumbl.Counter.val/2<br /> iex(3)&gt;</p> </td><td></td><td></td></tr> <tr><td>149</td><td></td><td>ERROR</td><td><p>The test for invalid attributes fails with Phoenix 1.2.5/Ecto 2.0 since the definition of cast/4 changed:</p> <p>changeset = User.changeset(%User{}, @invalid_attrs)</p> <p>To make it work as intended, the @invalid_attrs need to be changed to something like this:</p> <p>@invalid_attrs %{name: 123}</p> </td><td></td><td></td></tr> <tr><td>82</td><td></td><td>ERROR</td><td><p>“As you recall, the Plug.Conn struct has a field called assigns . We call setting a<br /> value in that structure an assign. Our function stores the given user as the<br /> :current_user assign, puts the user ID in the session, and finally configures the<br /> session, setting the :renew option to true . The last step is extremely important<br /> and it protects us from session fixation attacks. It tells Plug to send the session<br /> cookie back to the client with a different identifier, in case an attacker knew,<br /> by any chance, the previous one.”</p> <p>I believe this is incorrect. I noticed that up to this point, no serverside session storage had been implemented, so I consulted the docs which say that Phoenix defaults to using signed cookies for sessions instead of serverside storage. In this case, session fixation should be impossible.</p> <p>In any case, if using serverside session storage, why would one not just not allow overwriting the user_id in the session? Or encrypt the user_id as the session_id?</p> <p>As it stands, this section doesn’t really make sense. Or it’s not great advice. Or I missed something, but my research over a couple hours failed to clear this up and only made me more confident something was fishy.</p> </td><td></td><td></td></tr> <tr><td>238.4</td><td></td><td>TYPO</td><td><p>EPUB 238.4/551 Chapter 7 just before Diving Deeper into Ecto Queries</p> <p>It wouldn’t let me put the hyperlink in the errata so had to drop the http protocol and replaced it with URL</p> <p>URL localhost:4000/manage/videos/new: should not have the trailing colon and should be URL localhost:4000/manage/videos/new</p> </td><td></td><td></td></tr> <tr><td>1</td><td></td><td>SUGGEST</td><td><p>I’d change “… to know most of our ideas aren’t new.” to “… to know that most of our ideas aren’t new.”.</p> <p>I’d also cut down on the use of “so many” here: “It’s the combination of so many of the best ideas from so many other places that has so many people excited.”</p> </td><td></td><td></td></tr> <tr><td>5</td><td></td><td>SUGGEST</td><td><p>I’d remove most of the blanks before UserController in this line: get “/users”, UserController, :index</p> <p>Also, doesn’t the “/api/” scope need to precede the “/” scope?</p> </td><td></td><td></td></tr> <tr><td>7</td><td></td><td>SUGGEST</td><td><p>I’d change “… communicated to its own process …” to “… communicated with its own process …”.</p> </td><td></td><td></td></tr> <tr><td>10</td><td></td><td>SUGGEST</td><td><p>I’d change “… and if those pieces …” to “… and whether those pieces …”.</p> </td><td></td><td></td></tr> <tr><td>26</td><td></td><td>SUGGEST</td><td><p>I’d change “Tx” to “TX”.</p> </td><td></td><td></td></tr> <tr><td>37</td><td></td><td>SUGGEST</td><td><p>I’d change “… on building the controllers, views, and templates.” to “… on building controllers, views, and templates.”.</p> </td><td></td><td></td></tr> <tr><td>39</td><td></td><td>TYPO</td><td><p>There’s a free-floating dollar sign just above “First, run mix ecto.create to prep …”.</p> </td><td></td><td></td></tr> <tr><td>43</td><td></td><td>ERROR</td><td><p>The function all/1 simply returns a list, so the order shouldn’t change from 1/2/3 to 1/3/2 (as shown in the iex session.</p> </td><td></td><td></td></tr> <tr><td>46</td><td></td><td>ERROR</td><td><p>The book says “At runtime, Phoenix will translate this template to a function, …”. Doesn’t the translation occur at compile time?</p> </td><td></td><td></td></tr> <tr><td>61</td><td></td><td>TYPO</td><td><p>“… when your learn about authentication.” should be “… when you learn about authentication.”.</p> </td><td></td><td></td></tr> <tr><td>76</td><td></td><td>SUGGEST</td><td><p>Under the heading path_info, I see “List” (in monospace font). Then, under the heading req_headers, I see “list” (in proportional font). This is a jarring inconsistency.</p> </td><td></td><td></td></tr> <tr><td>76</td><td></td><td>SUGGEST</td><td><p>I’d rewrite tis sentence, to make it less awkward: “Sometimes a connection must be halted, such as a failed authorization.”</p> </td><td></td><td></td></tr> <tr><td>82</td><td></td><td>SUGGEST</td><td><p>The text says: “As you recall, the Plug.Conn struct has a field called assigns. We call setting a value in that structure an assign.” This is a fine explanation, but it appears quite a ways after the first uses of the term “assign”. I’d rework this to eliminate the forward reference.</p> </td><td></td><td></td></tr> <tr><td>84</td><td></td><td>SUGGEST</td><td><p>The first couple of line breaks in the “def create” example are ugly. I’d tweak them to make the code’s meaning more obvious.</p> </td><td></td><td></td></tr> <tr><td>98</td><td></td><td>SUGGEST</td><td><p>The “def change” example uses :string for short items and :text for longer ones. As I understand it, PostgreSQL treats these exactly the same, save that it imposes a 255 byte limit on :string. I’d either use :text for everything or explain why I’m not doing so.</p> </td><td></td><td></td></tr> <tr><td>101</td><td></td><td>SUGGEST</td><td><p>The iex examples reuse the user and view variables in a manner which I find confusing. Why not pick different names for the different uses?</p> </td><td></td><td></td></tr> <tr><td>102</td><td></td><td>SUGGEST</td><td><p>In “We need to change it so the video is built …”, I find the term “video” confusing. Are we referring to the video changeset or what? Please clarify this.</p> </td><td></td><td></td></tr> <tr><td>107</td><td></td><td>SUGGEST</td><td><p>I’d change “… by associating videos to users.” to “… by associating videos with users.”.</p> </td><td></td><td></td></tr> <tr><td>113</td><td></td><td>TYPO</td><td><p>“… which fetches the names and IDs tuples …” should be “… which fetches the name and ID tuples …”.</p> </td><td></td><td></td></tr> <tr><td>130</td><td></td><td>TYPO</td><td><p>“… function on your calculator module.” should be “… function in your calculator module.”.</p> </td><td></td><td></td></tr> <tr><td>133</td><td></td><td>SUGGEST</td><td><p>I’d change “… needs the database and restart the database transaction …” to “… needs the database, so we restart the database transaction …”.</p> </td><td></td><td></td></tr> <tr><td>141</td><td></td><td>SUGGEST</td><td><p>Why not put the :delete, :edit, and :show asserts in a comprehension?</p> </td><td></td><td></td></tr> <tr><td>147</td><td></td><td>SUGGEST</td><td><p>The text says “The reason our tests are slow is that …”. This may have been self-evident in this case, but often it won’t be. This might be a teachable moment for explaining how one might figure this out, what profiling tools are available, etc.</p> </td><td></td><td></td></tr> <tr><td>151</td><td></td><td>TYPO</td><td><p>“… is a uniqueness constraint checks in our changeset.” should be “… is a uniqueness constraint check in our changeset.”.</p> </td><td></td><td></td></tr> <tr><td>159</td><td></td><td>SUGGEST</td><td><p>The book says “We’re naming a pattern called id and then …”. I see a pattern (~r{…}), but no indication that it has been “named”. Wazzup?</p> </td><td></td><td></td></tr> <tr><td>4.5</td><td></td><td>ERROR</td><td><p>Safari Books Section “Building Forms”</p> <p>In Ecto 2.2.8 the :empty atom has been deprecated, so an empty map is required. With this change validate_length will not work on it’s own, causing a Postex error.</p> <p>Book Code:<br /> def​ changeset(model, params \\\\ ​:empty​) ​do​<br /> ​ \t model<br /> ​ \t |&gt; cast(params, ​~​w(name username), [])<br /> ​ \t |&gt; validate_length(​:username​, ​min:​ 1, ​max:​ 20)<br /> ​ end​</p> <p>Working Code<br /> def​ changeset(model, params \\\\ ​:{}​) ​do​<br /> ​ \t model<br /> ​ \t |&gt; cast(params, ​~​w(name username), [])<br /> |&gt; validate_required([ :username])<br /> ​ \t |&gt; validate_length(​:username​, ​min:​ 1, ​max:​ 20)<br /> ​ end​</p> </td><td></td><td></td></tr> <tr><td>N/A</td><td></td><td>ERROR</td><td><p>Safaribooks, section: Building Forms</p> <p>This code is not working as of 13th April, 2018. Already this issue is reported on the errata section but that contains typo in the solution. We have to replace the :empty atom with and empty map %{}. See the code given below,</p> <p>Code with error:<br /> def changeset(model, params \\\\ :empty) do<br /> model<br /> |&gt; cast(params, ~w(name username), [])<br /> |&gt; validate_length(:username, min: 1, max: 20)<br /> end</p> <p>Working version:<br /> def changeset(model, params \\\\ %{}) do<br /> model<br /> |&gt; cast(params, ~w(name username), [])<br /> |&gt; validate_length(:username, min: 1, max: 20)<br /> end</p> </td><td></td><td></td></tr> <tr><td>135</td><td></td><td>SUGGEST</td><td><p>:crypto.rand_bytes/1 is deprecated and should be replaced with :crypto.strong_rand_bytes/1</p> </td><td></td><td></td></tr> <tr><td>166</td><td></td><td>SUGGEST</td><td><p>I suggest you explicitly point out that in “String.replace(~r/[^\\w-]+/u, ”-“)” it is “\\w” and not “|w”. With italic rendering of Regexps it’s barely clear. Those who don’t know Regexp will find it hard to debug as it will continue working with awkward results.</p> </td><td></td><td></td></tr> <tr><td>20</td><td></td><td>ERROR</td><td><p>Although we don’t use any data model at this stage I still needed to add<br /> ``{:ecto_sql, “~&gt; 3.0”},<br /> into the deps() function of mix.exs<br /> Current changes to ecto split the validation and sql into two parts.</p> </td><td></td><td></td></tr> <tr><td>67</td><td></td><td>ERROR</td><td><p>In Defining the User Schema and Migration under schema timestamps should be timestamps() to avoid compiler warning.</p> </td><td></td><td></td></tr> <tr><td>58</td><td></td><td>ERROR</td><td><p>The</p> <p>iex&gt; Repo.insert(%User{<br /> …&gt; name: “José”, username: “josevalim”, password_hash: “&lt;3&lt;3elixir” …&gt; })</p> <p>gave the following error</p> <p>[error] an exception was raised logging %DBConnection.LogEntry{call: :prepare_execute, connection_time: 2986000, decode_time: 10000, params: [“Chris”, “phx”, “chrismccord”, ~N[2018-12-26 10:09:16], ~N[2018-12-26 10:09:16]], pool_time: 2707000, query: %Postgrex.Query{cache: :statement, columns: nil, name: “ecto_insert_users”, param_formats: nil, param_oids: nil, param_types: nil, ref: nil, result_formats: nil, result_oids: nil, result_types: nil, statement: “INSERT INTO \\”users\\" (\\“name\\”,\\“password_hash\\”,\\“username\\”,\\“inserted_at\\”,\\“updated_at\\”) VALUES ($1,$2,$3,$4,$5) RETURNING \\“id\\”“, types: nil}, result: {:ok, %Postgrex.Query{cache: :statement, columns: [”id“], name: ”ecto_insert_users“, param_formats: [:binary, :binary, :binary, :binary, :binary], param_oids: [1043, 1043, 1043, 1114, 1114], param_types: [Postgrex.Extensions.Raw, Postgrex.Extensions.Raw, Postgrex.Extensions.Raw, Postgrex.Extensions.Timestamp, Postgrex.Extensions.Timestamp], ref: #Reference&lt;0.3104968478.2288779267.165923&gt;, result_formats: [:binary], result_oids: [20], result_types: [Postgrex.Extensions.Int8], statement: ”INSERT INTO \\“users\\” (\\“name\\”,\\“password_hash\\”,\\“username\\”,\\“inserted_at\\”,\\“updated_at\\”) VALUES ($1,$2,$3,$4,$5) RETURNING \\“id\\”“, types: {Postgrex.DefaultTypes, #Reference&lt;0.3104968478.2288910340.165678&gt;}}, %Postgrex.Result{columns: [”id"], command: :insert, connection_id: 72633, messages: [], num_rows: 1, rows: [[3]]}}}: <strong></strong> (ArgumentError) argument error<br /> (stdlib) :ets.lookup(Telemetry.HandlerTable, [:rumbl, :repo, :query])<br /> lib/telemetry/handler_table.ex:59: Telemetry.HandlerTable.list_for_event/1<br /> lib/telemetry.ex:76: Telemetry.execute/3<br /> lib/ecto/adapters/sql.ex:770: Ecto.Adapters.SQL.log/4<br /> (db_connection) lib/db_connection.ex:1311: DBConnection.log/5<br /> (postgrex) lib/postgrex.ex:161: Postgrex.query/4<br /> lib/ecto/adapters/sql.ex:641: Ecto.Adapters.SQL.struct/10<br /> (ecto) lib/ecto/repo/schema.ex:651: Ecto.Repo.Schema.apply/4<br /> (ecto) lib/ecto/repo/schema.ex:264: anonymous fn/15 in Ecto.Repo.Schema.do_insert/3<br /> (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6<br /> (elixir) src/elixir.erl:265: :elixir.eval_forms/4<br /> (iex) lib/iex/evaluator.ex:249: IEx.Evaluator.handle_eval/5<br /> (iex) lib/iex/evaluator.ex:229: IEx.Evaluator.do_eval/3<br /> (iex) lib/iex/evaluator.ex:207: IEx.Evaluator.eval/3<br /> (iex) lib/iex/evaluator.ex:94: IEx.Evaluator.loop/1<br /> (iex) lib/iex/evaluator.ex:24: IEx.Evaluator.init/4<br /> (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3</p> <p>However checking postgresql shows that the data was inserted.</p> </td><td></td><td></td></tr> <tr><td>71</td><td></td><td>ERROR</td><td><p>To avoid the warning:</p> <p>warning: non-atom keys in cast/3 is deprecated. All keys must be atoms, got: `“password”`</p> <p>make changes in User.changeset/2 and User.registration_changeset/2 to cast</p> <p>|&gt; cast(params, [:name, :username], [])</p> <p>and</p> <p>|&gt; cast(params, [:password], [])</p> <p>respectively.</p> </td><td></td><td></td></tr> <tr><td>96</td><td></td><td>ERROR</td><td><p>Chapter 5, building the login section:</p> <p>To try out the new page, we have to logout, but we haven’t written that functionality yet. As a temporary workaround, instead of logging out you can clear your browser cookies or start a new session in incognito mode…</p> <p>Accessing the /sessions/new page is not prevented by anything at this point, so it is not necessary to clear browser cookies, or work in incognito.</p> </td><td></td><td></td></tr> </table> </div> <section class="cat"> <section class="cat"> <h3>Categories:</h3> <ul> <li><a href="/categories"><i>Browse All Categories</i></a></li> <li><a href="https://pragprog.com/categories/android-ios-and-mobile/"> Android, IOS, and Mobile </a></li> <li><a href="https://pragprog.com/categories/architecture-design-and-testing/"> Architecture, Design, and Testing </a></li> <li><a href="https://pragprog.com/categories/audio-books/"> Audio Books </a></li> <li><a href="https://pragprog.com/categories/beta/"> Beta </a></li> <li><a href="https://pragprog.com/categories/brain-teasers/"> Brain Teasers </a></li> <li><a href="https://pragprog.com/categories/cloud-and-networking/"> Cloud and Networking </a></li> <li><a href="https://pragprog.com/categories/data-and-data-science/"> Data and Data Science </a></li> <li><a href="https://pragprog.com/categories/distributions/"> Distributions </a></li> <li><a href="https://pragprog.com/categories/elixir-phoenix-and-otp/"> Elixir, Phoenix, and OTP </a></li> <li><a href="https://pragprog.com/categories/for-beginners/"> For Beginners </a></li> <li><a href="https://pragprog.com/categories/functional-programming/"> Functional Programming </a></li> <li><a href="https://pragprog.com/categories/game-dev-graphics-and-media/"> Game Dev, Graphics, and Media </a></li> <li><a href="https://pragprog.com/categories/go-language/"> Go Language </a></li> <li><a href="https://pragprog.com/categories/hardware-hobby-and-home/"> Hardware, Hobby, and Home </a></li> <li><a href="https://pragprog.com/categories/java-and-jvm-languages/"> Java and JVM Languages </a></li> <li><a href="https://pragprog.com/categories/javascript/"> Java&#8203;Script </a></li> <li><a href="https://pragprog.com/categories/management-people-and-teams/"> Management, People, and Teams </a></li> <li><a href="https://pragprog.com/categories/popular-libraries-and-frameworks/"> Popular Libraries and Frameworks </a></li> <li><a href="https://pragprog.com/categories/pragmatic-answers/"> Pragmatic Answers </a></li> <li><a href="https://pragprog.com/categories/pragmatic-express/"> Pragmatic Ex&#8203;Press </a></li> <li><a href="https://pragprog.com/categories/programming-languages/"> Programming Languages </a></li> <li><a href="https://pragprog.com/categories/python/"> Python </a></li> <li><a href="https://pragprog.com/categories/ruby-and-rails/"> Ruby and Rails </a></li> <li><a href="https://pragprog.com/categories/seven-in-seven/"> Seven in Seven </a></li> <li><a href="https://pragprog.com/categories/tools/"> Tools </a></li> <li><a href="https://pragprog.com/categories/web-development/"> Web Development </a></li> </ul> </section> </section> <aside> <div class="loginbox"> <h2>Releases, Offers &amp; More</h2> <p> Be the first to hear about our newest content, best promotions and upcoming events. Plus get <span style="font-weight: bold; font-size: 1.1em;">25% off</span> your next purchase. </p> <p> <a class="loginbtn" href="/newsletter">Newsletter Sign Up</a> </p> <hr> <h2>Download Accounts</h2> <p> Your email address is your account identifier. You can create a password, or just download from the links sent via email. </p> <p> <a href="https://transactions.sendowl.com/customer_accounts/164668" class="loginbtn">My Orders</a> </p> <p> (<a href="https://transactions.sendowl.com/order_recoveries/new?merchant_id=164668" style="color: white;">Resend order emails</a>) </p> </div> <div class="box"> <h2>How We're Different</h2> <ul class="compact"> <li><p>Hands-on instructions</p></li> <li><p>Solutions to real-world problems</p></li> <li><p>DRM-free ebooks</p></li> <li><p>Free updates within an edition</p></li> <li><p>Pioneered Beta books</p></li> <li><p>We're software developers, too</p></li> </ul> </div> </aside> <footer class="footer"> <ul style="margin-block-end: 0.5em"> <li><a href="/contact">Contact</a></li> <li><a href="/about">About Us</a></li> <li><a href="/privacy">Privacy</a></li> <li><a href="/terms-of-use">Terms of Use</a></li> <li><a href="/security">Security</a></li> <li><a href="/newsletter">Newsletter</a></li> <li><a href="https://www.giftya.com/brands/1568049/pragmatic-bookshelf" target="_blank">eGifts</a></li> <li><a href="/promotions">Partnerships &amp; Promotions</a></li> <li><a href="/careers">Careers</a></li> </ul> <div style="font-size: 70%; color: #C2CBC2"> &copy; 1999-2025 The Pragmatic Programmers, LLC. All Rights Reserved. </div> </footer> <script src="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js" data-cfasync="false"></script> <script> window.cookieconsent.initialise({ "palette": { "popup": { "background": "#915465", "text": "#ffdddd" }, "button": { "background": "#93535B" } }, "theme": "classic", "content": { "message": "This website uses cookies for navigation, order transactions, and general analytics. ", "href": "https://pragprog.com/privacy" } }); </script> </div> </body> </html>