Skip to content

Commit

Permalink
Allow for broadcast methods to accept a renderable (#364)
Browse files Browse the repository at this point in the history
* Allow for broadcast methods to accept a renderable

Rails supports [rendering of objects](https://edgeguides.rubyonrails.org/layouts_and_rendering.html#rendering-objects) such as [ViewComponents](https://github.com/github/view_component), but this support doesn't seem to be available in Turbo. This is just a stake in the sand for exploring the option to render a `renderable` in addition to `html` and `partial`.

This should allow for the following API call:

```ruby
message.broadcast_update_to(:messages, target: "message-count",
  renderable: MessagesCountComponent.new(count: Message.count))
```

where `MessagesCountComponent` is a supported Rails rendering object such as a ViewComponent.

It turns out that Rails does accept a long form argument for rendering objects: `renderable`. The only gotcha is that it [renders the layout by default](rails/rails#39869), causing the layout to be duplicated. This change explicitly prevents the layout from being rendered in the call to `ApplicationController.render` to prevent that duplication from happening.

* Remove reliance on ViewComponent library

* Update test to utilize #assert_broadcasts_text

* Add an example for rendering an object

* Use less confusing name for test component

* Remove unnecessary layout argument

This isn't necessary since its set within:
app/models/concerns/turbo/broadcastable.rb

---------

Co-authored-by: Joshua Klina <jklina@joshuas-mbp.lan>
  • Loading branch information
jklina and Joshua Klina committed Jun 20, 2023
1 parent a4b4620 commit 865377e
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 1 deletion.
15 changes: 14 additions & 1 deletion app/models/concerns/turbo/broadcastable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@
# end
# end
#
# If you want to render a renderable object you can use the `renderable:` option.
#
# class Message < ApplicationRecord
# belongs_to :user
#
# after_create_commit :update_message
#
# private
# def update_message
# broadcast_replace_to(user, :message, target: "message", renderable: MessageComponent.new)
# end
# end
#
# There are four basic actions you can broadcast: <tt>remove</tt>, <tt>replace</tt>, <tt>append</tt>, and
# <tt>prepend</tt>. As a rule, you should use the <tt>_later</tt> versions of everything except for remove when broadcasting
# within a real-time path, like a controller or model, since all those updates require a rendering step, which can slow down
Expand Down Expand Up @@ -352,7 +365,7 @@ def broadcast_rendering_with_defaults(options)

if o[:html] || o[:partial]
return o
elsif o[:template]
elsif o[:template] || o[:renderable]
o[:layout] = false
else
# if none of these options are passed in, it will set a partial from #to_partial_path
Expand Down
13 changes: 13 additions & 0 deletions test/dummy/app/components/message_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class MessageComponent
def initialize(text)
@text = text
end

def render_in(view_context)
"<div class='test-message'>#{@text}</div>".html_safe
end

def format
:html
end
end
18 changes: 18 additions & 0 deletions test/system/broadcasts_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ class BroadcastsTest < ApplicationSystemTestCase
end
end

test "Message broadcasts with renderable: render option" do
visit messages_path
wait_for_stream_to_be_connected

assert_broadcasts_text "Test message", to: :messages do |text, target|
Message.create(content: "Ignored").broadcast_append_to(target, renderable: MessageComponent.new(text))
end
end

test "Does not render the layout twice when passed a component" do
visit messages_path
wait_for_stream_to_be_connected

Message.create(content: "Ignored").broadcast_append_to(:messages, renderable: MessageComponent.new("test"))

assert_selector("title", count: 1, visible: false, text: "Dummy")
end

test "Users::Profile broadcasts Turbo Streams" do
visit users_profiles_path
wait_for_stream_to_be_connected
Expand Down

0 comments on commit 865377e

Please sign in to comment.