Module: Toys::Middleware

Overview

A middleware is an object that has the opportunity to alter the configuration and runtime behavior of each tool in a Toys CLI. A CLI contains an ordered list of middleware, known as the middleware stack, that together define the CLI's default behavior.

Specifically, a middleware can perform two functions.

First, it can modify the configuration of a tool. After tools are defined from configuration, the middleware stack can make modifications to each tool. A middleware can add flags and arguments to the tool, modify the description, or make any other changes to how the tool is set up.

Second, a middleware can intercept and change tool execution. Like a Rack middleware, a Toys middleware can wrap execution with its own code, replace it outright, or leave it unmodified.

Generally, a middleware is a class that implements the two methods defined in this module: #config and #run. To get default implementations that do nothing, a middleware can include Toys::Middleware or subclass Base, but this is not required.

Defined Under Namespace

Classes: Base, Spec

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.resolve_specs(*items) ⇒ Array<Toys::Middleware::Spec>

Resolve all arguments into an array of middleware specs. Each argument may be one of the following:

  • A Toys::Middleware object
  • A Spec
  • An array whose first element is a middleware name or class, and the subsequent elements are params that define what to pass to the class constructor (see spec_from_array)

Parameters:

Returns:



171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/toys/middleware.rb', line 171

def resolve_specs(*items)
  items.map do |item|
    case item
    when ::Array
      spec_from_array(item)
    when Spec
      item
    else
      spec(item)
    end
  end
end

.spec(middleware_object) ⇒ Toys::Middleware::Spec .spec(name, *args, **kwargs, &block) ⇒ Toys::Middleware::Spec

Create a middleware spec.

Overloads:

  • .spec(middleware_object) ⇒ Toys::Middleware::Spec

    Create a spec wrapping an existing middleware object

    Parameters:

    Returns:

  • .spec(name, *args, **kwargs, &block) ⇒ Toys::Middleware::Spec

    Create a spec indicating a given middleware name should be instantiated with the given arguments.

    Parameters:

    • name (String, Symbol, Class)

      The middleware name or class

    • args (Array)

      The arguments to pass to the constructor

    • kwargs (Hash)

      The keyword arguments to pass to the constructor

    • block (Proc, nil)

      The block to pass to the constructor

    Returns:



114
115
116
117
118
119
120
# File 'lib/toys/middleware.rb', line 114

def spec(middleware, *args, **kwargs, &block)
  if middleware.is_a?(::String) || middleware.is_a?(::Symbol) || middleware.is_a?(::Class)
    Spec.new(nil, middleware, args, kwargs, block)
  else
    Spec.new(middleware, nil, nil, nil, nil)
  end
end

.spec_from_array(array) ⇒ Toys::Middleware::Spec

Create a middleware spec from an array specification.

The array must be 1-4 elements long. The first element must be the middleware name or class. The other three arguments may include any or all of the following optional elements, in any order:

  • An array for the positional arguments to pass to the constructor
  • A hash for the keyword arguments to pass to the constructor
  • A proc for the block to pass to the constructor

Parameters:

  • array (Array)

    The array input

Returns:



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/toys/middleware.rb', line 135

def spec_from_array(array)
  middleware = array.first
  if !middleware.is_a?(::String) && !middleware.is_a?(::Symbol) && !middleware.is_a?(::Class)
    raise ::ArgumentError, "Bad middleware name: #{middleware.inspect}"
  end
  args = []
  kwargs = {}
  block = nil
  array.slice(1..-1).each do |param|
    case param
    when ::Array
      args += param
    when ::Hash
      kwargs = kwargs.merge(param)
    when ::Proc
      block = param
    else
      raise ::ArgumentError, "Bad param: #{param.inspect}"
    end
  end
  Spec.new(nil, middleware, args, kwargs, block)
end

Instance Method Details

#config(tool, loader) ⇒ void

This method returns an undefined value.

This method is called after a tool has been defined, and gives this middleware the opportunity to modify the tool definition. It is passed the tool definition object and the loader, and can make any changes to the tool definition. In most cases, this method should also call yield, which passes control to the next middleware in the stack. A middleware can disable modifications done by subsequent middleware by omitting the yield call, but this is uncommon.

This basic implementation does nothing and simply yields to the next middleware.

Parameters:

  • tool (Toys::Tool)

    The tool definition to modify.

  • loader (Toys::Loader)

    The loader that loaded this tool.



65
66
67
# File 'lib/toys/middleware.rb', line 65

def config(tool, loader) # rubocop:disable Lint/UnusedMethodArgument
  yield
end

#run(context) ⇒ void

This method returns an undefined value.

This method is called when the tool is run. It gives the middleware an opportunity to modify the runtime behavior of the tool. It is passed the tool instance (i.e. the object that hosts a tool's run method), and you can use this object to access the tool's options and other context data. In most cases, this method should also call yield, which passes control to the next middleware in the stack. A middleware can "wrap" normal execution by calling yield somewhere in its implementation of this method, or it can completely replace the execution behavior by not calling yield at all.

Like a tool's run method, this method's return value is unused. If you want to output from a tool, write to stdout or stderr. If you want to set the exit status code, call Context#exit on the context.

This basic implementation does nothing and simply yields to the next middleware.

Parameters:



90
91
92
# File 'lib/toys/middleware.rb', line 90

def run(context) # rubocop:disable Lint/UnusedMethodArgument
  yield
end