Class: Tina4::Route
- Inherits:
-
Object
- Object
- Tina4::Route
- Defined in:
- lib/tina4/router.rb
Instance Attribute Summary collapse
-
#auth_handler ⇒ Object
readonly
Returns the value of attribute auth_handler.
-
#auth_required ⇒ Object
Returns the value of attribute auth_required.
-
#cached ⇒ Object
Returns the value of attribute cached.
-
#handler ⇒ Object
readonly
Returns the value of attribute handler.
-
#method ⇒ Object
readonly
Returns the value of attribute method.
-
#param_names ⇒ Object
readonly
Returns the value of attribute param_names.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#path_regex ⇒ Object
readonly
Returns the value of attribute path_regex.
-
#swagger_meta ⇒ Object
readonly
Returns the value of attribute swagger_meta.
-
#template ⇒ Object
readonly
Returns the value of attribute template.
Class Method Summary collapse
-
.function_middleware?(mw) ⇒ Boolean
Detect Express/FastAPI-style function middleware.
Instance Method Summary collapse
-
#cache ⇒ Object
Mark this route as cacheable.
-
#function_middleware ⇒ Object
Function-style middleware attached to this route, in declaration order.
-
#initialize(method, path, handler, auth_handler: nil, swagger_meta: {}, middleware: [], template: nil) ⇒ Route
constructor
A new instance of Route.
-
#match?(request_path, request_method = nil) ⇒ Boolean
Returns params hash if matched, false otherwise.
-
#match_path(request_path) ⇒ Object
Returns params hash if matched, false otherwise.
-
#middleware(*middleware_classes) ⇒ Object
Dual-mode: getter (no args) returns the middleware array; setter (with args) appends middleware and returns self for chaining.
-
#no_auth ⇒ Object
Opt out of the secure-by-default auth on write routes.
-
#run_middleware(request, response) ⇒ Object
Run per-route CLASS-based middleware.
-
#secure ⇒ Object
Mark this route as requiring bearer-token authentication.
Constructor Details
#initialize(method, path, handler, auth_handler: nil, swagger_meta: {}, middleware: [], template: nil) ⇒ Route
Returns a new instance of Route.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/tina4/router.rb', line 12 def initialize(method, path, handler, auth_handler: nil, swagger_meta: {}, middleware: [], template: nil) @method = method.to_s.upcase.freeze @path = normalize_path(path).freeze @handler = handler @auth_handler = auth_handler @swagger_meta = @middleware = middleware.freeze @template = template&.freeze # Write routes are secure by default — bearer-token auth is enforced # on POST/PUT/PATCH/DELETE regardless of attached middleware. # Middleware is additive, never an auth bypass. tina4-book#141 # PY-10-02. Call .no_auth on the route to opt out explicitly. @auth_required = %w[POST PUT PATCH DELETE].include?(@method) @cached = false @param_names = [] @path_regex = compile_pattern(@path) @param_names.freeze end |
Instance Attribute Details
#auth_handler ⇒ Object (readonly)
Returns the value of attribute auth_handler.
8 9 10 |
# File 'lib/tina4/router.rb', line 8 def auth_handler @auth_handler end |
#auth_required ⇒ Object
Returns the value of attribute auth_required.
10 11 12 |
# File 'lib/tina4/router.rb', line 10 def auth_required @auth_required end |
#cached ⇒ Object
Returns the value of attribute cached.
10 11 12 |
# File 'lib/tina4/router.rb', line 10 def cached @cached end |
#handler ⇒ Object (readonly)
Returns the value of attribute handler.
8 9 10 |
# File 'lib/tina4/router.rb', line 8 def handler @handler end |
#method ⇒ Object (readonly)
Returns the value of attribute method.
8 9 10 |
# File 'lib/tina4/router.rb', line 8 def method @method end |
#param_names ⇒ Object (readonly)
Returns the value of attribute param_names.
8 9 10 |
# File 'lib/tina4/router.rb', line 8 def param_names @param_names end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
8 9 10 |
# File 'lib/tina4/router.rb', line 8 def path @path end |
#path_regex ⇒ Object (readonly)
Returns the value of attribute path_regex.
8 9 10 |
# File 'lib/tina4/router.rb', line 8 def path_regex @path_regex end |
#swagger_meta ⇒ Object (readonly)
Returns the value of attribute swagger_meta.
8 9 10 |
# File 'lib/tina4/router.rb', line 8 def @swagger_meta end |
#template ⇒ Object (readonly)
Returns the value of attribute template.
8 9 10 |
# File 'lib/tina4/router.rb', line 8 def template @template end |
Class Method Details
.function_middleware?(mw) ⇒ Boolean
Detect Express/FastAPI-style function middleware.
A Proc/Lambda/Method whose arity indicates 3+ positional params (req, resp, next_handler). Ruby arity quirk: required-args-only arity is non-negative; if the callable accepts a splat or optionals, arity is negated (-required-1). We treat arity >= 3 OR arity <= -4 as function-style. Anything else (a class with before_/after_ methods, or a 2-arg callable) is treated as class-style and goes through #run_middleware.
128 129 130 131 132 133 134 135 |
# File 'lib/tina4/router.rb', line 128 def self.function_middleware?(mw) return false if mw.is_a?(Class) || mw.is_a?(Module) return false unless mw.respond_to?(:arity) ar = mw.arity ar >= 3 || ar <= -4 rescue StandardError false end |
Instance Method Details
#cache ⇒ Object
Mark this route as cacheable. Returns self for chaining: Router.get(“/path”) { … }.cache
47 48 49 50 |
# File 'lib/tina4/router.rb', line 47 def cache @cached = true self end |
#function_middleware ⇒ Object
Function-style middleware attached to this route, in declaration order. The route dispatcher folds them into a Russian-doll continuation chain — first declared is the OUTERMOST layer (runs first on the way in, last on the way out). tina4-book#141 PY-10-01 — chapter 10 documented 8+ examples of function middleware for years; before this fix the framework silently ignored them.
115 116 117 |
# File 'lib/tina4/router.rb', line 115 def function_middleware @middleware.select { |mw| Route.function_middleware?(mw) } end |
#match?(request_path, request_method = nil) ⇒ Boolean
Returns params hash if matched, false otherwise
71 72 73 74 |
# File 'lib/tina4/router.rb', line 71 def match?(request_path, request_method = nil) return false if request_method && @method != "ANY" && @method != request_method.to_s.upcase match_path(request_path) end |
#match_path(request_path) ⇒ Object
Returns params hash if matched, false otherwise
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/tina4/router.rb', line 77 def match_path(request_path) match = @path_regex.match(request_path) return false unless match if @param_names.empty? {} else params = {} @param_names.each_with_index do |param_def, i| raw_value = match[i + 1] params[param_def[:name]] = cast_param(raw_value, param_def[:type]) end params end end |
#middleware(*middleware_classes) ⇒ Object
Dual-mode: getter (no args) returns the middleware array; setter (with args) appends middleware and returns self for chaining. Router.post(“/api”) { … }.middleware(AuthMiddleware)
Middleware is purely additive — registering middleware NEVER flips (POST/PUT/PATCH/DELETE) stays in effect; if a route truly wants to opt out of the built-in bearer check, call .no_auth explicitly. tina4-book#141 PY-10-02 — previously, attaching ANY middleware silently turned off auth_required, which let attackers bypass auth by routing through a logging middleware. Cross-framework parity.
63 64 65 66 67 68 |
# File 'lib/tina4/router.rb', line 63 def middleware(*middleware_classes) return @middleware if middleware_classes.empty? @middleware = @middleware.dup + middleware_classes self end |
#no_auth ⇒ Object
Opt out of the secure-by-default auth on write routes. Returns self for chaining: Router.post(“/login”) { … }.no_auth
40 41 42 43 |
# File 'lib/tina4/router.rb', line 40 def no_auth @auth_required = false self end |
#run_middleware(request, response) ⇒ Object
Run per-route CLASS-based middleware. Function-style middleware (Proc/Lambda taking 3+ args: req, resp, next_handler) is skipped here — it’s dispatched separately via #function_middleware which wraps the route handler in a continuation chain (see rack_app.rb).
Returns true if all class-based middleware passed, false if any returned literal false (halt request).
100 101 102 103 104 105 106 107 |
# File 'lib/tina4/router.rb', line 100 def run_middleware(request, response) @middleware.each do |mw| next if Route.function_middleware?(mw) result = mw.call(request, response) return false if result == false end true end |
#secure ⇒ Object
Mark this route as requiring bearer-token authentication. Returns self for chaining: Router.get(“/path”) { … }.secure
33 34 35 36 |
# File 'lib/tina4/router.rb', line 33 def secure @auth_required = true self end |