By Developers, For Developers

Historical errata for Programming Phoenix 1.4

PDF PgPaper PgTypeDescriptionFixed onComments
9TYPO

connections instead of connection:

IS:
crashing a database connections on purpose

SHOULD BE:
crashing a database connection on purpose

2018-06-19
57TYPO

Comma at the end of line “iex>​ alias Rumbl.Repo,” should be erased ?

2018-06-23
45SUGGEST

Encourage the use instead of tag. More info at www.w3.org.

2018-06-23
56-57TYPO

Unexpected tags after code following the ecto migration:

~

environmentsmigrations
environmentschanging
migrationschanging environments

OTPstarting key servicesEctostarting from OTP

The format of the paragraph is broken until found ~ again.

2018-06-23
66ERROR

The file where the supervision tree starts the Rumbl.Repo is located in lib/rumbl/application.ex instead of lib/rumbl.ex.

2018-06-23
17TYPO

“Elixir needs Erlang” says it needs v17 or greater, but hexdocs.pm Guides say v18 or greater. It also seems like Travis only runs tests on v18.3 and up.

2018-06-23
17TYPO

“Phoenix needs Elixir” says it requires Elixir 1.1 but the Guides on hexdocs.pm say 1.4 or greater.

2018-06-23
69TYPO

The responses on iex using changeset starts with # instead of %

iex> alias Rumbl.Accounts.User
iex> changeset = User.changeset(%User{username: “eric”},
%{})
Ecto.Changeset{changes:{}, …} #here starts with # instead of %
iex> import Ecto.Changeset
Ecto.Changeset
iex> changeset = put_change(changeset, :username, “ericmj”)
Ecto.Changeset{changes:{username: “ericmj”}, …} #here starts with # instead of %

2018-06-25
82ERROR

Sample route of endpoint.ex is wrong: lib/rumbl/endpoint.ex should be lib/rumbl_web/endpoint.ex.

2018-06-25Chris, there are three instances that need to be picked up for this one.
132ERROR

The text says “Next, let’s share it with the router, like this:” and has a code block starting with ‘scope “/manage”,…’.
A couple paragraphs down, it has the exact same code block, so I think this first code block is not correct.

2018-06-25
136ERROR

First, a “mix ecto.rollback” is performed.
The text says that the next step is to “migrate back up”, but it says “mix ecto.rollback” again when I think it should say “mix ecto.migrate”. The output also shows “backward” when it should show “forward”.

2018-06-23
139ERROR

The text says “video = Repo.update!(changeset)” but “Repo” has not yet been aliased.
I believe it should say “video = Rumbl.Repo.update!(changeset)”.

2018-06-23
103ERROR

In order to pipe through authenticate_user on the router, we need to import the function: lib/rumbl_web/router.ex
import RumblWeb.Auth, only: [authenticate_user: 2]

Then the function will be available to pipe:
pipe_through [:browser, :authenticate_user]

2018-06-25
110ERROR

When referring to Repo, needs to have Rumbl.Repo as per on the iex session we didn’t define the alias.

Solution: Add alias at the beginning of the iex session as per is used many times.
alias Rumbl.Repo

iex> video = Repo.update!(changeset)
…> |> Repo.update!()
iex> video = Repo.get(Rumbl.Multimedia.Video, video.id)

And in page 111
iex> video = Repo.preload(video, :user)
iex> Repo.one(query)

2018-06-23
133TYPO

“Comedy” instead of “Comdey”

iex> Repo.all from v in Video,
…> join: u in assoc(v, :user),
…> join: c in assoc(v, :category),
…> where: c.name == “Comdey”,
…> select: {u, v}

2018-06-23
80ERROR

The registration and creation of users were mentioned on the backend but not on the front end. And in pg. 137 the view has the email and password fields.

This could be added to lib/rumbl_web/templates/user/new.html.eex in some point on the “creating users” section on page: 80 or 81.

+ <%= inputs_for f, :credential, fn i -> %>
+

+ <%= text_input i, :email, placeholder: “Email” %>
+ <%= error_tag i, :email%>
+

+

+ <%= password_input i, :password, placeholder: “Password” %>
+ <%= error_tag i, :password%>
+

+ <% end %>

2018-06-25
40TYPO

Just like our RumblWeb.Endpoint starts our web server, the Rumb.Repo line ensures
our application startup starts our application Repo. Should be “the Rumbl.Repo line ensures”…

2018-06-19
41TYPO

We’ll add a couple function which allows user…Should be “We’ll add a couple of functions which allow user”…

2018-06-19
43OK

I’m coming from Rails, which has strong naming conventions, e.g. controllers are plural, tables are plural, models are singular; the names of classes correspond to the filenames in which you can find them..

It might be good to have a side-bar or footnote here, which discusses the specific naming conventions which Elixir / Phoenix have

2018-07-28This is probably a good idea. We will look at this as we ready the entire book for production. I am going to leave this one open. \n \nWe actually already address this in several places. Marking this fixed.
43ERROR

There might be an issue with the CSS… the “/users” part of that error message is rendered white on white - invisible

I get the error message:

UndefinedFunctionError at GET

When I select the text, I can see the “/users”:

UndefinedFunctionError at GET /users
function RumblWeb.UserController.init/1 is undefined (module RumblWeb.UserController is not available)


defp deps do
[
{:phoenix, “~> 1.3.2”},
{:phoenix_pubsub, “~> 1.0”},
{:phoenix_ecto, “~> 3.2”},
{:postgrex, “>= 0.0.0”},
{:phoenix_html, “~> 2.10”},
{:phoenix_live_reload, “~> 1.0”, only: :dev},
{:gettext, “~> 0.11”},
{:cowboy, “~> 1.0”}
]
end

Browser:
Google Chrome\t66.0.3359.181 (Official Build) (64-bit)
Revision\ta10b9cedb40738cb152f8148ddab4891df876959-refs/branch-heads/3359@{#828}
OS\tMac OS X

2018-06-25chris, I am also going to leave this one for you. \n \nThis was a hover styling issue on the plug debugger side. I have submitted a PR to plug to fix :)
45ERROR

index.html.eex references Routes, but:

UndefinedFunctionError at GET /users
function Routes.user_path/3 is undefined (module Routes is not available)

2018-06-25You used the phx.new generator for 1.3, which does not introduce the `Routes` alias, where our unreleased 1.4 generator uses the Routes alias. We will update the install instructions to avoid this confusion. Thanks!
56TYPO

looks like there is unfinished text / improperly formatted text at the end of page 56 and starting on page 57

2018-06-19
57TYPO

shouldn’t it say “lib/application.ex”, not “lib/rumbl.ex” ?

The supervisor for Rumbl.Repo is already listed in application.ex

2018-06-23
31TYPO

web/router.ex => lib/hello_web/router.ex

2018-06-23
34ERROR

There is a mention of the no longer existing web directory.

2018-06-23
62TYPO

“You’ll see a good example of this policy segregation when your learn about authentication.” should read “You’ll see a good example of this policy segregation when you learn about authentication.” Replace “your” by “you”

2018-06-19
63TYPO

“These build three input fields and a submit tag.” should read “These build two input fields and a submit tag” since only :name and :username inputs are created.

2018-06-23
108ERROR

Rolling back twice on this page. The second rollback should be a migrate command. Replace secong rollback with ` mix ecto.migrate`

2018-06-23
80TYPO

In the following text, simplify should be simplifying:

This new function isn’t very exciting, but as we grow our application, our purpose-built functions will pay dividends by simplify interactions with our controller

2018-06-19
81ERROR

Statement “Now we should be able to visit://localhost:4000/users/new and create new users with our registration changeset.” requires additional code to be true.

The template for users/new.html needs to have a field added to support entering the email. Also, new users may be created that lack an email once this is done. The users table is populated, but the credentials table is untouched. Validation of the email existence is also not done. Return value of the Accounts.register.user(user_params) call in UserController.create() is {:ok,
%Bernice.Accounts.User{meta: #Ecto.Schema.Metadata<:loaded, “users”>,
credential: #Ecto.Association.NotLoaded<association :credential is not loaded>,
id: 27, inserted_at: ~N[2018-06-02 18:49:44.334380], name: “test12”,
updated_at: ~N[2018-06-02 18:49:44.334388], username: “testusername12”}}
in such a case.

2018-06-25
125TYPO

“As it has evolved, we place such concepts in concepts to leave controllers as thin and simple as possible.” should read “As it has evolved, we place such concepts in Contexts to leave controllers as thin and simple as possible.”. Replace second “concepts” by “Contexts”.

2018-06-23
103ERROR

After “sharing” the authenticate_user/2 function with the controller by editing lib/rumbl_web.ex, the book states “Now let’s share it with the router”, and then lists the following text:
———————
relationships/listings/rumbl/lib/rumbl_web/router.change1.ex
———————
scope “/manage”, RumblWeb do
pipe_through [:browser, :authenticate_user]
resources “/videos”, VideoController
end

Given the context of the paragraph, the text should say to edit lib/rumbl_web.ex:
————-
def router do
quote do
use Phoenix.Router
import Plug.Conn
import Phoenix.Controller
import RumblWeb.Auth, only: [authenticate_user: 2] # New import
end
end

Basically, this is an error of omission since the code doesn’t work until the “import RumblWeb.Auth, only: [authenticate_user: 2]” line is added to 2 places in lib/rumbl_web.ex.
Also, this is a typo error because the text (which appears again on the top of page 104) appears in the wrong place.

2018-06-25
112TYPO

In the sentence near the middle of the page:
“to the new create_video function we wrote in the Media context, like this:”

The word “Media” should be replaced with “Multimedia”

2018-06-23
4545ERROR

The book’s code for index.html.eex just has

, but this will not produce output like the book shows on page 47. For that it should be

2018-06-25The "old" phoenix 1.3 phx.new generator used bootstrap, which uses the table class. The new 1.4 project generator does not use bootstrap in favor of a mostly classless markup. We will update the book instructions to use the unreleased project generator to resolve the confusion. Thanks!
80ERROR

When the rumble project is modifed to include the use of passwords, the ‘users/new’ form is never updated to allow a user to register with an email/password.

2018-06-25
95ERROR

The links in the header to no look like they do in the photo. I think there is possibly something wrong with the version of Bootstrap being used or just incorrect HTML/CSS. I’ve run into this problem multiple times throughout the book. Form inputs are another example, they need to be wrapped in form-group divs and the code examples in the book don’t do that.

2018-06-25
81ERROR

This section of the book is missing the explanation of how you add the email and password form fields so you can create users with credentials through the UI. I think it’s actually a good place to introduce nested form inputs using inputs_for, because I think that is what you need to do to get it working.

2018-06-25
110ERROR

This section doesn’t tell you to alias Rumbl.Repo, so when you call this:

video = Repo.update!(changeset)

you will get errors that say:

(UndefinedFunctionError) function Repo.get/2 is undefined (module Repo is not available)
Repo.get(Rumbl.Multimedia.Video, 2)

So you either need to alias Rumbl.Repo or refer to Rumbl.Repo fully-qualified

2018-06-23
45ERROR

At this point in the book, you have covered how to manually create routes. But in this page, you use the link helper with an argument to a named route, which (I think?) isn’t automatically created based on the router code up until this point. Readers who’ve seen other frameworks may understand what’s up, but people who have little experience with routers in other frameworks may be stumped.

2 possible solutions:

1) Change the way the link is coded in the view example so that it doesn’t use the undefined `user_path`
2) Change the routing definition so that `user_path` is defined (but it may be a little early here to introduce resources).

Great work on this version of the book, by the way. Loving it so far.

2018-06-25`resources "/users"` indeed defines the `Routes.user_path` so it is not an undefined function. Thanks!
51TYPO

“Don’t worry, though: things will pick up in a hurry.”

Colon out of no where?

2018-06-19
50-51SUGGEST

Can the book say something about assigns? Or tell me not to worry about it at least? Not talking about it is weird.

Error page:

def template_not_found(template, _assigns) do

layout:

<%= render view_module, view_template, assigns %>

2018-07-28Bruce, leaving this one to you
45TYPO

Super minor style detail: code at page 44 includes a empty line between “use” and “alias”, but code at page 45 doesn’t. Cheers!

2018-06-25
103TYPO

Code for route repeat twice on page 103 and 104. I check several time and they’re the same thing.

relationships/listings/rumbl/lib/rumbl_web/router.change1.ex
scope “/manage”, RumblWeb do
pipe_through [:browser, :authenticate_user]
resources “/videos”, VideoController end

2018-06-23
72SUGGEST

I have to use the package pbkdf2_elixir at this time because depending on bcrypt_elixir and comeonin don’t let me accomplish the examples in the book with the next error: An error occurred when loading Bcrypt.

“Make sure you have a C compiler and Erlang 20 installed.
If you are not using Erlang 20, either upgrade to Erlang 20 or
use version 0.12 of bcrypt_elixir”

I’m using a Windows 10 with Linux Subsystem and Visual Studio Code

I get the suggestion for replace this package from here:
github from user KronicDeth at project intellij-elixir issues 891

So with the code replacement I get:

defp put_pass_hash(changeset) do
case changeset do
Ecto.Changeset{valid?: true, changes:{password: pass}} ->
put_change(changeset, :password_hash, Pbkdf2.hash_pwd_salt(pass))
_ ->
changeset
end
end

2018-07-28We have moved to using pbkdf_elixir to avoid the dependency on a compiler. Thanks!
95ERROR

While updating app.html.eex the code from the file in app.change1.html.eex (cannot post link) causes an error: “function RumblWeb.Router.Helpers.static_push/2 is undefined or private” for css/jss assets; if just inserting the container section as from the book and leaving the original static_path/2 function there is no issue.

2018-06-25
45OK

There should not be Routes.user_path it just be user_path in the below snippet.

controllers_views_templates/listings/rumbl/lib/rumbl_web/templates/user/index.html.eex

Listing Users

<%= for user <- users do %> <tr> <td><b><%= first_name(user) %></b> (<%= user.id %>)</td> <td><%= link "View", to: Routes.user_path(conn, :show, user.id) %>

<% end %>

2018-07-28
45OK

In reference to #83303 submitted by me …

I believe the problem is that Phoenix did not generate the same rumbl_web.ex. In my version it did not add “as: Routes” to the alias of RumblWeb.Router.Helpers

So I’m not sure if the code generator needs updating or if the documentation needs correcting. You reference “as: Routes” later on on page 66 I believe but you suggest that it is automatically added for you at site creation.

Snippet pasted below of what my version created.

def controller do
quote do
use Phoenix.Controller, namespace: RumblWeb
import Plug.Conn
import RumblWeb.Router.Helpers
import RumblWeb.Gettext
end
end

2018-07-30To use the unreleased 1.4 `phx.new` generator, you can install it with: mix archive.install https://github.com/phoenixframework/archives/raw/master/1.4-dev/phx_new.ez
151TYPO

Under “Testing User Accounts”, in the last sentence of the first paragraph, right before the code block, it says “Crearte a new file…”, with an extra “r”. It should say “Create a new file…” instead.

2018-08-11
161ERROR

The tests for the “authenticate_by_email_and_pass” section do not work because the setup and test helpers are not correct. I solved them with these changes:

rumbl/test/rumbl/accounts/accounts_test.change1.exs:
setup do
{:ok, user: user_fixture(credential: %{email: email, password: pass})}
end

/rumbl/test/support/test_helpers.ex:
def user_fixture(attrs \\\\ {}) do
username = “user#{System.unique_integer([:positive])}”
{:ok, user} =
attrs
|> Enum.into(%{
name: “Some User”,
username: username,
credential: %{
email: “#{username}@localhost”,
password: “supersecret”
}
})
|> Accounts.register_user()
user
end

`

2018-07-30
157ERROR

The “video_fixture” function is missing the “title” attribute. This makes tests from page 163 fail.

2018-07-28
68TYPO

“On :success , we add a flash message to the conn and then redirect to” (last line on the page)

2018-07-28
0DEFER

Maybe mentioning IEx.pry, even in a side note, would be helpful for some. The book, especially combined with the official documents, is great, but being able to inspect the parsed request in conn, changeset validation on form submission etc. alongside them helped me a lot. Plus IEx.pry is an Elixir built in.

2019-07-21Good idea. We'll look at this one. \n \nsorry... we are out of time :( \n \nNext edition.
66TYPO

Line 4; “rumbl-web file” should be “rumbl_web file”.

2018-07-28
76TYPO

Ecto.Changest.cast_assoc

instead of

Ecto.Changeset.cast_assoc

in

Since we’re changing a structure that has elements in two tables, we need Ecto.Changest.cast_assoc .

2018-07-28
110ERROR

Sorry if this is wrong. I’m still very new.

I believe this line: iex> video = Repo.update!(changeset)

Should be: iex> video = Rumbl.Repo.update!(changeset)

When I attempt to tab complete on the books version most of the methods in the Ecto.Repo documentation don’t show up. When I try the book’s command I get the following error.

iex(17)> video = Repo.update!(changeset)

(UndefinedFunctionError) function Ecto.Repo.update!/1 is undefined or private
(ecto) Ecto.Repo.update!(#Ecto.Changeset<action: nil, changes: %{user: #Ecto.Changeset<action: :update, changes: %{}, errors: [], data: #Rumbl.Accounts.User<>, valid?: true>}, errors: [], data: #Rumbl.Multimedia.Video<>, valid?: true>)

I get the following with my suggested replacement.

iex(17)> video = Rumbl.Repo.update!(changeset)
[debug] QUERY OK db=0.3ms queue=0.1ms
begin []
[debug] QUERY OK db=3.1ms
UPDATE “videos” SET “user_id” = $1, “updated_at” = $2 WHERE “id” = $3 [4, {{2018, 7, 4}, {13, 55, 31, 73818}}, 3]
[debug] QUERY OK db=1.2ms
commit []
%Rumbl.Multimedia.Video{
meta: #Ecto.Schema.Metadata<:loaded, “videos”>,
description: “new video”,
id: 3,
inserted_at: ~N[2018-07-04 13:33:42.823787],
title: “new video”,
updated_at: ~N[2018-07-04 13:55:31.073818],
url: “hadtoremovetosubmiteratta”,
user: %Rumbl.Accounts.User{
meta: #Ecto.Schema.Metadata<:loaded, “users”>,
credential: #Ecto.Association.NotLoaded<association :credential is not loaded>,
id: 4,
inserted_at: ~N[2018-06-12 09:23:18.932756],
name: “James Carman”,
updated_at: ~N[2018-06-12 09:23:18.934313],
username: “jjcarman”
},
user_id: 4
}

2018-07-28
162ERROR

I must be misunderstanding this, but “setup” piece doesn’t (and shouldn’t) work with the parameters match like that. If “login_as” tag is unspecified for some test cases, the suite crashes with an error because of no matching setup method found (clearly because the parameter pattern doesn’t match).

Previous version of the book had it right, I think. I wonder how this code works for the authors.

— alg @ elixirforum.com

2018-07-30
28OK

At the bottom of the page it says that Phoenix will use webpack.js, but the phx.new generator seems to be using brunch instead of webpack.

2018-07-28We use webpack for 1.4. Closing this one.
49OK

The css class “phx-hero” used is not defined in the scaffold code, resulting in the rendered welcome page not matching the pictured output.

2018-07-30To use the unreleased 1.4 `phx.new` generator, you can install it with: mix archive.install https://github.com/phoenixframework/archives/raw/master/1.4-dev/phx_new.ez
56TYPO

Bottom of page: “You’ve seen template code before, but you’ll walk through it anyway” -> “… we’ll walk through it anyway”

2018-07-28
60OK

The “common code” uses the tag where previously the tag was used, it should be consistent.

2018-07-28Good suggestion. Too much effort to fix at this stage.
70TYPO

“If you’ve seen plenty MVC applications in the past” -> “… plenty of …”

2018-07-28
77ERROR

Seems like the display of the “New User” form should show the provided input of “Jackie”.

2018-07-28
98SUGGEST

“Don’t let the init function throw you off. Our plug won’t accept any options at run time. The init macro is simply a way to accept configuration options at compile time.”

This is unclear — where is the init macro? It looks like we’re defining a function here, not a macro.

2018-07-28
105ERROR

“Create a new web/views/session_view.ex file that looks like this:”

Should be … lib/rumbl_web/views/session_view.ex …

2018-07-28
135TYPO

where: c.name == “Comedey”

Should be “Comedy”

2018-07-28
121TYPO

Last sentence, first paragraph under “Adding Categories” is “First, let’s generate the model and migration, using phoenix.gen.model, like this:” Should be “schema” instead of “model” and “phx.gen.schema” instead of “phoenix.gen.model”.

2018-07-28
45ERROR

The listing for index.html.eex contains two apparent errors:

(1) “Routes.user_path()” should be “user_path()”; if “Routes” is included, it creates an error.
(2) The table tag needs a table class, too, to pick up the pretty table styling that the book shows, i.e.:

. (I don’t know if this is a bug in the book or a bug in the table styling in Phoenix’s default app.css file.)

`

But in the new version of Phoenix, it should be just user_path.

Also on page 47, the `rumble_web.ex` snippet` should be updated. It currently shows:

`alias RumblWeb.Router.Helpers, as: Routes`

Where as the default snippet is `import RumblWeb.Router.Helpers`

This is in chapter 3, Coding Views

2018-07-28 \nwe changed our css to be classless and the routes alias was added in the new generators.
127TYPO

Second paragraph, second sentence, “orsder” is misspelled.

2018-07-29
131TYPO

First paragraph, fourth sentence, “another” is misspelled “anther”.

2018-07-28
117ERROR

“Fill out the form and click “Submit”. You see a list of your new videos, just as you should."

I don’t see a list of videos — what I actually see is a detail page for the submitted video, localhost:4000/videos/1.

2018-07-28
122ERROR

When I `alias Ecto.{Changeset, Repo} and then run `video = Repo.update!(changeset)` I get an error. I think it’s supposed to be `video = Rumbl.Repo.update!`

2018-07-28
130ERROR

“First, let’s generate the model and migration, using phoenix.gen.model, like this”

The code makes it look like it should be “phoneix.gen.schema”?

2018-07-28
136TYPO

“to fetch the data in the orsder we want”.

2018-07-28
90SUGGEST

After the update to RumblWeb.UserController, it might be a good idea to include the changes needed to lib/rumbl_web/templates/user/new.html.eex to add credential-related fields, as credentials is not directly accessible via the f argument:

<%= inputs_for f, :credential, fn cf -> %>
<%= text_input cf, :email, placeholder: “Email” %>
<%= error_tag cf, :email %>
<%= password_input cf, :password, placeholder: “Password” %>
<%= error_tag cf, :password %>
<% end %>

2018-08-12
103ERROR

Next, let’s share it with the router, like this:

relationships/listings/rumbl/lib/rumbl_web/router.change1.ex

scope “/manage”, RumblWeb do
pipe_through [:browser, :authenticate_user]
resources “/videos”, VideoController
end

I believe the code in this part should be:

def router do
quote do
use Phoenix.Router
import Plug.Conn
import Phoenix.Controller
import RumblWeb.Auth, only: [authenticate_user: 2] # New import
end
end

2018-10-13
125SUGGEST

The interplay between the router and the injected action/2 is not very clear in the text (router does not call index/show/edit/etc directly; router calls action/2 which calls those functions); maybe adding the excellent explanation from the section “Overriding action/2 for custom arguments” in Phoenix.Controller documentation can sled more light in how it is used (by default).

2019-06-07
155163ERROR

I had to add this line to the video_fixture function in test_helpers to let the video desc section in multimedia_test.exs go through ok.

attrs =
Enum.into(
attrs,
%{
url: “…”,

# add title field to map.
title: “a title”,

description: “a description”
}
)

2018-07-28
112ERROR

iex> video = Repo.update!(changeset)
%Rumbl.Multimedia.Video{…}

Throws an error. Shouldn’t it be

iex(8)> video = Rumbl.Repo.update!(changeset)
[debug] QUERY OK db=0.9ms

2018-07-28
144TYPO

The word “comedy” in the example that shows queries with associations is wrongly spelt as “Comedey” (with an extra ‘e’).

2018-07-28
145SUGGEST

Prior to the definition of “constraint error,” there was no example/demo showing the result of adding a duplicate category. Probably need to reword the definition to forward reference the duplicate username error demonstration on PDF page 146 instead.

2018-07-29Tough call. We're going to leave this one as is.
162ERROR

To get the test “lists all user’s videos on index”, %{conn: conn, user: user} do test to work, I had to annotate with a @tag, just like in previous version, to force the login_as key into the config map passed into each test, so something like:

@tag login_as: “max”
test “lists all user’s videos on index”, %{conn: conn, user: user} do

Without this, I get following error:

(FunctionClauseError) no function clause matching in ELSWeb.VideoControllerTest.__ex_unit_setup_1/1

I also had to change the line:
assert html_response(conn, 200) =~ ~r/Listing videos/
to:
assert html_response(conn, 200) =~ ~r/Listing Videos/

2018-07-30
45TYPO

On template listiing, it shows:

