Skip to content

Commit

Permalink
Introduce RenderHelper to reuse StreamHelpers in model broadcasts (
Browse files Browse the repository at this point in the history
…#43)

* Introduce `TurboPower::RenderHelper` to reuse `StreamHelpers` in model broadcasts

* Update dummy app
  • Loading branch information
marcoroth authored Jun 7, 2024
1 parent 86a65ee commit 67cd84c
Show file tree
Hide file tree
Showing 18 changed files with 168 additions and 36 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
push:
pull_request:

env:
RAILS_ENV: test

jobs:
build:
runs-on: ubuntu-latest
Expand All @@ -26,5 +29,9 @@ jobs:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true

- name: Setup database
run: bundle exec rake db:create db:migrate
working-directory: test/dummy

- name: Run tests
run: bundle exec rake test
5 changes: 3 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ gem "rake", "~> 13.0"
gem "rubocop", "~> 1.21"

group :test do
gem "sprockets-rails"
gem "sqlite3"
gem "rails"
gem "simplecov"
gem "sprockets-rails"
gem "sqlite3", "~> 1.3"
end
1 change: 1 addition & 0 deletions lib/turbo_power.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require_relative "turbo_power/engine"
require_relative "turbo_power/attribute_transformations"
require_relative "turbo_power/stream_helper"
require_relative "turbo_power/render_helper"
require_relative "turbo_power/broadcasts"
require_relative "turbo_power/broadcastable"

Expand Down
2 changes: 1 addition & 1 deletion lib/turbo_power/broadcasts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module Broadcasts
def custom_broadcast_action_to(*streamables, action:, **attributes)
broadcast_stream_to(
*streamables,
content: turbo_stream_action_tag(action, **attributes)
content: RenderHelper.public_send(action, **attributes)
)
end

Expand Down
28 changes: 28 additions & 0 deletions lib/turbo_power/render_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module TurboPower
class RenderHelper
extend ActionView::Helpers::TagHelper
extend TurboPower::StreamHelper

def self.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)
tag.turbo_stream(template, **attributes, action: action, target: target)
elsif targets = convert_to_turbo_stream_dom_id(targets, include_selector: true)
tag.turbo_stream(template, **attributes, action: action, targets: targets)
else
tag.turbo_stream(template, **attributes, action: action)
end
end

private

def self.convert_to_turbo_stream_dom_id(target, include_selector: false)
if Array(target).any? { |value| value.respond_to?(:to_key) }
"#{"#" if include_selector}#{ActionView::RecordIdentifier.dom_id(*target)}"
else
target
end
end
end
end
5 changes: 5 additions & 0 deletions test/dummy/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.log
*.sqlite3
*.sqlite3-shm
*.sqlite3-wal
tmp/*
8 changes: 8 additions & 0 deletions test/dummy/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require_relative "config/application"

Rails.application.load_tasks
6 changes: 6 additions & 0 deletions test/dummy/app/channels/application_cable/channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end
6 changes: 6 additions & 0 deletions test/dummy/app/channels/application_cable/connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

module ApplicationCable
class Connection < ActionCable::Connection::Base
end
end
4 changes: 4 additions & 0 deletions test/dummy/app/models/message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# frozen_string_literal: true

class Message < ActiveRecord::Base
end
10 changes: 5 additions & 5 deletions test/dummy/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
require_relative "boot"

require "action_controller/railtie"
# require "action_cable/engine"
require "action_cable/engine"
# require "active_job/railtie"
# require "active_model/railtie"
# require "active_record/railtie"
require "sprockets/railtie"
require "active_model/railtie"
require "active_record/railtie"
# require "sprockets/railtie"

Bundler.require(*Rails.groups)

module Dummy
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
config.load_defaults 7.1

# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
Expand Down
10 changes: 10 additions & 0 deletions test/dummy/config/cable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
development:
adapter: async

test:
adapter: inline

production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: dummy_production
13 changes: 11 additions & 2 deletions test/dummy/config/database.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000

test:
adapter: sqlite3
database: db/combustion_test.sqlite
<<: *default
database: db/test.sqlite3

development:
<<: *default
database: db/development.sqlite3
11 changes: 11 additions & 0 deletions test/dummy/db/migrate/20240606204215_create_messages.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class CreateMessages < ActiveRecord::Migration[6.1]
def change
create_table :messages do |t|
t.text :content

t.timestamps
end
end
end
22 changes: 18 additions & 4 deletions test/dummy/db/schema.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# frozen_string_literal: true
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2024_06_06_204215) do
create_table "messages", force: :cascade do |t|
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

ActiveRecord::Schema.define do
# Set up any tables you need to exist for your test suite that don't belong
# in migrations.
end
3 changes: 2 additions & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Configure Rails Environment
ENV["RAILS_ENV"] = "test"

require 'simplecov'
require "simplecov"

SimpleCov.start do
add_filter "/test/"
Expand All @@ -12,6 +12,7 @@
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)

require_relative "../test/dummy/config/environment"
ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)]
require "rails/test_help"

require "turbo_power"
Expand Down
41 changes: 41 additions & 0 deletions test/turbo_power/broadcastable_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

require "test_helper"

module TurboPower
class BroadcastableTest < ActionCable::Channel::TestCase
include ::Turbo::Broadcastable::TestHelper

test "broadcast_dispatch_event" do
message = Message.new(id: 1)

detail = { id: message.id, action: "save" }
message.broadcast_dispatch_event(target: dom_id(message), name: "saved", detail: detail)

streams = capture_turbo_stream_broadcasts(message)

assert_equal 1, streams.length
assert_equal "message_1", streams.first["target"]
assert_equal "dispatch_event", streams.first["action"]

template_content = detail.to_json
assert_equal template_content, streams.first.at_css("template").content
end

test "broadcast_dispatch_event_to" do
message = Message.new(id: 1)

detail = { id: message.id, action: "save" }
message.broadcast_dispatch_event_to("messages", target: dom_id(message), name: "saved", detail: detail)

streams = capture_turbo_stream_broadcasts("messages")

assert_equal 1, streams.length
assert_equal "message_1", streams.first["target"]
assert_equal "dispatch_event", streams.first["action"]

template_content = detail.to_json
assert_equal template_content, streams.first.at_css("template").content
end
end
end
22 changes: 1 addition & 21 deletions test/turbo_stream_helper.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,7 @@
# frozen_string_literal: true

module TurboStreamHelpers
class ViewContext
private

def view_methods
ActionView::Base.instance_methods - Object.instance_methods
end

def method_missing(name, *params, &block)
view_methods.include?(name) ? nil : super
end

def respond_to_missing?(name, *_params, &_block)
view_methods.include?(name)
end
end

def view_context
ViewContext.new
end

def turbo_stream
Turbo::Streams::TagBuilder.new(view_context)
TurboPower::RenderHelper
end
end

0 comments on commit 67cd84c

Please sign in to comment.