Class: Otto::Route

Inherits:
Object
  • Object
show all
Defined in:
lib/otto/route.rb

Overview

Otto::Route

A Route is a definition of a URL path and the method to call when that path is requested. Each route represents a single line in a routes file.

Routes include built-in security features:

  • Class name validation to prevent code injection

  • Automatic security header injection

  • CSRF protection when enabled

  • Input validation and sanitization

e.g.

GET   /uri/path      YourApp.method
GET   /uri/path2     YourApp#method

Defined Under Namespace

Modules: ClassMethods

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(verb, path, definition) ⇒ Route

Initialize a new route with security validations

Parameters:

  • verb (String)

    HTTP verb (GET, POST, PUT, DELETE, etc.)

  • path (String)

    URL path pattern with optional parameters

  • definition (String)

    Class and method definition with optional key-value parameters Examples:

    "Class.method" (traditional)
    "Class#method" (traditional)
    "V2::Logic::AuthSession auth=authenticated response=redirect" (enhanced)
    

Raises:

  • (ArgumentError)

    if definition format is invalid or class name is unsafe



44
45
46
47
48
49
50
51
52
# File 'lib/otto/route.rb', line 44

def initialize(verb, path, definition)
  @pattern, @keys = *compile(path)

  # Create immutable route definition
  @route_definition = Otto::RouteDefinition.new(verb, path, definition, pattern: @pattern, keys: @keys)

  # Resolve the class
  @klass = safe_const_get(@route_definition.klass_name)
end

Instance Attribute Details

#klassClass (readonly)

Returns The resolved class object.

Returns:

  • (Class)

    The resolved class object



30
31
32
# File 'lib/otto/route.rb', line 30

def klass
  @klass
end

#ottoObject

Returns the value of attribute otto.



32
33
34
# File 'lib/otto/route.rb', line 32

def otto
  @otto
end

#route_definitionOtto::RouteDefinition (readonly)

Returns The immutable route definition.

Returns:



27
28
29
# File 'lib/otto/route.rb', line 27

def route_definition
  @route_definition
end

Instance Method Details

#call(env, extra_params = {}) ⇒ Array

Execute the route by calling the associated class method

This method handles the complete request/response cycle with built-in security:

  • Processes parameters through the security layer

  • Adds configured security headers to the response

  • Extends request/response with security helpers when enabled

  • Provides CSRF and validation helpers to the target class

Parameters:

  • env (Hash)

    Rack environment hash

  • extra_params (Hash) (defaults to: {})

    Additional parameters to merge (default: {})

Returns:

  • (Array)

    Rack response array [status, headers, body]



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/otto/route.rb', line 145

def call(env, extra_params = {})
  extra_params ||= {}
  req            = Rack::Request.new(env)
  res            = Rack::Response.new
  req.extend Otto::RequestHelpers
  res.extend Otto::ResponseHelpers
  res.request    = req

  # Make security config available to response helpers
  if otto.respond_to?(:security_config) && otto.security_config
    env['otto.security_config'] = otto.security_config
  end

  # NEW: Make route definition and options available to middleware and handlers
  env['otto.route_definition'] = @route_definition
  env['otto.route_options'] = @route_definition.options

  # Process parameters through security layer
  req.params.merge! extra_params
  req.params.replace Otto::Static.indifferent_params(req.params)

  # Add security headers
  if otto.respond_to?(:security_config) && otto.security_config
    otto.security_config.security_headers.each do |header, value|
      res.headers[header] = value
    end
  end

  klass.extend Otto::Route::ClassMethods
  klass.otto = otto

  # Add security helpers if CSRF is enabled
  if otto.respond_to?(:security_config) && otto.security_config&.csrf_enabled?
    res.extend Otto::Security::CSRFHelpers
  end

  # Add validation helpers
  res.extend Otto::Security::ValidationHelpers

  # NEW: Use the pluggable route handler factory (Phase 4)
  # This replaces the hardcoded execution pattern with a factory approach
  if otto&.route_handler_factory
    handler = otto.route_handler_factory.create_handler(@route_definition, otto)
    return handler.call(env, extra_params)
  else
    # Fallback to legacy behavior for backward compatibility
    inst = nil
    result = case kind
             when :instance
               inst = klass.new req, res
               inst.send(name)
             when :class
               klass.send(name, req, res)
             else
               raise "Unsupported kind for #{definition}: #{kind}"
             end

    # Handle response based on route options
    response_type = @route_definition.response_type
    if response_type != 'default'
      context = {
        logic_instance: (kind == :instance ? inst : nil),
        status_code: nil,
        redirect_path: nil
      }

      Otto::ResponseHandlers::HandlerFactory.handle_response(result, res, response_type, context)
    end

    res.body = [res.body] unless res.body.respond_to?(:each)
    res.finish
  end
end

#definitionObject



63
64
65
# File 'lib/otto/route.rb', line 63

def definition
  @route_definition.definition
end

#keysObject



71
72
73
# File 'lib/otto/route.rb', line 71

def keys
  @route_definition.keys
end

#kindObject



79
80
81
# File 'lib/otto/route.rb', line 79

def kind
  @route_definition.kind
end

#nameObject



75
76
77
# File 'lib/otto/route.rb', line 75

def name
  @route_definition.method_name
end

#pathObject



59
60
61
# File 'lib/otto/route.rb', line 59

def path
  @route_definition.path
end

#patternObject



67
68
69
# File 'lib/otto/route.rb', line 67

def pattern
  @route_definition.pattern
end

#pattern_regexpObject



130
131
132
# File 'lib/otto/route.rb', line 130

def pattern_regexp
  Regexp.new(@path.gsub('/*', '/.+'))
end

#route_optionsObject



83
84
85
# File 'lib/otto/route.rb', line 83

def route_options
  @route_definition.options
end

#verbObject

Delegate common methods to route_definition for backward compatibility



55
56
57
# File 'lib/otto/route.rb', line 55

def verb
  @route_definition.verb
end