`

<%= link “View”, to: Routes.user_path(@conn, :show, user.id) %>

2018-07-28Use a newer 1.4 beta please.
7485ERROR

`null: false` is not included in the sample code :

`add :user_id, references(:users, on_delete: :delete_all)`

i assume that it should be there because the explanation text says the following :

“Likewise, we shouldn’t allow credentials without users, so we also passed null: false.”

2018-07-30
127TYPO

We added a Multimedia.list_alphabetical_categories to fetch the data in the orsder we want.

Should be: order

2018-07-28
49ERROR

Under “Naming Conventions”, the inferred template directory is listing the Phoenix <=1.2 Location of “web/templates/user” but it should be “lib/rumbl_web/templates/user”.

2018-07-28
149ERROR

The video_fixture function needs a title, otherwise tests of pages 155/6 will fail because title is a required parameter, as defined in changeset function of page 139.

So the function video_fixture of page 149 can be:
def video_fixture(Accounts.User{} = user, attrs \\\\{}) do
attrs =
Enum.into(attrs, %{
url: “some hyperlink not allowed in the errata”,
title: “some test title”,
description: “a description”
})

{:ok, video} = Multimedia.create_video(user, attrs)
video
end

2018-07-30
157ERROR

The user_fixture function does not match with source code given and results in a compilation error.

2018-07-30
28SUGGEST

In the `Phoenix needs Elixir` section, it is stated at the end “That means you’ll have to install Node.js.”, but right next to it you drop to PostgreSQL, and then Node. You might want to fix this.

2018-07-29
156SUGGEST

for the test “GET /”

I’d like to know how
conn = get conn, “/”
is dealt with, what files are involved with this, where that get conn is translated

2018-07-29Good idea but not this beta. We will keep this open to consider it.
173SUGGEST

assert_error_sent comes from nowhere.
You could point to hexdocs.pm/phoenix/Phoenix.ConnTest.html and give some explanation, what parameters can be used apart from :not_found, etc

2018-07-29
123113ERROR

iex> Repo.one(query)

should be

iex> Rumbl.Repo.one(query)

2018-07-28
179171ERROR

What you have in the code is different from what is shown in the book.
In order for `mix test test/rumbl_web/controllers/auth_test.exs to pass all nine tests, one have to change the file test/support/test_helpers.ex, so that it resembles what you provide in the code that supports the book:
email: attrs[:email] || “#{username}@localhost”
password: attrs[:password] || “supersecret”

2018-07-30
38OK

The code example on this page makes use of the CSS class ‘phx-hero’. Following along with Phoenix v1.3.3, this code example does not match the image shown underneath on the same page. It appears as if that CSS class has been renamed to ‘jumbotron’ in more recent versions of Phoenix.

2018-07-30To use the unreleased 1.4 `phx.new` generator, you can install it with: mix archive.install https://github.com/phoenixframework/archives/raw/master/1.4-dev/phx_new.ez
39ERROR

The code example which shows ‘lib/rumbl/application.ex’ does not match the code as generated by more recent versions of Phoenix (v1.3.3). Specifically, the code example says ‘Rumbl.Repo’ whereas, in the latest version of Phoenix, this is passed as a parameter to ‘supervisor()’, like so: ‘supervisor(Rumbl.Repo, [])’

2018-07-30The phoenix 1.4 generators use the new child spec style, so the book is correct. To use the unreleased 1.4 `phx.new` generator, you can install it with: \n \nmix archive.install https://github.com/phoenixframework/archives/raw/master/1.4-dev/phx_new.ez \n
45ERROR

The code example on this page does not compile. ‘Routes.user_path(conn, :show, user.id)' should be 'user_path(conn, :show, user.id)’. Also, the CSS class ‘table’ is missing from the table in ‘index.html.eex’ so that the code example does not produce the same visual appearance as the image shown on page 47.

2018-07-28Use a newer Phoenix beta please.
49SUGGEST

In the prose on page 49, there is reference to the code example, but I think the references are incorrect. For example, in this sentence: “When Phoenix renders templates from a controller, it infers the name of the
view module, Rumbl.UserView , from the name of the controller module, Rumbl.UserController .” - Shouldn’t it be ‘RumblWeb’ and not ‘Rumbl’?

2018-07-28Good catch
49SUGGEST

There are several places in the book which clarifies that views are just modules and templates are just functions, for instance on page 49. The clarification is definitely useful, but I find it somewhat difficult to “map” these synonymous terms in my head and follow along when they are used interchangeably. Perhaps the it would be easier to follow along if your stick with one term or the other. Personally, if views are just modules and templates are just functions, I would prefer to use the most basic terms; ‘module’ and ‘function’ - There is no need to jump back and forth between synonymous terms in my opinion.

2018-07-28Let's see what editors say about consistency and style here.
51ERROR

The code example on page 51 no longer corresponds to the code generated by more recent versions of Phoenix. For example, in the book, we can see two paragraphs within the ‘main’ HTML tag, which are no longer present in the code generated by Phoenix (in Phoenix v1.3.3, only ‘<%= render view_module, view_template, assigns %>’ remains within the ‘main’-tags, and the other stuff has been moved out in-between the ‘body’-tags).

2018-07-28The code is correct for the unreleased phoenix 1.4 `phx.new` generator. You can install the unreleased generator with: \n \n$ mix archive.install https://github.com/phoenixframework/archives/raw/master/1.4-dev/phx_new.ez \n
52SUGGEST

In the summary of chapter 3, you might want to consider including the part about making using of nested templates to avoid duplication. I think that would be a valuable aspect to warrant pointing out in the summary.

2018-07-28Good idea, but I think we need to let this wait until next release.
9SUGGEST

This is a minor, subjective style suggestion. The phrase “Here’s the kicker.” is used both on page 4, and shortly thereafter on page 9. Since the phrases are used twice so close to each other, it stood out to me as repetitive. You might want to consider using it only once.

2018-07-28
53SUGGEST

To be more explicit and to avoid possible term equivocations in the reader’s mind, consider changing the sentence “Ecto is the Elixir framework for persistence” to “… framework for data persistence” or “… framework for persisting data.”

2018-07-28
57OK

Code example does not correspond to code generated by more recent version of Phoenix. ‘Rumbl.Repo’ should be ‘supervisor(Rumbl.Repo, [])’, etc.

2018-07-28Please use the updated generators. The code is correct as listed.
59TYPO

In this sentence: “With our repo calls in place, were ready to try it out even though we haven’t touched our controller code at all.”

‘Were’ should be ‘we’re’.

2018-07-28
59OK

This is a subjective suggestion; just a matter of taste.

In some places, the text is telling the reader what they are thinking. For example, in this sentence on page 59: “You can already see the benefits of our Accounts context in action.”

As a reader, I find it off-putting when I’m told directly what I’m seeing, hearing or thinking. What if I’m a reader who’s not seeing that?

The authors might want to consider rephrasing such sentences by using the pronoun ‘we’ instead of ‘you’, to soften it up a bit.

2018-07-28Thanks for the suggestion. Let's see what the editors say.
63ERROR

The code example on page 63 causes an error. ‘Routes.user_path(conn, :create)' should be 'user_path(conn, :create)’.

2018-07-28Use a newer beta version please.
66ERROR

The resulting form from the code example does not match the picture shown in the book on page 66, because the CSS classes are missing from the code example in ‘new.html.eex’.

2018-07-30The template is correct since the new 1.4 generators use a different, classless css setup
17ERROR

In the output for calling ‘elixir -v’ it shows as Elixir 1.1.0. This should say something newer like 1.4 or later

2018-07-28
248ERROR

Book says:
- “setup ​do​
​- \t {​:ok​, ​user:​ user_fixture(​email:​ email, ​password:​ pass)}
​- \t ​end​”

However, it needs a credential, so it should be:

setup do
{:ok, user: user_fixture(%{credential: %{email: email, password: pass}})}
end

If not, then tests fail with:

1) test authenticate_by_email_and_pass/2 returns unauthorized error with invalid password (Rumbl.AccountsTest)
test/rumbl/accounts/accounts_test.exs:73
(MatchError) no match of right hand side value: {:error, #Ecto.Changeset<action: :insert, changes: %{name: “Some User”, username: “user2244”}, errors: [credential: {“can’t be blank”, [validation: :required]}], data: #Rumbl.Accounts.User<>, valid?: false>}
stacktrace:
test/test_helper.exs:13: Rumbl.TestHelpers.user_fixture/1

2018-07-30
153161ERROR

Invalid attrs passed to the user fixture will cause the tests to fail.

In the book :

setup do
{:ok, user: user_fixture(email: email, password: pass)}
end

It should be :

setup do
{:ok, user: user_fixture(credential: %{email: email, password: pass})}
end

2018-07-30
75DEFER

A deeper explanation on how the references function works would be nice: even after reading Chapter 6 I felt a bit lost without reading further documentation. Thanks.

2019-01-23Good comment. We'll take a look at this one after we have completed the chapters and are readying for production based on the other comments.
74ERROR

Shouldn’t :user_id also have null: false? or is the following referring only to :password_hash

> Likewise, we shouldn’t allow credentials without users, so we also passed null: false .

2018-10-13
146OK

Page 146 of the epub: Inconsistent use of Comeonin.Bcrypt and Comeonin.Pbkdf2

Code section in book “authentication/listings/rumbl/lib/rumbl/accounts/accounts.change2.ex” is using Comeonin.pbkdf2.checkpw in the first cond, and comeonin.bcrypt.dummy_checkpw() in the block of the third cond.

2018-08-13The purpose is for misses not to take a predictable length of time; it doesn't need to match.
153OK

{:ok, user: user_fixture(email: email, password: pass)}

It should be:

{:ok, user: user_fixture(credential: %{email: email, password: pass})}

2018-08-11Good thinking, but the user_fixture function is actually a convenience function that takes a credential like this: \n \ndef user_fixture(attrs \\\\ %{}) do \n username = "user#{System.unique_integer([:positive])}" \n {:ok, user} = attrs \n |> Enum.into(%{ \n name: "Some User", \n username: username, \n credential: %{ \n email: attrs[:email] || "#{username}@example.com", \n password: attrs[:password] || "supersecret", } \n }) \n |> Accounts.register_user() \n user \nend
57SUGGEST

The text says “… in our application’s supervision tree in lib/application.ex, like …”. I was confused until I realized that actually meant “lib/rumbl/application.ex”. I think other new users to Phoenix might appreciate this change, as some (like me) might be left wondering “am I supposed to create `lib/application.ex`?”

2018-08-13
111TYPO

In the example code to create a new video you need a space in description:“new video”. Should be description: “new video”

2018-08-11
117TYPO

last word on the page: visting. Should be visiting

2018-08-11
44TYPO

defmodule RumblWeb.UserController do
use RumblWeb, :controller

should be: RumbleWeb.UserController do
use RumbleWeb, :controller

2018-08-12
45TYPO

RumblWeb -> RumbleWeb

2018-08-11Rumbl is the name of the app. It's correct as listed.
47TYPO

RumblWeb -> RumbleWeb

2018-08-11
49TYPO

RumblWeb -> RumbleWeb

2018-08-11
45 OK

I have to apologize for my typos corrections.
All my remarks that Rumble - Rumble are false, because I created the system with: mix phx.new rumble !!!!

2018-08-11no problem! Closing this one as not-a-problem
111TYPO

description:“new video” —> description: “new video”

2018-08-11
57TYPO

third line:
supervision tree in lib/application.ex, like this:
should probably be:
supervision tree in lib/rumbl/application.ex, like this:

2018-08-13
83TYPO

“We have seen both kinds of plugs in use. From the endpoint module in lib/rumbl/endpoint.ex, you can see an example of a module plug:”

the path should probably say: lib/rumbl_web/endpoint.ex

2018-10-13
191TYPO

Module is defined on previous page as Rumbl.Multimedia.Permalink but aliased as Rumbl.Permalink.

2018-10-13
153OK

Compilation error in file test/rumbl/accounts/accounts_test.exs ==

(CompileError) test/rumbl/accounts/accounts_test.exs:7: undefined function describe/2

I don understand this compilation error.
I am running: elixir v1.6.4 and Phoenix v1.2.5

2018-08-13If anyone else is getting this I will reopen.
171ERROR

user = user_fixture(username: “me”, email: “me@test”, password: “secret”)

Should be

user = user_fixture(username: “me”, credential: %{email: “me@test”, password: “secret”})

It throws an error without credentials: %{}

2018-10-13
59TYPO

$ mix phx.server
[info] Running HelloWeb.Endpoint with Cowboy on

HelloWeb.Endpoint should be RumblWeb.Endpoint

2018-10-13
59SUGGEST

After issuing the html command localhost:4000/users/ I got the error message
function Routes.user_path/3 is undefined (module Routes is not available).

I had two versions with the same error message: (elixir 1.6.4, Phoenix v1.2.5) and ( elixir 1.7.0, Phoenix 1.3.4) I don have the faintest idea what caused the problem. It seems I have to read the ebook without doiing the exercises. It is frustating!

2018-10-18
264TYPO

B4 is missing new chapter on channels

2018-09-09
81ERROR

From what I can tell, we need to update the Rumbl.Accounts.change_user methods here too. Otherwise the usage of inputs_for in the template raise an error about missing the “key: :credential” when we attempt to render the /users/new.

This is what I came up with:

defmodule Rumbl.Accounts do

def change_user(%User{} = user) do
User.registration_changeset(user, %{credential: %{}})
end

end

It may not be quite the correct fix, but it got me unstuck. Hopefully the next person won’t be derailed by this.

2019-01-21
155ERROR

Chapter 5: In-context Relationships

The snippet is displayed as:

> add ​:user_id​, references(​:users​, ​on_delete:​ ​:delete_all​, ​null:​ false)

Yet the book states:

> Likewise, we shouldn’t allow credentials without users, so we also passed null: false to the add :user_id column

To me this reads as though the snippet is incorrect (admittedly I am new to Elixir and Ecto migrations).

(The same is also true for the file phoenix14/code/authentication/listings/rumbl/priv/repo/migrations/20180410050016_create_credentials.change1.exs)

2018-10-18
107SUGGEST

Fill out the form and click “Submit” should not that be Fill out the form and click “Save”?

2019-01-21
49TYPO

When Phoenix renders templates from a controller, it infers the name of the
view module, RumblWeb.UserView , from the name of the controller module, Rum-
bl.UserController.

Controller name should be RumblWeb.UserController

2018-10-13
105TYPO

Comeonin.Bcrypt.dummy_checkpw() should be Comeonin.Pbkdf2.dummy_checkpw()

2018-10-13
156ERROR

I have an error when coding the following:

key :show not found in: [index: []]

<%= link “View”, to: Routes.user_path(@conn, :show, user.id) %>

2018-10-13
156SUGGEST

Phoenix has a feature to reload only for eex files, but if it is changed in some .ex file, the change will be made when you rerun the server. It took me some time to find out. This is with respect to my previous post.

2018-10-13
156SUGGEST

The changes are automatic, but if I remove router.ex get “/ users /: id”, UserController,: show, the server is reloading, and it continues to work, but when I restart the server it throws an error. Do not reload the changes only in the router.ex file

2018-10-13
127TYPO

in authenticaion/listings/rumbl/lib/rumbl/accounts/accounts.change2.ex it says:
“Comonin.Bcrypt.dummy_checkpw()”

Shoudln’t this be :
“Comonin.Pbkdf2.dummy_checkpw()” ?

2018-10-13
78SUGGEST

It wasn’t clear to me that the book’s intention was for me to type

alias RumblWeb.Router.Helpers, as: Routes

It read to me like I should expect to see it there already (which I did not).

I added it manually and got the form to work.

2018-10-13
58ERROR

Error in the change for ecto/listings/rumbl/lib/rumbl/accounts/accounts.change1.ex

Unless I missed something, it looks like you forgot to alias Rumbl.Accounts.User in the file. So the file should be:
alias Rumbl.Repo
alias Rumbl.Accounts.User

Without this line I am getting an error:
Protocol.UndefinedError at GET /users
protocol Ecto.Queryable not implemented for User, the given module does not exist. This protocol is implemented for: Atom, BitString, Ecto.Query, Ecto.SubQuery, Tuple

Hope this helps!

2018-10-13Chris, can you verify? I think this is an error from an earlier Phoenix install, correct? \n
83ERROR

Line 4 says ‘lib/rumbl/endpoint.ex’ it should be ‘lib/rumbl_web/endpoint.ex’

2018-10-13
89ERROR

Nothing important. Just a little thing.
In the book it says “Now let’s plug it in our controller, right after use Rumbl.Web:”
but in the code it’s right after “alias Rumbl.Accounts.User”

2018-10-13
46TYPO

In the blue Chris call out box, the link at the end is broken. It should be: (protocol removed and space inserted due to hyperlinks not being allowed) www.evanmiller. org/elixir-ram-and-the-template-of-doom.html

2018-10-13
93ERROR

accounts.change2.ex is using Comeonin.Bcrypt.dummy_checkpw to prevent timing attacks, yet the rest of the application is using PBKDF2, which I think it probably has a distinct time signature, so shouldn’t we use Comeonin.Pbkdf2.dummy_checkpw instead? Thanks.

2018-10-13
60TYPO

“We then pass the cast function a list of fields to tell Ecto that name and username are allowed to be casted as user input.”

Shouldn’t “casted” be “cast”?

2018-10-13
65TYPO

“Add a create function toUserController:” needs a space between “to” and “UserController”.

2018-10-13
28ERROR

Wouldn’t be “lib/hello/application.ex” instead of “lib/hello.ex”?

PDF Page 28
“Each Mix project also has a lib directory. Support for starting, stopping, and supervising each application is in lib/hello.ex.”

Reasoning:
PDF Page 32
“You can see two top-level files, hello.ex and hello_web.ex. The Hello module is an empty module which defines the top-level interface and documentation for your application.”

2018-10-13
124ERROR

From the online example file:
/titles/phoenix14/code/queries/listings/rumbl/lib/rumbl/multimedia/multimedia.change1.ex

‘alias Rumbl.Multimedia.Category’ is on both line 22 and 28 ;-)

2019-01-21
64ERROR

Should be

Doc also states that it creates a button.

2018-10-13
147TYPO

Space missing ‘thetest/rumbl/accounts’ should be ‘the test/rumbl/accounts’

2018-10-13
93ERROR

The “true” case in the “cond” in function “authenticate_by_email_and_pass” calls “Comeonin.Bcrypt.dummy_checkpw()”, but the book doesn’t install bcrypt_elixir. Changing to “Comeonin.Pbkdf2.dummy_checkpw()” fixes it.

2018-10-13
153ERROR

Test failure:
Comeonin.Bcrypt.dummy_checkpw/0 is undefined (module Comeonin.Bcrypt is not available)
adding {:bcrypt_elixir, “~> 1.0”} in the mix.exs file solved the problem

System info
OS X High Sierra 10.13.5
Erlang/OTP 20
Phoenix v1.4.0-dev

2018-10-13
94ERROR

When I try to create a new user, I get this error:

function Comeonin.Pbkdf2.hashpwsalt/1 is undefined (module Comeonin.Pbkdf2 is not available)

I checked all the code from the chapter and everything seems to be fine.

Thanks!

2018-10-13I think you missed adding the dependency.
29OK

