Class: Wurk::Middleware::Chain

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/wurk/middleware/chain.rb

Overview

Ordered list of middleware bound to a config/capsule. Sidekiq contract: ‘add` appends (removing any existing entry for the same klass), `prepend` pushes to index 0, `insert_before`/`insert_after` anchor relative to an existing entry, `invoke` walks the chain handing the inner block to each.

‘retrieve` instantiates a fresh instance per entry on every job — entries store klass + ctor args, not the live object. This keeps middleware thread-safe by construction and matches Sidekiq’s lifecycle.

Spec: docs/target/sidekiq-free.md §10.1.

Defined Under Namespace

Classes: Entry

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = nil) {|_self| ... } ⇒ Chain

Returns a new instance of Chain.

Yields:

  • (_self)

Yield Parameters:



21
22
23
24
25
# File 'lib/wurk/middleware/chain.rb', line 21

def initialize(config = nil)
  @config = config
  @entries = []
  yield self if block_given?
end

Instance Attribute Details

#configObject

Returns the value of attribute config.



19
20
21
# File 'lib/wurk/middleware/chain.rb', line 19

def config
  @config
end

#entriesObject (readonly)

Returns the value of attribute entries.



18
19
20
# File 'lib/wurk/middleware/chain.rb', line 18

def entries
  @entries
end

Instance Method Details

#add(klass) ⇒ Object



42
43
44
45
# File 'lib/wurk/middleware/chain.rb', line 42

def add(klass, *)
  remove(klass)
  @entries << Entry.new(klass, *)
end

#clearObject



79
80
81
# File 'lib/wurk/middleware/chain.rb', line 79

def clear
  @entries.clear
end

#copy_for(capsule) ⇒ Object

Bind a duplicate of this chain to ‘capsule`. Capsules share the parent config’s entries by reference initially but mutate independently after the first ‘add`/`remove`/etc. (Array#dup on `@entries`).



32
33
34
35
36
# File 'lib/wurk/middleware/chain.rb', line 32

def copy_for(capsule)
  copy = dup
  copy.instance_variable_set(:@config, capsule)
  copy
end

#eachObject



27
# File 'lib/wurk/middleware/chain.rb', line 27

def each(&) = @entries.each(&)

#empty?Boolean

Returns:

  • (Boolean)


71
72
73
# File 'lib/wurk/middleware/chain.rb', line 71

def empty?
  @entries.empty?
end

#exists?(klass) ⇒ Boolean Also known as: include?

Returns:

  • (Boolean)


66
67
68
# File 'lib/wurk/middleware/chain.rb', line 66

def exists?(klass)
  any? { |entry| entry.klass == klass }
end

#insert_after(oldklass, newklass) ⇒ Object



59
60
61
62
63
64
# File 'lib/wurk/middleware/chain.rb', line 59

def insert_after(oldklass, newklass, *)
  i = @entries.index { |entry| entry.klass == newklass }
  new_entry = i.nil? ? Entry.new(newklass, *) : @entries.delete_at(i)
  i = @entries.index { |entry| entry.klass == oldklass } || (@entries.size - 1)
  @entries.insert(i + 1, new_entry)
end

#insert_before(oldklass, newklass) ⇒ Object



52
53
54
55
56
57
# File 'lib/wurk/middleware/chain.rb', line 52

def insert_before(oldklass, newklass, *)
  i = @entries.index { |entry| entry.klass == newklass }
  new_entry = i.nil? ? Entry.new(newklass, *) : @entries.delete_at(i)
  i = @entries.index { |entry| entry.klass == oldklass } || 0
  @entries.insert(i, new_entry)
end

#invoke(*args, &block) ⇒ Object

Walks the chain inside-out. Each middleware receives a block that advances to the next; the innermost block is the caller’s ‘&block`, whose return value is propagated back out. Empty chain: `yield`.

Raises:

  • (ArgumentError)


86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/wurk/middleware/chain.rb', line 86

def invoke(*args, &block)
  raise ArgumentError, 'middleware chain requires a block' unless block
  return yield if @entries.empty?

  chain = retrieve
  traverse = lambda do
    if chain.empty?
      block.call
    else
      chain.shift.call(*args, &traverse)
    end
  end
  traverse.call
end

#prepend(klass) ⇒ Object



47
48
49
50
# File 'lib/wurk/middleware/chain.rb', line 47

def prepend(klass, *)
  remove(klass)
  @entries.unshift(Entry.new(klass, *))
end

#remove(klass) ⇒ Object



38
39
40
# File 'lib/wurk/middleware/chain.rb', line 38

def remove(klass)
  @entries.delete_if { |entry| entry.klass == klass }
end

#retrieveObject



75
76
77
# File 'lib/wurk/middleware/chain.rb', line 75

def retrieve
  map { |entry| entry.make_new(@config) }
end