Action builder which provides a nice DSL for building up a middleware sequence for Vagrant actions. This code is based heavily off of `Rack::Builder` and `ActionDispatch::MiddlewareStack` in Rack and Rails, respectively.
Usage
Building an action sequence is very easy:
app = Vagrant::Action::Builder.new.tap do |b| b.use MiddlewareA b.use MiddlewareB end Vagrant::Action.run(app)
This is the stack of middlewares added. This should NOT be used directly.
@return [Array]
This is a shortcut for a middleware sequence with only one item in it. For a description of the arguments and the documentation, please see {#use} instead.
@return [Builder]
# File lib/vagrant/action/builder.rb, line 31 def self.build(middleware, *args, &block) new.use(middleware, *args, &block) end
# File lib/vagrant/action/builder.rb, line 35 def initialize @stack = [] end
Runs the builder stack with the given environment.
# File lib/vagrant/action/builder.rb, line 115 def call(env) to_app(env).call(env) end
Deletes the given middleware object or index
# File lib/vagrant/action/builder.rb, line 109 def delete(index) index = self.index(index) unless index.is_a?(Integer) stack.delete_at(index) end
Returns a mergeable version of the builder. If `use` is called with the return value of this method, then the stack will merge, instead of being treated as a separate single middleware.
# File lib/vagrant/action/builder.rb, line 50 def flatten lambda do |env| self.call(env) end end
Returns the numeric index for the given middleware object.
@param [Object] object The item to find the index for @return [Integer]
# File lib/vagrant/action/builder.rb, line 123 def index(object) stack.each_with_index do |item, i| return i if item[0] == object return i if item[0].respond_to?(:name) && item[0].name == object end nil end
Implement a custom copy that copies the stack variable over so that we don't clobber that.
# File lib/vagrant/action/builder.rb, line 41 def initialize_copy(original) super @stack = original.stack.dup end
Inserts a middleware at the given index or directly before the given middleware object.
# File lib/vagrant/action/builder.rb, line 74 def insert(index, middleware, *args, &block) index = self.index(index) unless index.is_a?(Integer) raise "no such middleware to insert before: #{index.inspect}" unless index if middleware.kind_of?(Builder) middleware.stack.reverse.each do |stack_item| stack.insert(index, stack_item) end else stack.insert(index, [middleware, args, block]) end end
Inserts a middleware after the given index or middleware object.
# File lib/vagrant/action/builder.rb, line 90 def insert_after(index, middleware, *args, &block) index = self.index(index) unless index.is_a?(Integer) raise "no such middleware to insert after: #{index.inspect}" unless index insert(index + 1, middleware, *args, &block) end
Replaces the given middlware object or index with the new middleware.
# File lib/vagrant/action/builder.rb, line 98 def replace(index, middleware, *args, &block) if index.is_a?(Integer) delete(index) insert(index, middleware, *args, &block) else insert_before(index, middleware, *args, &block) delete(index) end end
Converts the builder stack to a runnable action sequence.
@param [Hash] env The action environment hash @return [Object] A callable object
# File lib/vagrant/action/builder.rb, line 136 def to_app(env) app_stack = nil # If we have action hooks, then we apply them if env[:action_hooks] builder = self.dup # These are the options to pass into hook application. options = {} # If we already ran through once and did append/prepends, # then don't do it again. if env[:action_hooks_already_ran] options[:no_prepend_or_append] = true end # Specify that we already ran, so in the future we don't repeat # the prepend/append hooks. env[:action_hooks_already_ran] = true # Apply all the hooks to the new builder instance env[:action_hooks].each do |hook| hook.apply(builder, options) end # The stack is now the result of the new builder app_stack = builder.stack.dup end # If we don't have a stack then default to using our own app_stack ||= stack.dup # Wrap the middleware stack with the Warden to provide a consistent # and predictable behavior upon exceptions. Warden.new(app_stack, env) end
Adds a middleware class to the middleware stack. Any additional args and a block, if given, are saved and passed to the initializer of the middleware.
@param [Class] middleware The middleware class
# File lib/vagrant/action/builder.rb, line 61 def use(middleware, *args, &block) if middleware.kind_of?(Builder) # Merge in the other builder's stack into our own self.stack.concat(middleware.stack) else self.stack << [middleware, args, block] end self end