It would be nice to not force people (specially new ones to tech) to install Postgres locally, you can suggest:
1. Installing docker
2. Run a command like docker run —name rumbl_dev —volume rumbl_dev:/var/lib/postgres/data/rumbl_dev -p 5432:5432 -e ‘POSTGRES_DB=rumbl_dev’ -e ‘POSTGRES_USER=postgres’ -e ‘POSTGRES_PASSWORD=postgres’ postgres:10.5
3. Suggest running ` docker start rumbl_dev

Thanks!

2018-10-13Ironically, the magic of Docker is just too much to learn here, and too much for the author team to support across platforms. We are going to go with a more basic approach without the technical burden of learning another stack. \n \nGood idea, but we can't really do this.
112SUGGEST

“Relational databases like Postgres are named that way for a reason. Dealing
with related data is the defining characteristic of that whole family of
databases, so management of relationships is the feature that makes or breaks
any persistence layer. ”

This is a common misconception, relational databases aren’t named that way because they manage relationships, but because the data is stored in relations(sets of tuples) as in Codd’s relational model.

2019-01-21
96TYPO

name -> username

“If the user is available, we show the name, followed by a logout link.”

2018-10-13
121TYPO

“First, let’s generate the schema and migration, using phoenix.gen.schema, like this:”

phoenix.gen.schema => phx.gen.schema

2019-01-21
106TYPO

“The up migration…” (last paragraph on page). ‘up’ seems out of place. Should be “The CreateVideos migration” or simply “The migration”

2019-01-21
136TYPO

“The Ecto.ConstraintError, which you saw when we tried to add a category twice.”

The ConstraintError is actually shown for the first time on the next page. It’s not encountered while adding categories (the create_category function even prevents it from happening ;-)).

2018-10-13
162ERROR

In the line “conn = get conn, video_path(conn, :index)”, ‘video_path’ must be prefixed with Routes.
This also applies to the code listed in video_controller_test.change1.exs (line 24)

2019-01-21
67TYPO

Hi,

First of all: great material, keep up your awesome work.

I may have found a little inconsistency. In the following paragraph you find the key(?) :success that is not used in the listing. This Couleur be reworked into :ok or plain success.

Easy enough. We insert the new user record and then match on the return code. On :success, we add a flash message to the conn and then redirect

2018-10-13
7967TYPO

Bottom of page says “On :success, we add a flash message…” where perhaps it should read “On :ok, we add a flash message…”

2018-10-13
79ERROR

Minor technical error at the bottom - need to alias Rumbl.Accounts.User in the iex session prior to running the credential fixup look

2018-10-13
83TYPO

The Anatomy of a Plug…
We have seen both kinds of plugs in use. From the endpoint module in
lib/rumbl/endpoint.ex, you can see an example of a module plug:

Should be: lib/rumbl_web/endpoint.ex. So the RumblWeb module, not the Rumbl module

2018-10-13
65ERROR

LINQ is not a persistence framework. A better comparison for Ecto in. NET would be to Entity Framework

2018-10-13
211TYPO

|> Annotation.changeset() should be |> Annotation.changeset(attrs)

Otherwise the test on page 213 won’t work.

2018-10-13
218SUGGEST

Not only ‘this.scheduleMessages’ runs forever even when it’s empty but also every time it joins a channel it will call a new ‘this.scheduleMessages’.

It means that after 5 joins we will end up with 5 ‘setTimeout’ running and each will call ‘this.scheduleMessages’ that will run forever.

After a considerable amount of joins it could become problem.

2019-01-23
1OK

This is a pretty hefty set of feedback, but the organization of the book seems a little off. It digs really deep into the theory of phoenix before people are dragged into the programming enough to care about that stuff yet.

Organization that would be super handy:
The whole book should be 1 example website that we’re creating as a group, in a test driven way. Using all the generators and everything that phoenix provides. As you go the book should give more and more details of why different things work the way they do (rather than dumping that all into chapter 2) and how the pieces put together. In the end we should have a complete example that is a real website with most everything that people will use creating websites (login, oauth, admin panels, channels, etc) complete with some details on how to deploy.

I recognize that this is a complete rework of the book, but I feel like it would bring a lot more adoption to phoenix (much like the original rails book that did the same)

2018-10-13Good ideas, but we can't really act on feedback like this in a second generation best selling book. But I will keep your comments in mind for other books I edit in the series! \n \nWe did try a test-first approach, but found it too repetitive, and found the test theory distracting from the primary concepts. It's not the best way to *code* phoenix, but may be a better way to *teach* phoenix apps. \n
211ERROR

On line 5 of the first code block, it should be Annotation.changeset(attrs) and not Annotation.changeset()
It’s actually like this in the downloadable source code but not in the book!

If this isn’t the case, then attrs variable is unused and a nonexistent changeset/1 function is called, which breaks the feature.

2018-10-13
72ERROR

Hey all,

Just wanted to let you know that the code as it stands for Authentication will not work if you try to use `Comeonin.Bcrypt later on in the book because you now need to add {:bcrypt_elixir, “~> 1.0”} to your dependencies. As of now, the book fails to mention that this dependency needs to be added to the Mixfile. If you dont add this dependency, you’ll get an error that `Comeonin.Bcrypt.dummy_checkpw/0 is undefined because the Comeonin.Bcrypt module is unavailable. So it looks like you need the native elixir bcrypt dependency to compile so that comeonin knows to include it as a module.

2018-10-13
49TYPO

Does this paragraph:

The view modules infer their template locations from the view module name. In our example, our Rumbl.UserView would look for templates in the web/templates/user/ directory.

Need to read as the following?

The view modules infer their template locations from the view module name. In our example, our RumblWeb.UserView would look for templates in the rumbl_web/templates/user/ directory.

Changes:

‘Rumbl.UserView’ to ‘RumblWeb.UserView’
and ‘web/templates/user/’ to ‘rumbl_web/templates/user/’

2018-10-13
49TYPO

Does this paragraph:

The view modules infer their template locations from the view module name. In our example, our Rumbl.UserView would look for templates in the web/templates/user/ directory.

Need to read as the following?

The view modules infer their template locations from the view module name. In our example, our RumblWeb.UserView would look for templates in the rumbl_web/templates/user/ directory.

Changes:

‘Rumbl.UserView’ to ‘RumblWeb.UserView’
and ‘web/templates/user/’ to ‘rumbl_web/templates/user/’

2018-10-13
49ERROR

Does this paragraph:

The view modules infer their template locations from the view module name. In our example, our Rumbl.UserView would look for templates in the web/templates/user/ directory.

Need to read as the following?

The view modules infer their template locations from the view module name. In our example, our RumblWeb.UserView would look for templates in the rumbl_web/templates/user/ directory.

Changes:

‘Rumbl.UserView’ to ‘RumblWeb.UserView’
and ‘web/templates/user/’ to ‘rumbl_web/templates/user/’

2018-10-13
93ERROR

page 93 on the page, 105 in my pdf viewer

authentication/listings/rumbl/lib/rumbl/accounts/accounts.change2.ex says to add Comeonin.Bcrypt.dummy_checkpw() but since we were previously instructed to install Comeonin.Pbkdf2, Comeonin.Bcrypt is not available, and fails. Should be Comeonin.Pbkdf2.dummy_checkpw().

Thanks!! Enjoying the book.

2018-10-13
93ERROR

page 93 on the page, 105 in my pdf viewer

authentication/listings/rumbl/lib/rumbl/accounts/accounts.change2.ex says to add Comeonin.Bcrypt.dummy_checkpw() but since we were previously instructed to install Comeonin.Pbkdf2, Comeonin.Bcrypt is not available, and fails. Should be Comeonin.Pbkdf2.dummy_checkpw().

Thanks!! Enjoying the book.

2018-10-13
105ERROR

page 93 on the page, 105 in my pdf viewer

authentication/listings/rumbl/lib/rumbl/accounts/accounts.change2.ex says to add Comeonin.Bcrypt.dummy_checkpw() but since we were previously instructed to install Comeonin.Pbkdf2, Comeonin.Bcrypt is not available, and fails. Should be Comeonin.Pbkdf2.dummy_checkpw().

Thanks!! Enjoying the book.

2019-01-21
7765SUGGEST

I was a bit surprised to encounter the use of the term “model”, since models as a concept has not been discussed much in the book. I was not really sure what this was referring to specifically, so I just took it as a general observation.

> The controller shouldn’t care about these short persistence details, but neither should the model.

It would be nice if this was clarified. Thanks for a great book so far!

2018-10-13
36SUGGEST

In code example 2 you have:

‘connection
|> UserController.action()
|> UserController.index()
|> UserView.render(“index.html”)’

the line “UserController.action()” seems a bit unnecessary, because “.index()” in the next line is the action. So perhaps the example should be:

‘connection
|> UserController.action()
|> UserController.index()
|> UserView.render(“index.html”)’

2018-10-13
86ERROR

in /rumbl/lib/rumbl_web/controllers/user_controller.change3.ex, when I make an error, the url changes from / users/new to /users/

2018-10-13
58ERROR

The new accounts.ex file seemingly removes the “alias Rumbl.Accounts.User” which causes an error. Consider adding the line there in the example so that it’s more obvious what’s being changed

2019-01-19
198191TYPO

When typing the sample IEx session as-is, the following error occurs:

iex> P.cast(“1”)

(UndefinedFunctionError) function Rumbl.Permalink.cast/1 is undefined (module Rumbl.Permalink is not available)
Rumbl.Permalink.cast(“1”)

There’s a typo in the module reference: the “Multimedia” part is omitted. It should be `alias Rumbl.Multimedia.Permalink, as: P`

2018-10-13
86ERROR

Currently, the text claims that response codes 400-499 are not-found and 500+ is for errors. In reality, only response code 404 is not found. Codes 400-499 are for client errors and codes 500+ are for server errors.

2018-10-13
48OK

The id param on the show method is a string and is causing me errors.
I had to rewrite the function to

def show(conn, %{“id” => id}) do
int_id = String.to_integer(id)
user = Accounts.get_user(int_id)
render(conn, “show.html”, user: user)
end

2018-10-18Chris, can you confirm?
19OK

OTP 19
elixir 1.6 (per book)
node 5.3.0 (per book)
npm 3.3.13 (whatever came with 5.3.0 nodejs)
mixed in phx_new 1.4-dev (per book)

ran “mix phx.new hello”

console reports “ (Mix) The task ”phx.new" could not be found"

2018-10-13Please see https://github.com/phoenixframework/phoenix/issues/2404
86SUGGEST

You casually make reference to a session without really introducing the concept. Would be useful for users to have more detail here.

2019-01-21Interesting observation. We'll take a look after we get through the new chapters.
86OK

Is there a best practice in terms of file location for storing plugs? Perhaps a plugs directory instead of in controllers?

2018-10-13
10593TYPO

We installed Comeonin with Pbkdf2, yet Comeonin.Bcrypt.dummy_checkpw() is used in the authentication/listings/rumbl/lib/rumbl/accounts/accounts.change2.ex file. Should it not be Comeonin.Pbkdf2.dummy_checkpw() instead?

2019-01-21
182TYPO

lib/rumbl_web/templates/layout.html.eex should be lib/rumbl_web/templates/layout/app.html.eex

2018-10-18
437ERROR

In the code listed on the page:
media.pragprog.com/titles/phoenix14/code/channels/listings/rumbl/lib/rumbl/multimedia/multimedia.change1.ex

The call in annotate_video to Annotation.changeset is not passing in attrs.

2018-10-13
87-89SUGGEST

Module vs. Function Plugs.

Newbie here.

More of a discussion here would be useful. I note you choose to use a module plug which gives a lot of flexibility, put also implies a single function per module. So if used in the router pipeline, you end up with:

:a
:b
:c
RumblWeb.Auth (implies a single plug, but a module can contain many functions - is there a reason for this?)

Why not have the module Auth define two functions: currentUser and authenticate?

That way, the above would be

:a
:b
:c
:current_user (In the Auth module)
:authenticate (In the Auth module)

Follows what appears to be the idiomatic norm of sequencing. You check if current_user exists and then you check if that current_user needs to be present in order to visit a page, either on a controller or router pipeline level.

2018-10-13We may address this a bit later.
41SUGGEST

I am a little bit troubled that the file `lib/rumbl/accounts/accounts.ex` contains the module `Rumbl.Accounts` while, one page above, `lib/rumbl/accounts/user.ex` contains a module `Rumbl.Accounts.User`.

Is this intentional, and if so, maybe would be nice to give a rationale on this, especially as the explanation at page 42 seems to underline what I am saying.

2018-10-13Good question. In this setting, Account is the context, and User is the schema.
74SUGGEST

The copy of the generator output ends with the generator’s instruction to run `mix ecto.migrate` - it seems that this is an instruction from the author which is confusing because you then go on to edit the migration, etc.

Maybe either truncate the pasted output from the generator, or at least change the background or text color or something so it’s clear that it’s not instructions.

2018-10-13
74SUGGEST

The copy of the generator output ends with the generator’s instruction to run `mix ecto.migrate` - it seems that this is an instruction from the author which is confusing because you then go on to edit the migration, etc.

Maybe either truncate the pasted output from the generator, or at least change the background or text color or something so it’s clear that it’s not instructions.

2018-10-13
83ERROR

This:
From the endpoint module in lib/rumbl/endpoint.ex

Should be this (I think)
From the endpoint module in lib/rumbl_web/endpoint.ex

2018-10-13
91TYPO

Assuming you switched from Bcrypt to use Pbkdf2 via comeonin. Page 91 has Pbkdf2 for checkpw but Bcrypt for dummy_checkpw.

2018-10-13
81ERROR

In addition to changing the create action to use the Accounts.register_user/1 method to generate the proper changeset, the new action also needs to have a proper changeset generated to render the form properly.

Something like adding the following to Rumbl.Accounts and using it in the new action:

def new_user(%User{} = user) do
User.registration_changeset(user, %{})
end

2019-03-15
48SUGGEST

The grammar seems a bit off here:

“Add that to lib/rumbl_web/templates/user/show.html.eex:”

This would be more correct to say:

“Add this to lib/rumbl_web/templates/user/show.html.eex:”

Since the example to add comes after the statement in question.

2018-10-13
54SUGGEST

Is this necessary here?

“We’re going to use Ecto’s default database adapter, PostgreSQL. Install Post- greSQL, consulting the Postgres homepage1 for details if necessary.”

I feel pretty confident that the reader could not have followed along to this point without getting many errors. Early on, on page 17, we were already told to install PostgreSQL.

My suggestion is to remove this bit about installing PostgreSQL again.

2018-10-13
63SUGGEST

I think this:
“Use a helper function, rather than HTML tags, to build the form, giving it an anonymous function.”

would probably sound better using a collective voice like used in the rest of the book. So, this:

“We use a helper function, rather than HTML tags, to build the form, giving it an anonymous function.”

2018-10-13I will let the editor make a choice here.
9179ERROR

Trying to update the users before the password with the iex command at the bottom of the page. It looks like:

iex(1)> alias Rumbl.Repo
Rumbl.Repo
iex(2)> alias Rumbl.Accounts.User
Rumbl.Accounts.User
iex(3)> for u <- Repo.preload(Repo.all(User), :credential) do
…(3)> Repo.update!(User.registration_changeset(u, %{
…(3)> credential: %{email: “#{u.username}@example.com”,
…(3)> password: “temppass”}
…(3)> }))
…(3)> end
[debug] QUERY OK source=“users” db=3.5ms decode=7.6ms
SELECT u0.“id”, u0.“name”, u0.“username”, u0.“inserted_at”, u0.“updated_at” FROM “users” AS u0 []
[debug] QUERY OK source=“credentials” db=3.7ms queue=0.1ms
SELECT c0.“id”, c0.“email”, c0.“password_hash”, c0.“user_id”, c0.“inserted_at”, c0.“updated_at”, c0.“user_id” FROM “credentials” AS c0 WHERE (c0.“user_id” = ANY ($1)) [[6, 5, 4, 3, 2, 1]]

(RuntimeError) you are attempting to change relation :credential of
Rumbl.Accounts.User but the `:on_replace` option of
this relation is set to `:raise`.

By default it is not possible to replace or delete embeds and
associations during `cast`. Therefore Ecto requires all existing
data to be given on update. Failing to do so results in this
error message.

If you want to replace data or automatically delete any data
not sent to `cast`, please set the appropriate `:on_replace`
option when defining the relation. The docs for `Ecto.Changeset`
covers the supported options in the “Related data” section.

However, if you don’t want to allow data to be replaced or
deleted, only updated, make sure that:

* If you are attempting to update an existing entry, you
are including the entry primary key (ID) in the data.

* If you have a relationship with many children, at least
the same N children must be given on update.

(ecto) lib/ecto/changeset/relation.ex:201: Ecto.Changeset.Relation.on_replace/2
(ecto) lib/ecto/changeset/relation.ex:284: Ecto.Changeset.Relation.single_change/6
(ecto) lib/ecto/changeset.ex:715: Ecto.Changeset.cast_relation/4
(stdlib) erl_eval.erl:677: :erl_eval.do_apply/6
(stdlib) erl_eval.erl:885: :erl_eval.expr_list/6
(stdlib) erl_eval.erl:408: :erl_eval.expr/5
(stdlib) erl_eval.erl:232: :erl_eval.expr/5
(elixir) lib/enum.ex:1925: Enum.“reduce/3-lists^foldl/2-0”/3

2019-06-08
86SUGGEST

status
The response codes aren’t quite accurate. Specifically 400 and 500 series codes. They are both error codes but 400-499 are bad client requests. 500-599 are bad server requests.

2018-10-13
79ERROR

If, like me, you didn’t actually type in all the code trying out ``changeset`` and testing the validity, but instead went directly to the bit about updating existing users…

You’ll need “alias Rumbl.Accounts.User”, either before or after `alias Rumbl.Repo` for the remaining code to work.

2018-10-13
72ERROR

Later in the book, I had issues about it not finding the ``Comeonin.Bcrypt.dummy_checkpw()` method. I had to add another dependency:

`{:bcrypt_elixir, “~> 1.0”}`

2018-10-13
93ERROR

authenticate_by_email_and_pass calls:
Comeonin.Bcrypt.dummy_checkpw()
which should be:
Comeonin.Pbkdf2.dummy_checkpw()

2018-10-13
171SUGGEST

“Let’s ease up the number of hashing rounds to speed up our test suite by adding these configuration lines to config/test.exs:”

Is probably better as:

“Let’s ease up the number of hashing rounds to speed up our test suite by adding this configuration line to config/test.exs:”

Now that there is only one line added.

2018-10-13
94SUGGEST

The paragraph following the session/new.hmtl.eex code listing states…

“We use form_for as in our new-user forms, but instead of passing a changeset, we pass the %Plug.Conn{} struct.”

However, it’s not clear from the code listing how the empty struct is being passed. Can this be clarified?

2018-10-13
50SUGGEST

At the end of page 49 it explains about RumblWeb.UserView.render and the end of page 50 it explains about Phoenix.View.render, with paragraphs on rendering error message in between.

I find it easier to get the context of behaviour if the explanation of Phoenix.View.render follows RumbleWeb.UserView.render directory, and move the error rendering paragraph to the bottom.

2018-10-13Interesting suggestion. I have read it a couple of times through and think I like it as is for now. Thought provoking idea though.
39ERROR

The PDF shows:

children = [
# Start the Ecto repository
Rumbl.Repo,
# Start the endpoint when the application starts
RumblWeb.Endpoint,
# Starts a worker by calling: Rumbl.Worker.start_link(arg)
# {Rumbl.Worker, arg},
]

Whereas the actual code generated is:

children = [
# Start the Ecto repository
supervisor(Rumbl.Repo, []),
# Start the endpoint when the application starts
supervisor(RumblWeb.Endpoint, []),
# Start your own worker by calling: Rumbl.Worker.start_link(arg1, arg2, arg3)
# worker(Rumbl.Worker, [arg1, arg2, arg3]),
]

Since the book is still in beta, when released it should correspond to whatever is the latest versions of software at that time.

2019-01-19
39SUGGEST

The last paragraph on page 39 and first paragraph on page 40 talk about the application’s supervision tree. This piece felt out of place in this section and was a distraction. I would suggest moving this somewhere else or just deleting.

2019-03-15
40TYPO

The first code sample on page 40 lists the path to the file as: controllers_views_templates/listings/rumbl/lib/rumbl/accounts/user.ex. However, the correct path is: rumbl/lib/rumbl/accounts/user.ex

2019-01-19
94TYPO

Actually this is more a question.

For some reason in RumblWeb.SessionView it needs an extra empty line after use statement to work for me:

defmodule RumblWeb.SessionView do
use RumblWeb, :view

end

If I don’t have the extra line as the book showed, I got RumblWeb.SessionView module not available.

Is this a bug in Phoenix itself?

2019-01-21We can't reproduce this error. Did you try the phoenix slack channel or mailing lists?
94OK

In the code snippet of “/templates/session/new.html.eex”
I think you forgot to include the flash message notification. You just wrote two input fields one for email and other for password. If something went wrong user can’t see any flash notification!

2019-01-22We are getting flash messages per CM. Hope this build resolves your problem. The flash notice is in the template.
93ERROR

Function “authenticate_by_email_and_pass(email, given_pass)”, it used in the cond-do block code, in the third cond,
true ->
Comeonin.Bcrypt.dummy_checkpw()
{:error, :not_found}
this produces error when you try to submit wrong login, it report that this Module, “Comeonin.Bcrypt” is not available. I think we need to include in mix.exs -> deps, the bcrypt algorithm package, like this:
{:comeonin, “~> 4.1”},
{:pbkdf2_elixir, “~> 0.12”},
{:bcrypt_elixir, “~> 0.12”} <<<<<<

After I did that, no more errors.
Thanks!

2019-01-21
nanaSUGGEST

I am trying to use Phoenix to create a REST server which will have a few nested APIs. I find no documentation in the book at all on how to deal with nested resources. There is documentation for nested resources in the online Ecto documentation, but none of that of course helps with what I need to do in Phoenix specific entities, e.g. contexts, controllers, views, etc. Shouldn’t this be documented???

2019-01-19We're not going to get to this kind of change in this book.
527ERROR

Code figures are incorrect and not same as downloaded code.

Call to compute_additional_info is passed wrong var name:
Task.start_link(fn -> compute_additional_info(ann, socket) end)
Should be “annotation” not “ann”

The function broadcast_annotation(socket, annotation) function should be broadcast_annotation(socket, user, annotation)

2019-01-23
71DEFER

The registration chapter seems a bit naive, in that no verification of the email address is performed. Adding this to the example might also show off some other interesting aspects of Phoenix.

2019-01-22We thought about it but found it difficult to walk the line between writing a nontrivial app and providing too much data for 300 page book. Longer books of this type are just too much to manage.
19OK

With Ubuntu 18 and Erlang 21.1 and Elixir 1.7.3 calling mix phx.server on new project fails with a missing cowboy plug error. It also fails with Erlang 20 and Elixir 1.6.6 - same error.

The default created phoenix project create with mix phx.new hello, the mix.exs file needs {:plug_cowboy, “~> 1.0”} in order to compile (or the new project does not compile with a missing cowboy plug error).

If you try with cowboy 1 then use above, with cowboy 2 then use 2.0 above.

2019-01-23we have not been able to reproduce this problem.
173SUGGEST

In the end of the ‘Testing Views and Templates’ section, there is this sentence: “Let’s move on to contexts.”. But contexts were treated before - this is the last section before the wrap up. I believe this sentence should be removed.

2019-01-21
74TYPO

- add :user_id, references(:users, on_delete: :delete_all, null: false)
+ add :user_id, references(:users, on_delete: :delete_all), null: false

2019-01-19
219211TYPO

> In that function, we build a video struct with the video ID.

I think this meant to say “we build an annotation struct”, as per the code.

2019-01-21
219211OK

The annotate_video/3 function takes a User struct and a video_id as its first two args. Going back to this snippet, I’m wondering why accept an ID for the one association and why a struct for the other. Presumably it has to do with piping to the put_user/2 function, but it would be nice to know why.

2019-01-21user_id isn't enough, but video_id is. We need the full user for put_user but we don't need the full video. I am not changing the book because answering this question inline adds a surprising amount of clutter. \n \nNice question though!
77OK

defmodule Rumbl.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
alias Rumbl.Accounts.Credential

schema “users” do
field :name, :string
field :username, :string
has_one :credential, Credential

timestamps()
end

def changeset(user, attrs) do
user
|> cast(attrs, [:name, :username])
|> validate_required([:name, :username])
|> validate_length(:username, min: 1, max: 20)
end

def registration_changeset(user, params) do
user
|> changeset(params)
|> cast_assoc(:credential, with: &Credential.changeset/2, required: true)
end
end

defmodule Rumbl.Accounts.Credential do
use Ecto.Schema
import Ecto.Changeset

schema “credentials” do
field :email, :string
field :password_hash, :string
belongs_to :user, Rumbl.Accounts.User

timestamps()
end

@doc false
def changeset(credential, attrs) do
credential
|> cast(attrs, [:email, :password])
|> validate_required([:email, :password])
|> validate_length(:password, min: 6, max: 100)
|> unique_constraint(:email)
|> put_pass_hash()
end

defp put_pass_hash(changeset) do
case changeset do
Ecto.Changeset{valid?: true, changes:{password: pass}} ->
put_change(changeset, :password_hash, Comeonin.Pbkdf2.hashpwsalt(pass))
_ ->
changeset
end
end
end

changeset = User.registration_changeset(User{},{username: “max”, name: “Max”,
credential: %{password: “123”}})

…(14)> (ArgumentError) unknown field `password`. Only fields, embeds and associations (except :through ones) are supported in changesets
(ecto) lib/ecto/changeset.ex:541: Ecto.Changeset.type!/2
(ecto) lib/ecto/changeset.ex:516: Ecto.Changeset.process_param/7
(elixir) lib/enum.ex:1925: Enum.“reduce/3-lists^foldl/2-0”/3
(ecto) lib/ecto/changeset.ex:501: Ecto.Changeset.cast/6
(rumbl) lib/rumbl/accounts/credential.ex:17: Rumbl.Accounts.Credential.changeset/2
(ecto) lib/ecto/changeset/relation.ex:113: Ecto.Changeset.Relation.do_cast/5
(ecto) lib/ecto/changeset/relation.ex:297: Ecto.Changeset.Relation.single_change/5
(ecto) lib/ecto/changeset/relation.ex:105: Ecto.Changeset.Relation.cast/4
(ecto) lib/ecto/changeset.ex:746: Ecto.Changeset.cast_relation/4

2019-01-21Need more context; where in the book; etc.
19TYPO

This is somewhere between a typo and a suggestion.

On page 19, you write:

> Let’s use a task now to create our first Phoenix project, like this:
> ​$ ​​mix​​ ​​phx.new​​ ​​hello
> …

Then you say:

> We’re all set! Run your Phoenix application:
> ​ \t​$ ​​cd​​ ​​hello​
> ​ \t​$ ​​mix​​ ​​ecto.create​
> ​ \t​$ ​​mix​​ ​​phx.server​

We’ll call the above SECTION A.

After SECTION A, you explain about database errors, and say you can also run the server inside iex. Then you explain that phx.new created a new project.

And then, after all that, you have what we’ll call SECTION B:

> At the bottom of the mix phx.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 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​​ ​​phx.server​
> …

Clearly, SECTION B is just a more verbose version of SECTION A. I would assume one was written, then the other, and someone forgot to remove the first version.

I think this could be fairly confusing for brand new users, so I thought I would point it out.

2019-01-19
127115ERROR

When editing a video, edit/2 raises an error:

(RuntimeError) attempting to cast or change association `user` from `Rumbl.Multimedia.Video` that was not loaded. Please preload your associations before manipulating them through changesets

I solved it with Repo.preload

2019-06-08
27ERROR

In the file structure figure right under “Elixir Configuration”, “application.ex” is found under “lib/” where as it seems to be under “lib/hello/” for me.

Might be worth validating.

2019-01-19
44SUGGEST

Book has: “undefined function: RumblWeb.UserController.init/1
(module RumblWeb.UserController is not available)”

RC2 returns: “function RumblWeb.UserController.init/1 is undefined (module RumblWeb.UserController is not available)”

Needs updating.

2019-01-19
58SUGGEST

This bug still exists in 5.0. The code sample does not include this line: “alias Rumbl.Accounts.User” and the user may think they are required to change the alias, not simply add “alias Rumbl.Repo”. Testing against rc2.

2019-01-19
161OK

The whole passage is kind of confusing, as there is this example code but no source file etc mentioned.

“Preparing for Logged-In Users
You might be tempted to place the user_id in the session for the Auth plug to pick up, like this:
conn()
|> fetch_session()
|> put_session(:user_id, user.id)
|> get(”/videos“)
This approach is a little messy because it assumes an implementation. We don’t want to store anything directly in the session, because we don’t want to leak implementation details. Alternatively, we could do a direct request….”

Where should this “tempting” code go? into the test?
Also: Where dont want to leak implementation details out to? Are we assuming an implementation inside the test and its messy because of this?

2019-01-21
62SUGGEST

Building Resource Routes: perhaps a reminder to bounce the server after changing the router.ex file.

2019-01-19
36TYPO

“A request enters through the endpoint (lib/rumbl_web/endpoint.ex) and then goes into the router (web/router.ex).” should probably read “A request enters through the endpoint (lib/rumbl_web/endpoint.ex) and then goes into the router (lib/rumbl_web/router.ex).”

2019-01-19
31TYPO

Nit picking here anyway and I am not sure if it’s a typo but it seems like there is one too many “only” in the following “This stack only calls the function that accepts only JSON requests, …”.

Did you mean “This stack only calls the function that accepts JSON requests, …”?

2019-01-19
80TYPO

In Creating Users, the document says, “At this point we have a couple options.” This is slangly. The correct expression is “At this point we have a couple OF options.” and this is even more slangly. :)

2019-01-19
233ERROR

