Implement multistep
This commit is contained in:
parent
49d299956f
commit
683bbdaa3c
|
@ -56,6 +56,7 @@ module Vagrant
|
|||
#
|
||||
class Action
|
||||
autoload :Environment, 'vagrant/action/environment'
|
||||
autoload :MultiStep, 'vagrant/action/multistep'
|
||||
autoload :Step, 'vagrant/action/step'
|
||||
autoload :Warden, 'vagrant/action/warden'
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
module Vagrant
|
||||
class Action
|
||||
class MultiStep < Step
|
||||
def initialize
|
||||
@steps = []
|
||||
end
|
||||
|
||||
def step(step_class, *extra_inputs)
|
||||
# Get the options hash and set the defaults
|
||||
options = {}
|
||||
options = extra_inputs.pop if extra_inputs.last.kind_of?(Hash)
|
||||
|
||||
# Append the step
|
||||
@steps << [step_class, extra_inputs, options]
|
||||
end
|
||||
|
||||
def call(params=nil)
|
||||
params ||= {}
|
||||
|
||||
# Instantiate all the steps
|
||||
instances = @steps.map { |s, inputs, options| [s.new, inputs, options] }
|
||||
|
||||
# For each step, call it with proper inputs, using the output
|
||||
# of that call as inputs to the next.
|
||||
instances.inject(params) do |inputs, data|
|
||||
step, extra_inputs, options = data
|
||||
|
||||
# If there are extra inputs for this step, add them to the
|
||||
# parameters based on the initial parameters.
|
||||
extra_inputs.each do |extra_input|
|
||||
inputs[extra_input] = params[extra_input]
|
||||
end
|
||||
|
||||
# If we have inputs to remap, remap them.
|
||||
if options[:map]
|
||||
options[:map].each do |from, to|
|
||||
# This sets the input to the new key while removing the
|
||||
# the old key from the same hash. Kind of sneaky, but
|
||||
# hopefully this comment makes it clear.
|
||||
inputs[to] = inputs.delete(from)
|
||||
end
|
||||
end
|
||||
|
||||
# Call the actual step, using the results for the next
|
||||
# iteration.
|
||||
step.call(inputs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -33,9 +33,15 @@ module Vagrant
|
|||
|
||||
# Validates that the output matches the specification provided, and
|
||||
# raises a RuntimeError if it does not.
|
||||
def self.validate_output(value)
|
||||
def self.process_output(value)
|
||||
# The return value must be a Hash, so we just coerce it to that.
|
||||
value = {} if !value.kind_of?(Hash)
|
||||
|
||||
# Verify that we have all the outputs
|
||||
missing = outputs - value.keys
|
||||
raise RuntimeError, "Missing output keys: #{missing}" if !missing.empty?
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
# This calls the step with the given parameters, and returns a hash
|
||||
|
@ -49,7 +55,7 @@ module Vagrant
|
|||
# @option options [Boolean] :validate_output Whether to validate the
|
||||
# output or not.
|
||||
# @return [Hash] Output
|
||||
def call(params, options=nil)
|
||||
def call(params={}, options=nil)
|
||||
options = {
|
||||
:method => :execute,
|
||||
:validate_output => true
|
||||
|
@ -61,8 +67,9 @@ module Vagrant
|
|||
# Call the actual implementation
|
||||
results = send(options[:method])
|
||||
|
||||
# Validate the outputs
|
||||
self.class.validate_output(results) if options[:validate_output]
|
||||
# Validate the outputs if it is enabled and the list of configured
|
||||
# outputs is not empty.
|
||||
results = self.class.process_output(results) if options[:validate_output]
|
||||
|
||||
# Return the final results
|
||||
results
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
require File.expand_path("../../../base", __FILE__)
|
||||
|
||||
describe Vagrant::Action::MultiStep do
|
||||
it "should compose a series of steps" do
|
||||
step_A = Class.new(Vagrant::Action::Step) do
|
||||
input :obj
|
||||
output :obj
|
||||
|
||||
def execute
|
||||
@obj << "A"
|
||||
return :obj => @obj
|
||||
end
|
||||
end
|
||||
|
||||
step_B = Class.new(Vagrant::Action::Step) do
|
||||
input :obj
|
||||
output :result
|
||||
|
||||
def execute
|
||||
return :result => (@obj << "B")
|
||||
end
|
||||
end
|
||||
|
||||
obj = []
|
||||
|
||||
ms = described_class.new
|
||||
ms.step step_A
|
||||
ms.step step_B
|
||||
ms.call(:obj => obj).should == { :result => ["A", "B"] }
|
||||
end
|
||||
|
||||
it "should allow for custom inputs to pass to specific steps" do
|
||||
step_A = Class.new(Vagrant::Action::Step) do
|
||||
def execute
|
||||
# Do nothing.
|
||||
end
|
||||
end
|
||||
|
||||
step_B = Class.new(Vagrant::Action::Step) do
|
||||
input :obj
|
||||
|
||||
def execute
|
||||
@obj << "B"
|
||||
end
|
||||
end
|
||||
|
||||
obj = []
|
||||
|
||||
ms = described_class.new
|
||||
ms.step step_A
|
||||
ms.step step_B, :obj
|
||||
ms.call(:obj => obj)
|
||||
|
||||
obj.should == ["B"]
|
||||
end
|
||||
|
||||
it "should be able to remap input names" do
|
||||
step_A = Class.new(Vagrant::Action::Step) do
|
||||
output :foo
|
||||
|
||||
def execute
|
||||
return :foo => "A"
|
||||
end
|
||||
end
|
||||
|
||||
step_B = Class.new(Vagrant::Action::Step) do
|
||||
input :from
|
||||
output :value
|
||||
|
||||
def execute
|
||||
return :value => @from
|
||||
end
|
||||
end
|
||||
|
||||
obj = []
|
||||
|
||||
ms = described_class.new
|
||||
ms.step step_A
|
||||
ms.step step_B, :map => { :foo => :from }
|
||||
ms.call.should == { :value => "A" }
|
||||
end
|
||||
end
|
|
@ -18,7 +18,7 @@ describe Vagrant::Action::Step do
|
|||
input :foo
|
||||
end
|
||||
|
||||
expect { step_class.new.call({}) }.to raise_error(ArgumentError)
|
||||
expect { step_class.new.call }.to raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "calls a custom method if given" do
|
||||
|
@ -31,27 +31,39 @@ describe Vagrant::Action::Step do
|
|||
step_class.new.call({}, :method => :prepare).should == { :foo => 12 }
|
||||
end
|
||||
|
||||
it "raises an exception if missing outputs" do
|
||||
step_class = Class.new(described_class) do
|
||||
output :foo
|
||||
|
||||
def execute
|
||||
return :bar => 12
|
||||
describe "outputs" do
|
||||
it "return an empty hash if no outputs are specified" do
|
||||
step_class = Class.new(described_class) do
|
||||
def execute
|
||||
return 12
|
||||
end
|
||||
end
|
||||
|
||||
step_class.new.call.should == {}
|
||||
end
|
||||
|
||||
expect { step_class.new.call({}) }.to raise_error(RuntimeError)
|
||||
end
|
||||
it "raises an exception if missing outputs" do
|
||||
step_class = Class.new(described_class) do
|
||||
output :foo
|
||||
|
||||
it "does nothing if missing outputs but we disabled validating" do
|
||||
step_class = Class.new(described_class) do
|
||||
output :foo
|
||||
|
||||
def execute
|
||||
return :bar => 12
|
||||
def execute
|
||||
return :bar => 12
|
||||
end
|
||||
end
|
||||
|
||||
expect { step_class.new.call }.to raise_error(RuntimeError)
|
||||
end
|
||||
|
||||
step_class.new.call({}, :validate_output => false).should == { :bar => 12 }
|
||||
it "does nothing if missing outputs but we disabled validating" do
|
||||
step_class = Class.new(described_class) do
|
||||
output :foo
|
||||
|
||||
def execute
|
||||
return :bar => 12
|
||||
end
|
||||
end
|
||||
|
||||
step_class.new.call({}, :validate_output => false).should == { :bar => 12 }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue