Cellane

Cellane

Agile Web Development with Rails 8: live product refresh not working in chapter 11

Hello, I was struggling a bit with completing chapter 11, iteration F4.

While the changes done in the “admin” (maintenance?) panel to store products showed up without me refreshing the main store page, it didn’t feel as smooth as it should have – the entire page refreshed, instead of just morphing the DOM to contain the new data. After spending a few hours investigating this, I found two root causes that should probably be addressed in the final book:

The entire rendering process is crashing due to not being able to render the product partial

This issue can be observed either from the terminal, where I could see this output (truncated):

02:38:35 web.1  | Started GET "/products/7" for ::1 at 2025-02-20 02:38:35 +0900
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413]   Product Load (0.2ms)  SELECT "products".* FROM "products" WHERE "products"."id" = 7 LIMIT 1 /*application='Depot'*/
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413] Performing Turbo::Streams::ActionBroadcastJob (Job ID: 40cc8022-d8d7-4b62-9fcb-053676ba5413) from Async(default) enqueued at 2025-02-19T17:38:35.448256000Z with arguments: "products", {action: :replace, target: "product_7", targets: nil, attributes: {}, partial: "store/product", locals: {product: #<GlobalID:0x0000000138317188 @uri=#<URI::GID gid://depot/Product/7>>}}
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413]   ActiveStorage::Attachment Load (0.1ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = 7 AND "active_storage_attachments"."record_type" = 'Product' AND "active_storage_attachments"."name" = 'image' LIMIT 1 /*application='Depot',job='Turbo%3A%3AStreams%3A%3AActionBroadcastJob'*/
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413]   ↳ app/views/store/_product.html.erb:3
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413]   ActiveStorage::Blob Load (0.0ms)  SELECT "active_storage_blobs".* FROM "active_storage_blobs" WHERE "active_storage_blobs"."id" = 7 LIMIT 1 /*application='Depot',job='Turbo%3A%3AStreams%3A%3AActionBroadcastJob'*/
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413]   ↳ app/views/store/_product.html.erb:3
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413]   Disk Storage (0.9ms) Generated URL for file at key: v7g25mgxk84oj9sxzjdpjyafgygx ()
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413]   Rendered store/_product.html.erb (Duration: 7.0ms | GC: 0.0ms)
02:38:35 web.1  | [ActiveJob] [Turbo::Streams::ActionBroadcastJob] [40cc8022-d8d7-4b62-9fcb-053676ba5413] Error performing Turbo::Streams::ActionBroadcastJob (Job ID: 40cc8022-d8d7-4b62-9fcb-053676ba5413) from Async(default) in 12.4ms: ActionView::Template::Error (Cannot generate URL for nrclient2.jpg using Disk service, please set ActiveStorage::Current.url_options.):
02:38:35 web.1  | /Users/milan/.local/share/mise/installs/ruby/3.4.1/lib/ruby/gems/3.4.0/gems/activestorage-8.0.1/lib/active_storage/service/disk_service.rb:138:in 'ActiveStorage::Service::DiskService#generate_url'
02:38:35 web.1  | /Users/milan/.local/share/mise/installs/ruby/3.4.1/lib/ruby/gems/3.4.0/gems/activestorage-8.0.1/lib/active_storage/service/disk_service.rb:117:in 'ActiveStorage::Service::DiskService#private_url'
02:38:35 web.1  | /Users/milan/.local/share/mise/installs/ruby/3.4.1/lib/ruby/gems/3.4.0/gems/activestorage-8.0.1/lib/active_storage/service.rb:125:in 'block in ActiveStorage::Service#url'
02:38:35 web.1  | /Users/milan/.local/share/mise/installs/ruby/3.4.1/lib/ruby/gems/3.4.0/gems/activesupport-8.0.1/lib/active_support/notifications.rb:210:in 'block in ActiveSupport::Notifications.instrument'
02:38:35 web.1  | /Users/milan/.local/share/mise/installs/ruby/3.4.1/lib/ruby/gems/3.4.0/gems/activesupport-8.0.1/lib/active_support/notifications/instrumenter.rb:58:in 'ActiveSupport::Notifications::Instrumenter#instrument'

Or in the browser’s developer console, where you can see the store main page unsubscribing from the WebSocket topic and resubscribing back to it – which should not happen. Also, the entire page just very obviously jumps, as it would if there was a full page reload happening.

Based on the error message, I thought that something akin to include ActiveStorage::SetCurrent that we previously added to ApplicationController also needs to be added to ApplicationCable::Channel but that doesn’t seem to be the case, as the solution is much simpler – the store/_product partial needs to be changed instead:

-    <%= image_tag(product.image.url, class: "object-contain w-40 h-48 shadow mr-6") %>
+    <%= image_tag(product.image, class: "object-contain w-40 h-48 shadow mr-6") %>

After this, the crash no longer happens in the server console, but the page still hard-refreshes. That’s because…

The WebSockets topics overlap with chapter 6

Chapter 6 adds after_commit -> { broadcast_refresh_later_to "products" }, introducing the products topic, and then chapter 11 reuses the topic to broadcast not the information to refresh the page, but to replace the affected part. I think these should not share the same topic, so after changing ProductsChannel like so:

-    stream_from "products"
+    stream_from "store/products"

ProductsController like so:

-        @product.broadcast_replace_later_to "products", partial: "store/product"
+        @product.broadcast_replace_later_to "store/products", partial: "store/product"

… and store’s view template like so:

- <%= turbo_stream_from "products" %>
+ <%= turbo_stream_from "store/products" %>

… everything starts working smoothly again, no full-page refreshes happen, and only the affected product gets replaced.

That was a fun one to figure out, as a Rails newbie! :face_with_hand_over_mouth:

(Also I feel like earlier in the chapter, the customer said to please honour the price of the product at the time of adding the product to the cart and I don’t think that’s happening yet but maybe it will be addressed later in the chapter…? Or maybe just not written yet, should probably involve putting the price on LineItem and copying the value over at the time of adding it to the cart.) I missed that this is part of the optional exercises on pages 144~145. My bad!

Marked As Solved

rubys

rubys

Author of Agile Web Development With Rails

Good catch, and good suggestion! And glad you are having fun! Thanks!

Where Next?

Popular Pragmatic Bookshelf topics Top

GilWright
Working through the steps (checking that the Info,plist matches exactly), run the demo game and what appears is grey but does not fill th...
New
simonpeter
When I try the command to create a pair of migration files I get an error. user=&gt; (create-migration "guestbook") Execution error (Ill...
New
conradwt
First, the code resources: Page 237: rumbl_umbrella/apps/rumbl/mix.exs Note: That this file is missing. Page 238: rumbl_umbrella/app...
New
alanq
This isn’t directly about the book contents so maybe not the right forum…but in some of the code apps (e.g. turbo/06) it sends a TURBO_ST...
New
leba0495
Hello! Thanks for the great book. I was attempting the Trie (chap 17) exercises and for number 4 the solution provided for the autocorre...
New
jskubick
I think I might have found a problem involving SwitchCompat, thumbTint, and trackTint. As entered, the SwitchCompat changes color to hol...
New
adamwoolhether
I’m not quite sure what’s going on here, but I’m unable to have to containers successfully complete the Readiness/Liveness checks. I’m im...
New
brunogirin
When I run the coverage example to report on missing lines, I get: pytest --cov=cards --report=term-missing ch7 ERROR: usage: pytest [op...
New
brunogirin
When running tox for the first time, I got the following error: ERROR: InterpreterNotFound: python3.10 I realised that I was running ...
New
mert
AWDWR 7, page 152, page 153: Hello everyone, I’m a little bit lost on the hotwire part. I didn’t fully understand it. On page 152 @rub...
New

Other popular topics Top

siddhant3030
I’m thinking of buying a monitor that I can rotate to use as a vertical monitor? Also, I want to know if someone is using it for program...
New
DevotionGeo
I know that -t flag is used along with -i flag for getting an interactive shell. But I cannot digest what the man page for docker run com...
New
AstonJ
I have seen the keycaps I want - they are due for a group-buy this week but won’t be delivered until October next year!!! :rofl: The Ser...
New
Exadra37
Oh just spent so much time on this to discover now that RancherOS is in end of life but Rancher is refusing to mark the Github repo as su...
New
DevotionGeo
The V Programming Language Simple language for building maintainable programs V is already mentioned couple of times in the forum, but I...
New
rustkas
Intensively researching Erlang books and additional resources on it, I have found that the topic of using Regular Expressions is either c...
New
PragmaticBookshelf
Rails 7 completely redefines what it means to produce fantastic user experiences and provides a way to achieve all the benefits of single...
New
PragmaticBookshelf
Author Spotlight Jamis Buck @jamis This month, we have the pleasure of spotlighting author Jamis Buck, who has written Mazes for Prog...
New
AstonJ
This is a very quick guide, you just need to: Download LM Studio: https://lmstudio.ai/ Click on search Type DeepSeek, then select the o...
New
Fl4m3Ph03n1x
Background Lately I am in a quest to find a good quality TTS ai generation tool to run locally in order to create audio for some videos I...
New

Sub Categories: