Designing Elixir Systems with OTP (Pragprog)

Ahh, given the context this makes sense. Thanks !

1 Like

In page 166 the authors present the following code (specs added by me):

  @spec start_quiz(Quiz.t, DateTime.t) :: reference
  def start_quiz(quiz, now) do
    Logger.info("starting quiz #{quiz.fields.title}...")
    QuizManager.build_quiz(quiz.fields)
    Enum.each(quiz.templates, &add_template(quiz, &1))

    timeout = DateTime.diff(quiz.end_at, now, :millisecond)
    Process.send_after(self(), {:end_quiz, quiz.fields.title}, timeout)
  end

This code is supposed to go on the file Mastery.Boundary.Proctor. However such code won’t compile, even after I finished the section, the reason being add_template is not defined:


== Compilation error in file lib/mastery/boundary/proctor.ex ==
** (CompileError) lib/mastery/boundary/proctor.ex:32: undefined function add_template/2
    (elixir) src/elixir_locals.erl:108: :elixir_locals."-ensure_no_undefined_local/3-lc$^0/1-0-"/2
    (elixir) src/elixir_locals.erl:109: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
    (stdlib) erl_eval.erl:680: :erl_eval.do_apply/6

Shouldn’t it not be Enum.each(quiz.templates, Quiz.add_template(quiz, &1)) instead?
I have re-read the proctor section several times now and I can’t find the page where this function is. I have to be missing something!

Also, the book mentions this is the file Workers/lib/mastery/bonudary/proctor.ex. Shouldn’t it not be Mastery/lib.... instead?

Would appreciate any possible guidance, I am kinda lost in this section :S

1 Like

There is a tiny wrapper in our source code for the Proctor module, though we don’t end up showing it in the book. Here’s the function:

  def add_template(quiz, template_fields) do
    QuizManager.add_template(quiz.fields.title, template_fields)
  end

It looks like this may be a simple oversight on our part. Sorry about the confusion!

This is just the devision of our code by book chapters that you’ll see if you download our source.

1 Like

On page 181, you have the following text:

  • Configure the project to use a repository.
  • Add a perstence function to our external API.
  • Wire that persistence function into the Boundary.QuizSession GenServer.

In the second bullet point, I believe you mean “persistence” instead of “perstence

:smiley:

1 Like

Good catch. Thanks!

1 Like

I’m just waiting for a coupon to buy (ElixirForum is not longer available).

1 Like

A new coupon will be coming later this month :003:

4 Likes

I prefer supporting the authors early on and be an overall pain in the backs by constantly reporting typos and other things I notice :stuck_out_tongue:

I do believe however this somehow helps them :smiley: (don’t crush my dreams by telling me otherwise!)

There are a few things in chapter 9 I would like to comment regarding configuration.

  1. Now when we create projects in Elixir the config folder is missing. This change was introduced in Elixir 1.9 (https://elixir-lang.org/blog/2019/06/24/elixir-v1-9-0-released/) and it may generate confusion for readers of the book, as those who create the mastery project using Elixir 1.9 will have a different project structure than those who don’t. (use Mix.Config is soft deprecated, config folder no longer created)

  2. In page 181 in Configure Mastery for Persistence the first config file is missing use Mix.Config at the top, which causes the file not to work. It is also missing the usual boilerplate line where you import config depending on the value of MIX_ENV.

Point 2 was specially painful for me as a reader and I had to download and checkout the source code to fix the issues I was having.

Entering the Phoenix part!

1 Like

Thanks for pointing these details out. In this case, it’s a matter of Elixir’s default behavior changing after we wrote the code. It use to generate the mentioned files and include the mentioned lines.

In these instances, it’s probably worth a look a look at our source code to see what’s really in the files. And, of course, we’ll fix these kind of things if we ever do a second edition.

1 Like

I have finally finished the book. With 30 to 60 minutes per day, 2-3 times a week, it took me =~ 6 months to finish it, with all the code samples, the DB, dialyzer and really making an effort to understand as much as I can.

It took me so long to finish this, that the slogan for the book “Do Fun Things with Big Loud Wildebeests” actually changed to Worker bees while I was reading it!

Overall I liked the layered architecture in the book. I know Bruce is a fan of layers and even though I am not sure I agree with some of the decisions taken the book explains itself in a way that helps me understand why certain decisions were made - for the authors, development is a series of trade-offs, a fact they make very clear.

The part I enjoyed the most though (I believe this is mainly from James) was the decoupling of the DB. They create a new project inside the main project of the book. At first it looks like non-sene: why not use another GitHub Repo or an Umbrella app? But after that, they actually explain all the differences and all the benefits of each approach an I can only say I became better by reading and understanding them all. Genius solution!

It also solves a problem I’ve had for years in Elixir now - to find a standardized way of solving persistence in Elixir (something I have really missed in other books).

The also pays some attention to tests, although they don’t follow TDD. I’d say it is still worth a look, specially for the integration tests on chapter 9 with timers.

Overall I would recommend this for anyone doing backend. If you feel you are comfortable with Elixir and GenServers, and your main struggles are architecture and decoupling, I would definitely recommend this !

7 Likes

Thanks so much for the kind words!

2 Likes

hi. thanks for book.
i want to tell about some inconsistence. on page 160 you say that poolboy is just another elixir dependency and we just need to configure it in our application.ex. but earlier you have shown that classical elixir dependency is usually an otp app and it even doesnt need to be added in the application.ex. it is weird a bit. as i know, poolboy is erlang library and we use it in the elixir app and that is why we need to start it manually. am i right?

There are different strategies you can use to allow users of your projects to interact with your code. Chris Keathly has a nice breakdown of the choices in this blog post.

One strategy is, as you said, to just build your own application that’s started by the OTP (“OTP applications” in Chris’s post). Sometimes though, it’s better to allow other applications to start some of your processes in their application tree. This gives the calling code a lot of choices about what exactly gets started and how your code will interact with those pieces.

Poolboy uses the latter strategy (called “Providing a Supervision Tree” in Chris’s post). This allows you to configure how many worker processes are started and point them at your modules that you want to run.

Hope that helps!

2 Likes

Hi!

I am following the book right now very seriously, writing every piece of code suggested and trying to understand as much as I can everything that’s going on.

Thank you for this work, it is an awesome book to learn about… how to actually shape programs. Basic syntax tutorials are great, LYSEFGG as well, but then when you start you first project without guidance, it can still be quite rough. This book fills this gap quite nicely.

I’ve got a little issue though with the code presented in the part that refers to the tests.
It is suggested to write this file. (Let me know if you want me to get it down from GitHub later).

When trying to run it with “mix test” I got:

warning: variable “right” does not exist and is being expanded to “right()”, please use parentheses to remove the ambiguity or change the variable name
nofile:1

  1. test a right response and a wrong response building responses checks answers (ResponseTest)
    test/test/response_test.exs:8
    ** (CompileError) nofile:1: undefined function right/0

So this “test” syntax for getting access to the new field in the context map does not work, right?
I need to use something like:

test “description”, context

And then access context fields directly? I tried, indeed, without success.

If that’s true then I think the book needs to be updated. The downloaded source code that goes together with the book contain different versions of the code, without this fixture.

There’s nothing wrong with the file linked here; it is upstream. This file is using the QuizBuilders module to build up the fixtures used, and there is a typo in that module file in the function template_fields/1:

 11   def template_fields(overrides \\ []) do
 12     Keyword.merge(
 13       [
 14         name: :single_digit_addition,
 15         category: :addition,
 16         instructions: "Add the numbers",
 17         raw: "<%= @left %> + <%= right%>", #<---This should be @right, not right
 18         generators: addition_generators(single_digits()),
 19         checker: &addition_checker/2
 20       ],
 21       overrides
 22     )
 23   end

So the issue is not the pattern match on “right” (as in not wrong) in this test, it’s that when that little template is compiled, it thinks “right” (as in not left) is a function instead of an assign.

A tangent -

I read the first few chapters of this book and really enjoyed it. In particular the section on how important it is to pick the right data structures really improved how I approach any new project. I had not yet read the testing chapter, which I just skimmed as part of looking at your problem. I really didn’t like all of the fixture setup. Being able to write something like assert right.correct seems elegant on its face, but I had to trace that statement several levels up the call stack just to figure out what function was actually being tested. The whole set of tests would have been more readable (to me) if the setup was in a setup block at the top of the page (so I could see how the assigns were getting built step by step without tracing through a bunch of supporting functions), and the test actually called the function being tested so I could see what it was and what arguments were passed to it. This would mean some code duplication, but less magic, and that tradeoff would have been worth it to me as a reader of the code.

That error isn’t actually referring to your test syntax. It’s a problem with this line:

The right in that template should be @right.

Hope that helps!

1 Like

Thanks for the kind words!

This is a fair critique. Our reasoning was that we were testing a pretty complex system in the book and we wanted to use some abstraction to ease the burden of keeping it all in your head at once. But I totally hear you that we could have over done it in some areas. I’m sure we did.

Thanks for the help! :slight_smile:
Guess I still gotta improve my Elixir debugging skills.
Btw, I fixed all the small errors and typos and now it is passing all the test. For anyone in trouble going through the book, feel free to check out the GitHub repo.