When changing the supervision tree and restart strategy back to the original, (on the code section named ‘opt/rumbl/lib/rumbl/application.ex’, a ‘Rumbl.InfoSys.Supervisor’ module is inserted on the children list, but this module doesn’t exist. The original children list has only ‘Rumbl.Repo’ and ‘Rumbl.Endpoint’.

2019-01-23
192DEFER

The solution provided for generating SEO friendly URLs in the book is really clean, however, it just extracts the ID from the slug. One might also need to check if the provided url matches the actual video slug.

Example:
localhost:4000/watch/1-seo-friendly-url
localhost:4000/watch/1-this-is-not-the-actual-slug

Accessing both urls will result in playing the video, which does not seem right. I am curious how you would go around this and will be happy to see your solution in one of the following editions.

2019-01-22
253ERROR

On the `otp/listings/rumbl/lib/rumbl_web/channels/video_channel.change1.ex` block of code at the end of the page, the new `compute_additional_info` function is called with `ann` as first argument, but `ann` is undefined on this context. It should be `annotation` as matched on line 3.

2019-01-21
254ERROR

The `broadcast_annotation` should take a third argument for the user being rendered (passed to the UserView on line 16 of the first block of code).

Both the function definition and the two calls to it should be fixed.

2019-01-23
254ERROR

On the second block of code on this page, there is call to `Accounts.get_user!` passing the `result.backend.name()` parameter, but the `get_user!` function expects an `id` instead of the username.

`Accounts.get_by(username: result.backend.name())` could be used instead

2019-01-23
3TYPO

“The router compiles down to the cat-quick pattern matching.”

I’m not sure if “cat-quick” is a typo or a term I’m unfamiliar with, but some googling didn’t turn up anything, so I’m guessing it’s a typo. If it’s not, it’s an unusual enough term that a definition would be helpful.

2019-01-19
93ERROR

Function “authenticate_by_email_and_pass(email, given_pass)”

uses Comeonin.Bcrypt.dummy_checkpw(), but Pbkdf2 is used in the rest of the example, should be changed to Comeonin.Pbkdf2.dummy_checkpw() (otherwise the Bcrypt must be included in the mix file and the 2 algorithms would have different timings, so the dummy-check would be useless).

2019-01-21
253245SUGGEST

> This file is ignored from version control so you can include sensitive credentials properly.

Depending on book audience, it might be worth clarifying that this is thanks to the generator creating a .gitignore file.

2019-01-21
18ERROR

The instructions for installing Phoenix need to be updated since the archives repository is no longer in use.

The command:
$ mix archive.install https: //github.com/phoenixframework/archives/raw/
master/1.4-dev/phx_new.ez

should be replaced with:
$ mix archive.install hex phx_new

2019-01-19
82ERROR

There is no (obvious?) explanation of changing the new action to use the registration_changeset. If the action still uses the regular User.changeset, then there is an error rendering the form.

2019-01-21
117TYPO

s/properly lockdown access/properly lock down access/

2019-01-21
126SUGGEST

“Use what you’ve learned to associate videos and categories in our application. In older versions of Phoenix, most developers put complex interactions in controllers. As it has evolved, we place such concepts in contexts to leave controllers as thin and simple as possible. These interactions now live in our context layer. We’ll put query functions in our schema layer.”

You could probably strike this whole paragraph. The first sentence is a little misleading, as it is an imperative that made me think for a moment that I was supposed to do so as “an exercise left to the reader”. The remainder of the paragraph is simply a topic that has already been mentioned so many times up to this point that it doesn’t really add anything here. Simply moving on to the following paragraph is sufficient.

2019-01-21
129SUGGEST

“Remember, the ^ operator (called the pin operator) means we want to keep ^username the same.”

Unless I’ve missed something, this is the first time in this book that the pin operator has been mentioned despite it having been used in several places prior to this. That may be fine, since I think this book assumes at least some familiarity with Elixir going in. However, this sentence reads as though the pin operator has already been discussed in this book.

2019-01-21
154ERROR

Broken link to Ecto.Adapters.SQL.Sandbox in the footnote.

2019-01-21
170TYPO

s/and finally make that no user_id is in the session/and finally make sure that no user_id is in the session/

2019-01-21
31SUGGEST

There is an empty note box at the top of this page. At the end of the previous page, there’s a “José says” box, and I wonder if it mistakenly bled onto page 35.

2019-01-19Issues like these will be fixed when the book goes to typesetting.
19SUGGEST

The output of the mix phx.new hello command is formatted like the text of the book, making the section extremely confusing. The output of the command, posing as book text, instructs the reader to set up their database, but the actual text of the book, lower, instructs the user not to set up their database.

2019-01-23
34OK

Is the second function in the pipeline supposed to say “browser?” I think it would make more sense to say “router” in the same way this general pipeline has been laid out before. I’m unsure how any browser fits in here in the request lifecycle.

2019-01-22The browser() is the pipeline from the router.
48TYPO

More of an inconsistency, but during the refactoring of the user display code to a new file, the code switches from using “” tags to “” tags.

2019-01-19I don't think we're going to catch all of this up.
40ERROR

The following iex session on p.40 did not work for me:

~/phoenix_apps/book/rumbl$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Compiling 1 file (.ex)
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> alias Rumbl.Accounts.User
Rumbl.Accounts.User
iex(2)> j = %User{name: “Jose”}

(CompileError) iex:2: Rumbl.Accounts.User.struct/1 is undefined, cannot expand struct Rumbl.Accounts.User
iex(2)>
==

Were we supposed to kill the server first? In any case, I got rid of the error like this:

~/phoenix_apps/book/rumbl$ iex -S mix
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Compiling 1 file (.ex)
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> import Rumble.Accounts.User
Rumble.Accounts.User
iex(2)> alias Rumble.Accounts.User
Rumble.Accounts.User
iex(3)> j = %User{name: “Jose”}
%Rumble.Accounts.User{id: nil, name: “Jose”, username: nil}
iex(4)>

2019-01-19
40ERROR

I think my earlier errata had to do with a spelling mistake Rumble v. Rumbl.

2019-01-19
43SUGGEST

The error:

key :show not found in: [index: []]

is caused by specifying:

Routes.user_path(@conn, :show, user.id)

in the link() function in templates/user/index.html, AND you have not defined a route in router.ex that goes to a :show action. It does NOT matter whether a :show action has been defined in the UserController. Only the route is needed to get rid of that error.

2019-01-23
45OK

The error:

key :show not found in: [index: []]

is caused by specifying:

Routes.user_path(@conn, :show, user.id)

in the link() function in templates/user/index.html, AND you have not defined a route in router.ex that goes to a :show action. It does NOT matter whether a :show action has been defined in the UserController. Only the route is needed to get rid of that error.

2019-01-23we can't reproduce this problem
51OK

The book says:
——
In particular, each template receives a couple of special assigns when rendering, namely view_module and view_template…:

<%= render view_module, view_template, assigns %>
——

Okay, but where does the actual assigns variable come from, i.e the 3rd argument to render()??

2019-01-19
37SUGGEST

I started over and renamed my project Rum because I had too many mistakes typing Rumble instead of Rumbl. When I say “Rumbl” in my mind, my fingers type “Rumble”.

2019-01-19Sorry... that's the only url we could find that was short and cheap.
233TYPO

In the testing section section, in the media.pragprog.com/titles/phoenix14/code/testing_mvc/listings/rumbl/test/rumbl/accounts/accounts_test.exs file, it says “describe register_user/2” but Accounts.register_user has an arity of 1

2019-01-21
40TYPO

username spelled incorrectly.

iex> user = %{usernmae: “jose”}

2019-01-19
106SUGGEST

Second paragraph under the router.change1.ex code block:

I suggest changing the sentence:
Furthermore, because pipelines are also plugs, nothing is stopping us from giving a plug, like authenticate_user, to pipe_through.

To something like:
Furthermore, because pipelines are also plugs, nothing is stopping us from passing a plug, like authenticate_user, to the function pipe_through.

Something more explicit. So far the book is great.

2019-01-21
111SUGGEST

“If you’re a careful reader…” I am not sure where this paragraph is going. It looks like you are going to discuss two way navigation and lazy loading, but ends up making vague comments about operations support.

2019-01-21
77SUGGEST

If the reader’s not working on a new console, they’ll need to run `“r Rumbl.Accounts.User`” to manually reload the module in order to successfully call the newly added function` “registration_changeset/2`” in the code example. It might be worth explicitly mentioning this and also showcase the nifty module reloading functionality in iex!

2019-01-21
74TYPO

potentially misplaced parenthesis in code sample. null is not an option on references() and doesn’t affect the SQL output
hxxps://hexdocs.pm/ecto/2.0.2/Ecto.Migration.html#references/2

- add :user_id, references(:users, on_delete: :delete_all, null: false)
+ add :user_id, references(:users, on_delete: :delete_all), null: false

2019-01-19
31SUGGEST

This statement “if you had the brilliant idea of converting the whole API site to accept only XML” sounds sarcastic due to the usage of the word “brilliant”.

I would suggest that the book should avoid sarcasm to create a welcoming atmosphere for learning, and to show the Elixir community as friendly.

A better way to phrase the same idea would be: “if you had the need to convert the whole API site to accept only XML”.

2019-01-19
254ERROR

In the compute_additional_info(annotation, socket) function, Accounts.get_user!(result.backend.name()) is used. But Accounts.get_user! function needs id, not username. It should be Accounts.get_user_by(result.backend.name()).

2019-01-23
154TYPO

“Let’s tests…” => “Let’s test…”

2019-01-21
160OK

Video controller wasn’t locked behind user authentication since requests are only piped through authenticate_user plug when scoped under “/manage” and Routes.video_path(:some_action) doesn’t resolve into a path with scoping as is.

2019-01-22We only scope video editing to /manage since the /watch urls are made to be public.
160SUGGEST

re: #84180

Looks like the issue comes from adding an additional videos resources scoped under “/manage” instead of replacing the original videos resources scoped under “/” with one under “/manage” in the router file. (see page 103 and 106)

I’d suggest making it a bit more explicit that it’s meant to be a replacement rather than an addition otherwise it’s easy for that vestigial line to stick around and cause trouble down the line.

2019-01-22
162TYPO

assert html_response(conn, 200) =~ ~r/Listing videos/
“videos” in the regex need to be capitalized into “Videos” for assertion to pass and match what gets autogenerated by the phoenix mix task

2019-01-21
169TYPO

Duplicated test “logout drops the session”, looks like the first occurrence was not supposed to be included

2019-01-21
140TYPO

1/3 of the way down the page there is a two-sentence paragraph with a typo in the second sentence:

“We updated our video with a category that exists. The update works, but suppose tried to update a video with a bad category:”

Typo: “…but suppose tried…”
Correction: “…but suppose we tried…”

2019-01-21
74ERROR

In the last paragraph on the page that starts with “Deleting a user should also delete the associated credentials”, the second to last sentence says: “we shouldn’t allow credentials without users, so we also passed null: false”.

For the code sample above the paragraph (create_credentials.change1.exs), that sentence makes it seem like :user_id should also have a null: false param passed to add. The code and sentence seem conflicting, so just wasn’t sure if it was an error or not.

Thanks!

2019-01-19
92OK

I think :not_found should be :notfound in the bottom most code example on page 92

2019-01-21
116TYPO

After creating the action function, I think you also want to tweak the params of the edit method to also be passed current user, along with the new and create method in the last code example on that page. This change to edit is shown on 117, but not explicitly stated.

2019-01-21
200SUGGEST

To continue the breakdown earlier of “topic:subtopic”, I’d consider rewording the following:
“They’ll be able to join any number of channels and any number of topics on a channel.”
into
“They’ll be able to join any number of channels and any number of subtopics on a channel.”

Even better would be to use videos as an example. Something to the effect of clients can join multiple channels such as the videos channel and multiple subtopics such as multiple individual videos.

2019-01-21Good comment but I think we want to leave the terminology alone here.
154TYPO

It seems like the :not_found constant used in the book has been replaced with :notfound?

elixir version:
[I] rumbl · master± ⟩ elixir -v
Erlang/OTP 21 [erts-10.2] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:1] [hipe]

Elixir 1.7.4 (compiled with Erlang/OTP 21)

2019-01-22This :not_found I think is the one introduced in our explicit error message.
5340ERROR

iex> user = %{usernmae: “jose”}

should be

iex> user = %User{usernmae: “jose”}

2019-01-19intentional misspelling to demonstrate struct
220TYPO

“Next, we modify our call to Multimedia.list_annotations by passing our last_seend_id on line 8.”

last_seend_id => last_send_id

2019-01-21
1TYPO

Incomplete sentence: “We know that Phoenix is a bold name for a bold framework, but look.”

2019-01-19we'll let the editors take this one. I think it reads fine.
16DEFER

When you say “Ecto allows us to organize our code in this way. It separates the code with side effects, which changes the world around us, from the code that’s only transforming data.” — you really need to take a knee and spend a few minutes talking about your notion of “side effects”. I’ve heard this in the Elixir forums too, and as far as my experience, this notion of “side effects” is not a universally understood definition. What do you mean exactly? Why is a database operation a “side effect” but a “transformation” not? I think this paragraph really loses people.

2019-01-22We'll look at this one. Good comment.
23SUGGEST

One of the endless points of confusion with Elixir (and Ruby for that matter) is knowing when the fat arrow (=>) should be used in place of the colon (:). This is the first time we see the fat arrow notation — you should spend a quick moment to explain why we use it here instead of the colon notation.

2019-01-19
40OK

You really need to explain more of your reasoning here with respect to contexts. “Contexts” is easily the most confusing part of Phoenix 1.4 and I think it’s honestly just because the explanations have been so lacking. Other languages/frameworks introduce “service classes” that serve much of the same purpose: i.e. isolating/abstracting bits of functionality to separate modules/classes. Contexts are not that different. But you can’t just jam them into the book like this (or indeed, into the framework) and expect anyone (even seasoned developers) to know what you’re doing. This section jumps from users to accounts and back again with a million alternative ways of doing things that are left completely un-explained.

If nothing else, mention “service classes” and the fact that this is nothing more than an organizational strategy and a way to deal with the single-responsibility-principle, because really, the organization into an “Accounts” context is entirely arbitrary and if a dozen other devs tackled this same problem, you’d undoubtedly end up with a dozen different equally valid solutions.

2019-01-23
51SUGGEST

“This allows you to render the view and template for your controller action in the layout with a plain render function call. No magic is happening here.”
This is frustrating because you don’t include an example. No magic?!? Really? You have not shown us what’s behind the curtain enough to make a claim like that. There’s another issue I’m filing about this, but it’s not at all clear from the code or text (i.e. it is black magic) as to how Phoenix renders views… there’s no clear example presented that shows why views are modules (why?!?) and templates are functions (why?!? this is so arbitrary), so without a clear in-your-face example, the claim that this is so un-magical is ludicrous. You could definitely save some friction by showing (not telling) how Phoenix renders the view.

2019-01-19We'll see what comments come in from technical reviewers. From over 500 comments so far we need to be judicious about the ones we address.
55OK

When you include the sample schema code for users with “field :name, :string” etc, it would be really nice to include a link to the list of all supported data types and options. We can guess stuff like “:string” and maybe “:integer”, but holy smokes, Ecto in particular is so sparse with its examples — why starve the reader with these critical bits of info? Throw us a link! Broadcast the options loudly! This is the place to demonstrate and teach what is possible.

2019-01-19
69SUGGEST

The code sample that includes “create table(:users) do” should have an aside as to why you use atom notation (i.e. :users) instead of a string (i.e. “users”). Really, both alternatives work, but it’s one of those bits of uncertainty that cripples newcomers and that experienced Elixir devs never think about. Help the newcomers out! Just mention a brief explanation here and you will avoid that headache!

2019-01-19
72OK

“By now, you should be seeing the value of the context API.”

This is sort of a throw-away statement… show, don’t tell. Can you explain and/or clarify why this context API is valuable? I’m not sold on it. I would have structured my code to have one context per resource, period… why all this self-congratulatory praise about being valuable? What exactly about this approach is good or useful and can you state that with an example or an explanation that doesn’t leave readers raising an eyebrow?

2019-01-19
72SUGGEST

“With our repo calls in place, we’re ready to try it out even though we haven’t touched our controller code at all.”

Remember: you should remind readers that they have to restart Phoenix… some of those changes don’t register right away.

2019-01-19
74OK

“In the past versions of Phoenix, you’d probably just use the Accounts.User.changeset.”

This is unclear… we’re using that function in our code in THIS version of Phoenix. What are you talking about? Are you suggesting that we should put some code in the context module instead of in the changeset function? If so, clarify that and explain your reasoning a bit more.

2019-01-19
102DEFER

With the code sample of:

plug :authenticate when action in [:index, :show]

Why are we only authenticating for 2 of the possible actions? Is that a security hole? What happens if malformed or unauthorized data is submitted to one of the other endpoints, e.g. :update?

2019-01-22
103OK

“Let’s add a tiny function to RumblWeb.Auth that receives the connection and the user, and stores the user ID in the session:”

Why are you adding this new “tiny” function into a module that has been tapped as a plug? Isn’t this breaking the single responsibility principle for a module and making it take on multiple responsibilities? Couldn’t/shouldn’t these other functions be in their own isolated module? I think you should at least explain your decision to include this function here.

2019-01-25Thanks for your comment.
106TYPO

“Next, lets write the function that does the actual work.”

Should be “let’s” — it’s a contraction of “let us”.

2019-01-21
106OK

The “authenticate_by_email_and_pass” function includes a “cond do” block — the default value of that is a “true” value — it would be nice to reinforce how those blocks work. Remember: this is an educational book and it should reinforce/repeat stuff that readers may have learned (or missed) from any generic Elixir info.

2019-01-21
115OK

The introduction of the “mix phx.gen.html” command needs some introduction. It’s brutally long. And if you aren’t completely clear on the reasoning/necessity of the context module, its arguments become even more baffling and onerous. I don’t know of any other framework that attempts to leverage such long-winded command-line arguments, so at a minimum, it would be nice to at least acknowledge that this is a long command.

2019-01-21I am having a hard time separating the comments about the book from comments about the framework. Going to wait on this one.
116OK

When we move the “authenticate” function from the user_controller to the RumbleWeb.Auth module, we change the name to “authenticate_user”… but it seems needlessly confusing. Why not just name it “authenticate_user” from the get-go? There’s already a lot going on here with moving a function between modules and changing its visibility from defp to def.

2019-01-21
117OK

“First, let’s share authenticate_user/2 across all controllers and routers.” — this is a key place to make mention of “arity” and function signatures… it’s a bit of a surprise that Elixir allows for multiple function defs of the same name. Remind the readers of this amazing feature! In the code sample too, you should mention that the import statement requires the arity to know which functions to import (this is very unlike languages like Python or Go):

import RumblWeb.Auth only: [authenticate_user: 2]

2019-01-21
130OK

There is a lot of discussion in this section about “side effects”. I think you should explain why you view certain actions as “side effects”. I think many developers may think of a database change or reading data from a socket as the “main event.”

2019-01-21
131OK

There are not enough examples in the Ecto docs to just name drop a bunch of operators and functions and expect anyone to magically know how to use them. My observations of developers using Phoenix is that Ecto is hands-down the single most frustrating and confusing obstacle that comes up, and I believe it’s due almost entirely to a scarcity of examples in its docs… there has to be enough examples in variations to make it clear what a particular option/function does. If you don’t take every opportunity in an educational book to stay ahead of those roadblocks, you are doing the readers (and Elixir + Phoenix) a disservice.

2019-01-21this comment isn't actionable.
132SUGGEST

Why are you using the pin operator in front of a string — it’s not even a variable:

where: ilike(u.username, ^“j%”)

Others have pointed out that the pin operator was never introduced in the book, and this usage goes beyond my understanding of it. I’m assuming there will be other readers left scratching their heads at this.

2019-01-21
132OK

Consider omitting “fantastic” in the sentence beginning with “Now, let’s say that we want to take advantage of this fantastic count feature”. It comes across as a bit snarky (same with the wink to usernames starting with j), especially when Ecto is so frustrating and it’s not at all clear what rules apply when queries get sliced up. Again, some more thorough examples in Ecto would help users understand how the same query could be represented in multiple variations, including as a series of fragments.

2019-01-21
133OK

When you say “we could use a longer list with more bindings if our query had joins.” I think you should show, not tell. It’s worse when you know that a tool CAN do what you want, but nobody tells you HOW to do it.

2019-01-21
133OK

It seems overly dramatic to say “you don’t have to panic and fork Ecto to build your own mapping layer” — forking?!? We barely understand Ecto purpose and very little of what it can do, so any mention of forking it seems absurd.

I know I have harped on this, but more than anything else, Ecto needs examples. I would even consider restructuring this chapter to start with raw database queries (the kind you could execute in your DB client) and then working backwards towards more idiomatic representations of them using fragments and finally the Ecto operators etc.

After committing seppuku on myself trying to adapt working database queries into ActiveRecord and other ORM’s, I think a case really can be made that ORMs are an anti-pattern — they definitely can be an enormous time-suck. With regards to Ecto, it is probably good to error on the side of too many examples and variations so that this database mapping layer is the useful tool it was intended to be.

2019-01-21maybe you should check out our ecto book. I don't think we could provide all of the detail you're looking for in a single chapter of the Phoenix book.
133OK

Consider clarifying the sentence that begins “A query fragment sends part of a query directly to the database” — it’s not clear from that sentence whether each fragment executes against the database separately or if fragments are bundled together into a larger query string that may or may not get executed later via some other command.

2019-01-21
141OK

“Second, you could configure the database references to either cascade the deletions or simply make the videos.category_id columns NULL on delete.”

Consider showing an example here (presumably using the :nilify_all option?). Show, don’t tell. It’s only after the reader has seen a few examples that the long-form documentation of options etc. become useful.

2019-01-21
145SUGGEST

In the paragraph that begins with “You can probably already tell that the decisions made throughout the Phoenix platform make testing a joy.” it comes across as overly subjective and self-congratulatory. Consider toning it down. The intro to the book did this really well, where you stated what goals you tried to achieve with Phoenix or Elixir and we (the readers) could see how it stacked up against some stiff competition. That intro had a much better vibe.

2019-01-21
150OK

Use a colon to emphasize the statement that follows it. These 2 sentences
“Libraries are like macros. Don’t use one when a simple function will do the job.”

should instead be represented as a single sentence with 2 clauses separated by a colon:

“Libraries are like macros: don’t use one when a simple function will do the job.”

2019-01-21I like the way it reads but we'll let the editors make a call here.
152ERROR

In the test labeled “requires password to be at least 6 chars long”, the following assertion:

assert %{password: [“should be at least 6 character(s)”]} …

must instead reference a nested structure:

assert %{credential: %{password: [“should be at least 6 character(s)”]}} …

2019-01-23
153SUGGEST

The test labeled “returns user with correct password” introduces for the first time the use of a second argument to the test macro:

test “returns user with correct password”, %{user: %User{id: id}} do

I have never seen this before, and I even poured over the ExUnit docs trying to spot where this is documented… I didn’t find it. Major “wow” / “WTF” moment there. You should really take the time to explain what that 2nd argument is… is it output of the setup? Or something else?

2019-01-21it's the test context. We can't be a resource for all elixir; going to leave that as is.
157SUGGEST

This section ends rather abruptly: “As expected, they are all green.” The section is titled “Using Ecto.Sandbox for Test Isolation and Concurrency” — but where was the talk about concurrency? Where are there demos/examples/explanations of different approaches? I felt like this section ended without accomplishing its mission.

2019-01-21
159OK

With the sentence reading “Then setup places a base conn into our test metadata and returns a new context with this connection, which flows into our page_controller_test as an optional second argument to the test macro.”

I really could not follow this…. I would not guess that the code under discussion had anything to do with arguments to the test macro. It really felt like black magic. I mentioned the magical 2nd argument to the test macro separately: this is its counterpart and I think there’s a great opportunity to up everyone’s testing game by connecting these dots.

2019-01-21
159OK

“Keep in mind RumblWeb.ConnCase is just a foundation. You can personalize it to your own application as needed.”

Suggesting customization without an example isn’t very useful and makes for a weak conclusion to this section. Consider refactoring the transition to the next section.

2019-01-21
161OK

“This approach is a little messy because it assumes an implementation. We don’t want to store anything directly in the session…”

Consider elaborating on this, especially what exactly you mean by an “implementation”. What exactly can the tests expect to be stored in the session? Or was the point that the conn should have the user details regardless of whether or not the auth was implemented using sessions (and not JWT, for example)?

2019-01-21
162ERROR

“We add a new test for the /videos route”
should instead reference the route as /manage/videos per previous definition.

2019-01-21
162OK

This page includes test code that makes the first use of @tag — what is this new sorcery? This would be a great opportunity to explain that feature and how to use it.

2019-01-21
152TYPO

Missing asserts in “does not accept long usernames” and “requires password to be at least 6 chars long” on the {error, changeset} lines.

2019-01-23I *think* what you're saying is that the assertion did not access the credential. If so we've fixed it.
162TYPO

The line conn = get conn, video_path(conn, :index) should use Routes.video_path(conn, :index) I believe. It is correct in the next code sample for that bit

2019-01-21
60ERROR

In listing user_controller.change1.ex, the User struct either needs to be fully specified (%Accounts.User{}) or the listing needs another alias for User.

2019-01-19
155OK

In chapter 3. Controllers, Coding Views an example is given how to create a function that parses a user’s first name from the user’s name field. Yet the hardcoded example-data used at that moment in the book only contains name fields with a first name. That makes the first_name function useless and the example confusing.

2019-01-22
158TYPO

In the code sample
testing_mvc/rumbl/test/support/conn_case.ex
The line:

import Rumbl.TestHelper

will not be here at this point as it is added a few pages later (PDF page 160).

2019-01-22
162ERROR

In the code sample: testing_mvc/listings/rumbl/lib/rumbl_web/controllers/auth.change1.ex

The user variable assigned on the first line is never used and will become a compiler warning.

2019-01-21
162ERROR

In the code sample testing_mvc/listings/rumbl/test/rumbl_web/controllers/video_controller_test.change1.exs

This line is included:

@tag login_as: “max”

This is not required for this section, is mentioned as to be added and is included in the next code sample.

2019-01-21
80ERROR

in Listing: authentication/listings/rumbl/lib/rumbl/accounts/accounts.change1.ex I only added the register_user method. But I also needed to change the change_user method to use registration_changeset in order to work as intended because the localhost:4000/users/new gets its initial changes from Accounts.change_user

2019-03-15
62ERROR

resources “/users”, UserController, only: [:index, :show, :new, :create] leads to an error. :new and :show have to be the other way around!
resources “/users”, UserController, only: [:index, :new, :show, :create]

2019-06-07
71ERROR

The line “alias Rumbl.Accounts.User” first referenced on p54 (controllers_views_templates/listings/rumbl/lib/rumbl/accounts/accounts.ex) needs to also be included in the follow-up code shown @ p71-72 (despite existing in the source code @ ecto/listings/rumbl/lib/rumbl/accounts/accounts.change1.ex).

This will prevent the following error message when viewing “localhost:4000/users”:

Protocol.UndefinedError at GET /users
protocol Ecto.Queryable not implemented for User, the given module does not exist. This protocol is implemented for: Atom, BitString, Ecto.Query, Ecto.SubQuery, Tuple

2019-01-19
5757ERROR

Tried to add new data via iex as per the instructions, but kept getting…

(CompileError) iex:7: Rumbl.Acounts.User.struct/1 is undefined, cannot expand struct Rumbl.Acounts.User

I think the server needs to be restarted before the Rumbl.Accounts.User changes are picked up - is that right?

2019-01-19
192TYPO

Third sentence in Wrapping Up section has an unnecessary “and” in it:

“We then and laid some foundation so we can play our videos in YouTube.”

2019-01-21
10ERROR

-> mix archive.install

on mac book pro gave the error:

(Mix) Cannot create archive without input directory, please pass -i as an option

Used to install:

mix archive.install hex phx_new 1.4.0

2019-01-19
38,72SUGGEST

Some examples in the book use gettext, for example on page 38 (PDF), but the only explanation for it is “:gettext for internationalization” on page 72 nothing more. Also, I think the topic internationalization and localization deserves a few pages in this book.

2019-01-19
238TYPO

In Rumbl.Counter.child_spec the MODULE macro is written as Module.

2019-01-21
241TYPO

The children definition includes Rumbl.InfoSys.Supervisor although that hasn’t been introduced yet.

2019-01-21
52TYPO

The closing paragraph of Chapter 3 says ‘In the next chapter, we’re going to back the context’. I guess the intended wording was ‘In the next chapter, we are going back to the context’.

2019-01-23
211TYPO

In the second paragraph is written “we build a video struct”, but in fact it must be “we build an annotation struct”.

2019-01-21
240TYPO

Last sentence of the first paragraph is missing the word “an”:

“This function simply returns atom of the table name to use for our ETS table.”
“This function simply returns [an] atom of the table name to use for our ETS table.”

2019-01-25
72TYPO

When adding the :comeonin and :pbkdf2_elixir dependencies to mix.exs, you also included a line for bcrypt_elixir. You should remove that.

2019-03-15
38,72OK

I have reported this for B5.0 previously and its marked as “Fixed in: B6.0”, but it is not fixed so I report this again.

