Skip to content

Commit

Permalink
Integration with Turbo::Broadcastable
Browse files Browse the repository at this point in the history
Overriding defaults for Turbo broadcast jobs allows one to get decorated
objects in model partials by default.

Resolves #910.
Requires #928.
  • Loading branch information
Alexander-Senko committed Sep 3, 2024
1 parent 4d06805 commit a1439ff
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 2 deletions.
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ platforms :jruby do
gem "activerecord-jdbcsqlite3-adapter"
end

if RUBY_VERSION >= "2.6.0"
gem "turbo-rails"
gem "redis", "~> 4.0"
end

if RUBY_VERSION >= "2.5.0"
gem "rails", "~> 6.0"
gem 'webrick'
Expand Down
24 changes: 24 additions & 0 deletions lib/draper/compatibility/broadcastable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

module Draper
module Compatibility
# It would look consistent to use decorated objects inside templates broadcasted with
# Turbo::Broadcastable.
#
# This compatibility patch fixes the issue by overriding the original defaults to decorate the
# object, that's passed to the partial in a local variable.
module Broadcastable
private

def broadcast_rendering_with_defaults(options)
return super unless decorator_class?

# Add the decorated current instance into the locals (see original method for details).
options[:locals] =
(options[:locals] || {}).reverse_merge!(model_name.element.to_sym => decorate)

super
end
end
end
end
7 changes: 5 additions & 2 deletions lib/draper/decoratable.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'draper/decoratable/equality'
require 'draper/compatibility/broadcastable'

module Draper
# Provides shortcuts to decorate objects directly, so you can do
Expand All @@ -11,6 +12,10 @@ module Decoratable
extend ActiveSupport::Concern
include Draper::Decoratable::Equality

included do
prepend Draper::Compatibility::Broadcastable if defined? Turbo::Broadcastable
end

# Decorates the object using the inferred {#decorator_class}.
# @param [Hash] options
# see {Decorator#initialize}
Expand Down Expand Up @@ -87,8 +92,6 @@ def decorator_class(called_on = self)
def ===(other)
super || (other.is_a?(Draper::Decorator) && super(other.object))
end

end

end
end
4 changes: 4 additions & 0 deletions spec/dummy/app/models/post.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
require 'turbo/broadcastable' if defined? Turbo::Broadcastable # HACK: looks weird, but works

class Post < ApplicationRecord
# attr_accessible :title, :body

broadcasts if defined? Turbo::Broadcastable
end
1 change: 1 addition & 0 deletions spec/dummy/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def attempt_require(file)
require 'draper'
attempt_require 'mongoid'
attempt_require 'devise'
attempt_require 'turbo-rails'
require 'active_model_serializers'

module Dummy
Expand Down
8 changes: 8 additions & 0 deletions spec/dummy/config/cable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# production:
# url: redis://redis.example.com:6379

local: &local
url: redis://localhost:6379

development: *local
test: *local
13 changes: 13 additions & 0 deletions spec/dummy/spec/models/post_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,17 @@
it_behaves_like 'a decoratable model'

it { should be_a ApplicationRecord }

describe 'broadcasts' do
let(:modification) { described_class.create! }

it 'passes a decorated object for rendering' do
expect do
modification
end.to have_enqueued_job(Turbo::Streams::ActionBroadcastJob).with { |stream, action:, target:, **rendering|
expect(rendering[:locals]).to include :post
expect(rendering[:locals][:post]).to be_decorated
}
end
end if defined? Turbo::Broadcastable
end

0 comments on commit a1439ff

Please sign in to comment.