Class: Takagi::MiddlewareStack
- Inherits:
-
Object
- Object
- Takagi::MiddlewareStack
- Includes:
- Singleton
- Defined in:
- lib/takagi/middleware_stack.rb,
sig/takagi/middleware_stack.rbs
Overview
Middleware stack for processing CoAP requests
Provides a Rack-style middleware chain for request/response processing. Middleware can be configured via Takagi.config.middleware or programmatically.
Instance Attribute Summary collapse
-
#middlewares ⇒ Object
readonly
Returns the value of attribute middlewares.
-
#router ⇒ Object
readonly
Returns the value of attribute router.
Instance Method Summary collapse
-
#call(request) ⇒ Takagi::Message::Outbound
Processes the request through the middleware stack and routes it.
-
#clear ⇒ Object
Clear all middleware (useful for testing).
-
#initialize ⇒ MiddlewareStack
constructor
A new instance of MiddlewareStack.
-
#instantiate_middleware(klass, options) ⇒ Object
Instantiate middleware with or without options.
-
#load_from_config ⇒ Object
Load middleware from Takagi configuration This is called lazily on first request to ensure config is loaded.
-
#load_middleware(config) ⇒ Object
Load and instantiate a middleware from configuration.
-
#reset! ⇒ Object
Reset configuration loaded flag (useful for testing).
-
#resolve_middleware_class(name) ⇒ Object
Resolve middleware class from name Supports both short names (e.g., "Logging") and full names (e.g., "MyApp::CustomMiddleware").
-
#use(middleware) ⇒ Object
Adds a new middleware to the stack.
Constructor Details
Instance Attribute Details
#middlewares ⇒ Object (readonly)
Returns the value of attribute middlewares.
28 29 30 |
# File 'lib/takagi/middleware_stack.rb', line 28 def middlewares @middlewares end |
#router ⇒ Object (readonly)
Returns the value of attribute router.
28 29 30 |
# File 'lib/takagi/middleware_stack.rb', line 28 def router @router end |
Instance Method Details
#call(request) ⇒ Takagi::Message::Outbound
Processes the request through the middleware stack and routes it
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/takagi/middleware_stack.rb', line 68 def call(request) # Lazily load configuration on first request load_from_config unless @config_loaded Takagi::Hooks.emit(:middleware_before_call, request: request) # Define the core application logic (routing) app = lambda do |req| block, params = @router.find_route(req.method.to_s, req.uri.path) if block block.call(req, params) else req.to_response('4.04 Not Found', { error: 'not found' }) end end # Build middleware chain (reverse order for proper execution) response = @middlewares.reverse.reduce(app) do |next_middleware, middleware| ->(req) { middleware.call(req, &next_middleware) } end.call(request) Takagi::Hooks.emit(:middleware_after_call, request: request, response: response) response end |
#clear ⇒ Object
Clear all middleware (useful for testing)
93 94 95 |
# File 'lib/takagi/middleware_stack.rb', line 93 def clear @middlewares.clear end |
#instantiate_middleware(klass, options) ⇒ Object
Instantiate middleware with or without options
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/takagi/middleware_stack.rb', line 147 def instantiate_middleware(klass, ) if .empty? # Try to instantiate without arguments klass.new else # Pass options to initializer # Support both keyword arguments and hash argument begin klass.new(**) rescue ArgumentError # Fall back to positional hash argument klass.new() end end rescue ArgumentError => e @logger.warn "Failed to instantiate #{klass}: #{e.}. Trying without options..." klass.new end |
#load_from_config ⇒ Object
Load middleware from Takagi configuration This is called lazily on first request to ensure config is loaded
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/takagi/middleware_stack.rb', line 39 def load_from_config return if @config_loaded return unless defined?(Takagi.config) @config_loaded = true # Check if middleware is globally disabled return unless Takagi.config.middleware.enabled # Load middleware stack from configuration Takagi.config.middleware.stack.each do |middleware_config| load_middleware(middleware_config) end @logger.debug "Loaded #{@middlewares.size} middleware(s)" if @middlewares.any? rescue StandardError => e @logger.error "Failed to load middleware configuration: #{e.}" @logger.debug e.backtrace.join("\n") end |
#load_middleware(config) ⇒ Object
Load and instantiate a middleware from configuration
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/takagi/middleware_stack.rb', line 107 def load_middleware(config) middleware_name = config[:name] = config[:options] || {} # Resolve middleware class klass = resolve_middleware_class(middleware_name) return unless klass # Instantiate middleware with options middleware = instantiate_middleware(klass, ) use(middleware) if middleware @logger.debug "Loaded middleware: #{middleware_name} with options: #{.inspect}" rescue StandardError => e @logger.warn "Failed to load middleware #{middleware_name}: #{e.}" @logger.debug e.backtrace.join("\n") end |
#reset! ⇒ Object
Reset configuration loaded flag (useful for testing)
98 99 100 101 |
# File 'lib/takagi/middleware_stack.rb', line 98 def reset! @middlewares.clear @config_loaded = false end |
#resolve_middleware_class(name) ⇒ Object
Resolve middleware class from name Supports both short names (e.g., "Logging") and full names (e.g., "MyApp::CustomMiddleware")
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/takagi/middleware_stack.rb', line 127 def resolve_middleware_class(name) # Try Takagi::Middleware namespace first begin return Object.const_get("Takagi::Middleware::#{name}") rescue NameError # Not in Takagi::Middleware namespace end # Try as a full class name begin Object.const_get(name) rescue NameError @logger.warn "Middleware class not found: #{name}" nil end end |
#use(middleware) ⇒ Object
Adds a new middleware to the stack
61 62 63 |
# File 'lib/takagi/middleware_stack.rb', line 61 def use(middleware) @middlewares << middleware end |