Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/channels/stimulus_reflex/channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ def receive(data)
private

def delegate_call_to_reflex(reflex)
if reflex.cable_ready
reflex.element.cable_ready = reflex.cable_ready if reflex.element
reflex.controller_element.cable_ready = reflex.cable_ready if reflex.controller_element
end

method_name = reflex_data.method_name
arguments = reflex_data.arguments
method = reflex.method(method_name)
Expand Down
25 changes: 23 additions & 2 deletions lib/stimulus_reflex/element.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
class StimulusReflex::Element < OpenStruct
include StimulusReflex::AttributeBuilder

attr_reader :attrs, :dataset
attr_reader :attrs, :dataset, :selector, :cable_ready
attr_writer :cable_ready

alias_method :data_attributes, :dataset

delegate :signed, :unsigned, :numeric, :boolean, :data_attrs, to: :dataset
delegate :broadcast, to: :cable_ready

def initialize(data = {}, selector: nil)
@selector = selector

def initialize(data = {})
@attrs = HashWithIndifferentAccess.new(data["attrs"] || {})
@dataset = StimulusReflex::Dataset.new(data)

Expand All @@ -29,4 +33,21 @@ def to_dom_id

"##{id}"
end

def method_missing(method_name, *arguments, &block)
if cable_ready.respond_to?(method_name)
xpath = selector ? selector.starts_with?("//") : false
args = arguments.first.to_h.reverse_merge(selector: selector, xpath: xpath)

cable_ready.send(method_name.to_sym, args)

cable_ready
else
super
end
end

def respond_to_missing?(method_name, include_private = false)
cable_ready.respond_to?(method_name) || super
end
end
6 changes: 4 additions & 2 deletions lib/stimulus_reflex/reflex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ class StimulusReflex::Reflex
class VersionMismatchError < StandardError; end

prepend StimulusReflex::CableReadiness

include ActiveSupport::Rescuable
include StimulusReflex::Callbacks
include ActionView::Helpers::TagHelper
include CableReady::Identifiable

attr_accessor :payload, :headers
attr_reader :channel, :url, :element, :selectors, :method_name, :broadcaster, :client_attributes, :logger
attr_reader :channel, :url, :element, :controller_element, :selectors, :method_name, :broadcaster, :client_attributes, :logger

alias_method :action_name, :method_name # for compatibility with controller libraries like Pundit that expect an action name

Expand All @@ -25,10 +26,11 @@ class VersionMismatchError < StandardError; end
# TODO remove xpath_controller and xpath_element for v4
delegate :id, :tab_id, :reflex_controller, :xpath_controller, :xpath_element, :permanent_attribute_name, :version, :suppress_logging, to: :client_attributes

def initialize(channel, url: nil, element: nil, selectors: [], method_name: nil, params: {}, client_attributes: {})
def initialize(channel, url: nil, element: nil, controller_element: nil, selectors: [], method_name: nil, params: {}, client_attributes: {})
@channel = channel
@url = url
@element = element
@controller_element = controller_element
@selectors = selectors
@method_name = method_name
@params = params
Expand Down
6 changes: 5 additions & 1 deletion lib/stimulus_reflex/reflex_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ def url
end

def element
StimulusReflex::Element.new(data)
@element ||= StimulusReflex::Element.new(data, selector: xpath_element)
end

def controller_element
@controller_element ||= StimulusReflex::Element.new(data, selector: xpath_controller)
end

def permanent_attribute_name
Expand Down
1 change: 1 addition & 0 deletions lib/stimulus_reflex/reflex_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def create_reflex_from_data(channel, reflex_data)
reflex_class.new(channel,
url: reflex_data.url,
element: reflex_data.element,
controller_element: reflex_data.controller_element,
selectors: reflex_data.selectors,
method_name: reflex_data.method_name,
params: reflex_data.params,
Expand Down
24 changes: 0 additions & 24 deletions test/broadcasters/broadcaster_test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,6 @@
class StimulusReflex::BroadcasterTestCase < ActionCable::Channel::TestCase
tests StimulusReflex::Channel

def assert_broadcast_on(stream, data, &block)
serialized_msg = ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(data))