Some examples in the book use gettext, for example on page 38 (PDF), but the only explanation for it is “:gettext for internationalization” on page 72 nothing more. Also, I think the topic internationalization and localization deserves a few pages in this book.

2019-03-15Please don't re-open suggestions. Internationalization is beyond the scope of this book.
81TYPO

In the sentence “Since we want to expose user functionality though
our context, we add the change_registration function.” “though” should be “through”.

2019-03-15
57TYPO

When inserting the User “Chris” to the database, I believe the username should be: “mccord” instead of “cmccord”

2019-03-15
27ERROR

Elixir Configuration section

Phoenix 1.4 project menu structure, under the lib folder, doesn’t find an application.ex file.

2019-03-15
39TYPO

lib/rumbl/application.ex

children = [
supervisor(Rumbl.Repo, []),
supervisor(RumblWeb.Endpoint, []),
]

has changed to a list.

2019-03-15
170160TYPO

“…so we can use user_fixture and video_feature…” should be “…so we can use user_fixture and video_fixture…”

2019-03-15
164154TYPO

One of the features that line provides is
should be
One of the features that file provides is

2019-03-15
49SUGGEST

On page 49: “Phoenix uses singular names throughout, avoiding confusing pluralization rules and naming inconsistencies.”
On page 55: “mix ecto.gen.migration create_users” , it is plural, also the migration on the next page (56) uses plural form i.e. users.
I think either the statement on page 49 should be modified or the migration on pages 55,56.

2019-03-15
72TYPO

Last Comma in following code causes syntax error.
defp deps do
[
…,
{:comeonin, “~> 4.1”},
{:bcrypt_elixir, “~> 1.0”},
{:pbkdf2_elixir, “~> 0.12”},
]
end

Should be

defp deps do
[
…,
{:comeonin, “~> 4.1”},
{:bcrypt_elixir, “~> 1.0”},
{:pbkdf2_elixir, “~> 0.12”}
]
end

2019-03-15
73ERROR

The listings and code downloads of the book place the accounts.ex file at lib/rumbl/accounts.ex but the generator command given on page 73 is not given. Instead the command generates a second accounts,ex at lib/rumbl/accounts/accounts.ex

If the accounts.ex file is moved into the accounts folder then the warning is given and the resulting output is much closer to the code in the source downloads

2019-07-21
103SUGGEST

I have trouble determining where the end of the shell output is. For instance, on page 103 I thought the instructions in the shell output (“Remember to update your repository by running migrations”) was instead an instruction by the book’s authors. I can see now in retrospect that the font is different, but that’s the sort of thing I apparently miss on my first read-through.

2019-03-15
23SUGGEST

In section “Pattern Matching in Function”, explain how to enter the iex shell.

2019-03-15
1OK

Please include parallel sections for developers only interested in building REST APIs.

2019-03-15
1OK

In another erratum, I suggested that parallel sections be added for those only interested in building REST API applications. However, I do see how that might be difficult with the current format of the book. So, I have an alternate suggestion. What not written a separate book intended only for API developers? I would probably be fairly short and would bypass everything specific to server rendered web apps. I for one would purchase it immediately. Thanks

2019-03-15It's really not a book. It's a blog post and there are several of them around. This doesn't fit in our flow.
39ERROR

In section “Working with Contexts”, there is a code snippet showing the children list in “/lib/rumbl/application”. That code snippet is out-of-date and will be confusing to readers. The snippet shows:

