Skip to content

Commit

Permalink
Stream Tag Builder: Support :renderable arguments
Browse files Browse the repository at this point in the history
When passed a valid `:renderable` option (like an object that responds
to `#render_in`), treat that object as both the `<turbo-stream>`
element's template contents _and_ attempt to treat it as the target.

For example, consider a simplified "component" class:

```ruby
class Component
  extend ActiveModel::Naming

  def initialize(id:, content:) = (@id, @content = id, content)
  def render_in(...) = @content
  def to_key = [@id]
end

component = Component.new(id: 1, content: "Hello, world")

turbo_stream.update(component) # => <turbo-stream action="update" target="component_1"><template>Hello, world</template></turbo-stream>
```
  • Loading branch information
seanpdoyle committed Sep 16, 2024
1 parent 33019f3 commit 5038ed8
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 0 deletions.
2 changes: 2 additions & 0 deletions app/models/turbo/streams/tag_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ def action_all(name, targets, content = nil, method: nil, allow_inferred_renderi
private
def render_template(target, content = nil, allow_inferred_rendering: true, **rendering, &block)
case
when target.respond_to?(:render_in) && content.nil?
target.render_in(@view_context, &block)
when content.respond_to?(:render_in)
content.render_in(@view_context, &block)
when content
Expand Down
32 changes: 32 additions & 0 deletions test/streams/streams_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,32 @@
class TestChannel < ApplicationCable::Channel; end

class Turbo::StreamsHelperTest < ActionView::TestCase
class Component
extend ActiveModel::Naming

def initialize(id:, content:) = (@id, @content = id, content)
def render_in(...) = @content
def to_key = [@id]
end

attr_accessor :formats

test "supports valid :renderable option object with nil content" do
component = Component.new(id: 1, content: "Hello, world")

assert_dom_equal <<~HTML.strip, turbo_stream.update(component)
<turbo-stream action="update" target="streams_helper_test_component_1"><template>Hello, world</template></turbo-stream>
HTML
end

test "supports valid :renderable option object with content" do
component = Component.new(id: 1, content: "Hello, world")

assert_dom_equal <<~HTML.strip, turbo_stream.update(component, "Raw content")
<turbo-stream action="update" target="streams_helper_test_component_1"><template>Raw content</template></turbo-stream>
HTML
end

test "with streamable" do
assert_dom_equal \
%(<turbo-cable-stream-source channel="Turbo::StreamsChannel" signed-stream-name="#{Turbo::StreamsChannel.signed_stream_name("messages")}"></turbo-cable-stream-source>),
Expand Down Expand Up @@ -75,4 +99,12 @@ class Turbo::StreamsHelperTest < ActionView::TestCase
<turbo-stream action="highlight" targets=".a-selector"><template></template></turbo-stream>
HTML
end

test "supports valid :partial option objects" do
message = Message.new(id: 1, content: "Hello, world")

assert_dom_equal <<~HTML.strip, turbo_stream.update(message)
<turbo-stream action="update" target="message_1"><template><p>Hello, world</p></template></turbo-stream>
HTML
end
end

0 comments on commit 5038ed8

Please sign in to comment.