new_messages = broadcasts(stream)
if block
old_messages = new_messages
clear_messages(stream)

yield
new_messages = broadcasts(stream)
clear_messages(stream)

(old_messages + new_messages).each { |m| pubsub_adapter.broadcast(stream, m) }
end

message = new_messages.find { |msg| ActiveSupport::JSON.decode(msg) == serialized_msg }

unless message
puts "\n\nActual: #{ActiveSupport::JSON.decode(new_messages.first)}\n\nExpected: #{data}\n\n"
end

assert message, "No messages sent with #{data} to #{stream}"
end

setup do
stub_connection(session_id: SecureRandom.uuid)
def connection.env
Expand Down
61 changes: 61 additions & 0 deletions test/reflex_element_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

require_relative "test_helper"
require "mocha/minitest"

class StimulusReflex::ReflexElementTest < ActionCable::Channel::TestCase
tests StimulusReflex::Channel

setup do
stub_connection(session_id: SecureRandom.uuid)
def connection.env
@env ||= {}
end

@element = StimulusReflex::Element.new({}, selector: "#element-selector")

@reflex = StimulusReflex::Reflex.new(subscribe, url: "https://test.stimulusreflex.com", client_attributes: {id: "666", version: StimulusReflex::VERSION})
@cable_ready = StimulusReflex::CableReadyChannels.new(@reflex)
@element.cable_ready = @cable_ready
end

def build_payload(operations = [])
{
"cableReady" => true,
"operations" => Array.wrap(operations),
"version" => CableReady::VERSION
}
end

test "broadcasts updates using element.broadcast" do
expected = build_payload(
{"selector" => "#element-selector", "xpath" => false, "html" => "<p>Some HTML</p>", "reflexId" => "666", "operation" => "innerHtml"}
)

assert_broadcast_on(@reflex.stream_name, expected) do
@element.inner_html(html: "<p>Some HTML</p>")
@element.broadcast
end
end

test "selector can be overwritten" do
expected = build_payload(
{"selector" => "#overwritten", "xpath" => false, "html" => "<p>Some HTML</p>", "reflexId" => "666", "operation" => "innerHtml"}
)

assert_broadcast_on(@reflex.stream_name, expected) do
@element.inner_html(html: "<p>Some HTML</p>", selector: "#overwritten").broadcast
end
end

test "broadcasts using element.broadcast chained" do
expected = build_payload [
{"selector" => "#element-selector", "xpath" => false, "html" => "<p>Some HTML</p>", "reflexId" => "666", "operation" => "innerHtml"},
{"name" => "abc", "detail" => {"some" => "key"}, "reflexId" => "666", "selector" => "#element-selector", "operation" => "dispatchEvent"}
]

assert_broadcast_on(@reflex.stream_name, expected) do
@element.inner_html(html: "<p>Some HTML</p>").dispatch_event(name: "abc", detail: {some: "key"}).broadcast
end
end
end
24 changes: 24 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,30 @@ def connection_gid(ids)
end
end

def assert_broadcast_on(stream, data, &block)
serialized_msg = ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(data))

new_messages = broadcasts(stream)
if block
old_messages = new_messages
clear_messages(stream)

yield
new_messages = broadcasts(stream)
clear_messages(stream)

(old_messages + new_messages).each { |m| pubsub_adapter.broadcast(stream, m) }
end

message = new_messages.find { |msg| ActiveSupport::JSON.decode(msg) == serialized_msg }

unless message
puts "\n\nActual: #{ActiveSupport::JSON.decode(new_messages.first)}\n\nExpected: #{data}\n\n"
end

assert message, "No messages sent with #{data} to #{stream}"
end

StimulusReflex.configuration.parent_channel = "ActionCable::Channel::Base"
ActionCable::Server::Base.config.cable = {adapter: "test"}
ActionCable::Server::Base.config.logger = Logger.new(nil)
Expand Down