children = [

  1. Start the Ecto repository
    supervisor(Rumbl.Repo, []),
  2. Start the endpoint when the application starts
    supervisor(RumblWeb.Endpoint, []),

While the actual code is:

children = [
# Start the Ecto repository
Rumbl.Repo,
# Start the endpoint when the application starts
RumblWeb.Endpoint
# Starts a worker by calling: Rumbl.Worker.start_link(arg)
# {Rumbl.Worker, arg},
]

2019-03-15
76SUGGEST

Increase the minimum and maximum length of password. See OWASP recommendations here:
www.owasp.org/index.php/Authentication_Cheat_Sheet#Password_Length

2019-03-15This actually turns into a pretty expensive change for us in the book since the code will percolate through the example code for the whole book. Different apps and use cases will have different requirements. These are configurable as we've specified them. \n \nI think we have to close this without a code change, but I left a line in prose to point to these guidelines.
79ERROR

On page 72, pbkdf2_elixir is used as hashing library. But the hash string on page 79 looks like a bcrypt hash, as it starts with $2b$. A pbkdf2 hash should start with something like $pbkdf2-sha512$.

2019-03-15Now that is one we never would have caught!
81TYPO

Any view that requires the user credentials will need to use a specific
changeset for registration. Since we want to expose user functionality though
our context, we add the change_registration function.

This should be “through”.

2019-03-15
190SUGGEST

The first time the issue of “behavior” vs. “behaviour” comes up is on the bottom of page 190 (Section “Extending Schemas with Ecto Types”).

However, the infobox “Behaviour or behavior?” does not appear until page 236, where “behavior” is actually spelled “behaviour” in the text.

I suggest putting the infobox after the first occurrence. I actually made the mistake of writing “@behavior” when typing the listing and only realized the different spelling when I tried to run my code and received a warning.

2019-03-15
28ERROR

I don’t see any application.ex file in my lib folder

2019-03-15
33ERROR

The tree diagram on p. 33 should include the file: channels\\user_socket.ex

2019-03-15
74TYPO

Phoenix let’s us know …
Shouldn’t it be?
Phoenix lets us know …

2019-03-15
75OK

When reviewing the user changeset, you explain “cast()”, then you jump directly to the “length” validation, omitting the “required” validation.

2019-03-15The pages don't always line up exactly for me. \n \nI am seeing "validates existence and length". Are you seeing something different? \n \nMarking this "not a problem"... please reopen if I have missed something.
41OK

When playing around in the terminal with the map/struct as explained on page 41:

alias Rumbl.Accounts.User
#=> Rumbl.Accounts.User
jose = %User{name: “Jose Valim”}

it raises a compilation error:

(CompileError) iex:2: Rumbl.Accounts.User.struct/1 is undefined, cannot expand struct Rumbl.Accounts.User

"

2019-03-15
41TYPO

Errautm #84651 is wrong, - I started “iex” without passing the option “-S mix”. My bad, sorry for that.

2019-03-15No problem! Glad you're tracking along with us in the book, and helping others for any problems you do find.
164TYPO

3rd paragraph after “Using Ecto.Sandbox for Test Isolation and Concurrency”
>“Let’s tests…”
should be “Let’s test…”

2019-03-15
52TYPO

“…we’re going to go back the context…”

Seems like it should be:

“…we’re going to go back to the context…”

2019-03-15
80 SUGGEST

On page 80 and 81 it jumps between using view in iex -s mix and displaying error pages and then repeating using view in iex -s mix with the same content.

2019-03-15
83TYPO

“In the next chapter, we’re going to go back the context” should be “In the next chapter, we’re going to go back TO the context”

2019-03-15
92SUGGEST

On the steps after:
-r Rumbl.Accounts.User
alias Rumbl.Accounts.User

Its not clear clear on the next commands what is an input and what is an output.

It should be:
changeset = User.changeset(User{username: “eric”},{})

and then be clear that:
Ecto.Changeset{changes:{}, …} is the output.

When I was typing it out, I wasn’t sure and had to mess with a couple times to get it right.

There should be an introduction with coding conventions with what is input and what is output.

2019-03-15
98ERROR

When running:
mix phx.gen.context Accounts Credentials credentials email:string:unique password_hash:string user_id:references:users

The output at this time should be (if i am not mistaken):
The Rumbl.Accounts context currently has 6 functions and 1 files in its directory.

In the text it says:
The Rumbl.Accounts context currently has 8 functions and 2 files in its directory.

2019-03-15
86ERROR

I ran the generate context command, but it didn’t warn me about generating code in an existing context. It created the file /rumblr/accounts/accounts.ex (with the same name/module name as the file accounts.ex in the /rumbl directory)

2019-06-08
93ERROR

The function change_registration is created, but never used. It could be used right below in the register_user function, but the authors inlined the same call that change_registration is doing.

2019-07-21
97SUGGEST

You invite the reader to visit localhost:4000 and assume they are not logged in.
But they are logged in already because you invited them to do so on page 95.
So, the reader will see the header with the logout link.

You either have to instruct readers how to delete the session, or assume that they see the logout header.

2019-03-15
116ERROR

A section in this page says “Phoenix frees you from memorizing unnecessary singular and plural conventions by consistently using singular forms in schemas, controllers, and views.”

However this is wrong, because plural is used on schema definitions.

2019-06-08Added a "in most cases".
94SUGGEST

You write, “When a user isn’t found, we use comeonin’s dummy_checkpw() function to simulate a password check with variable timing. This hardens our authentication layer against timing attacks, which is crucial to keeping our application secure.”

As far as I can tell, if dummy_checkpw() were not called then the only thing that a timing attack would be able to determine is whether or not a user with the provided email address exists in the DB. But the registration form already makes this information known because a “has already been taken” error will be rendered if an email of an existing user is entered (see unique_constraint(:email) call on page 76).

I feel like this is either a mistake in the book or could use more clarification, especially since this is security related.

Thanks, and the book is great!

2019-07-21José? What do you think?
116TYPO

“Then, the list_user_videos and get_user_videos utility functions will scope a request to the videos a user can see.” - actually you defined the “get_user_video!” utility function, not “get_user_videos”.

2019-03-15
266ERROR

‘npm install’ throws an error ‘npm ERR! Could not install from “../../../deps/phoenix” as it does not contain a package.json file’
it’s resolved by ‘mix deps.get’ for the whole umbrella project first

at the same time, running ‘mix test’ gives me error that ‘(Mix) Could not start application rumbl_web’ because no ‘RumblWeb.Application’ module is found

2019-03-15
189DEFER

Users are likely to get a slightly different error when trying to click the “Watch” button from the videos-index page after making the changes to render video links using slugs:

[debug] (Ecto.Query.CastError) lib/rumbl/multimedia.ex:124: value `“10-”` in `where` cannot be cast to type :id in query:

The problem is that no data migration was applied when the slug column was added to the videos table, so all extant videos will have incorrect slugified link paths—e.g., “/watch/1-” rather than “/watch/1-hello.” I worked around this problem by manually updating the slug fields for rows in the videos table.

2019-07-21
152SUGGEST

It’s mentioned here that we could add :nifily_all to the references function. I tried to do that without rolling back (by creating a new migration), and couldn’t find a proper way to do that. It would be nice if the book showed how to do that the right way

2019-06-08
167TYPO

“we checked our the ability” should be “we checked our ability”

2019-06-07
171SUGGEST

On the code “user = conn.assigns[:current_user]” the variable “user” is unused. So maybe just check for “conn.assigns[:current_user]”?

2019-06-08
122SUGGEST

On the top of the page, the text says:
<<Remember to update your repository by running migrations:
$ mix ecto.migrate>>
But the migration should not happen at this time, since after this:

  • There are some changes done to the generated migration (adding not null to column name and adding unique index on categories.name);
  • On page 123, the text continues: <<Finally, migrate your database with your two new migrations […]
    [info] create table categories
    […]>>
2019-06-07
132SUGGEST

Hi,

Is the pin operator needed when the value is a literal in the following expression?

< j_users = from u in users_count, where: ilike(u.username, ^“j”)>>

It is the same as:

iex> j_users = from u in users_count, where: ilike(u.username, “j”)

Regards,
Dan

2019-06-07Not necessary. It's a stylistic choice, such as the optional commas after the last pair in a map or a keyword dict. Should the user decide to change the literal to a variable, it will still work.
137SUGGEST

About the phrase:
<<Oops. Our application blows up with a constraint error, similar to the one we
saw when creating duplicated categories.>>
There is no earlier code in the book demonstrating creating duplicated categories. The only place where categories are created is in the seeding script, where the creation of a duplicated category is prevented by first reading:
Repo.get_by(Category, name: name) || Repo.insert!(%Category{name: name})

Best regards,
Dan

2019-06-08
140ERROR

The following code:
<<
iex> changeset = foreign_key_constraint(changeset, :videos, name: :videos_category_id_fkey, message: “still exist”)
>>

fails with:

(CompileError) iex:17: undefined function foreign_key_constraint/3

2019-07-21
76SUGGEST

The footnote link to OWASP leads to a valid page but the page redirects visitors to a Github URL which I can’t add here due to anti-spam rules.

2019-06-07
13TYPO

“We will add a treatment of LiveVeiw in the last chapter.”

Excerpt From: Chris McCord, Bruce Tate, José Valim. “Programming Phoenix ≥ 1.4 (for M Chambers).” Apple Books.

LiveView typo - Love the book btw

2019-06-07
67TYPO

I think the code here: media.pragprog.com/titles/phoenix14/code/ecto/listings/rumbl/lib/rumbl_web/controllers/user_controller.change3.ex

Should read:

defmodule RumblWeb.UserController do
use RumblWeb, :controller

alias Rumbl.Accounts
alias Rumbl.Accounts.User

def index(conn, _params) do
users = Accounts.list_users()
render(conn, “index.html”, users: users)
end

def show(conn, %{“id” => id}) do
user = Accounts.get_user(id)
render(conn, “show.html”, user: user)
end

def new(conn, _params) do
changeset = Accounts.change_user(%User{})
render(conn, “new.html”, changeset: changeset)
end

def create(conn, %{“user” => user_params}) do
case Accounts.create_user(user_params) do
{:ok, user} ->
conn
|> put_flash(:info, “#{user.name} created!”)
|> redirect(to: Routes.user_path(conn, :index))

{:error, %Ecto.Changeset{} = changeset} ->
render(conn, “new.html”, changeset: changeset)
end
end
end

Notes here: elixirforum.com/t/programming-phoenix-book-club/20449/72?u=astonj

2019-06-08
manyTYPO

p 39: last paragraph “By default, our application starts […] controlls” => controls

p 76: last paragraph “Keep in mind […] excelent” => excellent

p 112: first paragraph “Digging deeper […] Ecto.Assocation.NotLoaded” => Ecto.Association.NotLoaded

p 189: 2nd to last paragraph “With Phoenix.Param properly implemented […] server back up” => server backup

p 223: First page of Chapter 11 2nd paragraph “Most successful projects […] neccesary” => necessary

p 247: end of first paragraph “Now if you start up […] eachother” => each other (more standard)

p 274: paragraph just after full code block “Nice! The first test […] funciton” => funky town jk, function, great song though no?

p 274: 3rd paragraph “You can see […] pure funcitons” => functions

2019-06-07
5,6ERROR

For some reason when reading the MOBI version in the kindle.app (1.25.2 (52078)) on macos, the indentation for code snippets is correct, but not when using the kindle.app (6.16.1) on ipad (ios 12.1.4). Then the code snippets have lost indentation and the font looks extra bold.

Don’t know if this is a kindle.app issue or something that can be adjusted in the book’s formatting.

2019-07-21I have sent this issue to the prags as a tooling bug report. Thanks for catching it. I am leaving this open until we get confirmation.
197TYPO

The part “Though we place our implementation in the same file as the video definition, it could as easily exist elsewhere.” is incorrect. The example didn’t place the implementation in the same file as the video definition.

2019-06-07
217ERROR

on the window.userToken snippet, the code calls Routes.static_url. However, the generated code on app.html.eex is using Routes.static_path. It is a bit confusing because I don’t know if this was updated in this snippet

2019-07-21
221ERROR

When we generated our Multimedia context, the main file was created in rumbl/multimedia/multimedia.ex. However, the filepath in the book points to rumbl/multimedia.ex

2019-07-21Updated your Phoenix version and the patches should match!
224DEFER

I think it would be nice to have some explanation here around naming, because Phoenix.View.render_many and render_one need a function render() to be defined in the passed module, this may be not clear to the reader.

2019-06-07
230TYPO

In this wrapping up, it says we built a server-side channel with support for both WebSockets and long-polling. However when the socket was created, it had “longpoll” set to false, so this affirmation is not true.

2019-06-07
255ERROR

I’m trying to run the InfoSys.Cache example. I’ve previously built the app in B6.0 of the book. When I try to start the app in the umbrella/apps/info_sys directory, I get an error:

(Mix) Could not start application info_sys: InfoSys.Application.start(:normal, []) returned an error: shutdown: failed to start child: InfoSys.Supervisor
(EXIT) already started: #PID<0.171.0>

I’ve tried several methods to remediate the issue, but I’m not sure what the correct fix, or what I’m missing. The InfoSys app was done differently in B6.0, so maybe there’s some existing call somewhere, but I’ve been unable to identify it.

2019-06-08
258TYPO

“let’s make our base supervisor in lib/rumbl/info_sys/supervisor.ex”: This path is incorrect because it points to “rumbl”. It should be “lib/info_sys” instead

2019-06-08
263SUGGEST

This page contains a listing with the code for schedule_clear. However all this code was already presented on the previous listing. So if the reader copied all code of the previous listing, the cache is already expiring old values.

2019-06-08
241ERROR

children = [
{Counter, 5}, # new counter worker
]

should be:

children = [
{InfoSys.Counter, 5}, # new counter worker
]

and the same for all the following occurrences.

2019-06-08
247ERROR

iex> {:ok, agent} = start_link(fn -> 5 end)

should be:

iex> {:ok, agent} = Agent.start_link(fn -> 5 end)

2019-06-08
250TYPO

The file path is wrong in the following paragraph:

<<Armed with what little we know about our supervision strategy, let’s make
our base supervisor in lib/rumbl/info_sys/supervisor.ex, like this:>>

Instead of “lib/rumbl/info_sys/supervisor.ex” it should be “apps/info_sys/lib/info_sys/supervisor.ex”.

2019-06-07
251ERROR

Both the main application supervisor and the child supervisor are named InfoSys.Supervisor in the listing “otp/listings/rumbl_umbrella/apps/info_sys/lib/info_sys/application.change2.ex”:

The line:

opts = [strategy: :one_for_one, name: InfoSys.Supervisor]

should be:

opts = [strategy: :one_for_one, name: InfoSys.InfoSupervisor]

or else the application will fail at start with the error:

[…]
failed to start child: InfoSys.Supervisor
(EXIT) already started: #PID<0.nnn.0>

2019-06-08
255TYPO

The changes listed in “otp/listings/rumbl_umbrella/apps/info_sys/lib/info_sys/cache.change1.ex” are already included in listing “otp/listings/rumbl_umbrella/apps/info_sys/lib/info_sys/cache.ex” from page 253.

2019-06-08
257TYPO

The path of the file is wrong in the following paragraph:

“Let’s create our interface in lib/rumbl/info_sys.ex, like this:”

It should be:

“Let’s create our interface in apps/info_sys/lib/info_sys.ex, like this:”

2019-06-08
272ERROR

The tests from the listing “testing_otp/listings/rumbl_umbrella/apps/info_sys/test/cache_test.exs” do not work unless they are preceded with a “clear_interval” tag:

@tag clear_interval: 10
test “key value pairs can be put and fetched from cache”, %{name: name} do
[…]

@tag clear_interval: 10
test “unfound entry returns error”, %{name: name} do
[…]

2019-07-21
280SUGGEST

Regarding the paragraph:

<<Let’s take the test with results. First, we spawn a Wolfram backend with
start_link. Next, we use assert_receive to ensure that the backend reports the
successful %InfoSys.Result{} that we asked for.>>

There is no spawning with start_link and also there is no assert_receive in the first test case. All the spawning is and checking the results is made inside the InfoSys module (with Task.yield_many). Probably the paragraph is describing an older version of the code.

Also, in the InfoSysTest module, the TestBackend.start_link/4 is not required. If it is commented, all the tests are still passing.

2019-06-08
281TYPO

In the last paragraph on the page, there is no model_case in the code. Only data_case and conn_case.

2019-06-08
285TYPO

On the last two paragraphs on the bottom of the page, you are describing the test “join replies with video annotations”, while in the above listing “testing_otp/listings/rumbl_umbrella/apps/rumbl_web/test/rumbl_web/channels/video_channel_test.exs” you are listing another test: “inserting new annotations”.

2019-06-08
287TYPO

The test case “new annotations triggers InfoSys” from the listing “testing_otp/listings/rumbl_umbrella/apps/rumbl_ … umbl_web/channels/video_channel_test.change1.exs” from the middle of the page is already listed in the previous listing with the same name (also an error) from the previous page, 286.

2019-06-07
251ERROR

Application starts the supervisor twice and crashes.

defmodule InfoSys.Application do
@moduledoc false
use Application
def start(_type, _args) do
children = [
InfoSys.Supervisor
]
opts = [strategy: :one_for_one, name: InfoSys.Supervisor]
Supervisor.start_link(children, opts)
end
end

2019-06-08
256ERROR

The init function doesn’t produce a table of the module name. The code works if a new (named) table is created and passed as an option to the put or fetch functions. As written, returns put returns an ArgumentError
(stdlib) :ets.insert(InfoSys.Cache_cache, {“one plus one?”, “two”})

:ets.all() returns two #Reference tables, which I’m presuming is the generated table.

2019-06-08
255TYPO

The “changes” (adding the schedule clear function and calling it from the handle_info(:clear, state) call) are already included in the code snippet from page 252-253.

2019-06-08
4228ERROR

In the second paragraph, you state that the application.ex file is lib/application.ex when it is actually in lib//application.ex. In this particular case, it is lib/hello/application.ex

2019-06-07
289ERROR

There is exactly one occurrence of “model_case” in the whole book, and it is in this page. I don’t know what the book is referring to here

2019-06-07
5541TYPO

In the paragraph right before the listing for lib/rumbl/accounts.ex, second sentence, there is a missing word “of” after the word “couple”. It should say, “We’ll add a couple of functions …”.

2019-06-07
292DEFER

On this page, default_video was created as a module attribute, but default_user was created as a function. Maybe use the same pattern on both to avoid confusion?

2019-06-08
218ERROR

The generated UserSocket module from Phoenix has the connect function with this signature:

def connect(%{“token” => token}, socket, _connect_info)

In the listing in this page where UserSocket is modified, this third param is not there. So in my case I forgot to remove this extra param when typing the code from the book and got an error later on in the book (around page 290) when running tests. I think it would be nice to add here that we should remove this param and maybe some explanation of why.

2019-06-08
295TYPO

There’s a repeated listing in this page. The test “new annotations triggers InfoSys” was already added on previous listing. There’s two different explanations for the same listing, they could be merged.

2019-06-07
238SUGGEST

I think it’s important to mention that this dep also needs to be added to rumbl_web project:

{:info_sys, in_umbrella: true}

2019-06-08
55SUGGEST

The following sentences have redundancy that should be consolidated:

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 filename
is different from ours because Ecto prepends a timestamp to maintain
the ordering of migrations.

2019-06-07
94ERROR

The end of page 94 says “Let’s logout and…”, but the logout functionality is only created a few pages later, thus we can’t (easily) logout yet.

Great job with the book, btw.

2019-07-21
223TYPO

In the phrase: “but neccesary details of such a refactoring exercise”, “neccesary” is misspelled. It should be “necessary”.

2019-06-07
224TYPO

In the sentence: “That way you can see one of the interesting aspects of observer, it’s ability to show a high level view of processes that can help you decide where to split an umbrella application.” It should be “That way you can see one of the interesting aspects of observer: its ability to show a high level view of processes that can help you decide where to split an umbrella application.” Notice the colon and the change of “it’s” to “its”.

2019-06-07
225OK

In the sentences: “There’s more. Since communication happens with …”, I think it would be better to write it “There’s more: since communication happens with…”

2019-06-07
226ERROR

iex -S mix phoenix.server

should be

iex -S mix phx.server

2019-06-07
261SUGGEST

Suggestion.

By the time I got the InfoSys.compute working, I was confused about the supervision tree we set up. It looks like this: InfoSys.InfoSupervisor -> InfoSys.Supervisor -> InfoSys.TaskSupervisor -> (Wolfram backend). The names are very similar, and somehow it just didn’t gel to me why there are 3 layers of supervision. I felt like the OTP chapter made all of the layers of supervision really quickly and didn’t really explain that Task.Supervisor is a generic supervisor that we’re renaming. Not sure if others feel this way, but I think a little more explanation along the way of these different layers would help.

2019-06-08
125SUGGEST

Text states: “We also import our Repo and Category.” Earlier text states “Importing Ecto.Query makes the…”. Code above the paragraph lists “import Ecto.Query” , “alias Rumbl.Repo” & “alias Rumbl.Multimedia.Category”. Is the statement saying we import our Repo and Category correct or should it say “We also alias our Repo and Category”?

2019-06-07
171SUGGEST

For the listing in testing_mvc/listings/rumbl/config/test.change1.exs you change the config:

config :pbkdf2_elixir, :rounds, 1

so that the tests aren’t slow. I saw this slowdown as soon as I started testing “logged in users” on page 161. You should move this optimization to that page. It’s what I did, because I read the chapter first, then went back and ran the exercises with a re-read.

2019-06-07
161ERROR

This is a duplicate of #84698 from B6.0, I’m just adding more context. For the listing: testing_mvc/listings/rumbl/lib/rumbl_web/controllers/auth.change1.ex you use this line in the condition block:

user = conn.assigns[:current_user] ->
conn

Doing so results in a warning, as the very next line rebinds user. Here’s the warning:
Compiling 7 files (.ex)
warning: variable “user” is unused (if the variable is not meant to be used, prefix it with an underscore)
lib/rumbl_web/controllers/auth.ex:17

I’m marking this as a technical error but maybe that’s too harsh :-)

2019-06-08
69TYPO

iex> -r Rumbl.Accounts.User

should be

iex> r Rumbl.Accounts.User

2019-07-21
296SUGGEST

Add a graphic/image of our goal InfoSys supervision tree. It’ll give a visual context of how the pieces go together.

2019-06-07No time.
249DEFER

Add a graphic/image of the goal InfoSys supervision tree. It’ll give a visual context of how the pieces go together.

2019-07-21
269TYPO

“Note we’re using our user-facing create_user function instead of the user-facing
register_user function.”

Should maybe be:
“… our API-facing… instead of the user-facing…”

2019-06-08
285SUGGEST

After adding this listing testing_otp/listings/rumbl_umbrella/apps/rumbl_web/test/rumbl_web/channels/video_channel_test.exs

I consistently got this error:
Compilation error in file test/rumbl_web/channels/video_channel_test.exs

(CompileError) test/rumbl_web/channels/video_channel_test.exs:3: module RumblWeb.TestHelpers is not loaded and could not be found

I had to add this to apps/rumbl_web/test/test_helper.exs
Code.require_file “../../rumbl_web/test/support/test_helpers.exs”, DIR

Once I did that it compiled and the everything worked as expected.

2019-06-08
118TYPO

Not sure if this is the right page number. The `get_user_video!/2` function here includes a line I think is missing in the code download for the Testing MVC chapter.

Inside the `code/testing_mvc/rumbl/lib/rumbl/multimedia.ex` file, the `|> user_videos_query(user)` line from this page is missing from inside the `get_user_video!/2` function.

2019-06-08
96ERROR

This logout link will not work because Phoenix HTML changes the method from `delete` to `post` because the HTML spec only supports get/post/dialog methods in a `

` tag.
See: phoenix_html:/priv/static/phoenix_html.js#L34
I believe the remedy is to use `post` for logout, or a distinct route for it.

2019-07-21Phoenix has something called Plug.MethodOverride in your endpoint, which uses the method given in _method to be your application method. So everything should work, since we do pass the _method as parameter which should be rewritten.
33ERROR

When I run
mix phx.new hello
I have to manually add “cd assets && npm install …”

The console output on page 33 indicates that this happens automatically.

2019-06-07
56ERROR

Footnote link to documentation of ecto migration is wrong

2019-06-07
67SUGGEST

We now have a literal dependency on Ecto.Changeset in our web controller. Two pages earlier there was an extra aside regarding not coupling form_for to Ecto. Effectively we leaked persistence details into the fronted code.

2019-06-08The note about coupling between form_for and Ecto is not from the perspective that `form_for does not know about changeset, because it does, but rather that form_form does not work *only* with changesets. Your domain and the web part need to agree on which data structure they will use to portray changes, errors, etc, and the changeset is this data structure. \n \nThe fact that Ecto 3 ships with a persistence layer out of the box aims to show you can use Ecto for casting/validation without having to persist anything anywhere.
74OK

Tweaking xxx_create_credentials.exs to reference :users breaks the generated test scripts.
In general, there is suspiciously little testing in these chapters. When there is, it’s done via iex.
At least some commentary like "tests break here, we’re gonna fix them in chapter 42’ or something would be nice.

2019-06-08We don't write tests as we go along so we can discuss Ecto and Phoenix concepts without a lot of disruption - although in real life, they would go side by side. We discuss exactly this in the testing chapter. \n \nWe do know that some resources can do development and testing side by side but after a couple tries we found it best to not go this route for this particular book.
79OK

Updating existing users with properly hashed temporary passwords and placeholder emails should probably be a migration, not something someone does (or forgets to do during deployment) on a console.

2019-06-07
76TYPO

typo:
OWASP has an excelent set …

correct:
OWAS has an excellent set …

2019-06-07
55TYPO

“allow us to work more easy with changesets” should be “allow us to work more easily with changesets”.

2019-06-07
8672SUGGEST

Update comeonin and pbkdf2_elixir to their latest versions. i.e.
defp deps do
[
…,
{:comeonin, “>= 5.1.1”}, # instead of 4.1
{:pbkdf2_elixir, “>= 1.0.2”} # instead of 0.12
]
end

2019-06-08
77ERROR

I could not get the function put_pass_hash/1 to work (maybe because I try to many things at the same time as reading ..?), my problem was that the field password_hash is marked as “not null” in database and that the changeset was always valid? = false.
I changed my function to following to get it to work:
defp put_pass_hash(changeset) do
pass = get_change(changeset, :password)

case pass do
nil -> changeset
_ -> put_change(changeset, :password_hash, Comeonin.Pbkdf2.hashpwsalt(pass))
end
end

2019-06-08
39TYPO

« This tree is a piece of code that controlls » —> controls.

2019-06-07
41TYPO

« We’ll add a couple functions which allows […] » —> allow.
Unless you meant « Adding a couple functions allows » (the fact of adding those functions allows XYZ), in which case I’d advocate using a coma : « We’ll add a couple functions, which allows […] ».

2019-06-07
127ERROR

On page 127, it’s suggested that we do the following:

def alphabetical(query) do
from c in query, order_by: c.name
end

def list_alphabetical_categories do
Category
|> Category.alphabetical()
|> Repo.all()
end

However, that gives me the following error: “lib/rumbl/multimedia/category.ex:19: undefined function c/0”

line 19 is the middle line of Category.alphabetical/1.

I’ve found the following code to work:

def list_alphabetical_categories do
query = from c in Category, order_by: c.name
Repo.all(query)
end

It seems important to the compiler that `````“from c in Category, order_by: c.name” by assigned to something. I do not know why.

Hopefully this is helpful. Sorry if it is a duplicate, I did not have time to read through all the currently submitted errata and there is not a search function.

2019-06-08
23ERROR

“[name: name] is shorthand for [{name: name}] .” Should this not be: [name: name] is shorthand for [{:name, name}] .

2019-06-07
37TYPO

“We need to do one more bit of housekeeping.” => bits

2019-06-07
212TYPO

The top of page 212 says “Remember to update your repository by running migrations:” and then “And now you can migrate your database:”, both with the “mix ecto.migrate” code sample.

2019-06-07
66,92SUGGEST

I made it to page 92 without understanding the “Routes.some_path” bit (from page 66 — the first time it is actually explained). At this point in the book (page 92), “Routes.page_path(conn, :index)” had become confusing. It took the online Phoenix guides to straighten me out on the matter. The path-helpers section of the guide helped, but it really clicked when seeing the more-on-path-helpers section with the “mix phx.routes” output. Which I promptly ran on my machine and realized what (I) had been missing.

TLDR:
“Routes.some_path” from page 66 may not be explained well enough as to how it works.

I’m not sure if page 66 is the correct place to explain it; however it seemed to be the only place that it was explained.

2019-06-07
129ERROR

With regards to Ecto queries: “In Elixir, the ^ operator (called the pin operator) means we want
to match against this specific version of ^username rather than binding to
the username variable.” Ecto’s documentation states that the ^ (caret) is used for injecting a value or expression for interpolation (as part of it’s DSL) seen at: hexdocs.pm/ecto/Ecto.Query.html#module-interpolation-and-casting

2019-06-08
153164TYPO

“We close by trying to register two identical users, then asserting the accounts
system claims our username has already been taken.”

In the testing_mvc/listings/rumbl/test/rumbl/accounts/accounts_test.exs file, the enforce unique usernames test should either be the last test in the describe block, or the wording should change to “We close by setting a password that is too short and then test for a specific error.” (from the previous paragraph)

2019-06-07
30SUGGEST

José mentioned 8080 (HTTPS) as alternative, additional endpoint. There is nothing inherently wrong with it, but it might be better to use 8080 port in that example accordingly it’s expected use: alternative “plain” HTTP port. Optionally, change port to 8443 and leave it as HTTPS port.

Again: It’s not a part of any standard, it’s not a mistake per se, book is not really suffering because of it, but this is not something you will expect in most of the real life situations.

2019-06-07
230SUGGEST

This whole section on copying files around is pretty much ignorant of version control. This approach will lose all history for files etc. Granted, this is a book about phoenix and elixir and not about revision control systems, but still.

2019-06-07
231ERROR

rumbl_umbrella/apps/rumbl/
lib/rumbl/application.ex needs more clean up than mentioned in the text.

My file still contained

  1. Tell Phoenix to update the endpoint configuration
    # whenever the application is updated.
    def config_change(changed, _new, removed) do
    RumblWeb.Endpoint.config_change(changed, removed)
    :ok
    end

at this point, which needs to be removed. The compiler will also warn about this.

2019-06-08
247ERROR

The text says to change lib/rumbl/application.ex, but the listing says rumbl_umbrella/apps/info_sys/lib/info_sys/application.ex

In general the preceding section is somewhat imprecise regarding which files are changed in which step

2019-06-08
251SUGGEST

Each of our supervisors will have two
children, first a Task supervisor to spawn tasks isolated under their own
supervisor, followed by a InfoSys.Cache GenServer,

The code has the order of children the other way around.

2019-06-08
250SUGGEST

Text says “We also pass the initial state of an empty list, which
we don’t intend to use. ”

Code does not pass an empty list. Arguably opts could be bound to an empty list at runtime, but that’s not guaranteed.

2019-06-07
264SUGGEST

On this page the InfoSys.Result struct suddenly stops containing an url field. Previous results always contained an url field, which is always nil. So maybe remove the url from the struct e.g. on page 257

2019-07-21
259SUGGEST

The text says

The compute function
takes a String.t query, a Keyword.t list of options, and returns a type we’ll define
later called InfoSys.Result.t.

But the type InfoSys.Result.t is already defined two pages earlier.

2019-07-21
274TYPO

funciton should be function

2019-06-07
275ERROR

Regarding the TestBackend module, the text says “Since our backends are tasks, we’ll need to fire up a task in start_link.”

Why do we need to fire up a task? The given start_link implementation can be replaced by one that does nothing except raising an exception without affecting any test (i.e. TestBackend.start_link does not seem to even get called anywhere). The actual wolfram implementation itself is not concerned about task handling, why should its test replacement?

2019-06-08
255ERROR

When trying to run the code in iex listed on page 255, where you take the cache for “a test spin”, I get the following error when running “Cache.put(”one plus one?“, ”two“)”:

iex(4)> Cache.put(“one plus one?”, “two”)

(ArgumentError) argument error
(stdlib) :ets.insert(InfoSys.Cache_cache, {“one plus one?”, “two”})
(info_sys) lib/info_sys/cache.ex:7: InfoSys.Cache.put/3

2019-06-08
261ERROR

When running the “InfoSys.compute(”what is elixir?“)” in iex, the following error appears:

iex(1)> InfoSys.compute(“what is elixir?”)

(exit) exited in: GenServer.call(InfoSys.TaskSupervisor, {:start_task, [{:nonode@nohost, #PID<0.405.0>, #PID<0.405.0>}, [#PID<0.405.0>], :monitor, {InfoSys.Wolfram, :compute, [“what is elixir?”, [limit: 10]]}], :temporary, :brutal_kill}, :infinity)
(EXIT) no process: the process is not alive or there’s no process currently associated with the given name, possibly because its application isn’t started
(elixir) lib/gen_server.ex:979: GenServer.call/3
(elixir) lib/task/supervisor.ex:447: Task.Supervisor.async/6
(elixir) lib/enum.ex:1327: Enum.“map/2-lists^map/1-0”/2

2019-06-08
230SUGGEST

“We’ll need all three of the dependencies, :comeonin , :pbkdf2_elixir and :sweet_xml .” You talk about sweet_xml here like it was previously a dependency — it was not. Page 230 was the first time anything was even mentioned about the XML parser. Perhaps consider reworking this into where it is actually introduced / used in the book.

2019-06-07
232ERROR

“Oops. That didn’t work as we’ve not yet fetched dependencies. We’ll have to run it later after we’ve fetched our dependencies.” Everything here runs fine, in fact the only commands we are running are to change directories and to fetch dependencies… Perhaps this is a hold over from an earlier version of the book?

2019-06-07
251TYPO

“Now you’re ready to add the new supervisor into our application’s supervision
tree in lib/rumbl.ex , like this:
otp/listings/rumbl_umbrella/apps/info_sys/lib/info_sys/application.change2.ex”

The path “lib/rumbl.ex” is wrong. The path should be “info_sys/application.ex”.

2019-06-08
258TYPO

callback name() :: String.t" does not follow the convention of mix format or the rest of the code sample. It should be "callback name() :: String.t()”

2019-06-08
259SUGGEST

1. “Create a config/dev.secret.exs file and include your WolframAlpha key under
Mix.Config under the top-level umbella app”
2. “When you ran mix phx.new , phoenix generated a .gitignore entry to ignore all
*.secret.exs files inside your config directory.”

You should really consider making it clear that you mean rumbl_umbrella/apps/rumbl/config/dev.secret.exs

Your wording here could lead some to put this in the top level rumbl_umbrella/config directory.

Also, typo: “umbella” should be “umbrella”.

2019-06-08
116TYPO

It says “Then, the list_user_videos and get_user_videos utility functions”, but then we define get_user_video (singular).

2019-06-08
9TYPO

LiveVeiw should be LiveView

2019-06-07
279SUGGEST

“let’s require the file in each test suite. Add the
following line to the top of your rumbl_umbrella/apps/info_sys/test/test_helper.exs :”
You say that it should be done in each test suite, then you only mention one file. Consider making this more explicit as this is needed for the test to run properly.

2019-06-08
279SUGGEST

Per my previous Erratum on this page, I believed that the entry was needed in the second file. However it appears to not be needed and was an issue with mix. In either case, this step should be cleared up.

2019-06-07
284TYPO

“testing_otp/listings/rumbl_umbrella/apps/rumbl_web/test/support/test_helpers.exs”
should be file extension “ex” — not “exs”

2019-06-08
122TYPO

It says “and create an unique index for it” - the word “an” should be “a”.

2019-06-07
126TYPO

The sentence doesn’t make sense:
“We’ll put query functions in our schema layer and leave complex interactions, such as those between our multimedia and users will go in in contexts to leave con- trollers as thin and simple as possible.”

2019-06-07
133ERROR

Chapter 5, accounts.change2.ex:

The functions “Comeonin.Pbkdf2.checkpw” and “Comeonin.Pbkdf2.dummy_checkpw” are now removed.
Their alternatives are “Pbkdf2.verify_pass” “Pbkdf2.no_user_verify” in that order.

2019-06-08
251ERROR

The registered name of the module InfoSys.Supervisor is the same as the name in the InfoSys.Application module which causes a runtime error.

2019-06-08José?
177178ERROR

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

This test fails with an error:
“key :current_user not found in: %{}”

In a line which has code “if conn.assigns.current_user do”

I think this should have worked as the condition is checking the presence of the key but I am not sure why this is failing.

2019-06-08
149OK

To avoid confusion with the generated test_helper.ex, I suggest to rename the test_helpers.ex to something else.

2019-07-21
55SUGGEST

hould suggest and tell people to use :utc_datetime instead of the default timestamps()?

2019-06-08Hi Ken, I agree :utc_datetime is important. However there are only so many things we can talk about in the book. For example, a discussion on :decimal would be extremely important as well but we can't fit it all in. Hopefully other books such as the Ecto book and the official documentation give those topics the attention they deserve.
93ERROR

Now the latest version of the API for Comeonin and Pbkdf2 are different:

def authenticate_by_email_and_pass(email, given_pass) do
user = get_user_by_email(email)

cond do
user ->
case Pbkdf2.check_pass(user.credential, given_pass) do
{:ok, _} ->
{:ok, user}
_ ->
{:error, :unauthorized}
end

true ->
Pbkdf2.no_user_verify()
{:error, :not_found}
end
end

2019-07-21
105SUGGEST

1. In testing `authenticate_user`, error occurs:

Why not change `conn.assigns.current_user` to `conn.assigns[:current_user]` to make the code more robust but to assign `nil` in test case?

2019-06-08
217OK

In `scheduleMessages`, there is typo for `this.schedulerTimer` and `this.scheduleTimer`. An extra `r` is added.

2019-06-07
220OK

For the `lastSeenId`, I don’t think it’s good to set it when receiving “new_annotation” event, as it might be a much later event message from others whose is watching and commenting. Also, collecting the max ids when joining the channel cannot represent the real max id I have seen.

I think it’s better to set the `lastSeenId` in `renderAtTime` before rendering annotation. And so the definition of it should be moved out of `Video` object’s definition as a global variable.

To cater the case that the user is posting the comment himself, we can set the retrieved & filtered annotations to be instance variable and add his posted comments into it to utilize the same `scheduleMessages` method.

2019-06-07
219DEFER

Whole UI will be refreshed when restarting with `mix phx.server`, there maybe channces that we cannot see the effect of loading duplicate annotations if we don’t keep an eye on the page. I think you may remind the reader about this.

2019-07-21
223TYPO

Duplicate `and` exists in:

>Both the channels and and the MVC

2019-06-07
232SUGGEST

Moving Assets

1. We should also move the css file as well.

2019-06-08
232ERROR

`cd ..` should not be the correct command to go back to umbrella project root to test both apps. Last statement of below code should be updated.

cd apps/rumbl_web/assets
npm install
cd ..

2019-06-08
247ERROR

for the `:one_for_all` change, which `application.ex` should be updated? In which folder should I run `iex -S mix phx.server`? When I change the one in info_sys and run the command in umbrella folder, it’s complaining the error `module Repo was given as a child to a supervisor but it does not exist`.

2019-06-08
251TYPO

add the new supervisor into our application’s supervision tree in `lib/rumbl.ex` (Location 9760), should be changed to `lib/info_sys/application.ex`?

2019-06-08
251ERROR

The name of the application should be changed? Else the application failed to start with error: `already started`. The `opts = [strategy: :one_for_one, name: InfoSys.Supervisor]` should be changed to `opts = [strategy: :one_for_one, name: MODULE]`?

2019-06-08
255ERROR

`cache.ex` already includes every about scheduling for cache expiration in the first place (Page 254), then the paragraphs in page 255 & 256 about changing the state to includes `timer` seems not necessary. Should the first place not including that piece of logic?

2019-06-08
275ERROR

Why the `TestBackend` need to have `start_link` function defined? Shouldn’t it implement the `Backend` behaviour? It seems I can run the test successfully without the `start_link` function.

2019-06-08
280ERROR

The testing code doesn’t seem to match the paragraphs in the book. No code needs to spawn the Wolfram backend and use `assert_receive`. It’s all encapsulated in InfoSys, isn’t it?

2019-06-08
284ERROR

Wrong default value of `insert_video` in `test_helpers.exs`, should change the `[]` to `%{}`

def insert_video(user, attrs \\\\ []) do

2019-06-08
285TYPO

typo `video:#{vid}` to `videos:#{vid}` in below paragraph:

>Then we call the `subscribe_and_join` test helper to attempt to join the channel
>responsible for the `video:#{vid}` topic.

2019-06-08
285SUGGEST

I don’t see which part of the code refers to this paragraph:

>Next, we make sure we’ve joined the right topic by comparing the video ID from our connected test socket with the one returned from our test helper.

2019-06-08
171SUGGEST

The suggestion to configure the tests like this

`config :pbkdf2_elixir, :rounds, 1`

for the tests to run faster should be mentioned much earlier. I was wondering the whole time why the hell everything is so slow, until i finally read that particular section of the book.

2019-06-07
259TYPO

use Mix.Config
config :rumbl, :wolfram, app_id: “12345-6789”

I had to change ‘:rumbl’ to ‘:info_sys’, otherwise the app_id returned by InfoSys.Wolfram.id function is blank and the Wolfram query fails with a DOWN process status.

Contrary to a previous comment, I was unable to put the dev secret in the rumbl config folder, since with the new umbrella structure, all config has been moved to top-level umbrella directory.

2019-06-08
248SUGGEST

I find the following statement under “Registering Processes” a bit vague:

“Our named process can be either local, meaning visible to a single node, or global, meaning visible to all connected nodes. OTP automatically provides this feature with the :name option in start_link.”

It mentions the two visibility modes (local, global), but then offers no explanation on how to do that.

2019-06-08
286ERROR

test “new annotations triggers InfoSys”, %{socket: socket, video: vid} do

I had to comment out the line:

assert_broadcast “new_annotation”, %{body: “2”, at: 123}

otherwise received the following:

19:11:57.288 [error] Task #PID<0.368.0> started from #PID<0.367.0>
terminating
(UndefinedFunctionError) function InfoSys.compute/2 is undefined
(module InfoSys is not available)
InfoSys.compute(“1 + 1”, [limit: 1, timeout: 10000])
(rumbl_web) lib/rumbl_web/channels/video_channel.ex:59:
RumblWeb.VideoChannel.compute_additional_info/2
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Function: #Function<1.42554334/0 in RumblWeb.VideoChannel.handle_in/4>
Args: []

2019-07-21
286ERROR

in general, when running mix test from rumbl_umbrella/apps/rumbl_web/
I receive the following message:
warning: function InfoSys.compute/2 is undefined (module InfoSys is not available)
lib/rumbl_web/channels/video_channel.ex:59

Apparently the executing test script is trying to reference the line:

defp compute_additional_info(annotation, socket) do
for result <- InfoSys.compute(annotation.body,

in rumbl_umbrella/apps/rumbl_web/lib/rumbl_web/channels/video_channel.ex, probably because
info_sys is a separate app and cannot be seen during this script execution.

2019-06-08
280ERROR

Running the following rumbl_umbrella\\apps\\info_sys\\mix test

produces the following output:

1) test makes request, reports results, then terminates (InfoSys.Backends.WolframTest)
test/backends/wolfram_test.exs:4
(ArgumentError) argument error
code: actual = hd InfoSys.compute(“1 + 1”, [])
stacktrace:
:erlang.hd([])
test/backends/wolfram_test.exs:6: (test)

….

Finished in 1.6 seconds
10 tests, 1 failure

Randomized with seed 94000

2019-06-08
230SUGGEST

One of the most confusing aspects of this book is the splitting up of the rumbl app into its respective places within the umbrella parent app.

According to your book instructions, all configuration is to take place at the parent level. However, upon perusing the code supplied on the website, this has a completely different organisation, where in rumbl_umbrella/config.exs one reads, … # By default, the umbrella project as well as each child

  1. application will require this configuration file, as
  2. configuration and dependencies are shared in an umbrella
  3. project. While one could configure all applications here,
  4. we prefer to keep the configuration of each individual
  5. child application in their own app, but all other
  6. dependencies, regardless if they belong to one or multiple
  7. apps, should be configured in the umbrella to avoid confusion.
    import_config “../apps/*/config/config.exs”

, which leads one to believe that we need config sections in both rumbl and rumbl_web (… and info_sys … ) sub-apps.

I am trying to follow your instructions as per the book, and now I have what appears to be 3 isolated apps with config at the umbrella level. Is this correct, or will it now lead to warnings that other sub-app components can’t be found?

Also, you point out in the book that the ‘rumbl_web’ sub-app can reference the ‘rumbl’ sub-app by means of the in-place in-umbrella reference in its own mix.exs. So is one to understand that all testing code in rumbl_web can freely reference rumbl components. But what happens when said rumbl components want to reference rumbl_web, for example in application.ex there is a call to rumbl_web to update its config if the rumbl app changes, but surely this will produce warnings that rumbl_web is not available? And later on in the book, if we are to follow this config method, what happens when say rumbl_web wishes to reference info_sys sub-app components?

I’m only asking these questions to try to clarify how one goes about referencing sub-apps from others in without encountering warnings.

2019-07-21You are correct! We have fixed this in recent editions by putting all configuration in a single place (the umbrella root). No more ad-hoc rules about what goes where!
239SUGGEST

“The new rumbl child app does not have a web component” … so shouldn’t we also comment out the line: ?
def config_change(changed, _new, removed) do
# Commented out, since this app no-longer has a web component.
# RumblWeb.Endpoint.config_change(changed, removed)
:ok
end

2019-07-21
274SUGGEST

I find this listing

testing_otp/listings/rumbl_umbrella/apps/info_sys/test/cache_test.change1.exs

pretty confusing.

Could you clearify
1. what this `@tag clear_interval: 60_000` is doing
2. how ExUnit prevents the `eventually` function from running forever, if `func` is not returning what we expect

2019-07-21
233SUGGEST

extract of comments residing in from rumbl/config/config.exs generated by “mix phx.new rumbl —umbrella”

  1. By default, the umbrella project as well as each child
  2. application will require this configuration file, as
  3. configuration and dependencies are shared in an umbrella
  4. project. While one could configure all applications here,
  5. we prefer to keep the configuration of each individual
  6. child application in their own app, but all other
  7. dependencies, regardless if they belong to one or multiple
  8. apps, should be configured in the umbrella to avoid confusion.

this expresses the opinion of authors of “mix” to configure most child app config in the child apps themselves

please clarify - if needed - in the chapter

2019-07-21
79SUGGEST

Readers may copy the minimum password validation without ever checking OWASP. The min should be increased from 6 to 16 for safety.

2019-07-21Fair point. We don't want to chase OWASP here but we should mention that these rules are lax in a warning.
11SUGGEST

“because it supports a feature called lightweight processes. You can create hundreds of thousands of processes without breaking a sweat” repeats anecdote from Concurrency is Hard, but not by emphasizing the point and instead acting like this is the first mention of Processes.

2019-07-21
11SUGGEST

“because it supports a feature called lightweight processes. You can create hundreds of thousands of processes without breaking a sweat” repeats anecdote from Concurrency is Hard, but not by emphasizing the point and instead acting like this is the first mention of Processes.

2019-07-21
324315TYPO

Line: 3 ``and …“to think about sending datata between the client and the server.”
Should be data.

2019-07-21
113TYPO

After the snippet of code for the create function one can read:

“That’s simple enough. We create a user with the create_video function and then redirect if it’s successful,”

I think it should be:

“That’s simple enough. We create a VIDEO with the create_video function and then redirect if it’s successful”

2019-07-21
126TYPO

Where it says: “We also import our Ecto.Query module and alias Repo and Category. ”

Should be: “We also import our Rumbl.Repo module and alias Repo and Category.”

I think :D

2019-07-21
5TYPO

“onother” instead of “another”. It’s in the sentence that starts with “If your machine has more than one core…”

2019-07-18
7TYPO

“And while this is just one example, we have the option to make similar trade-off at different times in our stacks.”

I think it should read “similar trade-offs” (with the “s”).

2019-07-21
82TYPO

Missing ).

…> Repo.update!(User.registration_changeset(u, %{password: “temppass”})

Should be:

…> Repo.update!(User.registration_changeset(u, %{password: “temppass”}))

2019-07-21
6348TYPO

Text:

iex> Accounts.get_user(“1”)
%Rumbl.Accounts.User{
id: “1”,
name: “José”,
username: “josevalim”
}

Should be:

iex> Accounts.get_user(1)


The :id value is a number not a string.

2019-07-21Hi Patrick! For now we are working with hardcoded entries and the hardcoded entries contains strings as ids. So it should be correct. Thanks for reaching out!
113TYPO

The following phrase “We create a user with the create_video…” should be “We create a video with the create_video”…

2019-07-21
151ERROR

In test/rumbl/accounts_test.exs the lines

@valid_attrs %{
name: “User”,
username: “eva”,
password: “secret”
}

should be:

valid_attrs %{ name: "User", username: "eva", credential: %{ \temail: "evaexample.com“,
\tpassword: ”secret"
}
}

2019-07-21
152ERROR

See #85419 for context.

The test “requires password to be al least 6 characters long” should be rewritten al follows:

test “requires password to be at least 6 characters long” do
invalid_credentials = Map.put(valid_attrs[:credential], :password, "12345") invalid_attrs = Map.put(valid_attrs, :credential, invalid_credentials)

assert {:error, changeset} = Accounts.register_user(invalid_attrs)
assert %{credential: %{password: [“should be at least 6 character(s)”]}} = errors_on(changeset)

assert [] = Accounts.list_users()
end

2019-07-21
34TYPO

A sentence in chapter 2 refers to a rumbl directory where it should refer to hello instead. “Support for starting, stopping, and supervising each application is in lib/rumbl/application.ex.” Should be “lib/hello/application.ex.”

2019-07-21
113TYPO

> We create a user with the create_video function

Should read

We create a video with the create_video function

2019-07-21
113ERROR

Paragraph at the end of the page.
`That’s simple enough. We create a user` ——> `That’s simple enough. We create a video`

2019-07-21
239ERROR

:comeonin is no longer a required dep. In general, since v5 of Comeonin no longer requires direct use (we use pbkdf2_elixir directly instead), it might make sense to no longer refer to it.

2019-07-21
79ERROR

The method ``registration_changeset does not validate_required([:password]) which will allow blank passwords and bypass the length requirement. It should be added.

2019-07-21
96ERROR

In the “new.html.eex” section, the closing tag for the form_for code injection is on a new line.
I have a compile error that goes away when I put the tag after the “fn f ->” part.

Compile error:
lib/rumbl_web/templates/session/new.html.eex:7: missing terminator: end (for “fn” starting at line 6)

2019-07-21
49TYPO

supervising each application is in lib/rumbl/application.ex

should be

supervising each application is in lib/hello/application.ex

In the context of things, correct?

2019-07-21
224TYPO

“Our” repeated in “our our socket”

2019-07-18
228TYPO

“Our presence data now has friendluy usernames.” should have “friendly” instead of “friendluy”.

2019-07-18
237ERROR

Found two errors in the Umbrella chapter.

1. You need to update presence.ex to :rumbleweb otp app and RumblWeb.PubSub
2. When copying the assets, there is no instruction to copy the css files.

2019-07-21
243TYPO

In “ We used Observer to understand the important behind applications.” “important” should be “importance”.

2019-07-18
76ERROR

The validation code doesn’t actually cover the unique index on username, so the form isn’t actually “done” at this point.

Adding |> unique_constraint(:username) to changeset() will resolve the issue and give a validation error on the form properly instead of exploding.

2019-07-21
172ERROR

In the code example you have a “renders new.html” test inside video_view_test.exs. The first line in the test creates “owner” but “owner” isn’t used after that so it throws a warning in the console. Also, this book is awesome.

2019-07-21
275266ERROR

The path in the code listing for info_sys.ex is wrong. It should be in the info_sys folder, not the the info_sys/lib folder.

2019-07-21
93ERROR

Actually, Pdfbk now has an even simpler API that renders the whole cond unnecessary, as it will by default call the fake verify when the user exists, and returns the {:ok, user} or {:error, msg}

so the func can just be…

def authenticate_by_username_and_pass(username, given_pass) do
user = get_user_by(username: username)
Pbkdf2.check_pass(user, given_pass)
end

2019-07-21Yes, the new API is neat but we can to keep returning relevant semantic errors such as {:error, :unauthorized} and similar. But using the function also works! Thanks!
60TYPO

At it’s core => At its core

2019-07-18
80ERROR

The registration_changeset/2 function checks the password’s length. I tried the code but if the password is empty, I have the following error: “null value in column ”password_hash" violates not-null constraint". I have to add an additional constraint: validate_required([:password]) in order to avoid the crash.

2019-07-21
428ERROR

After forming umbrella project any test that uses video_fixture from test helpers are broken. It seems video_fixture insert the record but the id of returning record is nil. I changed the schema definition of video and added the option :read_after_writes like following:
@primary_key {:id, Rumbl.Multimedia.Permalink, auto_generate: true, read_after_writes: true}
All tests are passing.

2019-07-21
295ERROR

I downloaded code for the book, setup it and run “mix test”. Tests pass, but there are errors in console for “video_channel_test”. This error gone when i commented the line 31 in “video_channel.ex”:

{:ok, _} = RumblWeb.Presence.track(socket, socket.assigns.user_id, %{device: “browser”})

Errors when you run “mix test”:

[error] Task #PID<0.552.0> started from RumblWeb.Presence_shard0 terminating

(stop) exited in: DBConnection.Holder.checkout(#PID<0.547.0>, [log: #Function<11.21419755/1 in Ecto.Adapters.SQL.with_log/3>, source: “users”, timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
(EXIT) no process: the process is not alive or there’s no process currently associated with the given name, possibly because its application isn’t started

2019-07-21
296TYPO

Code for the book contains a test “join replies with video annotations” inside “video_channel_test.exs” file, but there is no such a test in “Testing Channels and OTP” chapter.

2019-07-21
96OK

When I submit the login form with wrong credentials, the login form retains the email (but not the password). Explain from where the email is retrieved.

2019-07-21
11196ERROR

The new.html.eex snippet has, I believe, a syntax error. It is shown as:

Login

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

<%= text_input f, :username, placeholder: “Username” %>

<%= password_input f, :password, placeholder: “Password” %>

<%= submit “Log in” %>
<% end %>

But I believe the %> must be on the same line as the fn f ->, like so:

Login

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

<%= text_input f, :username, placeholder: “Username” %>

<%= password_input f, :password, placeholder: “Password” %>

<%= submit “Log in” %>
<% end %>

Without that a syntax error is presented that is hard to figure out:

(TokenMissingError) lib/rumbl_web/templates/session/new.html.eex:6: missing terminator: end (for “fn” starting at line 5)

2019-07-21
34ERROR

Has “lib/rumbl/application.ex” instead of “lib/hello/application.ex”

2019-07-21
80ERROR

The code for the registration_changeset found in chapter 5 has a bug media.pragprog.com/titles/phoenix14/code/authentication/listings/rumbl/lib/rumbl/accounts/user.change1.ex. The code will block registration if the password is very short - but will allow the registration if the password is blank. Need to add validate_required() to the pipeline to make sure that the password is present after casting the params into the changeset.

2019-07-21
28ERROR

In the book, the message was:
please define a clause for render/2 or define a template at “lib/hello_web/templates/hello”.

But I received:
please define a matching clause for render/2 or define a template at

2019-07-21
82ERROR

“Repo.update!(User.registration_changeset(u, %{password: ”temppass“})” is missing a parens.

2019-07-21
100SUGGEST

In “Wrapping Up” the third bullet point states:

> We briefly introduced relationships.

However, the concept of relationships has not been introduced.

2019-07-21
96SUGGEST

We are advised to log out:

> Let’s logout and visit /sessions/new in our browser to try some login attempts.

However, we have not yet built the logout functionality which makes it difficult to do so unless we delete the browser cookies.

2019-07-21
96SUGGEST

In Line 6 of the code example the code “%>” starts at the first column of a new line. If this is replicated in code, I receive this error:

Compilation error in file lib/rumbl_web/views/session_view.ex

(TokenMissingError) lib/rumbl_web/templates/session/new.html.eex:7: missing terminator: end (for “fn” starting at line 6)

moving the “%>” to directly after “fn f ->” (instead of at the start of a new line) allows the template to compile.

2019-07-21
113TYPO

“We create a user with the create_video function”. Should be “We create a video…”.

2019-07-23
9782TYPO

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

Second line is missing a closing parenthesis

2019-07-23
34TYPO

The application.ex file is listed as lib/rumbl/application.ex, but it should be lib/hello/application.ex (the example project rumble is introduced in the next chapter, this chapter’s project is called hello).

2019-09-26
203TYPO

> We extract the video ID useing pattern matching: “videos:” <> video_id will math all topics starting with “videos:” and assign the rest fo the topic to the video_id variable.

Should be of and not fo.

2019-09-26
223TYPO

channels spelled chanenls when specifying file:

lib/rumbl_web/chanenls/video_channel.ex

should be

lib/rumbl_web/channels/video_channel.ex

(First paragraph, second sentence after heading “Tracking Presence in Channels”

2019-09-26
267ERROR

In the second paragraph it says there is a :url field on the result struct but that was not included on the previous page.

2019-09-26Duplicate of #85793.
269ERROR

Application.fetch_env!(:rumbl, :wolfram)[:app_id] on line 39 of the code sample should be :info_sys instead of :rumbl

2019-09-26
240SUGGEST

In the Copying the Web Source Files section, users need to add RumblWeb.Presence to the list of supervised children and make some changes to RumblWeb.Presence, most notable the :otp_app and RumblWeb.PubSub.

Then in the moving assets section on 241, readers haven’t been directed to perform mix deps.get before being instructed to run npm install. However after they are directed to run npm install, they are directed to do mix deps.get and then run npm install again. This needs to be cleaned up because it is super confusing.

That being said, everything has been super helpful so far.

2019-09-26
295ERROR

the mix test command at the bottom of the page intermittently produces an error:

mix test test/rumbl_web/channels/video_channel_test.exs
.

Finished in 0.1 seconds
1 test, 0 failures

Randomized with seed 253238
23:22:45.409 [error] Postgrex.Protocol (#PID<0.335.0>) disconnected: (DBConnection.ConnectionError) owner #PID<0.408.0> exited

Client #PID<0.411.0> is still using a connection from owner at location:

(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:572: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:554: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:153: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:78: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:319: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:232: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

The connection itself was checked out by #PID<0.411.0> at location:

(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:572: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:554: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:153: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:78: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:232: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

In addition to this, if you run mix test for the entire umbrella you receive this error:
23:19:47.326 [error] Task #PID<0.589.0> started from RumblWeb.Presence_shard0 terminating

(stop) exited in: DBConnection.Holder.checkout(#PID<0.586.0>, [log: #Function<11.104730475/1 in Ecto.Adapters.SQL.with_log/3>, source: “users”, timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
(EXIT) shutdown: “owner #PID<0.585.0> exited”
(db_connection) lib/db_connection/holder.ex:87: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection/holder.ex:68: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection.ex:1030: DBConnection.checkout/3
(db_connection) lib/db_connection.ex:1340: DBConnection.run/6
(db_connection) lib/db_connection.ex:596: DBConnection.execute/4
(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:572: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:554: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:153: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:78: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:232: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: #Function<2.79494727/0 in Phoenix.Presence.handle_diff/5>
Args: []
23:19:47.327 [error] Task #PID<0.588.0> started from RumblWeb.Presence_shard0 terminating

(stop) exited in: DBConnection.Holder.checkout(#PID<0.586.0>, [log: #Function<11.104730475/1 in Ecto.Adapters.SQL.with_log/3>, source: “users”, timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
(EXIT) shutdown: “owner #PID<0.585.0> exited”
(db_connection) lib/db_connection/holder.ex:87: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection/holder.ex:68: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection.ex:1030: DBConnection.checkout/3
(db_connection) lib/db_connection.ex:1340: DBConnection.run/6
(db_connection) lib/db_connection.ex:596: DBConnection.execute/4
(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:572: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:554: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:153: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:78: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:232: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: #Function<2.79494727/0 in Phoenix.Presence.handle_diff/5>
Args: []

Good catch! Basically the Presence wants to query the DB and send updates but the test has shut down and there are no database connections. To fix this consistently, we would need to query the presence supervisor and ask all of its children to shutdown at the end of each test. But to do so, we will need to add a new API to Phoenix. If you can open up an issue in Phoenix issues tracker, it would be extra helpful. Thank you!
391ERROR

While following along with chapter 12 I tried to test the information services annotation creation but got an error on Presence: “ArgumentError: :ets.lookup(RumblWeb.Presence, :pool_size)” and a traceback to the “RumblWeb.Presence.list” call in video_channel.ex.

After some searching I found the cause: while creating the umbrella app the rumbl_umbrella/apps/rumbl_web/lib/rumbl_web/channels/presence.ex is copied from the old app and thus misconfigured to use the “otp_app: rumbl”, which should be “otp_app: rumbl_web” now. Similarly “pubsub_server: Rumbl.PubSub” should read “pubsub_server: RumblWeb.PubSub”.

Apologies if this is covered elsewhere and I missed it.

Thank you for an amazing book!

2019-09-26
9274ERROR

The changeset function sets username AND name as required. In the example, the changeset is
changeset = User.changeset(User{username: “eric”},{}) but this misses the name field so this does not properly create a changeset with a set of changes but instead returns a changeset with errors. I think all you would need to do is add a name and this example would be fine.

2019-09-26
188DEFER

On p.188 (of the PDF) the instruction is to create a param.ex file with a protocol implementation of to_param
I followed the instructions verbatim and nothing happens when I attempt to continue with the IEx instructions on p.189
Code:
github dot com/nelsonic/rumbl/tree/3a459389e26e55c32232acf3822f3b1156810a25

Happy to jump on a Skype/Zoom call to illustrate.
Elixir 1.9.0 (compiled with Erlang/OTP 22)
Phoenix v1.4.9

203TYPO

“and assign the rest fo the topic to the video_id variable”
should be:
“and assign the rest of the topic to the video_id variable”

fo >> of

2019-09-25
212TYPO

Grammar (missing word) in the following sentence:
“If an application needs change in the future, we can revisit this decision.”
could be:
“If an application needs to change in the future, we can revisit this decision.”

2019-09-25
241ERROR

On page 241 we are advised to move the rumbl/assets/js to rumbl_umbrella/apps/rumbl_web/assets/
That’s fine, but then people also need to update the path to the phoenix and phoenix_html dependencies in apps/rumbl_web/assets/package.json from:

“dependencies”: {
“phoenix”: “file:../deps/phoenix”,
“phoenix_html”: “file:../deps/phoenix_html”
},

to:

“dependencies”: {
“phoenix”: “file:../../../deps/phoenix”,
“phoenix_html”: “file:../../../deps/phoenix_html”
},

And then run npm install

As Sam McDavid noted (see above) there are a number of other changes required to get the app running.
This is what I had to do:

First in the file `apps/rumbl_web/lib/rumbl_web/application.ex`, add `RumblWeb.Presence` to the list of supervised child processes, e.g:
`
children = [
RumblWeb.Endpoint,
RumblWeb.Presence
]
```

Then in the `apps/rumbl_web/lib/rumbl_web/channels/presence.ex` file, change:
```
use Phoenix.Presence,
otp_app: :rumbl,
pubsub_server: Rumbl.PubSub
```

to:
```
use Phoenix.Presence,
otp_app: :rumbl_web,
pubsub_server: RumblWeb.PubSub
```

Fully working app:
githubcom/nelsonic/rumbl/tree/25e68f6355c9f1610d0b9f45271f3d63e1b77686

2019-09-26
265ERROR

While following the OTP chapter, after painstakingly inputting the code for info_sys/lib/info_sys/cache.change1.ex

When attempting to follow the iex instructions:

iex> alias InfoSys.Cache
iex> Cache.put(“one plus one?”, “two”)

I get the error:

(ArgumentError) argument error
(stdlib) :ets.insert(InfoSys.Cache_cache, {“one plus one?”, “two”})
(info_sys) lib/info_sys/cache.ex:5: InfoSys.Cache.put/3

Try it for yourself by copy-pasting the listing: /titles/phoenix14/code/otp/listings/rumbl_umbrella/apps/info_sys/lib/info_sys/cache.change1.ex

Complete code until this point: gitio/fj7Pm
Sadly unable to continue with the chapter if this does not work. :-(

2019-09-25
265ERROR

Please ignore/delete errata report #85670
I missed the step of adding InfoSys.Cache to Children list in application.ex

gitio/fj7XA
Cache.put and Cache.fetch work in iex as expected.

2019-09-25
295ERROR

Seconding Sam’s report #85642, when attempting to run the test:

mix test apps/rumbl_web/test/rumbl_web/channels/video_channel_test.exs

Getting the following output (errors):

21:46:15.944 [error] Task #PID<0.389.0> started from #PID<0.388.0> terminating

(UndefinedFunctionError) function nil.request/1 is undefined (module nil is not available)
nil.request(‘api wolframalpha com/v2/query?appid=1234&input=1+%2B+1&format=plaintext’)
(info_sys) lib/info_sys/wolfram.ex:30: InfoSys.Wolfram.fetch_xml/1
(info_sys) lib/info_sys/wolfram.ex:15: InfoSys.Wolfram.compute/2
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(elixir) lib/task/supervised.ex:35: Task.Supervised.reply/5
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: &InfoSys.Wolfram.compute/2
Args: [“1 + 1”, [limit: 1, timeout: 10000]]
21:46:16.047 [error] Task #PID<0.390.0> started from RumblWeb.Presence_shard0 terminating

(stop) exited in: DBConnection.Holder.checkout(#PID<0.385.0>, [log: #Function<11.104730475/1 in Ecto.Adapters.SQL.with_log/3>, source: “users”, timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
(EXIT) shutdown: “owner #PID<0.384.0> exited”
(db_connection) lib/db_connection/holder.ex:87: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection/holder.ex:68: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection.ex:1030: DBConnection.checkout/3
(db_connection) lib/db_connection.ex:1340: DBConnection.run/6
(db_connection) lib/db_connection.ex:596: DBConnection.execute/4
(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:572: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:554: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:153: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:10: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:232: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: #Function<2.79494727/0 in Phoenix.Presence.handle_diff/5>
Args: []

1) test new annotations triggers InfoSys (RumblWeb.Channels.VideoChannelTest)
apps/rumbl_web/test/rumbl_web/channels/video_channel_test.exs:32
No message matching Phoenix.Socket.Broadcast{event: “new_annotation”, payload:{body: “2”, at: 123}} after 100ms.
Process mailbox:
Phoenix.Socket.Message{event: “presence_state”, join_ref: nil, payload:{}, ref: nil, topic: “videos:254”}
Phoenix.Socket.Broadcast{event: “presence_diff”, payload:{joins: ], user:{username: “user194”}}}, leaves: %{}}, topic: “videos:254”}
Phoenix.Socket.Message{event: “presence_diff”, join_ref: nil, payload:{joins: ], user:{username: “user194”}}}, leaves: %{}}, ref: nil, topic: “videos:254”}
Phoenix.Socket.Message{event: “new_annotation”, join_ref: nil, payload:{at: 123, body: “1 + 1”, id: 31, user: %{id: 436, username: “user194”}}, ref: nil, topic: “videos:254”}
code: assert_broadcast “new_annotation”, %{body: “2”, at: 123}
stacktrace:
test/rumbl_web/channels/video_channel_test.exs:42: (test)

.21:46:16.057 [error] Task #PID<0.396.0> started from #PID<0.395.0> terminating

(UndefinedFunctionError) function nil.request/1 is undefined (module nil is not available)
nil.request(‘/v2/query?appid=1234&input=the+body&format=plaintext’)
(info_sys) lib/info_sys/wolfram.ex:30: InfoSys.Wolfram.fetch_xml/1
(info_sys) lib/info_sys/wolfram.ex:15: InfoSys.Wolfram.compute/2
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(elixir) lib/task/supervised.ex:35: Task.Supervised.reply/5
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: &InfoSys.Wolfram.compute/2
Args: [“the body”, [limit: 1, timeout: 10000]]
21:46:16.058 [error] Task #PID<0.397.0> started from RumblWeb.Presence_shard0 terminating

(stop) exited in: DBConnection.Holder.checkout(#PID<0.392.0>, [log: #Function<11.104730475/1 in Ecto.Adapters.SQL.with_log/3>, source: “users”, timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
(EXIT) no process: the process is not alive or there’s no process currently associated with the given name, possibly because its application isn’t started
(db_connection) lib/db_connection/holder.ex:87: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection/holder.ex:68: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection.ex:1030: DBConnection.checkout/3
(db_connection) lib/db_connection.ex:1340: DBConnection.run/6
(db_connection) lib/db_connection.ex:596: DBConnection.execute/4
(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:572: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:554: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:153: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:10: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:232: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: #Function<2.79494727/0 in Phoenix.Presence.handle_diff/5>
Args: []
.

Finished in 0.3 seconds
3 tests, 1 failure

Randomized with seed 752605
21:46:16.070 [error] Task #PID<0.401.0> started from RumblWeb.Presence_shard0 terminating

(stop) exited in: DBConnection.Holder.checkout(#PID<0.399.0>, [log: #Function<11.104730475/1 in Ecto.Adapters.SQL.with_log/3>, source: “users”, timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
(EXIT) shutdown: “owner #PID<0.398.0> exited”
(db_connection) lib/db_connection/holder.ex:87: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection/holder.ex:68: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection.ex:1030: DBConnection.checkout/3
(db_connection) lib/db_connection.ex:1340: DBConnection.run/6
(db_connection) lib/db_connection.ex:596: DBConnection.execute/4
(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:572: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:554: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:153: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:10: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:232: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: #Function<2.79494727/0 in Phoenix.Presence.handle_diff/5>
Args: []
21:46:16.070 [error] Task #PID<0.402.0> started from RumblWeb.Presence_shard0 terminating

(stop) exited in: DBConnection.Holder.checkout(#PID<0.399.0>, [log: #Function<11.104730475/1 in Ecto.Adapters.SQL.with_log/3>, source: “users”, timeout: 15000, pool_size: 10, pool: DBConnection.Ownership])
(EXIT) shutdown: “owner #PID<0.398.0> exited”
(db_connection) lib/db_connection/holder.ex:87: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection/holder.ex:68: DBConnection.Holder.checkout/2
(db_connection) lib/db_connection.ex:1030: DBConnection.checkout/3
(db_connection) lib/db_connection.ex:1340: DBConnection.run/6
(db_connection) lib/db_connection.ex:596: DBConnection.execute/4
(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:572: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:554: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:153: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:18: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:10: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:232: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Function: #Function<2.79494727/0 in Phoenix.Presence.handle_diff/5>
Args: []

have retraced steps and copied-pasted code from listings to ensure no PEBKAC.
Sadly, unable to fix. Code snapshot up to this point: gitio/fj7hX
Please help/fix. Thanks!

Elixir 1.9.0 (compiled with Erlang/OTP 22)
Phoenix v1.4.9
MacOS 10.14.6

2019-09-26Thanks Nelson, I marked this as a duplicate of that one. Please see my reply there!
239DEFER

You instruct the reader “We’re going to need three dependencies, :comeonin, :pbkdf2_elixir and :sweet_xml.” whereas the code listing does not contain `:comeonin` (nor do I think it needs to as it seems to be a transitive dependency through :pbkdf2_elixir). The code listing does contain sweet_xml - but we have not used this library up to this point in the book - so its a bit confusing for it to suddenly appear

2019-09-26
241SUGGEST

there is no instruction to copy the css over; the styles we added before (video.css from pp 184-185) are minimal but they do exist so its weird to discard them now

2019-09-26
242TYPO

When adding back the test speed-up config you have the listing as:

config :pbkdf2_elixir, :rounds, 1

whereas before it was

config :pbkdf2_elixir, rounds: 1

(which I understand to be equivalent to `config :pbkdf2_elixir, {:rounds, 1}` but not actually `config :pbkdf2_elixir, :rounds, 1` - I did not try the latter so maybe it also works)

2019-09-25
239SUGGEST

expounding on #85674 - also the listing elides jason, which was in the default depset from the generator for me

I don’t believe we do any direct json parsing in rumbl (and I understand jason to already be a transitive dep of ecto_sql AND postgrex - so I’m unclear why its in the generator output to begin with) - but the text should maybe have an explicit note re: removing it - or the listing should retain it

2019-09-26Jason is an optional dependency for both Ecto and Postgrex, so those dependencies should not bring it in. Maybe other dependencies are bringing Jason but it is best to be explicit and bring it in rather than depend on someone else doing it for us. Thanks!
80ERROR

This code snippet is missing an alias:

iex> recompile()
iex> alias Rumbl.Repo
iex> for u <- Repo.all(User) do
…> Repo.update!(User.registration_changeset(u, %{password: “temppass”})) …> end

MISSING: alias Rumbl.Accounts.User

2019-09-25This carries over inside an open iex session.
217TYPO

The first line of the scheduleMessages function uses scheduleTimer instead of schedulerTimer:

wrong: clearTimeout(this.scheduleTimer)
right: clearTimeout(this.schedulerTimer)

2544TYPO

On second paragraph, “UndefinedFunctionError” is spelled missing a c: “UndefiniedFuntionError”. Full text:
Before moving forward, let’s briefly review the error page. At the top the page
we get the exception name: UndefinedFuntionError.

2019-09-26
126DEFER

The last paragraph on the page begins with “Importing Ecto.Query makes the Ecto query language available to us.”, and after a few more sentences describing Ecto.Query it says “We also import the Ecto.Query module and alias Repo and Category.”

That basically boils down to “Importing Ecto.Query. Also importing Ecto.Query and […]”, which is both confusing and redundant :)

2019-09-26
131OK

The fourth bullet point says “When the select part is omitted, the whole struct is returned, as if we’d written select: u.”, but the query on this page (“Repo.one(from u in User, where: u.username == ^username)”) does not have a select part. The reader will have to go four pages back to find an example of using select.

2019-09-25the text says we omit the select... e.g. it's the default.
26SUGGEST

The text says “The router will call the world action on our controller, passing all of the information we need. We call the functions invoked by the router on our controller’s actions, but don’t get confused. They’re just functions.”

1) remove apostrophe on “controller’s”
2) Second sentence is confusingly worded. Consider revising to “We will refer to any functions that the router invokes on our controllers as ‘actions’, but don’t get confused.”

2019-09-25
27TYPO

“Finally, since name: name is the last argument, of a function, we can omit the brackets.”

Stray comma, remove the second one.

2019-09-25
305TYPO

> “In every case, render/1 is a pure function that takes socket.assigns as it’s lone argument.”

Should be `its`.

2019-09-26
307TYPO

> That’s because under the hood, this LiveView is an GenServer!

Should be `a GenServer` instead of `an`.

2019-09-26
308TYPO

“We’d have to estalish a separate route for each link and form.”

Should be `establish`.

2019-09-26
141TYPO

“Along the way we’ll explore some of the the different ways you can use Ecto to retrieve data from the
database.”

2019-09-25
267ERROR

The text states “Our struct has :score for storing relevance, :text to describe the result, :url for the URL it came from, and the :backend to use for the computation.”, but the struct in question (defined on the previous page) is defined like this:

defmodule Result do
defstruct score: 0, text: nil, backend: nil
end

That is, it does not have the URL field the text describes.

2019-09-26
269ERROR

On page 268, we’re told to add this config:

config :info_sys, :wolfram, app_id: wolfram_app_id

But on page 269, we’re told to fetch the same ID as

defp id, do: Application.fetch_env!(:rumbl, :wolfram)[:app_id]

One uses :info_sys as the app name, the other one :rumbl. They need to have the same name for the code to work.

2019-09-26Duplicate of #85794. Thanks!
142ERROR

The example when inserting the “test” Category is wrong. The output has a field with the name: “hello”, it should be “test”.

2019-09-25
171189TYPO

The mix test command on pdf shows 9 tests, but should actually be 6. On the previous page, the number of tests is 2, and 4 were added, leading 6. This is annoying because I thought I missed something, but counting proved it not to be the case.

25TYPO

The word “Function” in the exception “UndefinedFunctionError” was missing a letter “c”

210ERROR

About programming the token in app.html.eex, the code needed to be added, namely:
"

"
Works only when accessing the server locally. If I try to access it thru another computer, I get an error ni Google Chrome: “GET localhost:4000/hs/app.js net::ERR_CONNECTION_REFUSED”
The code which works in other computers is:
“<script type=”text/javascript" src=“<%= Routes.static_path(@conn, ”/js/app.js“) %>”>

"
Which is exactly the code from before the modification

26TYPO

The ebook the error complains:

undefined function: HelloWeb.HelloView.render/2
(module HelloWeb.HelloView is not available)

but in my terminal complains to:
` ` (UndefinedFunctionError) function HelloWeb.HelloView.render/2 is undefined (module HelloWeb.HelloView is not available)`

154TYPO

There is “…having a hardcoded valid email and password…” in the first paragraph, but we using usernames instead of emails to identify the user. Also in the previous page test named “returns not found error with no matching user for email”, but again - usernames.

183165ERROR

In Part I, Chapter 8, the tests don’t seem to have been updated to reflect changes made progressively to the Multimedia context. One example is on this page for the use of `Multimedia.list_videos` .

Earlier, that function was already changed to use a `user` parameter to scope the video list by user. I’ve been changing my tests as I go along, to reflect that and other similar cases of filtering by user.

187169ERROR

In the “setup”, I needed to also assign “:current_user” to “nil”, not just implement “bypass_through”, since the pipeline doesn’t (yet) add that expected key to assigns:

conn
|> assign(:current_user, nil)
|> bypass_through(RumblWeb.Router, :browser)
|> get(“/”)

187169SUGGEST

The solution I suggested in a previous erratum for this page involved the missing key :current_user on conn.assigns. That solution changed the test, which is not DRY.

It seems better to change the RumblWeb.Auth plug’s “call” function to check the map for that key:

cond do
Map.has_key?(conn.assigns, :current_user) && conn.assigns.current_user ->
conn
… etc.

189173ERROR

Variable “owner” is unused in the last test (line 25 in listing)

163SUGGEST

I could be just me, but I had great difficulty getting through this code. This forum post describes my prob. Perhaps it could be made clearer, I don’t know. The two “setup do” implementations proved to be an unsurmountable issue. It might have been assumed that I would only use one, but I did not understand that I should have only used one “setup do”. After only using one “setup do” I am running 4 tests instead of the book’s indication of 5.

add .com after elixirforum

elixirforum/t/having-a-problem-with-code-in-programming-phoenix-book-1-4/26097

121121ERROR

Not Found

The requested URL /titles/phoenix14/code/relationships/listings/rumbl/priv/repo/migratio ƒ 80513030504_add_category_id_to_video.change1.exs was not found on this server.

Apache/2.4.7 (Ubuntu) Server at media-origin.pragprog.com Port 80

99ERROR

“By passing the :method option to link, Phoenix generates a form tag instead of an anchor tag. Links without a specified HTTP method will default to GET, and Phoenix will render a simple link.”

This doesn’t seem to be case anymore since phoenix_html PR#157.

152TYPO

In a synopsis of 3 separate failing tests, the same test is listed twice (testing a password that’s too short). The username uniqueness test isn’t mentioned

147ERROR

In the part of the page:

Whether you knew it or not, Phoenix has already been generating default tests for you, such as test/rumbl_web/controllers/video_controller_test.exs, test/rumbl/accounts/, and test/rumbl/multimedia/.

My test directory don’t generated automatically the folder accounts and multimedia.
My test directory:
test
├── rumbl
│   └── multimedia_test.exs
├── rumbl_web
│   ├── channels
│   ├── controllers
│   │   ├── page_controller_test.exs
│   │   └── video_controller_test.exs
│   └── views
│   ├── error_view_test.exs
│   ├── layout_view_test.exs
│   └── page_view_test.exs
├── support
│   ├── channel_case.ex
│   ├── conn_case.ex
│   └── data_case.ex
└── test_helper.exs

148ERROR

For take the exactly same errors like: 3 tests, 1 failure.
It was needed to delete the files: video_controller_test.exs and multimedia_test.exs.

166ERROR

The link for download the source code redirects to the first Programming Phoenix book and not to the actual version 1.4 of the book.
So the correct link is:

pragprog.com/titles/phoenix14/source_code

124TYPO

Typo in iex sample such that output doesn’t match input:

iex> Rumbl.Repo.insert! %Rumbl.Multimedia.Category{name: “Test”}
%Rumbl.Multimedia.Category{
meta: #Ecto.Schema.Metadata<:loaded, “categories”>,
id: 1,
inserted_at: ~N[2019-05-19 13:06:12],
name: “hello”,
updated_at: ~N[2019-05-19 13:06:12]
}

the sixth line should be:
name: “Test”,

9983ERROR

warning: function Rumbl.Accounts.User.registration_changeset/2 is undefined or private. Did you mean one of:

* register_changeset/2

As the error describes, we declared a function called register_changeset/2 in the Rumbl.Accounts.User module, but are trying to call Rumbl.Accounts.User.registration_changeset/2.

Great book so far, thanks.

223ERROR

The private function on pg 223 for the auth controller assigns the token with a key :user_token. However, where the token is used on the client side (assets/js/socket.js) the parameter expects a userToken with a key :token. Updating the JS to use :user_token so it matches the auth controller corrects a failure in the browser console because of an unknown token.

47ERROR

At page 47, I run into a confusing part:

Excerpt from the page

With our user in place, let’s define our Accounts context. We will add a couple
of functions which will allow user account fetching. Let’s create a new file in
lib/rumbl/accounts.ex and key this in:
controllers_views_templates/listings/rumbl/lib/rumbl/accounts.ex

Change this

def list_users do
%User{id: “1”, name: “Jose”, username: “josevalim”},
%User{id: “2”, name: “Bruce”, username: “redrapids”},
%User{id: “3”, name: “Chris”, username: “chrismccord”}
end

To this
def list_users do
[%User{id: “1”, name: “Jose”, username: “josevalim”},
%User{id: “2”, name: “Bruce”, username: “redrapids”},
%User{id: “3”, name: “Chris”, username: “chrismccord”}]
end

Elixir can use multiple maps only if they are enclosed in a list like the above code.

For better context search on google for Solved:Possible errata in programming phoenix 1.4 version p1.0 pdf

202TYPO

Maybe is the version different that I use but the log is different on my server output logs for this:
[info] CONNECTED TO RumblWeb.UserSocket in 216µs
Transport: :websocket
Serializer: Phoenix.Socket.V2.JSONSerializer
Connect Info: %{}
Parameters: %{“token” => “undefined”, “vsn” => “2.0.0”}
[info] JOINED videos:2 in 58µs
Parameters: %{}

172SUGGEST

Page 172 contains copy regarding the length of time it takes to run the auth tests. I ran into slow tests by page 156 and was wondering if I’d screwed something up.

I suggest pulling forward the config rounds to 1 from page 172 to page 156 OR correct the test output at the bottom of page 156 (and elsewhere) to show ‘Finished in 6.3 seconds’

206ERROR

When I try to do an Annotation I get this error on console:
TypeError: msgContainer is null

I copy and paste the cody and the error continues..
for access to try:
gitlab.com/romenigld/programming-phoenix-1.4/commit/893664f9cd90bee2548b7a6fb3fbea89b413c93f

206ERROR

Sorry it was an error of mine rather use id on msg-container I put class.
And fixed when I change for the correct id tag.
The previous mistake was my mistake.

155ERROR

assert [%Video{id: ^id1}, %Video{id: ^id2}] = Multimedia.list_videos()

AFAIK there’s no guarantee that the rows will be returned in any specific order. Fails on my machine (unless I’m doing something wrong).

238SUGGEST

I’m using the Phoenix version Phoenix v1.4.10 and the ‘Copying the Web Source Files’ it wasn’t necessary to do. All was in the place.

238SUGGEST

I do a mistake, sorry. It needs to copy!

253239SUGGEST

$ mix test

This command fails as the config/test.ex file was not updated, some users may not have postgres as a users.

300287SUGGEST

Instead of loading the stubbed HTTP client in ```test_helper.exs, I’d suggest adding it to test/support/http_client.ex and adding the test/support to elixirc_paths. This way running tests from apps/rumbl_web would work as well.

167ERROR

The code doesn’t run as written until the setup function is added. This must happen later in the book.
The tests kept failing in the authenticate_user function.

I saw something similar where you add the login_as: as part of the setup. The tests don’t run until the describe is added later in the text. I spent some time trying to figure out why those tests wouldn’t run, then you added the describe and the @tag. Also, the tests on page 163 change radically later in the text.

320307SUGGEST

The page ends with “Typically, the handle_event will update the state in some way, but it doesn’t have to. Let’s take a look at a third use case, command-line completion.” but the next one continues with the “counter” example. I think the last sentence is misplaced.

L4557TYPO

In the “Setting Up Category Seed Data” section, a sample IEx session executes this command:
```elixir
Rumbl.Repo.insert! %Rumbl.Multimedia.Category{name: “Test”}
```

This book shows the following return value:
```elixir
%Rumbl.Multimedia.Category{
meta: #Ecto.Schema.Metadata<:loaded, “categories”>,
id: 1, ​ inserted_at: ~N[2019-05-19 13:06:12],
name: “hello”,
updated_at: ~N[2019-05-19 13:06:12]
}
```

You’ll notice that the `name` in the return value is “hello”. It should be
“Test” since that is what was passed in.

293TYPO

In the parte before to test has:
“Then we assert the socket has a user_id assigned and the join reply contains the previously creatrd annotations.”
First:
There in the final of the paragrapg has a gramaticall error in the word ‘creatrd’.
Second:
So the assert which really is doing is with the video_id.
assert socket.assigns.video_id == vid.id
And when I try to do the test it complains and error:
21:11:37.405 [error] Postgrex.Protocol (#PID<0.305.0>) disconnected: (DBConnection.ConnectionError) owner #PID<0.373.0> exited

Client #PID<0.376.0> is still using a connection from owner at location:

(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:580: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:562: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:177: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:17: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:79: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:319: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:257: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

The connection itself was checked out by #PID<0.376.0> at location:

(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:580: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:562: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:177: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:17: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:79: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:257: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

So I modified the assert for the user_id:
assert socket.assigns.user_id == user.id

and the test work’s:
mix test apps/rumbl_web/test/channels/video_channel_test.exs
==> rumbl_web
.

Finished in 0.3 seconds
1 test, 0 failures

293ERROR

Sorry but if I try the test again sometimes the mix complains for this:
mix test apps/rumbl_web/test/channels/video_channel_test.exs
==> rumbl_web
.

Finished in 0.2 seconds
1 test, 0 failures

Randomized with seed 377126

~/workspace/phoenix/Programming Phoenix 1.4/rumbl_umbrella   master ● ? 
 ✔  mix test apps/rumbl_web/test/channels/video_channel_test.exs
==> rumbl_web
.

Finished in 0.2 seconds
1 test, 0 failures

Randomized with seed 77689
21:24:32.324 [error] Postgrex.Protocol (#PID<0.304.0>) disconnected: (DBConnection.ConnectionError) owner #PID<0.373.0> exited

Client #PID<0.376.0> is still using a connection from owner at location:

(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:580: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:562: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:177: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:17: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:79: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:319: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:257: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

The connection itself was checked out by #PID<0.376.0> at location:

(ecto_sql) lib/ecto/adapters/postgres/connection.ex:80: Ecto.Adapters.Postgres.Connection.execute/4
(ecto_sql) lib/ecto/adapters/sql.ex:580: Ecto.Adapters.SQL.execute!/4
(ecto_sql) lib/ecto/adapters/sql.ex:562: Ecto.Adapters.SQL.execute/5
(ecto) lib/ecto/repo/queryable.ex:177: Ecto.Repo.Queryable.execute/4
(ecto) lib/ecto/repo/queryable.ex:17: Ecto.Repo.Queryable.all/3
(rumbl_web) lib/rumbl_web/channels/presence.ex:79: RumblWeb.Presence.fetch/2
(phoenix) lib/phoenix/presence.ex:318: anonymous fn/5 in Phoenix.Presence.handle_diff/5
(stdlib) maps.erl:257: :maps.fold_1/3
(phoenix) lib/phoenix/presence.ex:316: anonymous fn/4 in Phoenix.Presence.handle_diff/5
(elixir) lib/task/supervised.ex:90: Task.Supervised.invoke_mfa/2
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

55TYPO

Now, change your show.html.eex
template to render it:

Showing User

<%= render “user.html”, user: @user %>

it must be :
<%= render “user.html”, user: user %>

153TYPO

> “We start by defining a new describe block on line 4 which…”
This and all following line numbers are incorrect.

> test “returns not found error with no matching user for email” do
“email” should be “username”, also in the paragraph that follows.

282ERROR

IMHO the 4th unit test which stops and restarts the Infosys.Cache interferes with the other 3 unit tests of the “cache_test.exs” file.
I guess the order the tests are executed is random, especially because “asyn: true” is set at the beginning.
Therefore, killing the cache OTP process could lead to race conditions for the other 3 test cases (the cache could die while some other tests is using it), or am i am missing something?

172ERROR

At this point there are only 6 tests in the file, but the book states on page 172:

"
Now let’s run our new tests:
$ mix test test/rumbl_web/controllers/auth_test.exs
………
Finished in 1.9 seconds
9 tests, 0 failures
"

63TYPO

not able to launch for …/users/new to get the desired output. Getting error message
[debug] (Ecto.CastError) expected params to be a :map, got: `:empty`

63TYPO

My earlier entry #86163
actually you need to use %{} in place of :empty in the default value for params in definition of changeset/2 in the web/model/user.ex

68ERROR

The resources line used:

resources “/users”, UserController, only: [:index, :show, :new, :create]

has a bug, since :show is before :new if you go to /users/new it will route to the show controller with an id of “new” which both makes the new page inaccessible and generates a very confusing error. It needs to change to:

resources “/users”, UserController, only: [:index, :new, :show, :create]

Now /users/new will route to the new controller instead.

23TYPO

The output from mix phx.new is shown as:

Fetch and install dependencies? [Yn] y

  • running mix deps.get
  • running mix deps.compile

    $ cd hello
    $ cd assets
    $ npm install

    $ cd ..

The newer versions of phx.new automatically do the npm install for you, you don’t have to manually type it in. The output shown from a later version of phx.new on page 44 is correct, it shows the npm install command automatically being run.

xvTYPO

Text: “OTP ‘GenServer‘s directly from the Elixir environment”

Not sure what’s with the left apostrophes. Was that meant to be backticks like `GenServers`?

205190ERROR

As the code for Permalink is presented, cast(“–1”) will return :error while cast(–1) will return {:ok, –1}. I doubt this is the expected behaviour. Suggestion is to add a guard check for integer > 0 to the relevant function definition:

def cast(integer) when is_integer(integer) and integer > 0 do
{:ok, integer}
end

26TYPO

If I’m understanding it correctly, the sentence, “We call the functions invoked by the router on our controller’s actions, but don’t get confused,” should read, “We call the functions invoked by the router on our controllers ‘actions’, but don’t get confused.” This is a pernicious typo because the printed version is grammatically correct but technically incorrect (again, if I’m understanding its intention correctly).

144128TYPO

double “in” :
“will go in in contexts”

304TYPO

“Now, you can get a better look at how we’ll encorporate changing state in the LiveView”

“encorporate” should be “incorporate”

113SUGGEST

“We need to associate the video with the current user’s session” -> “We need to associate the video with the current session’s user”

137SUGGEST

“changeset error messages: Beautiful error messages for the consumption of humans.” This is technically ambiguous: it may indicate error messages such as “Please do not eat the humans.” How about “for consumption by humans”?

147ERROR

“test/rumbl/accounts/” and “test/rumbl/multimedia/” (two mentions in the same paragraph, the second asking the reader to delete them):
these directories do not exist. They were also not mentioned in the console output shown in Using Generators, p. 103, when other files such as multimedia_test.exs were created.

173ERROR

Remove line 25 of listing: “owner = %Rumbl.Accounts.User{}”.
“owner” is unused, so this line generates a warning.

182SUGGEST

“That way, external dependencies are never imported and available on the global JavaScript scope…”. Ambiguous: “are never imported nor available” or “are never imported and are available”?

182TYPO

“you can add the watch option"> ”you can add the —watch option" [double dash]

186SUGGEST

The text appears contradictory on whether slugs can change after creation: “The whole premise of a slug is that you can automatically generate a permanent field from other fields, some of which may be updateable. … If a change is made to the title, we build a slug based on the new title…” The code resolves the contradiction by including a permanent ID in the slug before the changeable title, which is ignored. News-oriented web sites that readers may be familiar with often base the slug on the original title with no numeric ID, use the full slug for lookups, and don’t change it, so explaining the differing approach in the text could avoid confusion.

215TYPO

The code in “channels/listings/rumbl/assets/js/video.change6.js” uses “this.scheduleTimer” on one line but “this.schedulerTimer” on the next line.

223SUGGEST

“Later, as Phoenix improves, we’ll show a browser icon for browser tabs…”: Does this refer to an upcoming Phoenix feature, and if so what? Or does it mean to say Rumbl, referring to a feature that could be written to support a future mobile client implementation?

257SUGGEST

Minor inconsistency, which may or may not be intentional: One of the two start_link calls uses parens, while the other doesn’t.

287ERROR

testing_otp/listings/rumbl_umbrella/apps/info_sys/test/backends/wolfram_test.exs
In test “no query results reports an empty list”, append “= []” to the assert line.

295TYPO

“new annotations triggers InfoSys” -> “new annotation triggers InfoSys”

51ERROR

*elixir

Request: POST /sessions
(exit) an exception was raised:
(FunctionClauseError) no function clause matching in String.split/3
(elixir) lib/string.ex:407: String.split(nil, “$”, [trim: true])
(pbkdf2_elixir) lib/pbkdf2.ex:109: Pbkdf2.verify_pass/2
(phxcodes) lib/phxcodes/accounts.ex:24: Phxcodes.Accounts.authenticate_by_username_and_pass/2
(phxcodes) lib/phxcodes_web/controllers/session_controller.ex:9: PhxcodesWeb.SessionController.create/2
(phxcodes) lib/phxcodes_web/controllers/session_controller.ex:1: PhxcodesWeb.SessionController.action/2
(phxcodes) lib/phxcodes_web/controllers/session_controller.ex:1: PhxcodesWeb.SessionController.phoenix_controller_pipeline/2


It Happened when after creating a new user and logged in, i tried to log out et log in again >>>>
the message displayed is :

"
FunctionClauseError at POST /sessions
no function clause matching in String.split/3
"

139TYPO

‘New User’ view in the book has 4 fields.
I think that this view has 3 fields.
Thanks.

281TYPO

The book shows ‘1 test, 0 failures’.
I think that ‘2 tests, 0 failures’ is correct.

172TYPO

The book shows ‘9 tests, 0 failures’.
I think that ‘6 tests, 0 failures’ is correct.

293TYPO

The book shows “Then we asset the socket has a user_id assigned and the join reply contains the previously ‘creatrd’ annotations”.
I think that ‘creatrd’ may be ‘created’.

141125TYPO

Typo on “behaviour” in the before last sentence “The downside is that the upsert behavour is often database specific, so make sure to explore the different options available to your database of choice.”

139TYPO

The screenshot does not reflect the current state of the code as there’s no email field.

153SUGGEST

Seeing the accounts_test.ex execution time, I think the book omitted to describe how to speed up password hashing, something like :

  1. In config/test.ex
    config :pbkdf2_elixir, :rounds, 1
62ERROR

The link to Ecto.Migration API in the footnote is wrong. It should be “/ecto_sql/Ecto.Migration.html” instead of “ecto/Ecto.Migration.html”.

96ERROR

“[…] anyone would be able to find whether an email is registered on the website.” should read “[…] anyone would be able to find whether a username is registered on the website.” since usernames and not emails are used to log in.

Ch.4TYPO

“When we created our application, Phoenix generated an Ecto repository, called Rumble.Repo”

Should be Rumbl without the ‘e’.

I’m on Kindle for Mac, and it is 17% of the way through, location is 1976 of 12423, not sure what page number exactly, hope that helps!

188SUGGEST

We really need to generate slugs for existing items in the db, e.g. in the migration

Ch.7TYPO

“The downside is that the upsert behavour…”

I realize behavior is spelled differently in the UK/English etc, but I believe this should either be ‘behavior’ or ‘behaviour’, it seems to be missing the ‘i’.

I am on Kindle for Mac, so it’s hard to say the exact location, but it says it’s 38%, Location 4618 of 12423.

Ch.7TYPO

Again, on Kindle for Mac so exact location is tough but it’s at 39%, or location 4739 of 12423.

“Complex interactions, such as those between our multimedia and users, will go in in…”

Duplicated the word “in”.

80ERROR

“Then, our function delegates to the put_pass_hash function to compute and store the user hash in the database, like this:”

I think this part is not precise and for me it was misleading until I read the contents of that function.

What I think this part should say is:

“Then, our function delegates to the put_pass_hash function to compute and store the user hash in the changeset, like this:”

94SUGGEST

In the first code listing on this page there is a call to ````Pbkdf2.no_user_verify() to prevent timing attacks.

As documentation of this function says: “This function is only useful if it is used as part of a policy of hiding usernames.”.

Up to this point usernames were not directly exposed in the app, however the new function authenticate_by_username_and_pass/2 returns different response for existing username (:unauthorized) and for not existing (:not_found).

Therefore existence of given username is exposed. Maybe function should respond with the same error for both cases?

205ERROR

Though it doesn’t seem to impact the running of the app or the code, after adding the Permalink module, iex gives a couple of behaviour-related warnings:

warning: function embed_as/1 required by behaviour Ecto.Type is not implemented (in module Rumbl.Multimedia.Permalink)
lib/rumbl/multimedia/permalink.ex:1: Rumbl.Multimedia.Permalink (module)

warning: function equal?/2 required by behaviour Ecto.Type is not implemented (in module Rumbl.Multimedia.Permalink)
lib/rumbl/multimedia/permalink.ex:1: Rumbl.Multimedia.Permalink (module)

188172SUGGEST

Page 172 suggests adding ‘config :pbkdf2_elixir, :rounds, 1’ to ‘rumbl/config/test.exs’ to speed up the test suite. This should be suggested somewhere near the beginning of the ‘Testing MVC’ chapter.

The very first test file that readers get to write (rumbl/test/rumbl/accounts_test.exs) involves creating a user record that goes through the slow hashing rounds. For me, running the 5 test examples in the file took around 3 seconds without the pbkdf2_elixir rounds config, but the book shows ‘Finished in 0.1 seconds’. 0.1 seconds is obviously the result of adding the rounds config, but the book never mentions that config until page 172.

Some readers might not notice the discrepancy and just suffer from slow tests until they get to the page that introduces the config, or some might notice the difference early and spend their time trying to figure out why their tests are not finishing in 0.1 seconds.

65SUGGEST

Around this page you introduce a pattern in the code samples “path/accounts.change1.ex” and it is not clear if the reader should create this new file or update the existing accounts.ex file. On this page the example is updating the Accounts context which it feels strange to introduce a new file or rename the accounts.ex file. I ultimately just updated the accounts.ex file but this naming convention is not explained. Also on this page you are missing the accounts folder in the path.

Categories: