Class: Cloudenvoy::Middleware::Chain

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

Overview

The class below was originally taken from Sidekiq. See: github.com/mperham/sidekiq/blob/master/lib/sidekiq/middleware/chain.rb

Middleware are callables configured to run before/after a message is processed. Middlewares can be configured to run on the client side (when jobs are pushed to Cloud Tasks) as well as on the server side (when jobs are processed by your application)

To add a middleware on publishers:

Cloudenvoy.configure do |config|

config.publisher_middleware do |chain|
  chain.add MyPublisherHook
end

end

To modify middlewares on subscribers, just call with another block:

Cloudenvoy.configure do |config|

config.subscriber_middleware do |chain|
  chain.add MySubscriberHook
  chain.remove ActiveRecord
end

end

To insert immediately preceding another entry:

Cloudenvoy.configure do |config|

config.publisher_middleware do |chain|
  chain.insert_before ActiveRecord, MyPublisherHook
end

end

To insert immediately after another entry:

Cloudenvoy.configure do |config|

config.publisher_middleware do |chain|
  chain.insert_after ActiveRecord, MyPublisherHook
end

end

This is an example of a minimal server middleware:

class MySubscriberHook

def call(subscriber, msg, queue)
  puts "Before work"
  yield
  puts "After work"
end

end

This is an example of a minimal client middleware, note the method must return the result or the job will not push to Redis:

class MyPublisherHook

def call(publisher, msg, queue, redis_pool)
  puts "Before push"
  result = yield
  puts "After push"
  result
end

end

Instance Method Summary collapse

Constructor Details

#initialize {|_self| ... } ⇒ Chain

Build a new middleware chain.

Yields:

  • (_self)

Yield Parameters:



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

def initialize
  @entries = nil
  yield self if block_given?
end

Instance Method Details

#add(klass, *args) ⇒ Array<Cloudenvoy::Middleware::Chain::Entry>

Add a middleware at the end of the list.

Parameters:

  • klass (Class)

    The middleware class to add.

  • *args (Arry<any>)

    The list of arguments to the middleware.

Returns:

  • (Array<Cloudenvoy::Middleware::Chain::Entry>)

    The updated list of middlewares



116
117
118
119
# File 'lib/cloudenvoy/middleware/chain.rb', line 116

def add(klass, *args)
  remove(klass) if exists?(klass)
  entries << Entry.new(klass, *args)
end

#clearArray<Cloudenvoy::Middleware::Chain::Entry>

Empty the list of middlewares.

Returns:

  • (Array<Cloudenvoy::Middleware::Chain::Entry>)

    The updated list of middlewares



201
202
203
# File 'lib/cloudenvoy/middleware/chain.rb', line 201

def clear
  entries.clear
end

#each(&block) ⇒ Object

Iterate over the list middlewares and execute the block on each item.

Parameters:

  • &block (Proc)

    The block to execute on each item.



86
87
88
# File 'lib/cloudenvoy/middleware/chain.rb', line 86

def each(&block)
  entries.each(&block)
end

#empty?Boolean

Checks if the middlware list is empty

Returns:

  • (Boolean)

    Return true if the middleware list is empty.



182
183
184
# File 'lib/cloudenvoy/middleware/chain.rb', line 182

def empty?
  @entries.nil? || @entries.empty?
end

#entriesArray<Cloudenvoy::Middleware::Chain::Entry>

Return the list of middlewares.

Returns:

  • (Array<Cloudenvoy::Middleware::Chain::Entry>)

    The list of middlewares



95
96
97
# File 'lib/cloudenvoy/middleware/chain.rb', line 95

def entries
  @entries ||= []
end

#exists?(klass) ⇒ Boolean

Checks if middleware has been added to the list.

Parameters:

  • klass (Class)

    The middleware class to check.

Returns:

  • (Boolean)

    Return true if the middleware is in the list.



173
174
175
# File 'lib/cloudenvoy/middleware/chain.rb', line 173

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

#insert_after(oldklass, newklass, *args) ⇒ Array<Cloudenvoy::Middleware::Chain::Entry>

Add a middleware after another middleware.

Parameters:

  • oldklass (Class)

    The middleware class after which the new middleware should be inserted.

  • newklass (Class)

    The middleware class to insert.

  • *args (Arry<any>)

    The list of arguments for the inserted middleware.

Returns:

  • (Array<Cloudenvoy::Middleware::Chain::Entry>)

    The updated list of middlewares



159
160
161
162
163
164
# File 'lib/cloudenvoy/middleware/chain.rb', line 159

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

#insert_before(oldklass, newklass, *args) ⇒ Array<Cloudenvoy::Middleware::Chain::Entry>

Add a middleware before another middleware.

Parameters:

  • oldklass (Class)

    The middleware class before which the new middleware should be inserted.

  • newklass (Class)

    The middleware class to insert.

  • *args (Arry<any>)

    The list of arguments for the inserted middleware.

Returns:

  • (Array<Cloudenvoy::Middleware::Chain::Entry>)

    The updated list of middlewares



143
144
145
146
147
148
# File 'lib/cloudenvoy/middleware/chain.rb', line 143

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

#invoke(*args) ⇒ Object

Invoke the chain of middlewares.

Parameters:

  • *args (Array<any>)

    The args to pass to each middleware.



210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/cloudenvoy/middleware/chain.rb', line 210

def invoke(*args)
  return yield if empty?

  chain = retrieve.dup
  traverse_chain = lambda do
    if chain.empty?
      yield
    else
      chain.shift.call(*args, &traverse_chain)
    end
  end
  traverse_chain.call
end

#prepend(klass, *args) ⇒ Array<Cloudenvoy::Middleware::Chain::Entry>

Add a middleware at the beginning of the list.

Parameters:

  • klass (Class)

    The middleware class to add.

  • *args (Arry<any>)

    The list of arguments to the middleware.

Returns:

  • (Array<Cloudenvoy::Middleware::Chain::Entry>)

    The updated list of middlewares



129
130
131
132
# File 'lib/cloudenvoy/middleware/chain.rb', line 129

def prepend(klass, *args)
  remove(klass) if exists?(klass)
  entries.insert(0, Entry.new(klass, *args))
end

#remove(klass) ⇒ Object

Remove a middleware from the list.

Parameters:

  • klass (Class)

    The middleware class to remove.



104
105
106
# File 'lib/cloudenvoy/middleware/chain.rb', line 104

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

#retrieveArray<any>

Return a list of instantiated middlewares. Each middleware gets initialize with the args originally passed to ‘add`, `insert_before` etc.

Returns:

  • (Array<any>)

    The list of instantiated middlewares.



192
193
194
# File 'lib/cloudenvoy/middleware/chain.rb', line 192

def retrieve
  map(&:make_new)
end