Programming Phoenix LiveView Book Club!

Chapter 5

This chapter was a little lighter than the previous, but still had some very good content.

  1. Building a form for data that is not persisted.
    Before, when I have created changesets for data that is not saved, I have used Ecto embedded schemas. It works fine, but I like this approach also since there is no use statement required. :smiley:
  2. File uploads. Once again, LiveView shows that it can make complex things, such as file uploads, easy. With just a few function calls you get, not only the upload functionality, but also drag and drop, validation, progress bar and preview?? Amazing!
    And throughout all this we, the developers, get to keep our mind in the server without having to context switch our brains between the frontend and backend. :slight_smile:

Outdated form syntax

I mentioned it in my previous post, but the syntax used to build the form is slightly outdated.
We no longer need to do :let={f} when declaring the form, and instead of <.input field={{f, :first_name}} ... /> we can do <.input field={@form[:first_name]} .... This is nice since the :let={f} stuff was pretty confusing imo.
Also, the assignment to the socket is slightly different, using the to_form function: assign(socket, :form, to_form(changeset)).

Once again, I’m sure this will be fixed for the final release.

I’m excited to start the next chapter about Function Components :smiley:

5 Likes

Please don’t forget to post this in the book’s errata as well :blush:

1 Like

Chapter 1

I completely feel the sentiment presented in the first section, where the complexity of the JavaScript ecosystem and the job description of a front-end engineer is discussed.

In truth, we can’t even show a single diagram of a typical SPA because there are no typical SPAs!

Yes!

Also liked the way LiveView was presented in an ELI5 fashion. One thing though, “Keeping brain in one place” is not something super useful for me. I am cool with context switching, and I believe the back and forth between Elixir and JavaScript a bit more annoying in LiveView (when you have complex enough app) than when you are using SPA, as in that case, you can wear the Elixir or JS hat for longer- making the context switch less, felt.

The rest of it has been mechanical- installation and inspection. I particularly enjoyed the section LiveView Transfers Data Efficiently. And Your Turn happened for me few days ago when checking out @m4hi2’s problem.

The one errata worthy note I found out has been posted already (live_path).

One thing I’d have liked would be, a dedicated section about what LiveView is good for and what are the limitations. And a link to PhoenixPhrenzy contest winners could be sported there (some really awesome apps there if I remember. Also a question, what happened to that site?)

That’s about it. From the perspective of backend engineer and SPA engineer both (who I’d suspect are target audience for this book, i.e. @myself) this is great introduction, interesting enough to look forward to chapters.next!

My apologies for late review, certain personal issues needed dealing but I’m back now and will soon post Chapter 2 review.

Thank you everyone for your reviews.

4 Likes

Chapter 3

Completely agree with @Tbobbe, a quote I noted down as well:

I also, really enjoyed the chapter and it gave a nice background to the structure of a Phoenix application that have been a good reminder to me. The separation of boundary and core layer was really nicely described. If someone wants to dig more into this separation, it has also been explored by Gary Bernhardt as “Imperative Shell, Functional Core” in [1] and Andy Matuschak as “Object Layer and Value Layer” in [2].

[1] Ruby Conf 12 - Boundaries by Gary Bernhardt - YouTube
[2] Andy Matuschak - Functioning as a Functionalist - YouTube

6 Likes

Good afternoon, dr.a. I have been spending the last weeks trying to fix a live route issue to know avail and was about to abandon my goal to master LiveView. Since I have been retired for 15 years from the University of Illinois at Urbana-Champaign, I have not been actively maintaining a website where I post my papers. My email is wn.dvs1@gmail.com. If you send me your email, I will send you one of the papers. My name is Wayne Davis and I offered years of advanced tutorials in discrete-event simulation. What a porposed that an entirely new paradigm for discrete-event simulation is needed, abandoning the stochastic queueing networks which have outlived their effectiveness.

2 Likes

Chapter 4

I have to admit I struggled to get through this chapter, it’s quite dense, goes through a lot of generated code and often defers concepts to later chapters. While I personally would have liked having this chapter later, after those concepts had been described I can see the appeal for those that just want to get up and running building their own app.

Good thing I signed up for a book club to keep me motived to power through! :slight_smile:

