app/channels/stimulus_reflex/channel.rb in stimulus_reflex-3.4.2 vs app/channels/stimulus_reflex/channel.rb in stimulus_reflex-3.5.0.pre0

- old
+ new

@@ -1,61 +1,39 @@ # frozen_string_literal: true class StimulusReflex::Channel < StimulusReflex.configuration.parent_channel.constantize + attr_reader :reflex_data + def stream_name ids = connection.identifiers.map { |identifier| send(identifier).try(:id) || send(identifier) } [ params[:channel], ids.select(&:present?).join(";") ].select(&:present?).join(":") end def subscribed super - fix_environment! stream_from stream_name end def receive(data) - url = data["url"].to_s - selectors = (data["selectors"] || []).select(&:present?) - selectors = data["selectors"] = ["body"] if selectors.blank? - target = data["target"].to_s - factory = StimulusReflex::ReflexFactory.new(target) - reflex_class = factory.call - method_name = factory.method_name - arguments = (data["args"] || []).map { |arg| object_with_indifferent_access arg } - element = StimulusReflex::Element.new(data) - permanent_attribute_name = data["permanentAttributeName"] - form_data = Rack::Utils.parse_nested_query(data["formData"]) - params = form_data.deep_merge(data["params"] || {}) - + @reflex_data = StimulusReflex::ReflexData.new(data) begin begin - reflex = reflex_class.new(self, - url: url, - element: element, - selectors: selectors, - method_name: method_name, - params: params, - client_attributes: { - reflex_id: data["reflexId"], - xpath_controller: data["xpathController"], - xpath_element: data["xpathElement"], - reflex_controller: data["reflexController"], - permanent_attribute_name: permanent_attribute_name - }) - delegate_call_to_reflex reflex, method_name, arguments - rescue => invoke_error - message = exception_message_with_backtrace(invoke_error) - body = "Reflex #{target} failed: #{message} [#{url}]" + reflex = StimulusReflex::ReflexFactory.create_reflex_from_data(self, @reflex_data) + delegate_call_to_reflex reflex + rescue => exception + error = exception_with_backtrace(exception) + error_message = "\e[31mReflex #{reflex_data.target} failed: #{error[:message]} [#{reflex_data.url}]\e[0m\n#{error[:stack]}" if reflex - reflex.rescue_with_handler(invoke_error) - reflex.broadcast_message subject: "error", body: body, data: data, error: invoke_error + reflex.rescue_with_handler(exception) + puts error_message + reflex.broadcast_message subject: "error", data: data, error: exception else - puts "\e[31m#{body}\e[0m" + puts error_message if body.to_s.include? "No route matches" initializer_path = Rails.root.join("config", "initializers", "stimulus_reflex.rb") puts <<~NOTE @@ -81,62 +59,62 @@ if reflex.halted? reflex.broadcast_message subject: "halted", data: data else begin - reflex.broadcast(selectors, data) - rescue => render_error - reflex.rescue_with_handler(render_error) - message = exception_message_with_backtrace(render_error) - body = "Reflex failed to re-render: #{message} [#{url}]" - reflex.broadcast_message subject: "error", body: body, data: data, error: render_error - puts "\e[31m#{body}\e[0m" + reflex.broadcast(reflex_data.selectors, data) + rescue => exception + reflex.rescue_with_handler(exception) + error = exception_with_backtrace(exception) + reflex.broadcast_message subject: "error", data: data, error: exception + puts "\e[31mReflex failed to re-render: #{error[:message]} [#{reflex_data.url}]\e[0m\n#{error[:stack]}" end end ensure if reflex commit_session(reflex) - reflex.logger.print + report_failed_basic_auth(reflex) if reflex.controller? + reflex.logger&.print end end end private - def object_with_indifferent_access(object) - return object.with_indifferent_access if object.respond_to?(:with_indifferent_access) - object.map! { |obj| object_with_indifferent_access obj } if object.is_a?(Array) - object - end - - def delegate_call_to_reflex(reflex, method_name, arguments = []) + def delegate_call_to_reflex(reflex) + method_name = reflex_data.method_name + arguments = reflex_data.arguments method = reflex.method(method_name) - required_params = method.parameters.select { |(kind, _)| kind == :req } - optional_params = method.parameters.select { |(kind, _)| kind == :opt } - if arguments.size == 0 && required_params.size == 0 + policy = StimulusReflex::ReflexMethodInvocationPolicy.new(method, arguments) + + if policy.no_arguments? reflex.process(method_name) - elsif arguments.size >= required_params.size && arguments.size <= required_params.size + optional_params.size + elsif policy.arguments? reflex.process(method_name, *arguments) else raise ArgumentError.new("wrong number of arguments (given #{arguments.inspect}, expected #{required_params.inspect}, optional #{optional_params.inspect})") end end def commit_session(reflex) store = reflex.request.session.instance_variable_get("@by") store.commit_session reflex.request, reflex.controller.response - rescue => e - message = "Failed to commit session! #{exception_message_with_backtrace(e)}" - puts "\e[31m#{message}\e[0m" + rescue => exception + error = exception_with_backtrace(exception) + puts "\e[31mFailed to commit session! #{error[:message]}\e[0m\n#{error[:backtrace]}" end - def exception_message_with_backtrace(exception) - "#{exception}\n#{exception.backtrace.first}" + def report_failed_basic_auth(reflex) + if reflex.controller.response.status == 401 + puts "\e[31mReflex failed to process controller action \"#{reflex.controller.class}##{reflex.controller.action_name}\" due to HTTP basic auth. Consider adding \"unless: -> { @stimulus_reflex }\" to the before_action or method responible for authentication.\e[0m" + end end - def fix_environment! - ([ApplicationController] + ApplicationController.descendants).each do |controller| - controller.renderer.instance_variable_set(:@env, connection.env.merge(controller.renderer.instance_variable_get(:@env))) - end + def exception_with_backtrace(exception) + { + message: exception.to_s, + backtrace: exception.backtrace.first, + stack: exception.backtrace.join("\n") + } end end