Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: hotwired/turbo-rails
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.0.12
Choose a base ref
...
head repository: hotwired/turbo-rails
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.0.13
Choose a head ref
  • 4 commits
  • 18 files changed
  • 3 contributors

Commits on Mar 2, 2025

  1. Drop Support for ruby@2.x.x (#698)

    Commit to requiring `ruby@3.1`
    
    Remove `ruby@2.6`, `ruby@2.7`, `ruby@3.0`, `rails@6.1`, and `rails@7.0`
    from the CI matrix, along with any `RUBY_VERSION < "3"` conditionals.
    
    The implementation changes include replace older syntaxes with newer
    variants. For example, utilize `...` in place of `*args, &block`, rely
    on `**`-ing attribute `Hash` arguments, etc.
    seanpdoyle authored Mar 2, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    primeos Michael Weiss
    Copy the full SHA
    3fcec46 View commit details
  2. turbo_stream tag builder: support :partial with block (#701)

    The Background
    ---
    
    Consider an application with a shared `application/flash` partial that
    accepts its message as a partial-local assignment:
    
    ```erb
    <%# app/views/application/_flash.html.erb %>
    
    <p role="alert"><%= message %></p>
    ```
    
    Also consider an `application/layout.turbo_stream.erb` template for
    appending the `Flash` messages to a element with `[id="flashes"]`:
    
    ```erb
    <%# app/views/layouts/application.turbo_stream.erb %>
    
    <% flash.each do |name, message| %>
      <%= turbo_stream.append "flashes", partial: "application/flash", locals: { message: message } %>
    <% end %>
    ```
    
    This works as you'd expect, since the `:partial` and `:locals` keyword
    arguments are forwarded along to the underlying `render` call.
    
    The Scenario
    ---
    
    Now consider that the `application/flash` message changed its interface
    to expect the `message` as block content yielded to the partial instead
    of an assignment to the `:locals` options:
    
    ```diff
     <%# app/views/application/_flash.html.erb %>
    
    -<p role="alert"><%= message %></p>
    +<p role="alert"><%= yield %></p>
    ```
    
    The `layouts/application.turbo_stream.erb` template would need to change
    as well:
    
    ```diff
     <%# app/views/layouts/application.turbo_stream.erb %>
    
     <% flash.each do |name, message| %>
    -  <%= turbo_stream.append "flashes", partial: "application/flash", locals: { message: message } %>
    +  <%= turbo_stream.append "flashes", partial: "application/flash" do %>
    +    <span style="color: red"><%= message %></span>
    +  <%= end %>
     <% end %>
    ```
    
    The Problem
    ---
    
    This style of invocation of `turbo_stream.append` does not work the same
    as if it were passed a block of content generated by calling `render`
    with the same keywords.
    
    The presence of a `&block` argument triggers an entirely separate code
    path than the presence of the `**rendering` keywords.
    
    To work around this issue, you'd have to capture the rendering
    separately:
    
    ```diff
     <%# app/views/layouts/application.turbo_stream.erb %>
    
     <% flash.each do |name, message| %>
    -  <%= turbo_stream.append "flashes", partial: "application/flash", locals: { message: message } %>
    +  <% content = capture do %>
    +    <%= render partial: "application/flash" do %>
    +      <span style="color: red"><%= message %></span>
    +    <% end %>
    +  <% end %>
    +
    +  <%= turbo_stream.append "flashes", content %>
     <% end %>
    ```
    
    The Proposal
    ---
    
    This commit alters the tag builder's decision making process to
    incorporate a check for a combination of both a `&block` and `:partial`
    or `:layout` keyword arguments.
    seanpdoyle authored Mar 2, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    primeos Michael Weiss
    Copy the full SHA
    80bb910 View commit details

Commits on Mar 3, 2025

  1. Revert "Keep flash in Turbo Frame requests (#699)" (#716)

    This reverts commit 0e42702.
    jorgemanrubia authored Mar 3, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    primeos Michael Weiss
    Copy the full SHA
    34fdefe View commit details
  2. Bump version

    jorgemanrubia committed Mar 3, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    primeos Michael Weiss
    Copy the full SHA
    77f3604 View commit details
9 changes: 3 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -5,17 +5,15 @@ jobs:
strategy:
fail-fast: false
matrix:
rails: [ "6.1", "7.0", "7.1", "7.2" ]
ruby: [ "2.7", "3.0", "3.1", "3.2", "3.3" ]
rails: [ "7.1", "7.2", "8.0" ]
ruby: [ "3.1", "3.2", "3.3", "3.4" ]
allow-fail: [ false ]
include:
- { ruby: "2.6", rails: "6.1" }
- { ruby: "3.3", rails: "main", allow-fail: true }
- { ruby: "3.2", rails: "main", allow-fail: true }
- { ruby: "head", rails: "main", allow-fail: true }
exclude:
- { ruby: "2.7", rails: "7.2" }
- { ruby: "3.0", rails: "7.2" }
- { ruby: "3.1", rails: "8.0" }

env:
FERRUM_PROCESS_TIMEOUT: 25
@@ -37,7 +35,6 @@ jobs:

- name: Run Bug Template Tests
run: ruby bug_report_template.rb || ruby bug_report_template.rb
continue-on-error: ${{ startsWith(matrix.ruby, '2') || false }}

- name: Run tests
id: test
16 changes: 3 additions & 13 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -15,21 +15,11 @@ gem "sprockets-rails"

gem 'rake'
gem 'byebug'

if RUBY_VERSION < "3"
gem "rack", "< 3"
gem "puma", "< 6"
else
gem "rack"
gem "puma"
end
gem 'puma'
gem 'rack'

group :development, :test do
if rails_version == "6.1"
gem "importmap-rails", "0.6.1"
else
gem "importmap-rails"
end
gem 'importmap-rails'
end

group :test do
2 changes: 0 additions & 2 deletions app/channels/turbo/streams/broadcasts.rb
Original file line number Diff line number Diff line change
@@ -38,8 +38,6 @@ def broadcast_refresh_to(*streamables, **opts)
end

def broadcast_action_to(*streamables, action:, target: nil, targets: nil, attributes: {}, **rendering)
attributes.deep_symbolize_keys! if RUBY_VERSION < "3"

broadcast_stream_to(*streamables, content: turbo_stream_action_tag(
action, target: target, targets: targets, template: render_broadcast_action(rendering), **attributes)
)
1 change: 0 additions & 1 deletion app/controllers/turbo/frames/frame_request.rb
Original file line number Diff line number Diff line change
@@ -23,7 +23,6 @@ module Turbo::Frames::FrameRequest
included do
layout -> { "turbo_rails/frame" if turbo_frame_request? }
etag { :frame if turbo_frame_request? }
before_action { flash.keep if turbo_frame_request? }

helper_method :turbo_frame_request?, :turbo_frame_request_id
end
9 changes: 2 additions & 7 deletions app/helpers/turbo/streams/action_helper.rb
Original file line number Diff line number Diff line change
@@ -22,12 +22,7 @@ module Turbo::Streams::ActionHelper
# message = Message.find(1)
# turbo_stream_action_tag "remove", target: [message, :special]
# # => <turbo-stream action="remove" target="special_message_1"></turbo-stream>
def turbo_stream_action_tag(action, attributes = {})
attributes.deep_symbolize_keys! if RUBY_VERSION < "3"

target = attributes.delete(:target)
targets = attributes.delete(:targets)
template = attributes.delete(:template)
def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil, **attributes)
template = action.to_sym.in?(%i[ remove refresh ]) ? "" : tag.template(template.to_s.html_safe)

if target = convert_to_turbo_stream_dom_id(target)
@@ -44,7 +39,7 @@ def turbo_stream_action_tag(action, attributes = {})
# turbo_stream_refresh_tag
# # => <turbo-stream action="refresh"></turbo-stream>
def turbo_stream_refresh_tag(request_id: Turbo.current_request_id, **attributes)
turbo_stream_action_tag(:refresh, attributes.with_defaults({ "request-id": request_id }.compact))
turbo_stream_action_tag(:refresh, "request-id": request_id.presence, **attributes)
end

private
2 changes: 1 addition & 1 deletion app/models/concerns/turbo/broadcastable.rb
Original file line number Diff line number Diff line change
@@ -508,7 +508,7 @@ def broadcast_target_default
self.class.broadcast_target_default
end

def extract_options_and_add_target(rendering, target: broadcast_target_default)
def extract_options_and_add_target(rendering = {}, target: broadcast_target_default)
broadcast_rendering_with_defaults(rendering).tap do |options|
options[:target] = target if !options.key?(:target) && !options.key?(:targets)
end
6 changes: 4 additions & 2 deletions app/models/turbo/streams/tag_builder.rb
Original file line number Diff line number Diff line change
@@ -240,8 +240,8 @@ def prepend_all(targets, content = nil, **rendering, &block)
#
# turbo_stream.refresh request_id: "abc123"
# # => <turbo-stream action="refresh" request-id="abc123"></turbo-stream>
def refresh(**options)
turbo_stream_refresh_tag(**options)
def refresh(...)
turbo_stream_refresh_tag(...)
end

# Send an action of the type <tt>name</tt> to <tt>target</tt>. Options described in the concrete methods.
@@ -267,6 +267,8 @@ def render_template(target, content = nil, allow_inferred_rendering: true, **ren
content.render_in(@view_context, &block)
when content
allow_inferred_rendering ? (render_record(content) || content) : content
when block_given? && (rendering.key?(:partial) || rendering.key?(:layout))
@view_context.render(formats: [ :html ], layout: rendering[:partial], **rendering, &block)
when block_given?
@view_context.capture(&block)
when rendering.any?
2 changes: 1 addition & 1 deletion bug_report_template.rb
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
gem "rails"
gem "propshaft"
gem "puma"
gem "sqlite3", "~> 1.4"
gem "sqlite3"
gem "turbo-rails"

gem "capybara"
2 changes: 1 addition & 1 deletion lib/turbo/engine.rb
Original file line number Diff line number Diff line change
@@ -164,7 +164,7 @@ class TurboStreamEncoder < IdentityEncoder
ActiveSupport.on_load(:action_dispatch_system_test_case) do
app.config.turbo.test_connect_after_actions.map do |method|
class_eval <<~RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block) # def visit(*args, &block)
def #{method}(...) # def visit(...)
super.tap { connect_turbo_cable_stream_sources } # super.tap { connect_turbo_cable_stream_sources }
end # end
RUBY
8 changes: 4 additions & 4 deletions lib/turbo/system_test_helper.rb
Original file line number Diff line number Diff line change
@@ -51,8 +51,8 @@ def connect_turbo_cable_stream_sources(**options, &block)
#
# In addition to the filters listed above, accepts any valid Capybara global
# filter option.
def assert_turbo_cable_stream_source(*args, &block)
assert_selector(:turbo_cable_stream_source, *args, &block)
def assert_turbo_cable_stream_source(...)
assert_selector(:turbo_cable_stream_source, ...)
end

# Asserts that a `<turbo-cable-stream-source>` element is absent from the
@@ -75,8 +75,8 @@ def assert_turbo_cable_stream_source(*args, &block)
#
# In addition to the filters listed above, accepts any valid Capybara global
# filter option.
def assert_no_turbo_cable_stream_source(*args, &block)
assert_no_selector(:turbo_cable_stream_source, *args, &block)
def assert_no_turbo_cable_stream_source(...)
assert_no_selector(:turbo_cable_stream_source, ...)
end

Capybara.add_selector :turbo_cable_stream_source do
2 changes: 1 addition & 1 deletion lib/turbo/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Turbo
VERSION = "2.0.12"
VERSION = "2.0.13"
end
2 changes: 1 addition & 1 deletion test/dummy/app/controllers/messages_controller.rb
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ def section

def create
respond_to do |format|
format.html { redirect_to message_url(id: 1), notice: "Message was successfully created." }
format.html { redirect_to message_url(id: 1) }
format.turbo_stream { render turbo_stream: turbo_stream.append(:messages, "message_1"), status: :created }
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p><%= yield %></p>
1 change: 0 additions & 1 deletion test/dummy/app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@
</head>

<body class="<%= "turbo-native" if turbo_native_app? %>">
<%= flash[:notice] %>
<%= yield %>
</body>
</html>
16 changes: 0 additions & 16 deletions test/frames/frame_request_controller_test.rb
Original file line number Diff line number Diff line change
@@ -35,22 +35,6 @@ class Turbo::FrameRequestControllerTest < ActionDispatch::IntegrationTest
assert_not_equal etag_with_frame, etag_without_frame
end

test "frame requests keep the flash" do
message = Message.create!

post messages_path
assert_equal @request.flash[:notice], 'Message was successfully created.'

get messages_path, headers: { "Turbo-Frame" => "true" }
assert_equal @request.flash[:notice], 'Message was successfully created.'

get messages_path
assert_equal @request.flash[:notice], 'Message was successfully created.'

get messages_path
assert_nil @request.flash[:notice]
end

test "turbo_frame_request_id returns the Turbo-Frame header value" do
turbo_frame_request_id = "test_frame_id"

42 changes: 42 additions & 0 deletions test/streams/streams_helper_test.rb
Original file line number Diff line number Diff line change
@@ -19,6 +19,48 @@ def to_key

attr_accessor :formats

test "turbo_stream builder captures block when called without :partial keyword" do
rendered = turbo_stream.update "target_id" do
tag.span "Hello, world"
end

assert_dom_equal <<~HTML.strip, rendered
<turbo-stream action="update" target="target_id">
<template>
<span>Hello, world</span>
</template>
</turbo-stream>
HTML
end

test "turbo_stream builder forwards block to partial when called with :partial keyword" do
rendered = turbo_stream.update "target_id", partial: "application/partial_with_block" do
"Hello, from application/partial_with_block partial"
end

assert_dom_equal <<~HTML.strip, rendered
<turbo-stream action="update" target="target_id">
<template>
<p>Hello, from application/partial_with_block partial</p>
</template>
</turbo-stream>
HTML
end

test "turbo_stream builder forwards block to partial when called with :layout keyword" do
rendered = turbo_stream.update "target_id", layout: "application/partial_with_block" do
"Hello, from application/partial_with_block partial"
end

assert_dom_equal <<~HTML.strip, rendered
<turbo-stream action="update" target="target_id">
<template>
<p>Hello, from application/partial_with_block partial</p>
</template>
</turbo-stream>
HTML
end

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

4 changes: 1 addition & 3 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -8,9 +8,7 @@
ActionCable.server.config.logger = Logger.new(STDOUT) if ENV["VERBOSE"]

module ActionViewTestCaseExtensions
def render(*args, &block)
ApplicationController.renderer.render(*args, &block)
end
delegate :render, to: ApplicationController
end

class ActiveSupport::TestCase
6 changes: 3 additions & 3 deletions turbo-rails.gemspec
Original file line number Diff line number Diff line change
@@ -9,10 +9,10 @@ Gem::Specification.new do |s|
s.homepage = "https://github.com/hotwired/turbo-rails"
s.license = "MIT"

s.required_ruby_version = ">= 2.6.0"
s.required_ruby_version = ">= 3.1"

s.add_dependency "actionpack", ">= 6.0.0"
s.add_dependency "railties", ">= 6.0.0"
s.add_dependency "actionpack", ">= 7.1.0"
s.add_dependency "railties", ">= 7.1.0"

s.files = Dir["{app,config,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]