My main take aways from the chapter in no particular order:

  • Learning a bit more about use and the way that the MyAppWeb module was constructed to allow for use MyAppWeb, :router.
  • A single LiveView handles different states through live actions.
  • Function components vs Live components, looks similar to “Presentational” vs “Container” components in React.
  • That the core components are generated and put in your application to be modified as you need, rather than learning how to override them.

Onwards to Part 2!

4 Likes

Hi, I read chapter 1 thru 5 during the easter holiday, then it took some time to get around to actually write some code. I also read ahead a bit - I would have preferred some testing to be covered earlier than chapter 10. I’m keeping the tests green as I go.
I like the emphasis in chapter 1 on giving an understanding on how the lifecycle works (including the network views, although finding the exact view based on the description involved some fiddling around). As well as the emphasis on design / separation of concerns.
That, combined with having done the phoenix guides, was (I thought) enough to get me started on an experiment to see if I could rebuild an existing application. In hindsight chapter 7 (form components, which also deals with relations) and chapter 10 (testing) would have been useful. It’s not always easy to be patient :-).
I’m also reading through ‘Elixir in Action’ - saves some guessing what the difference between def and defp is ;-).
I like the focus on interactivity in part 3 (dashboards, presence). Not sure about part 4 - from skimming it, it seems to go front-end only with hardcoded data (I’ve burnt myself with hardcoding too much recently).

1 Like

Hi Tbobbe! I’ve just started working through the book and am finding your comments incredibly helpful. Thank you! You don’t by chance have a github repo where you’re working on this project, do you?

2 Likes

Hi @bolducp!
I’m very happy you have found my comments helpful :smiley:
Sorry to disappoint, but I haven’t got any repo for my code. Although, the only changes I’ve made to the code from the book are the ones I’ve mentioned in my comments :slight_smile:

Hopefully I can continue reading the book soon, thank you for the extra motivation! :smiley:

2 Likes

I’m at page 95 in the book where the greeting is assigned to socket. The book shows the following code,

  def mount(_params, _session, socket) do
    {:ok,
      socket
      |> assign(:greeting, "Welcome to Pento!")
      |> stream(:products, Catalog.list_products())}
  end

What if I write it like the following instead? It looks a bit simpler and more readable, even if I like pipe operator in other places.

  def mount(_params, _session, socket) do
    socket = assign(socket, :greeting, "Welcome to Pento!")
    {:ok, stream(socket, :products, Catalog.list_products())}
  end

Is there any added benefit of assigning greeting to socket inside the :ok tuple? Or botht he codes are the same functionality and performance wise?

I’d say the difference between the two is readability. Setting up things in a pipeline when you can is more idiomatic.

Thanks @Tbobbe. (after noticing I skipped ‘try this’ in chapter 2) I did the same as you, and was tempted to skip the Mailer as well. Turns out to be a bit “now draw the rest of the owl” for me. I thought I’d use an AWS dev account I already had set up for Simple Email Service. On the phoenix side I needed to add some config, install another dependency (gen_smtp) and read some forum threads of other people who struggled with swoosh. It is almost working after a few hours (no more compiler errors etc after everything is in the right place) but no mails coming out yet.

As often with configuration - it is just a few lines, but they need to be the right few ones. Turns out by default, in ‘dev’ you can see the mails that would go out under /dev/mailbox that might be enough for this exercise.

On the other hand, I’d like to get a taste for how observable this combination (Swoosh and SES) would be in production. So this exercise gives me a bit of a feel for that.

Swoosh.Adapters.AmazonSES — Swoosh v1.14.1 is not bad - keep in mind that it does not mention switching the local mailbox for an actual mailer. Otherwise you’ll get an error about false/post/4 is undefined. Issues Sending Emails with Swoosh - #5 by ScriptyScott explains this. I used ‘Finch’ instead of ‘Hackney’ - I don’t know the differences between the two and Finch was already included in my install.

Code on Page # 137 doesn’t result in the screenshot on page # 138.

(Beta 10, PDF).

