class Middlegem::Stack
{Stack} is a class that represents a chain of middlewares, which, when called, can arbitrarily transform a given input. Most of the functionality provided by middlegem
lies in this class. Using {Stack} is simple: create a new instance with the desired definition, add whatever middlewares you want to use, and {#call} it.
A very basic example of usage is:
class LastNameMiddleware < Middlegem::Middleware def call(name) "The Honorable #{name}" end end class EmailStringMiddleware < Middlegem::Middleware def initialize(email) @email = email end def call(name) "#{@email} <#{name}>" end end definitions = [ LastNameMiddleware, EmailStringMiddleware ] stack = Middlegem::Stack.new(Middlegem::ArrayDefinition.new(definitions)) stack.middlewares += [EmailStringMiddleware.new('mail@test.com'), LastNameMiddleware.new] stack.call('Jacob') # => "mail@test.com <The Honorable Jacob>"
Notice that, even though the EmailStringMiddleware
was added before the LastNameMiddleware
, the LastNameMiddleware
was still run first since it was defined first. That is a core principle of middlegem
—rather than providing extensive methods to insert middleware in a specific place along the chain, middlegem
allows you to define the order explicitly. Also note that there are a variety of ways that you could specify the middleware order by extending {Definition}.
@author Jacob Lockard @since 0.1.0 @see Middleware
@see Definition
@see ArrayDefinition
Attributes
The {Definition} used to determine what middlewares are permitted in this stack and in what order they should be run. Note that this attribute may be any object that is a valid definition according to {Definition.valid?}. @return [Definition] the middleware definition of this middleware stack.
An array containing the middlewares represented by this {Stack}. You can insert middlewares in any way you like by accessing this attribute directly and using ruby's built-in array methods. If desired, you can even assign a new array to it. All middlewares will be validated before being run. To be run, a middleware must be valid as defined by {Middleware.valid?} and it must be defined according to the {Definition} instance in {#definition}. @return [Array<Object>] the middlewares contained in this stack.
Public Class Methods
Creates a new instance of {Stack} with the given middleware definition and, optionally, an array of middlewares. Note that middlewares will be validated, not immediately, but before being run. @param definition [Definition] the middleware definition to use to determine
what middleware to permit in this stack and in what order to run them. May be any object that is a valid definition according to {Definition.valid?}.
@param middlwares [Array<Object>] an optional array of initial middlewares in this stack.
# File lib/middlegem/stack.rb, line 72 def initialize(definition, middlewares: []) unless Definition.valid?(definition) raise InvalidDefinitionError, "The middleware definition #{definition} is invalid!" end @definition = definition @middlewares = middlewares end
Public Instance Methods
Transforms the given input by calling all the middlewares in this stack, as defined by the middleware {#definition}. Note that, as mentioned in {Middleware}, middlewares in middlegem
transform argument lists. Thus, the arguments are already splatted—there is no need to pass a single array of arguments as the only parameter, unless you actually want to transform just a single array. Also, middleware must return an array of arguments, which will be splatted when passed to {Middleware#call}.
Midlewares are validated before being run or sorted. If a middleware is encountered that is either invalid or unpermitted, an appropriate error will be raised.
@param args [Array<Object>] the array of input arguments. @return [Array<Object>] the output of the last middleware in the chain. @raise [InvalidMiddlewareError] when one of the middlewares in {#middlewares} is not valid,
as defined by {Middleware.valid?}.
@raise [UnpermittedMiddlewareError] when one of the middlewares in {#middlewares} has not
been defined, and is thus not permitted, according to the {Definition} instance in {#definition}.
# File lib/middlegem/stack.rb, line 98 def call(*args) # Validate the middlewares. middlewares.each { |m| ensure_valid!(m) } # Sort the middlewares. sorted = definition.sort(middlewares) # Run each middleware with the output of the previous one, ensuring that each output is # valid. For the first middleware, use `args` as the input. last_output = args sorted.each do |middleware| last_output = middleware.call(*last_output) ensure_valid_output!(middleware, last_output) end last_output end
Private Instance Methods
Ensures that the given middleware is a valid middleware for this middleware stack, raising an appropriate error if not. A middleware is valid if:
-
it is valid according to {Middleware.valid?}, and
-
it is defined according to {#definition}.
@param middleware [Object] the middleware to validate. @return [void]
# File lib/middlegem/stack.rb, line 124 def ensure_valid!(middleware) unless Middleware.valid?(middleware) raise InvalidMiddlewareError, "The middleware #{middleware} is not a valid middleware!" end unless definition.defined?(middleware) raise UnpermittedMiddlewareError, "The middleware #{middleware} has not been defined!" end end
Ensures that the given output of the given middleware is valid for all the intents and purposes of this stack. Essentially, the output is valid if it can be passed, splatted, to the next middleware, or returned at the end of the stack. Currently, this method only checks whether the output is an “array” (as defined by the splat operator). If the output is invalid, an appropriate error will be raised. @param middleware [Object] the middleware whose output is being validated. This object is
only used to generate appropriate error messages.
@param output [Object] the middleware output to validate. @return [void]
# File lib/middlegem/stack.rb, line 143 def ensure_valid_output!(middleware, output) unless output == [*output] raise InvalidMiddlewareOutputError, <<~ERR The middleware #{middleware} outputted #{output}, which is not an array! ERR end end