Class: Middlegem::ArrayDefinition

Inherits:
Definition show all
Defined in:
lib/middlegem/array_definition.rb

Overview

ArrayDefinition is an implementation of Definition that allows middlewares to be explicitly defined and ordered by class in an array. A basic example of usage is:

definition = Middlegem::ArrayDefinition.new([
  MiddlewareOne,   # appends '1'
  MiddlewareTwo,   # appends '2'
  MiddlewareThree, # appends '3'
  MiddlewareFinal  # appends '.'
])

stack = Middlegem::Stack.new(definition, middlewares: [
  MiddlewareThree.new,
  MiddlewareFinal.new,
  MiddlewareOne.new,
  MiddlewareTwo.new
])

stack.call('hello') # => ['hello123.']

Notice that the middlewares are called in the order they are specified in the definition array.

If two or more middlewares are encountered that have the same class, they will be left in the order they were added. This behavior can be overriden by setting a tie resolver. The following code, for example, raises an error when multiple MiddlewareFinal middlewares are encountered:

middlewares = [
  MiddlewareOne,
  MiddlewareTwo,
  MiddlewareThree,
  MiddlewareFinal
]

tie_resolver = proc do |ties|
  raise "Can't run multiple MiddlewareFinals!" if ties.first.is_a? MiddlewareFinal
  ties
end

definition = Middlegem::ArrayDefinition.new(middlewares, resolver: tie_resolver)

stack = Middlegem::Stack.new(definition, middlewares: [
  MiddlewareTwo.new,
  MiddlewareOne.new,
  MiddlewareFinal.new,
  MiddlewareThree.new,
  MiddlewareFinal.new
])

stack.call('hello') # => RuntimeError (Can't run multiple MiddlewareFinals!)

When the two MiddlewareFinal instances are encountered, the tie resolver is run, which raises the error.

Of course, this is only scratching the surface of what is possible with a custom tie resolver. You might, for example, simply skip other instances of MiddlewareFinal, rather than raising an error. A word of caution is in order, however! It is not recommended to try anything too complicated with the tie resolver because it is run for all ties whatsoever. That means that, while you could technically try to sort middlewares with the same class based on some other factor—there is even an example in spec/middlegem/array_definition_spec.rb—it potentially results in long if/else or case/when constructions because each type must be dealt with separately. Use at your own risk!

In general, if you need to use a tie resolver for anything but the most basic of tasks, you should probably just create your own Definition implementation with the required functionality. ArrayDefinition is intended primarily for defining middlewares according to their classes and nothing more.

See Also:

Author:

  • Jacob Lockard

Since:

  • 0.1.0

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Definition

valid?

Constructor Details

#initialize(defined_classes, resolver: nil) ⇒ ArrayDefinition

Creates a new instance of Middlegem::ArrayDefinition with the given array of defined classes and, optionally, a custom tie resolver.

Parameters:

  • defined_classes (Object<Class>)

    an ordered array of classes to be defined by this Middlegem::ArrayDefinition (see #defined_classes).

  • resolver (#call, nil) (defaults to: nil)

    a callable object to use when middlewares of the same class are encountered (see #resolver). If a nil resolver is passed (the default), the default resolver will be used, which keeps tied middlewares in the order they are passed to #sort.

Since:

  • 0.1.0



96
97
98
99
100
101
102
103
# File 'lib/middlegem/array_definition.rb', line 96

def initialize(defined_classes, resolver: nil)
  resolver = ->(*ties) { ties } if resolver.nil?

  @defined_classes = defined_classes
  @resolver = resolver

  super()
end

Instance Attribute Details

#defined_classesArray<Class>

An array of the middleware classes defined by this Middlegem::ArrayDefinition. Middlewares will only be permitted if their class is in this array will be run in the order specified here.

Returns:

  • (Array<Class>)

    the array of defined classes.

Since:

  • 0.1.0



79
80
81
# File 'lib/middlegem/array_definition.rb', line 79

def defined_classes
  @defined_classes
end

#resolver#call (readonly)

The callable object to use to break ties when sorting middlewares. When multiple middlewares of the same type are encountered, this object will be called with an array of all tied middlewares. The resolver should sort and return the array as appropriate.

Returns:

  • (#call)

    the middleware tie resolver.

Since:

  • 0.1.0



86
87
88
# File 'lib/middlegem/array_definition.rb', line 86

def resolver
  @resolver
end

Instance Method Details

#defined?(middleware) ⇒ bool

Determines whether the given middleware is defined according to this Middlegem::ArrayDefinition by checking whether its class is contained in the list of defined classes (i.e. #defined_classes).

Parameters:

  • middleware (Object)

    the middleware to check.

Returns:

  • (bool)

    whether the middleware is defined.

Since:

  • 0.1.0



110
111
112
# File 'lib/middlegem/array_definition.rb', line 110

def defined?(middleware)
  defined_classes.any? { |c| matches_class?(middleware, c) }
end

#matches(middlewares, klass) ⇒ Array<Object> (private)

Gets all the middlewares in the given array whose class is the given class.

Parameters:

  • middlewares (Array<Object>)

    the array of middlewares to search.

  • klass (Class)

    the class to search for.

Returns:

  • (Array<Object>)

    the matched middlewares.

Since:

  • 0.1.0



144
145
146
# File 'lib/middlegem/array_definition.rb', line 144

def matches(middlewares, klass)
  middlewares.select { |m| matches_class?(m, klass) }
end

#matches_class?(middleware, klass) ⇒ Boolean (protected)

Should determine whether the given middleware’s evaluated class is equal to the given one. The default implementation naturally just uses instance_of?, but you are free to override this method for other situations. You may want is use is_a? instead, for example, or perhaps a middleware’s “class” is based on some other criterion.

Parameters:

  • middleware (Object)

    the middleware to check.

  • klass (Class)

    the class against which to check the middleware.

Returns:

  • (Boolean)

    whether the given middleware has the given class.

Since:

  • 0.2.0



134
135
136
# File 'lib/middlegem/array_definition.rb', line 134

def matches_class?(middleware, klass)
  middleware.instance_of? klass
end

#sort(middlewares) ⇒ Array<Object>

Sorts the given array of middlewares according to this Middlegem::ArrayDefinition. Middlewares are sorted according to the order in which their classes are specified in #defined_classes. If multiple middlewares of the same type are encountered, they will be resolved with the #resolver.

Parameters:

  • middlewares (Array<Object>)

    the middlewares to sort.

Returns:

  • (Array<Object>)

    the sorted middlewares.

Since:

  • 0.1.0



120
121
122
# File 'lib/middlegem/array_definition.rb', line 120

def sort(middlewares)
  defined_classes.map { |c| resolver.call(matches(middlewares, c)) }.flatten
end