Fails to compile, with error ** (Key Error) key :form not found in: %{...

1 Like

I managed to get the mail sent in the end. For anyone else who stumbles on this:

I needed to add put_provider_option(:security_token, <temporary token> at the end of the new() call in deliver. aws sts get-session-token generates a temporary access key, secret and token.

To get there, I added some error logging (the generated code silently swallows the :error response of Mailer.deliver it gets passed on but does not seem to be logged at the top level.
AWS SES returned this error:

%{
  code: "InvalidClientTokenId",
  message: "The security token included in the request is invalid."
}

There was no security token, so yes, that was invalid.

I also added an IO.Inspect to see what mail was generated. I tried to make my own new, in Pento.Mailer but the provider_options remained empty.

What did I learn?

  • I am glad I did this early. There are a few things to think about
  • The lack of error logging here raises questions about how to make a Phoenix LiveView app observable in production (I am likely to make mistakes and want to get quick feedback on those…).
  • Swoosh produces logs repeating the content of the message at least 5 times (this can be addressed by changing the logging level apparently, so won’t affect production). Not sure why it isn’t one time.
  • SMTP with SES is a good idea in theory, as you can (found this on another thread) create an IAM role with username and password that only can only send mails. But failed with the same invalid token warning.
  • I guess that means for me, for production I would need the more complicated Swoosh.Adapters.ExAwsAmazonSES adapter to fetch credentials on-demand.

So I’m sharing the final solution as a way of paying it forward, I wouldn’t have gotten this far without several blog- and forum posts, stack overflow and the ExAwsAmazonSES documentation (Read carefully, Everything is in there. I’m not sure why the AmazonSES documentation refers to using ExAwsAmazonSES if you have ex_aws already configured. I may be missing something, but ExAwsAmazonSES seems to be the better option).
I ended up with ex_aws in the end, so I don’t have to generate the security token by hand. It uses AWS_PROFILE, and loads the associatd key and secret as well. It will use aws sts and do the key rotation. No credentials in the production code or configuration code.

Dependencies used

      {:ex_aws, "~> 2.1"},
      {:ex_aws_sts, "~> 2.1"},
      {:configparser_ex, "~> 4.0"},
      {:hackney, "~> 1.9"},
      {:swoosh, "~> 1.14.1"},
      {:gen_smtp, "~> 1.2.0"},

Config for ex_aws and ExAwsAmazonSES in runtime.exs

I Found the idea to do this in runtime.exs in a suggestion. I might put the code in ~dev.exs~ again. I probably need to find an explanation of what to put where (dev.exs, config.exs, runtime.exs etc) I was following someone elses’ example and it seems to work for me. For production I’d probably use environment variables for the key and the secret, after creating a narrow IAM user that only has the permissions the app needs. So this will need some more factoring out.

 if config_env() == :prod or config_env() == :dev do
    IO.puts("Configuring AWS credentials")
    config :ex_aws,
      # Below works if you export AWS_PROFILE (specified by :awscli)
      access_key_id: [{:system, "AWS_ACCESS_KEY_ID"}, {:awscli, :system, 30}, :instance_role],
      secret_access_key: [{:system, "AWS_SECRET_ACCESS_KEY"}, {:awscli, :system, 30}, :instance_role],
      region: "eu-west-2" # London. fill in your own region here. 

    IO.puts("Configuring ExAwsAmazonSEs Mailer. config_env: #{config_env()}")
    config :pento, Pento.Mailer,
            adapter: Swoosh.Adapters.ExAwsAmazonSES

    config :swoosh, :api_client, Swoosh.ApiClient.Hackney
  else
    IO.puts("Not Configuring AWS credentials and mailer. config_env: #{config_env()}")
  end

Feedback most welcome. I’m glad it finally works and hope I can save someone some time in the future.

1 Like

Did anyone make the username editable? @Tbobbe indicated that it might be much work. I’d hope not, as editing is something that Phoenix LiveView should make easy. The username has to be unique, but so is the e-mail address, so there is prior art in the generated auth code. Any thoughts?

Yes, I did! It was actually not too much work after I gave it another look. :slight_smile:

1 Like

I did too, not too hard indeed. I followed some existing traces in the code (changing e-mail and password, with some tests) and decided to do a bit less than those. Adopting a test or two and copy-paste-modifying the UI was fairly straightforward indeed.

Chapter three was uneventful - fewer exercises than chapter 2. On to chapter 4. I found reading “Elixir in Action” in parallel helps. It took me a while to wrap my reading around how pattern matching on return values works.
I read through some of the next chapters in Programming Phoenix LiveView again, reading becomes easier. I quite liked the explanation (in context of use) of schemaless changesets.