Remove step stuff. Failed experiment for now. Too radical for point release.
This commit is contained in:
parent
b5ae4672b7
commit
5bfcbcba66
|
@ -1,186 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
class Action
|
|
||||||
class MultiStep < Step
|
|
||||||
# This class is used as a placeholder to represent a parameter that
|
|
||||||
# needs to be replaced at runtime. For example: A step might take a
|
|
||||||
# parameter B that is outputted as A from a previous step. An instance
|
|
||||||
# of this class can represent that the previous A should be remapped
|
|
||||||
# to this new B.
|
|
||||||
class Param
|
|
||||||
attr_reader :name
|
|
||||||
attr_reader :variable
|
|
||||||
|
|
||||||
def initialize(name, variable)
|
|
||||||
@name = name
|
|
||||||
@variable = variable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Represents a remapping that comes from the group inputs.
|
|
||||||
class GroupInput < Param; end
|
|
||||||
|
|
||||||
# Represents a remapping that comes from the output of a previous step.
|
|
||||||
class StepOutput < Param; end
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@step_names = []
|
|
||||||
@steps = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
# This returns a custom object that represents an input parameter
|
|
||||||
# given to this group.
|
|
||||||
#
|
|
||||||
# @param [Symbol] key Parameter name of the input to use from the group.
|
|
||||||
# @return [GroupInput] A `param` type that can be used for remappings.
|
|
||||||
def input(key)
|
|
||||||
return GroupInput.new(:global, key)
|
|
||||||
end
|
|
||||||
|
|
||||||
# This returns a custom object that represents an output parameter
|
|
||||||
# from another step in this group.
|
|
||||||
#
|
|
||||||
# @param [Object] name Name of the step. This is either an explicit name
|
|
||||||
# like a symbol or the class for the step if it is unique.
|
|
||||||
# @param [Symbol] output The output variable name from the step.
|
|
||||||
# @return [StepOutput] A `param` type that can be used for remappings.
|
|
||||||
def output(name, output)
|
|
||||||
return StepOutput.new(name, output)
|
|
||||||
end
|
|
||||||
|
|
||||||
def step(step_class, *extra_inputs)
|
|
||||||
# Determine the name for this step.
|
|
||||||
step_name = nil
|
|
||||||
if step_class.is_a?(Symbol)
|
|
||||||
step_name = step_class
|
|
||||||
step_class = extra_inputs.shift
|
|
||||||
else
|
|
||||||
step_name = step_class
|
|
||||||
end
|
|
||||||
|
|
||||||
if @steps.has_key?(step_name)
|
|
||||||
raise NameError, "Step with name #{step_name} already exists."
|
|
||||||
elsif !step_class.is_a?(Class)
|
|
||||||
raise ArgumentError, "Step class must be a class."
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the options hash and set the defaults
|
|
||||||
maps = {}
|
|
||||||
maps = extra_inputs.pop if extra_inputs.last.kind_of?(Hash)
|
|
||||||
|
|
||||||
# Go over each extra input and handle the mapping. This is typically
|
|
||||||
# syntactic sugar.
|
|
||||||
extra_inputs.each do |direct|
|
|
||||||
if direct.is_a?(Symbol)
|
|
||||||
# Symbols are assumed to be inputs to this group
|
|
||||||
direct = input(direct)
|
|
||||||
end
|
|
||||||
|
|
||||||
maps[direct] = direct.variable
|
|
||||||
end
|
|
||||||
|
|
||||||
# Take the missing inputs and map them together. For example
|
|
||||||
# the input of ':a' maps directly to the previous output of ':a'
|
|
||||||
(step_class.inputs - maps.values).each do |input|
|
|
||||||
maps[input] = input
|
|
||||||
end
|
|
||||||
|
|
||||||
# Turn the pure symbols into mapping from last steps
|
|
||||||
maps.keys.each do |from|
|
|
||||||
if from.kind_of?(Symbol)
|
|
||||||
new_from = nil
|
|
||||||
if @step_names.last.nil?
|
|
||||||
new_from = input(from)
|
|
||||||
else
|
|
||||||
new_from = output(@step_names.last, from)
|
|
||||||
end
|
|
||||||
|
|
||||||
maps[new_from] = maps.delete(from)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Verify that all the mappings are correctly satisfied.
|
|
||||||
maps.each do |from, to|
|
|
||||||
if !mapping_satisfied?(step_class, from, to)
|
|
||||||
raise ArgumentError, "Mapping from #{from.variable} to #{to} fails."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Append the step
|
|
||||||
@step_names << step_name
|
|
||||||
@steps[step_name] = [step_class, maps]
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(params=nil)
|
|
||||||
params ||= {}
|
|
||||||
|
|
||||||
# Instantiate all the steps
|
|
||||||
steps = @step_names.map do |name|
|
|
||||||
step_class, maps = @steps[name]
|
|
||||||
[name, step_class.new, maps]
|
|
||||||
end
|
|
||||||
|
|
||||||
# For each step, call it with proper inputs, using the output
|
|
||||||
# of that call as inputs to the next.
|
|
||||||
entered_steps = []
|
|
||||||
step_outputs = {}
|
|
||||||
result = steps.inject(params) do |inputs, data|
|
|
||||||
name, step, mappings = data
|
|
||||||
|
|
||||||
# If we have inputs to remap, remap them.
|
|
||||||
mappings.each do |from, to|
|
|
||||||
if from.kind_of?(GroupInput)
|
|
||||||
# Group inputs get their data from the initial parameters given
|
|
||||||
# to this group.
|
|
||||||
inputs[to] = params[from.variable]
|
|
||||||
elsif from.kind_of?(StepOutput)
|
|
||||||
# Step outputs get their data from a previous step's output.
|
|
||||||
inputs[to] = step_outputs[from.name][from.variable]
|
|
||||||
else
|
|
||||||
raise ArgumentError, "Unknown type of remapping."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Call the actual step, using the results for the next
|
|
||||||
# iteration.
|
|
||||||
entered_steps << step
|
|
||||||
begin
|
|
||||||
step_outputs[name] = step.call_enter(inputs)
|
|
||||||
rescue Exception => e
|
|
||||||
entered_steps.reverse.each do |s|
|
|
||||||
s.call_exit(e)
|
|
||||||
end
|
|
||||||
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return a shallow dup of the results
|
|
||||||
step_outputs[name].dup
|
|
||||||
end
|
|
||||||
|
|
||||||
entered_steps.reverse.each do |s|
|
|
||||||
s.call_exit(nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def mapping_satisfied?(step, from, to)
|
|
||||||
# If the step inputs don't contain to then we fail
|
|
||||||
return false if !step.inputs.include?(to)
|
|
||||||
|
|
||||||
if from.kind_of?(GroupInput)
|
|
||||||
# We assume that inputs from the group are satisfied since
|
|
||||||
# it has to come in.
|
|
||||||
return true
|
|
||||||
elsif from.kind_of?(StepOutput)
|
|
||||||
# Verify that the outputs of the step exist to map.
|
|
||||||
return @steps[from.name][0].outputs.include?(from.variable)
|
|
||||||
end
|
|
||||||
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,133 +0,0 @@
|
||||||
module Vagrant
|
|
||||||
class Action
|
|
||||||
# A step is a callable action that Vagrant uses to build up
|
|
||||||
# more complex actions sequences.
|
|
||||||
#
|
|
||||||
# A step must specify the inputs it requires and the outputs
|
|
||||||
# it will return, and can implement two methods: `enter` and
|
|
||||||
# `exit` (both are optional, but a step is useless without
|
|
||||||
# at least one).
|
|
||||||
#
|
|
||||||
# The inputs are guaranteed to be ready only by the time
|
|
||||||
# `enter` is called and are available as instance variables.
|
|
||||||
# `enter` is called first.
|
|
||||||
#
|
|
||||||
# `exit` is called at some point after `enter` (the exact time
|
|
||||||
# is not guarateed) and is given one parameter: `error` which
|
|
||||||
# is non-nil if an exception was raised at some point during
|
|
||||||
# or after `enter` was called. The return value of `exit` does
|
|
||||||
# nothing.
|
|
||||||
class Step
|
|
||||||
class UnsatisfiedRequirementsError < RuntimeError; end
|
|
||||||
|
|
||||||
# The keys given to this will be required by the step. Each key
|
|
||||||
# should be a symbol.
|
|
||||||
def self.input(*keys)
|
|
||||||
inputs.concat(keys)
|
|
||||||
end
|
|
||||||
|
|
||||||
# The values that this step provides. Each key should be a symbol.
|
|
||||||
def self.output(*keys)
|
|
||||||
outputs.concat(keys)
|
|
||||||
end
|
|
||||||
|
|
||||||
# This is the array of required keys.
|
|
||||||
def self.inputs
|
|
||||||
@inputs ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
# The array of keys that are provided by this Step.
|
|
||||||
def self.outputs
|
|
||||||
@outputs ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
# Validates that the output matches the specification provided, and
|
|
||||||
# raises a RuntimeError if it does not.
|
|
||||||
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
|
|
||||||
# of the outputs.
|
|
||||||
#
|
|
||||||
# Additional options may be provided via the options hash at the end.
|
|
||||||
#
|
|
||||||
# @param [Hash] params Parameters for the step.
|
|
||||||
# @param [Hash] options Options hash
|
|
||||||
# @option options [Symbol] :method Method to execute.
|
|
||||||
# @option options [Boolean] :validate_output Whether to validate the
|
|
||||||
# output or not.
|
|
||||||
# @return [Hash] Output
|
|
||||||
def call(params={})
|
|
||||||
# Call the actual implementation
|
|
||||||
results = nil
|
|
||||||
begin
|
|
||||||
results = call_enter(params)
|
|
||||||
rescue UnsatisfiedRequirementsError
|
|
||||||
# This doesn't get an `exit` call called since enter
|
|
||||||
# was never even called in this case.
|
|
||||||
raise
|
|
||||||
rescue Exception => e
|
|
||||||
call_exit(e)
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
|
|
||||||
# No exception occurred if we reach this point. Call exit.
|
|
||||||
call_exit(nil)
|
|
||||||
|
|
||||||
# Return the final results
|
|
||||||
results
|
|
||||||
end
|
|
||||||
|
|
||||||
# This method will only call the `enter` method for the step.
|
|
||||||
#
|
|
||||||
# The parameters given here will be validated as the inputs for
|
|
||||||
# the step and used to call `enter`. The results of `enter` will
|
|
||||||
# be validated as the outputs and returned.
|
|
||||||
#
|
|
||||||
# @param [Hash] inputs
|
|
||||||
# @return [Hash]
|
|
||||||
def call_enter(inputs={})
|
|
||||||
# Set and validate the inputs
|
|
||||||
set_inputs(inputs)
|
|
||||||
|
|
||||||
# Call the actual enter call
|
|
||||||
results = nil
|
|
||||||
results = send(:enter) if respond_to?(:enter)
|
|
||||||
|
|
||||||
# Validate the outputs if it is enabled and the list of configured
|
|
||||||
# outputs is not empty.
|
|
||||||
self.class.process_output(results)
|
|
||||||
end
|
|
||||||
|
|
||||||
# This method will call `exit` with the given error.
|
|
||||||
def call_exit(error)
|
|
||||||
send(:exit, error) if respond_to?(:exit)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# Sets the parameters for the step.
|
|
||||||
#
|
|
||||||
# This will raise an exception if all the `requires` are not
|
|
||||||
# properly met. Otherwise, the parameters are set as instance variables
|
|
||||||
# on this instance.
|
|
||||||
def set_inputs(params)
|
|
||||||
inputs = self.class.inputs
|
|
||||||
remaining = inputs - params.keys
|
|
||||||
raise UnsatisfiedRequirementsError, "Missing parameters: #{remaining}" if !remaining.empty?
|
|
||||||
|
|
||||||
inputs.each do |key|
|
|
||||||
instance_variable_set("@#{key}", params[key])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,233 +0,0 @@
|
||||||
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 enter
|
|
||||||
@obj << "A"
|
|
||||||
return :obj => @obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
step_B = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :obj
|
|
||||||
output :result
|
|
||||||
|
|
||||||
def enter
|
|
||||||
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 enter
|
|
||||||
# Do nothing.
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
step_B = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :obj
|
|
||||||
|
|
||||||
def enter
|
|
||||||
@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 enter
|
|
||||||
return :foo => "A"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
step_B = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :from
|
|
||||||
output :value
|
|
||||||
|
|
||||||
def enter
|
|
||||||
return :value => @from
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
obj = []
|
|
||||||
|
|
||||||
ms = described_class.new
|
|
||||||
ms.step step_A
|
|
||||||
ms.step step_B, :foo => :from
|
|
||||||
ms.call.should == { :value => "A" }
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should be able to reference variables from steps before other steps" do
|
|
||||||
step_A = Class.new(Vagrant::Action::Step) do
|
|
||||||
output :foo
|
|
||||||
|
|
||||||
def enter
|
|
||||||
return :foo => 10
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
step_B = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :from
|
|
||||||
output :value
|
|
||||||
|
|
||||||
def enter
|
|
||||||
return :value => @from
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
step_C = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :number
|
|
||||||
output :value
|
|
||||||
|
|
||||||
def enter
|
|
||||||
return :value => @number * 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
obj = []
|
|
||||||
|
|
||||||
g = described_class.new
|
|
||||||
g.step step_A
|
|
||||||
g.step step_B, :foo => :from
|
|
||||||
g.step step_C, g.output(step_A, :foo) => :number
|
|
||||||
g.call.should == { :value => 20 }
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should error if multiple steps of the same class are given without explicit names" do
|
|
||||||
step_A = Class.new(Vagrant::Action::Step)
|
|
||||||
|
|
||||||
g = described_class.new
|
|
||||||
g.step step_A
|
|
||||||
expect { g.step step_A }.to raise_error(NameError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should not error if multiple steps of the same class are given with custom names" do
|
|
||||||
step_A = Class.new(Vagrant::Action::Step)
|
|
||||||
|
|
||||||
g = described_class.new
|
|
||||||
g.step step_A
|
|
||||||
expect { g.step :another, step_A }.to_not raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should error if a step is not a class" do
|
|
||||||
g = described_class.new
|
|
||||||
expect { g.step :foo }.to raise_error(ArgumentError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should not allow a step that doesn't have all inputs satisfied" do
|
|
||||||
step_A = Class.new(Vagrant::Action::Step) do
|
|
||||||
output :output_A
|
|
||||||
end
|
|
||||||
|
|
||||||
step_B = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :input_B
|
|
||||||
end
|
|
||||||
|
|
||||||
g = described_class.new
|
|
||||||
g.step step_A
|
|
||||||
expect { g.step step_B }.to raise_error(ArgumentError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should not allow remapping from outputs that don't exist" do
|
|
||||||
step_A = Class.new(Vagrant::Action::Step) do
|
|
||||||
output :output_A
|
|
||||||
end
|
|
||||||
|
|
||||||
step_B = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :input_B
|
|
||||||
end
|
|
||||||
|
|
||||||
g = described_class.new
|
|
||||||
g.step step_A
|
|
||||||
expect { g.step step_B, :output_B => :input_B }.to raise_error(ArgumentError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should not allow remapping to inputs that don't exist" do
|
|
||||||
step_A = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :input_A
|
|
||||||
end
|
|
||||||
|
|
||||||
g = described_class.new
|
|
||||||
expect { g.step step_A, g.input(:foo) => :input_B }.to raise_error(ArgumentError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should call the enter methods in order, and the exit in reverse order" do
|
|
||||||
step = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :key
|
|
||||||
input :data
|
|
||||||
output :data
|
|
||||||
|
|
||||||
def enter
|
|
||||||
@data << @key
|
|
||||||
return :data => @data
|
|
||||||
end
|
|
||||||
|
|
||||||
def exit(error)
|
|
||||||
@data << @key
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
g = described_class.new
|
|
||||||
g.step step
|
|
||||||
g.step :two, step, g.input(:key2) => :key
|
|
||||||
g.step :three, step, g.input(:key3) => :key
|
|
||||||
result = g.call(:data => [], :key => "1", :key2 => "2", :key3 => "3")
|
|
||||||
result[:data].should == %W[1 2 3 3 2 1]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should halt the steps and call exit with the error if an error occurs" do
|
|
||||||
step = Class.new(Vagrant::Action::Step) do
|
|
||||||
input :key
|
|
||||||
input :data
|
|
||||||
output :data
|
|
||||||
|
|
||||||
def enter
|
|
||||||
@data << @key
|
|
||||||
raise Exception, "E" if @data.last == "2"
|
|
||||||
return :data => @data
|
|
||||||
end
|
|
||||||
|
|
||||||
def exit(error)
|
|
||||||
prefix = error ? error.message : ""
|
|
||||||
@data << "#{prefix}#{@key}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
g = described_class.new
|
|
||||||
g.step step
|
|
||||||
g.step :two, step, g.input(:key2) => :key
|
|
||||||
g.step :three, step, g.input(:key3) => :key
|
|
||||||
|
|
||||||
# Run the actual steps
|
|
||||||
data = []
|
|
||||||
expect do
|
|
||||||
result = g.call(:data => data, :key => "1", :key2 => "2", :key3 => "3")
|
|
||||||
end.to raise_error(Exception)
|
|
||||||
|
|
||||||
# Verify the result hit the methods in the proper order
|
|
||||||
data.should == %W[1 2 E2 E1]
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,112 +0,0 @@
|
||||||
require File.expand_path("../../../base", __FILE__)
|
|
||||||
|
|
||||||
describe Vagrant::Action::Step do
|
|
||||||
describe "call_enter" do
|
|
||||||
it "calls enter with inputs and returns the outputs" do
|
|
||||||
step_class = Class.new(described_class) do
|
|
||||||
input :in
|
|
||||||
output :out
|
|
||||||
|
|
||||||
def enter
|
|
||||||
return :out => @in * 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
step_class.new.call_enter(:in => 12).should == { :out => 24 }
|
|
||||||
end
|
|
||||||
|
|
||||||
it "raises an exception if not all required parameters are given" do
|
|
||||||
step_class = Class.new(described_class) do
|
|
||||||
input :foo
|
|
||||||
end
|
|
||||||
|
|
||||||
expect { step_class.new.call_enter }.to raise_error(Vagrant::Action::Step::UnsatisfiedRequirementsError)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "return an empty hash if no outputs are specified" do
|
|
||||||
step_class = Class.new(described_class) do
|
|
||||||
def enter
|
|
||||||
return 12
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
step_class.new.call_enter.should == {}
|
|
||||||
end
|
|
||||||
|
|
||||||
it "raises an exception if missing outputs" do
|
|
||||||
step_class = Class.new(described_class) do
|
|
||||||
output :foo
|
|
||||||
|
|
||||||
def enter
|
|
||||||
return :bar => 12
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
expect { step_class.new.call_enter }.to raise_error(RuntimeError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "call_exit" do
|
|
||||||
it "should simply call the `exit` method with the given argument" do
|
|
||||||
step_class = Class.new(described_class) do
|
|
||||||
def exit(error)
|
|
||||||
raise RuntimeError, error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
expect { step_class.new.call_exit(7) }.to raise_error(RuntimeError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "calling" do
|
|
||||||
it "calls enter then exit" do
|
|
||||||
step_class = Class.new(described_class) do
|
|
||||||
input :obj
|
|
||||||
|
|
||||||
def enter
|
|
||||||
@obj << "enter"
|
|
||||||
end
|
|
||||||
|
|
||||||
def exit(error)
|
|
||||||
@obj << "exit"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
obj = []
|
|
||||||
step_class.new.call(:obj => obj)
|
|
||||||
obj.should == ["enter", "exit"]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "calls exit with nil if no exception occurred" do
|
|
||||||
step_class = Class.new(described_class) do
|
|
||||||
input :obj
|
|
||||||
|
|
||||||
def exit(error)
|
|
||||||
@obj << error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
obj = []
|
|
||||||
step_class.new.call(:obj => obj)
|
|
||||||
obj.should == [nil]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "calls exit with an exception if it occurred" do
|
|
||||||
step_class = Class.new(described_class) do
|
|
||||||
input :obj
|
|
||||||
|
|
||||||
def enter
|
|
||||||
raise RuntimeError, "foo"
|
|
||||||
end
|
|
||||||
|
|
||||||
def exit(error)
|
|
||||||
@obj << error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
obj = []
|
|
||||||
expect { step_class.new.call(:obj => obj) }.to raise_error(RuntimeError)
|
|
||||||
obj[0].should be_kind_of(RuntimeError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue