Class: CMDx::Middlewares

Inherits:
Object
  • Object
show all
Defined in:
lib/cmdx/middlewares.rb

Overview

Ordered list of middlewares wrapping a task’s lifecycle. Each middleware is a callable with the signature ‘call(task) { next_link.call }`; Runtime builds a nested chain and requires each middleware to yield to the next.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeMiddlewares

Returns a new instance of Middlewares.



11
12
13
# File 'lib/cmdx/middlewares.rb', line 11

def initialize
  @registry = []
end

Instance Attribute Details

#registryObject (readonly)

Returns the value of attribute registry.



9
10
11
# File 'lib/cmdx/middlewares.rb', line 9

def registry
  @registry
end

Instance Method Details

#deregister(middleware = nil, at: nil) ⇒ Middlewares

Removes a middleware by reference or by index.

Parameters:

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

    the exact middleware to remove

  • at (Integer, nil) (defaults to: nil)

    index to remove

Returns:

Raises:

  • (ArgumentError)

    when neither or both of ‘middleware`/`:at` are given, or when `:at` isn’t an Integer



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/cmdx/middlewares.rb', line 66

def deregister(middleware = nil, at: nil)
  if at.nil? && middleware.nil?
    raise ArgumentError, "provide either a middleware or an at: index"
  elsif !at.nil? && !middleware.nil?
    raise ArgumentError, "provide either a middleware or an at: index, not both"
  elsif !at.nil? && !at.is_a?(Integer)
    raise ArgumentError, "at must be an Integer"
  end

  if at.nil?
    registry.reject! { |mw, _opts| mw == middleware }
  else
    registry.delete_at(at)
  end

  self
end

#empty?Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/cmdx/middlewares.rb', line 85

def empty?
  registry.empty?
end

#initialize_copy(source) ⇒ void

This method returns an undefined value.

Parameters:



17
18
19
# File 'lib/cmdx/middlewares.rb', line 17

def initialize_copy(source)
  @registry = source.registry.dup
end

#process(task) { ... } ⇒ void

This method returns an undefined value.

Walks the middleware chain around ‘task`’s lifecycle. The final link yields to ‘block`, which is expected to run the actual lifecycle.

Parameters:

Yields:

  • the innermost link — the task’s lifecycle body

Raises:

  • (MiddlewareError)

    when a middleware forgets to yield to ‘next_link`, which would otherwise silently skip the task



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/cmdx/middlewares.rb', line 102

def process(task)
  processed = false
  count = registry.size

  chain = lambda do |i|
    if i == count
      processed = true
      yield
    else
      mw, opts = registry[i]

      if Util.satisfied?(opts[:if], opts[:unless], task)
        mw.call(task) { chain.call(i + 1) }
      else
        chain.call(i + 1)
      end
    end
  end
  chain.call(0)

  processed || begin
    raise MiddlewareError, "middleware did not yield the next_link"
  end
end

#register(callable = nil, **options, &block) { ... } ⇒ Middlewares

Inserts a middleware. With no ‘:at`, appends. With `:at`, inserts at the given (clamped) index — supports negative indexing. `:if`/`:unless` gates evaluated against the task at process time.

Parameters:

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

    provide either this or a block

  • block (#call, nil)

    middleware callable when ‘callable` is omitted

  • options (Hash{Symbol => Object})

Options Hash (**options):

  • :if (Symbol, Proc, #call)

    gate that must evaluate truthy

  • :unless (Symbol, Proc, #call)

    gate that must evaluate falsy

  • :at (Integer)

    insertion index (see implementation)

Yields:

  • the middleware body, receiving ‘(task)` and `next_link` via block

Returns:

Raises:

  • (ArgumentError)

    when both or neither of ‘callable`/block are given, when the callable doesn’t respond to ‘#call`, or when `:at` isn’t an Integer



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/cmdx/middlewares.rb', line 35

def register(callable = nil, **options, &block)
  middleware = callable || block
  at = options.delete(:at)

  if callable && block
    raise ArgumentError, "provide either a callable or a block, not both"
  elsif !middleware.respond_to?(:call)
    raise ArgumentError, "middleware must respond to #call"
  elsif !at.nil? && !at.is_a?(Integer)
    raise ArgumentError, "at must be an Integer"
  end

  entry = [middleware, options.freeze]

  if at.nil?
    registry << entry
  else
    at = [at.clamp(-registry.size - 1, registry.size), registry.size].min
    registry.insert(at, entry)
  end

  self
end

#sizeInteger

Returns:

  • (Integer)


90
91
92
# File 'lib/cmdx/middlewares.rb', line 90

def size
  registry.size
end