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