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.

Entries store klass + ctor args, not the live object: every job gets a fresh instance per entry (invoke builds each as it reaches it, retrieve builds them all up front). 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:



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

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

Instance Attribute Details

#configObject

Returns the value of attribute config.



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

def config
  @config
end

#entriesObject (readonly)

Returns the value of attribute entries.



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

def entries
  @entries
end

Instance Method Details

#add(klass) ⇒ Object



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

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

#clearObject



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

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).



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

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

#eachObject



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

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

#empty?Boolean

Returns:

  • (Boolean)


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

def empty?
  @entries.empty?
end

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

Returns:

  • (Boolean)


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

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

#insert_after(oldklass, newklass) ⇒ Object



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

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



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

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.

Hot path — one job pays this per invocation, so instantiation is folded into an index walk over @entries: no instance array from retrieve, no traversal lambda, and no shift (which memmoves the array per entry). Cost is one allocation per entry (the middleware itself), zero to walk.

Raises:

  • (ArgumentError)


92
93
94
95
96
97
# File 'lib/wurk/middleware/chain.rb', line 92

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

  traverse(@entries, 0, args, &block)
end

#prepend(klass) ⇒ Object



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

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

#remove(klass) ⇒ Object



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

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

#retrieveObject



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

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