lib/stimulus_reflex/reflex.rb in stimulus_reflex-3.4.2 vs lib/stimulus_reflex/reflex.rb in stimulus_reflex-3.5.0.pre0
- old
+ new
@@ -1,22 +1,24 @@
# frozen_string_literal: true
-ClientAttributes = Struct.new(:reflex_id, :reflex_controller, :xpath_controller, :xpath_element, :permanent_attribute_name, keyword_init: true)
+ClientAttributes = Struct.new(:reflex_id, :tab_id, :reflex_controller, :xpath_controller, :xpath_element, :permanent_attribute_name, keyword_init: true)
class StimulusReflex::Reflex
include ActiveSupport::Rescuable
include StimulusReflex::Callbacks
+ include ActionView::Helpers::TagHelper
+ include CableReady::Identifiable
+ attr_accessor :payload
attr_reader :cable_ready, :channel, :url, :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
delegate :connection, :stream_name, to: :channel
delegate :controller_class, :flash, :session, to: :request
delegate :broadcast, :broadcast_message, to: :broadcaster
- delegate :reflex_id, :reflex_controller, :xpath_controller, :xpath_element, :permanent_attribute_name, to: :client_attributes
- delegate :render, to: :controller_class
+ delegate :reflex_id, :tab_id, :reflex_controller, :xpath_controller, :xpath_element, :permanent_attribute_name, to: :client_attributes
def initialize(channel, url: nil, element: nil, selectors: [], method_name: nil, params: {}, client_attributes: {})
if is_a? CableReady::Broadcaster
message = <<~MSG
@@ -35,11 +37,12 @@
@method_name = method_name
@params = params
@broadcaster = StimulusReflex::PageBroadcaster.new(self)
@logger = StimulusReflex::Logger.new(self)
@client_attributes = ClientAttributes.new(client_attributes)
- @cable_ready = StimulusReflex::CableReadyChannels.new(stream_name)
+ @cable_ready = StimulusReflex::CableReadyChannels.new(stream_name, reflex_id)
+ @payload = {}
self.params
end
def request
@request ||= begin
@@ -68,21 +71,19 @@
stack.call(env)
end
req = ActionDispatch::Request.new(env)
- path_params = Rails.application.routes.recognize_path_with_request(req, url, req.env[:extras] || {})
- path_params[:controller] = path_params[:controller].force_encoding("UTF-8")
- path_params[:action] = path_params[:action].force_encoding("UTF-8")
+ # fetch path params (controller, action, ...) and apply them
+ request_params = StimulusReflex::RequestParameters.new(params: @params, req: req, url: url)
+ req = request_params.apply!
- req.env.merge(ActionDispatch::Http::Parameters::PARAMETERS_KEY => path_params)
- req.env["action_dispatch.request.parameters"] = req.parameters.merge(@params)
- req.tap { |r| r.session.send :load! }
+ req
end
end
- def morph(selectors, html = "")
+ def morph(selectors, html = nil)
case selectors
when :page
raise StandardError.new("Cannot call :page morph after :#{broadcaster.to_sym} morph") unless broadcaster.page?
when :nothing
raise StandardError.new("Cannot call :nothing morph after :selector morph") if broadcaster.selector?
@@ -93,20 +94,29 @@
broadcaster.append_morph(selectors, html)
end
end
def controller
- @controller ||= begin
- controller_class.new.tap do |c|
- c.instance_variable_set :"@stimulus_reflex", true
- instance_variables.each { |name| c.instance_variable_set name, instance_variable_get(name) }
- c.set_request! request
- c.set_response! controller_class.make_response!(request)
- end
+ @controller ||= controller_class.new.tap do |c|
+ c.instance_variable_set :@stimulus_reflex, true
+ c.set_request! request
+ c.set_response! controller_class.make_response!(request)
end
+
+ instance_variables.each { |name| @controller.instance_variable_set name, instance_variable_get(name) }
+ @controller
end
+ def controller?
+ !!defined? @controller
+ end
+
+ def render(*args)
+ controller_class.renderer.new(connection.env.merge("SCRIPT_NAME" => "")).render(*args)
+ end
+
+ # Invoke the reflex action specified by `name` and run all callbacks
def process(name, *args)
reflex_invoked = false
result = run_callbacks(:process) {
public_send(name, *args).tap { reflex_invoked = true }
}
@@ -127,9 +137,14 @@
def params
@_params ||= ActionController::Parameters.new(request.parameters)
end
- def dom_id(record_or_class, prefix = nil)
- "#" + ActionView::RecordIdentifier.dom_id(record_or_class, prefix).to_s
+ # morphdom needs content to be wrapped in an element with the same id when children_only: true
+ # Oddly, it doesn't matter if the target element is a div! See: https://docs.stimulusreflex.com/appendices/troubleshooting#different-element-type-altogether-who-cares-so-long-as-the-css-selector-matches
+ # Used internally to allow automatic partial collection rendering, but also useful to library users
+ # eg. `morph dom_id(@posts), render_collection(@posts)`
+ def render_collection(resource, content = nil)
+ content ||= render(resource)
+ tag.div(content.html_safe, id: dom_id(resource).from(1))
end
end