diff --git a/lib/vagrant/action/builder.rb b/lib/vagrant/action/builder.rb index 4be32d2fd..f10d3e58a 100644 --- a/lib/vagrant/action/builder.rb +++ b/lib/vagrant/action/builder.rb @@ -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 diff --git a/lib/vagrant/action/environment.rb b/lib/vagrant/action/environment.rb new file mode 100644 index 000000000..974ae4483 --- /dev/null +++ b/lib/vagrant/action/environment.rb @@ -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 diff --git a/lib/vagrant/actions/middleware_stack.rb b/lib/vagrant/actions/middleware_stack.rb deleted file mode 100644 index f28bf16b7..000000000 --- a/lib/vagrant/actions/middleware_stack.rb +++ /dev/null @@ -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 diff --git a/test/vagrant/action/builder_test.rb b/test/vagrant/action/builder_test.rb index ad6f8d9f3..a0581b20d 100644 --- a/test/vagrant/action/builder_test.rb +++ b/test/vagrant/action/builder_test.rb @@ -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 diff --git a/test/vagrant/action/environment_test.rb b/test/vagrant/action/environment_test.rb new file mode 100644 index 000000000..1d9ef3d2d --- /dev/null +++ b/test/vagrant/action/environment_test.rb @@ -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