Class: Otto::RouteHandlers::LogicClassHandler
- Inherits:
-
BaseHandler
- Object
- BaseHandler
- Otto::RouteHandlers::LogicClassHandler
- Defined in:
- lib/otto/route_handlers/logic_class.rb
Overview
Handler for Logic classes (new in Otto Framework Enhancement)
Logic classes use a constrained signature: initialize(context, params, locale)
- context: The authentication strategy result (user info, session data)
- params: Merged request parameters. Path captures win over form and query parameters (Rack order between those two), and a JSON body sits below all of them.
- locale: The locale string from env
A Logic class may also declare a route_params: keyword on initialize
to receive the path captures on their own, separate from anything the
caller sent in the query string or body:
def initialize(context, params, locale, route_params: {})
IMPORTANT: Logic classes do NOT receive the Rack request or env hash. This is intentional - Logic classes work with clean, authenticated contexts. For endpoints requiring direct request access (sessions, cookies, headers, or logout flows), use controller handlers (Controller#action or Controller.action).
Constant Summary collapse
- ROUTE_PARAMS_KEYWORD_TYPES =
Parameter types (from Method#parameters) that name a keyword argument
%i[key keyreq].freeze
Instance Attribute Summary
Attributes inherited from BaseHandler
#otto_instance, #route_definition
Instance Method Summary collapse
-
#accepts_route_params? ⇒ Boolean
protected
Whether the Logic class constructor declares a
route_params:keyword (or accepts arbitrary keywords). -
#extract_logic_params(req, env) ⇒ Hash
protected
Extract logic parameters including JSON body parsing.
-
#handler_name ⇒ String
protected
Format handler name for Logic routes.
-
#invoke_target(req, _res) ⇒ Array
protected
Invoke Logic class with constrained signature.
-
#json_body?(req) ⇒ Boolean
protected
Whether the request carries a JSON body worth parsing.
-
#parse_json_body(req, env) ⇒ Hash
protected
Parse JSON request body with error handling.
-
#route_params ⇒ Hash
protected
Path captures matched by the router, keyed by the route's placeholder names.
Methods inherited from BaseHandler
#call, #finalize_response, #handle_execution_error, #handle_local_error, #handle_response, #initialize, #setup_request_response, #target_class
Constructor Details
This class inherits a constructor from Otto::RouteHandlers::BaseHandler
Instance Method Details
#accepts_route_params? ⇒ Boolean (protected)
Whether the Logic class constructor declares a route_params: keyword
(or accepts arbitrary keywords). Logic classes opt in by declaring it;
the three-positional signature keeps working unchanged.
91 92 93 94 95 96 97 |
# File 'lib/otto/route_handlers/logic_class.rb', line 91 def accepts_route_params? return @accepts_route_params if defined?(@accepts_route_params) @accepts_route_params = target_class.instance_method(:initialize).parameters.any? do |type, name| type == :keyrest || (ROUTE_PARAMS_KEYWORD_TYPES.include?(type) && name == :route_params) end end |
#extract_logic_params(req, env) ⇒ Hash (protected)
Extract logic parameters including JSON body parsing
Starts from req.params, which already carries Rack's own precedence (form body over query string) with the path captures merged on top by setup_request_response. A JSON body sits below all of those: a JSON key can never replace a path capture, a query parameter, or a form field. JSON bodies are only read for methods that carry a body (never GET or HEAD).
111 112 113 114 115 116 117 118 119 |
# File 'lib/otto/route_handlers/logic_class.rb', line 111 def extract_logic_params(req, env) logic_params = req.params.dup return logic_params unless json_body?(req) json_params = parse_json_body(req, env) return logic_params if json_params.empty? Otto::Static.indifferent_params(json_params.merge(logic_params)) end |
#handler_name ⇒ String (protected)
Format handler name for Logic routes
160 161 162 |
# File 'lib/otto/route_handlers/logic_class.rb', line 160 def handler_name "#{target_class.name}#call" end |
#invoke_target(req, _res) ⇒ Array (protected)
Invoke Logic class with constrained signature
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/otto/route_handlers/logic_class.rb', line 38 def invoke_target(req, _res) env = req.env # Get strategy result (guaranteed to exist from RouteAuthWrapper) strategy_result = env['otto.strategy_result'] # Extract params including JSON body parsing logic_params = extract_logic_params(req, env) # Get locale locale = env['otto.locale'] || 'en' # Instantiate Logic class. Path captures travel separately so a Logic # class can read the router's value by name, whatever the body sent. logic = if accepts_route_params? target_class.new(strategy_result, logic_params, locale, route_params: route_params) else target_class.new(strategy_result, logic_params, locale) end # Execute standard Logic class lifecycle logic.raise_concerns if logic.respond_to?(:raise_concerns) result = if logic.respond_to?(:process) logic.process else logic.call || logic end context = { logic_instance: logic, request: req, status_code: logic.respond_to?(:status_code) ? logic.status_code : nil, } [result, context] end |
#json_body?(req) ⇒ Boolean (protected)
Whether the request carries a JSON body worth parsing
124 125 126 127 128 129 |
# File 'lib/otto/route_handlers/logic_class.rb', line 124 def json_body?(req) return false if req.get? || req.head? return false unless req.content_type&.include?('application/json') req.body&.size&.positive? || false end |
#parse_json_body(req, env) ⇒ Hash (protected)
Parse JSON request body with error handling
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/otto/route_handlers/logic_class.rb', line 136 def parse_json_body(req, env) req.body.rewind json_data = JSON.parse(req.body.read) json_data.is_a?(Hash) ? json_data : {} rescue JSON::ParserError => e # Base context pattern: create once, reuse for correlation log_context = Otto::LoggingHelpers.request_context(env) Otto.structured_log(:error, 'JSON parsing error', log_context.merge( handler: handler_name, error: e., error_class: e.class.name, duration: Otto::Utils.now_in_μs - @start_time )) Otto::LoggingHelpers.log_backtrace(e, log_context.merge(handler: handler_name)) {} end |
#route_params ⇒ Hash (protected)
Path captures matched by the router, keyed by the route's placeholder
names. Always present in params as well (where they take precedence);
this copy is for Logic classes that must not confuse a path value with
one the caller put in the query string or body.
82 83 84 |
# File 'lib/otto/route_handlers/logic_class.rb', line 82 def route_params Otto::Static.indifferent_params((@extra_params || {}).dup) end |