Builder fully tested

This commit is contained in:
Mitchell Hashimoto 2010-07-03 18:51:40 +02:00
parent 6eefc8e874
commit c88adbc0f7
5 changed files with 87 additions and 26 deletions

View File

@ -1,26 +1,50 @@
module Vagrant
class Action
# 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.
class Builder
# Initializes the builder. An optional block can be passed which
# will be evaluated in the context of the instance.
def initialize(&block)
instance_eval(&block) if block_given?
end
# Returns the current stack of middlewares. You probably won't
# need to use this directly, and its recommended that you don't.
#
# @return [Array]
def stack
@stack ||= []
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
def use(middleware, *args, &block)
stack << [middleware, args, block]
end
def to_app
inner = @ins.last
# Converts the builder stack to a runnable action sequence.
#
# @param [Vagrant::Action::Environment] env The action environment
# @return [Object] A callable object
def to_app(env)
items = stack.collect do |item|
klass, args, block = item
lambda { |app| klass.new(app, env, *args, &block) }
end
@ins[0...-1].reverse.inject(inner) { |a,e| e.call(a) }
items << lambda { |env| } # The final step, which simply returns
items[0...-1].reverse.inject(items.last) { |a,e| e.call(a) }
end
# Runs the builder stack with the given environment.
def call(env)
to_app.call(env)
to_app(env).call(env)
end
end
end

View File

@ -0,0 +1,17 @@
module Vagrant
class Action
# Represents an action environment which is what is passed
# to the `call` method of each action. This environment contains
# some helper methods for accessing the environment as well
# as being a hash, to store any additional options.
class Environment < Hash
# The {Vagrant::Environment} object represented by this
# action environment.
attr_reader :env
def new(env)
@env = env
end
end
end
end

View File

@ -1,22 +0,0 @@
module Vagrant
module Actions
# Represents a middleware stack for Vagrant actions. Vagrant
# actions are created and can be extended with middlewares.
#
# The exact nature of how this will work is not set in stone.
class MiddlewareStack
# Initializes the middleware stack with the given name.
def initialize(key)
@stack = []
end
def use(klass)
@stack << klass
end
def run(endpoint)
@stack << endpoint
end
end
end
end

View File

@ -36,7 +36,41 @@ class ActionBuilderTest < Test::Unit::TestCase
end
context "converting to an app" do
should "initialize each middleware with app and env" do
# TODO: better testing of this method... somehow
result = mock("result")
env = {:a => :b}
middleware = mock("middleware")
middleware.expects(:new).with(anything, env).returns(result)
@instance.use middleware
assert_equal result, @instance.to_app(env)
end
end
context "calling" do
def mock_middleware
middleware = Class.new do
def initialize(app, env)
@app = app
end
def call(env)
@app.call(env)
end
end
end
should "convert to an app then call with the env" do
mw = mock_middleware
mw.any_instance.expects(:call).with() do |env|
assert env.has_key?(:key)
true
end
@instance.use(mw)
@instance.call(:key => :value)
end
end
end
end

View File

@ -0,0 +1,8 @@
require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
class ActionEnvironmentTest < Test::Unit::TestCase
setup do
@klass = Vagrant::Action::Environment
end
end