dchelimsky-rspec-stories 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +5 -0
- data/License.txt +22 -0
- data/Manifest.txt +78 -0
- data/README.txt +23 -0
- data/Rakefile +87 -0
- data/init.rb +4 -0
- data/lib/spec/runner/formatter/story/html_formatter.rb +174 -0
- data/lib/spec/runner/formatter/story/plain_text_formatter.rb +194 -0
- data/lib/spec/runner/formatter/story/progress_bar_formatter.rb +42 -0
- data/lib/spec/runner/options_extensions.rb +25 -0
- data/lib/spec/stories.rb +11 -0
- data/lib/spec/story/extensions.rb +3 -0
- data/lib/spec/story/extensions/main.rb +86 -0
- data/lib/spec/story/extensions/regexp.rb +9 -0
- data/lib/spec/story/extensions/string.rb +9 -0
- data/lib/spec/story/given_scenario.rb +14 -0
- data/lib/spec/story/runner.rb +57 -0
- data/lib/spec/story/runner/plain_text_story_runner.rb +48 -0
- data/lib/spec/story/runner/scenario_collector.rb +18 -0
- data/lib/spec/story/runner/scenario_runner.rb +54 -0
- data/lib/spec/story/runner/story_mediator.rb +137 -0
- data/lib/spec/story/runner/story_parser.rb +247 -0
- data/lib/spec/story/runner/story_runner.rb +74 -0
- data/lib/spec/story/scenario.rb +14 -0
- data/lib/spec/story/step.rb +70 -0
- data/lib/spec/story/step_group.rb +89 -0
- data/lib/spec/story/step_mother.rb +38 -0
- data/lib/spec/story/story.rb +39 -0
- data/lib/spec/story/version.rb +15 -0
- data/lib/spec/story/world.rb +124 -0
- data/resources/rake/verify_rcov.rake +7 -0
- data/rspec-stories.gemspec +35 -0
- data/spec/spec.opts +6 -0
- data/spec/spec/runner/formatter/story/html_formatter_spec.rb +135 -0
- data/spec/spec/runner/formatter/story/plain_text_formatter_spec.rb +600 -0
- data/spec/spec/runner/formatter/story/progress_bar_formatter_spec.rb +82 -0
- data/spec/spec/runner/most_recent_spec.rb +0 -0
- data/spec/spec/runner/options_extensions_spec.rb +31 -0
- data/spec/spec/runner/resources/a_bar.rb +0 -0
- data/spec/spec/runner/resources/a_foo.rb +0 -0
- data/spec/spec/runner/resources/a_spec.rb +1 -0
- data/spec/spec/runner/resources/custom_example_group_runner.rb +14 -0
- data/spec/spec/runner/resources/utf8_encoded.rb +7 -0
- data/spec/spec/runner_spec.rb +11 -0
- data/spec/spec/spec_classes.rb +133 -0
- data/spec/spec/story/builders.rb +46 -0
- data/spec/spec/story/extensions/main_spec.rb +161 -0
- data/spec/spec/story/extensions_spec.rb +14 -0
- data/spec/spec/story/given_scenario_spec.rb +27 -0
- data/spec/spec/story/runner/plain_text_story_runner_spec.rb +90 -0
- data/spec/spec/story/runner/scenario_collector_spec.rb +27 -0
- data/spec/spec/story/runner/scenario_runner_spec.rb +214 -0
- data/spec/spec/story/runner/story_mediator_spec.rb +143 -0
- data/spec/spec/story/runner/story_parser_spec.rb +401 -0
- data/spec/spec/story/runner/story_runner_spec.rb +294 -0
- data/spec/spec/story/runner_spec.rb +93 -0
- data/spec/spec/story/scenario_spec.rb +18 -0
- data/spec/spec/story/step_group_spec.rb +157 -0
- data/spec/spec/story/step_mother_spec.rb +84 -0
- data/spec/spec/story/step_spec.rb +272 -0
- data/spec/spec/story/story_helper.rb +2 -0
- data/spec/spec/story/story_spec.rb +84 -0
- data/spec/spec/story/world_spec.rb +423 -0
- data/spec/spec_helper.rb +84 -0
- data/story_server/prototype/javascripts/builder.js +136 -0
- data/story_server/prototype/javascripts/controls.js +972 -0
- data/story_server/prototype/javascripts/dragdrop.js +976 -0
- data/story_server/prototype/javascripts/effects.js +1117 -0
- data/story_server/prototype/javascripts/prototype.js +4140 -0
- data/story_server/prototype/javascripts/rspec.js +149 -0
- data/story_server/prototype/javascripts/scriptaculous.js +58 -0
- data/story_server/prototype/javascripts/slider.js +276 -0
- data/story_server/prototype/javascripts/sound.js +55 -0
- data/story_server/prototype/javascripts/unittest.js +568 -0
- data/story_server/prototype/lib/server.rb +24 -0
- data/story_server/prototype/stories.html +176 -0
- data/story_server/prototype/stylesheets/rspec.css +136 -0
- data/story_server/prototype/stylesheets/test.css +90 -0
- metadata +154 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec/runner/formatter/story/plain_text_formatter'
|
2
|
+
|
3
|
+
module Spec
|
4
|
+
module Runner
|
5
|
+
module Formatter
|
6
|
+
module Story
|
7
|
+
class ProgressBarFormatter < PlainTextFormatter
|
8
|
+
|
9
|
+
def story_started(title, narrative) end
|
10
|
+
def story_ended(title, narrative) end
|
11
|
+
|
12
|
+
def run_started(count)
|
13
|
+
@start_time = Time.now
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_ended
|
18
|
+
@output.puts
|
19
|
+
@output.puts
|
20
|
+
@output.puts "Finished in %f seconds" % (Time.now - @start_time)
|
21
|
+
@output.puts
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def scenario_ended
|
26
|
+
if @scenario_failed
|
27
|
+
@output.print red('F')
|
28
|
+
@output.flush
|
29
|
+
elsif @scenario_pending
|
30
|
+
@output.print yellow('P')
|
31
|
+
@output.flush
|
32
|
+
else
|
33
|
+
@output.print green('.')
|
34
|
+
@output.flush
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec/runner/options'
|
2
|
+
|
3
|
+
module Spec
|
4
|
+
module Runner
|
5
|
+
module OptionsExtensions
|
6
|
+
|
7
|
+
STORY_FORMATTERS = {
|
8
|
+
'plain' => ['spec/runner/formatter/story/plain_text_formatter', 'Formatter::Story::PlainTextFormatter'],
|
9
|
+
'p' => ['spec/runner/formatter/story/plain_text_formatter', 'Formatter::Story::PlainTextFormatter'],
|
10
|
+
'html' => ['spec/runner/formatter/story/html_formatter', 'Formatter::Story::HtmlFormatter'],
|
11
|
+
'h' => ['spec/runner/formatter/story/html_formatter', 'Formatter::Story::HtmlFormatter'],
|
12
|
+
'progress' => ['spec/runner/formatter/story/progress_bar_formatter', 'Formatter::Story::ProgressBarFormatter'],
|
13
|
+
'r' => ['spec/runner/formatter/story/progress_bar_formatter', 'Formatter::Story::ProgressBarFormatter']
|
14
|
+
}
|
15
|
+
|
16
|
+
def story_formatters
|
17
|
+
@format_options ||= [['plain', @output_stream]]
|
18
|
+
@formatters ||= load_formatters(@format_options, STORY_FORMATTERS)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Spec::Runner::Options.send(:include, Spec::Runner::OptionsExtensions)
|
data/lib/spec/stories.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec/expectations'
|
2
|
+
require 'spec/runner/options_extensions'
|
3
|
+
require 'spec/story/extensions'
|
4
|
+
require 'spec/story/given_scenario'
|
5
|
+
require 'spec/story/runner'
|
6
|
+
require 'spec/story/scenario'
|
7
|
+
require 'spec/story/step'
|
8
|
+
require 'spec/story/step_group'
|
9
|
+
require 'spec/story/step_mother'
|
10
|
+
require 'spec/story/story'
|
11
|
+
require 'spec/story/world'
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Spec
|
2
|
+
module Story
|
3
|
+
module Extensions
|
4
|
+
module Main
|
5
|
+
def Story(title, narrative, params = {}, &body)
|
6
|
+
::Spec::Story::Runner.story_runner.Story(title, narrative, params, &body)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Calling this deprecated is silly, since it hasn't been released yet. But, for
|
10
|
+
# those who are reading this - this will be deleted before the 1.1 release.
|
11
|
+
def run_story(*args, &block)
|
12
|
+
runner = Spec::Story::Runner::PlainTextStoryRunner.new(*args)
|
13
|
+
runner.instance_eval(&block) if block
|
14
|
+
runner.run
|
15
|
+
end
|
16
|
+
|
17
|
+
# Creates (or appends to an existing) a namespaced group of steps for use in Stories
|
18
|
+
#
|
19
|
+
# == Examples
|
20
|
+
#
|
21
|
+
# # Creating a new group
|
22
|
+
# steps_for :forms do
|
23
|
+
# When("user enters $value in the $field field") do ... end
|
24
|
+
# When("user submits the $form form") do ... end
|
25
|
+
# end
|
26
|
+
def steps_for(tag, &block)
|
27
|
+
steps = rspec_story_steps[tag]
|
28
|
+
steps.instance_eval(&block) if block
|
29
|
+
steps
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates a context for running a Plain Text Story with specific groups of Steps.
|
33
|
+
# Also supports adding arbitrary steps that will only be accessible to
|
34
|
+
# the Story being run.
|
35
|
+
#
|
36
|
+
# == Examples
|
37
|
+
#
|
38
|
+
# # Run a Story with one group of steps
|
39
|
+
# with_steps_for :checking_accounts do
|
40
|
+
# run File.dirname(__FILE__) + "/withdraw_cash"
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # Run a Story, adding steps that are only available for this Story
|
44
|
+
# with_steps_for :accounts do
|
45
|
+
# Given "user is logged in as account administrator"
|
46
|
+
# run File.dirname(__FILE__) + "/reconcile_accounts"
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# # Run a Story with steps from two groups
|
50
|
+
# with_steps_for :checking_accounts, :savings_accounts do
|
51
|
+
# run File.dirname(__FILE__) + "/transfer_money"
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# # Run a Story with a specific Story extension
|
55
|
+
# with_steps_for :login, :navigation do
|
56
|
+
# run File.dirname(__FILE__) + "/user_changes_password", :type => RailsStory
|
57
|
+
# end
|
58
|
+
def with_steps_for(*tags, &block)
|
59
|
+
steps = Spec::Story::StepGroup.new do
|
60
|
+
extend StoryRunnerStepGroupAdapter
|
61
|
+
end
|
62
|
+
tags.each {|tag| steps << rspec_story_steps[tag]}
|
63
|
+
steps.instance_eval(&block) if block
|
64
|
+
steps
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
module StoryRunnerStepGroupAdapter
|
70
|
+
def run(path, options={})
|
71
|
+
runner = Spec::Story::Runner::PlainTextStoryRunner.new(path, options)
|
72
|
+
runner.steps << self
|
73
|
+
runner.run
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def rspec_story_steps # :nodoc:
|
78
|
+
$rspec_story_steps ||= Spec::Story::StepGroupHash.new
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
include Spec::Story::Extensions::Main
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Spec
|
2
|
+
module Story
|
3
|
+
class GivenScenario
|
4
|
+
def initialize(name)
|
5
|
+
@name = name
|
6
|
+
end
|
7
|
+
|
8
|
+
def perform(instance, ignore_name)
|
9
|
+
scenario = Runner::StoryRunner.scenario_from_current_story(@name)
|
10
|
+
Runner::ScenarioRunner.new.run(scenario, instance)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec/story/runner/scenario_collector.rb'
|
2
|
+
require 'spec/story/runner/scenario_runner.rb'
|
3
|
+
require 'spec/story/runner/story_runner.rb'
|
4
|
+
require 'spec/story/runner/story_parser.rb'
|
5
|
+
require 'spec/story/runner/story_mediator.rb'
|
6
|
+
require 'spec/story/runner/plain_text_story_runner.rb'
|
7
|
+
|
8
|
+
module Spec
|
9
|
+
module Story
|
10
|
+
module Runner
|
11
|
+
def self.run_options # :nodoc:
|
12
|
+
Spec::Runner.options
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.story_runner # :nodoc:
|
16
|
+
unless @story_runner
|
17
|
+
@story_runner = create_story_runner
|
18
|
+
run_options.story_formatters.each do |formatter|
|
19
|
+
register_listener(formatter)
|
20
|
+
end
|
21
|
+
self.register_exit_hook
|
22
|
+
end
|
23
|
+
@story_runner
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.scenario_runner # :nodoc:
|
27
|
+
@scenario_runner ||= ScenarioRunner.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.world_creator # :nodoc:
|
31
|
+
@world_creator ||= World
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.create_story_runner
|
35
|
+
Runner::StoryRunner.new(scenario_runner, world_creator)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Use this to register a customer output formatter.
|
39
|
+
def self.register_listener(listener)
|
40
|
+
story_runner.add_listener(listener) # run_started, story_started, story_ended, #run_ended
|
41
|
+
world_creator.add_listener(listener) # found_scenario, step_succeeded, step_failed, step_failed
|
42
|
+
scenario_runner.add_listener(listener) # scenario_started, scenario_succeeded, scenario_pending, scenario_failed
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.register_exit_hook # :nodoc:
|
46
|
+
at_exit do
|
47
|
+
exit Runner.story_runner.run_stories unless $!
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.dry_run
|
52
|
+
run_options.dry_run
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Spec
|
2
|
+
module Story
|
3
|
+
module Runner
|
4
|
+
class PlainTextStoryRunner
|
5
|
+
# You can initialize a PlainTextStoryRunner with the path to the
|
6
|
+
# story file or a block, in which you can define the path using load.
|
7
|
+
#
|
8
|
+
# == Examples
|
9
|
+
#
|
10
|
+
# PlainTextStoryRunner.new('path/to/file')
|
11
|
+
#
|
12
|
+
# PlainTextStoryRunner.new do |runner|
|
13
|
+
# runner.load 'path/to/file'
|
14
|
+
# end
|
15
|
+
def initialize(*args)
|
16
|
+
@options = Hash === args.last ? args.pop : {}
|
17
|
+
@story_file = args.empty? ? nil : args.shift
|
18
|
+
yield self if block_given?
|
19
|
+
end
|
20
|
+
|
21
|
+
def []=(key, value)
|
22
|
+
@options[key] = value
|
23
|
+
end
|
24
|
+
|
25
|
+
def load(path)
|
26
|
+
@story_file = path
|
27
|
+
end
|
28
|
+
|
29
|
+
def run(story_runner=Spec::Story::Runner.story_runner)
|
30
|
+
raise "You must set a path to the file with the story. See the RDoc." if @story_file.nil?
|
31
|
+
mediator = Spec::Story::Runner::StoryMediator.new(steps, story_runner, @options)
|
32
|
+
parser = Spec::Story::Runner::StoryParser.new(mediator)
|
33
|
+
|
34
|
+
story_text = File.read(@story_file)
|
35
|
+
parser.parse(story_text.split("\n"))
|
36
|
+
|
37
|
+
mediator.run_stories
|
38
|
+
end
|
39
|
+
|
40
|
+
def steps
|
41
|
+
@step_group ||= Spec::Story::StepGroup.new
|
42
|
+
yield @step_group if block_given?
|
43
|
+
@step_group
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Spec
|
2
|
+
module Story
|
3
|
+
module Runner
|
4
|
+
class ScenarioCollector
|
5
|
+
attr_accessor :scenarios
|
6
|
+
|
7
|
+
def initialize(story)
|
8
|
+
@story = story
|
9
|
+
@scenarios = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def Scenario(name, &body)
|
13
|
+
@scenarios << Scenario.new(@story, name, &body)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Spec
|
2
|
+
module Story
|
3
|
+
module Runner
|
4
|
+
class ScenarioRunner
|
5
|
+
def initialize
|
6
|
+
@listeners = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(scenario, world)
|
10
|
+
@listeners.each { |l| l.scenario_started(scenario.story.title, scenario.name) }
|
11
|
+
run_story_ignoring_scenarios(scenario.story, world)
|
12
|
+
|
13
|
+
world.start_collecting_errors
|
14
|
+
|
15
|
+
unless scenario.body
|
16
|
+
@listeners.each { |l| l.scenario_pending(scenario.story.title, scenario.name, '') }
|
17
|
+
return true
|
18
|
+
end
|
19
|
+
|
20
|
+
world.instance_eval(&scenario.body)
|
21
|
+
if world.errors.empty?
|
22
|
+
@listeners.each { |l| l.scenario_succeeded(scenario.story.title, scenario.name) }
|
23
|
+
else
|
24
|
+
if Spec::Example::ExamplePendingError === (e = world.errors.first)
|
25
|
+
@listeners.each { |l| l.scenario_pending(scenario.story.title, scenario.name, e.message) }
|
26
|
+
else
|
27
|
+
@listeners.each { |l| l.scenario_failed(scenario.story.title, scenario.name, e) }
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_listener(listener)
|
35
|
+
@listeners << listener
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def run_story_ignoring_scenarios(story, world)
|
41
|
+
class << world
|
42
|
+
def Scenario(name, &block)
|
43
|
+
# do nothing
|
44
|
+
end
|
45
|
+
end
|
46
|
+
story.run_in(world)
|
47
|
+
class << world
|
48
|
+
remove_method(:Scenario)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Spec
|
2
|
+
module Story
|
3
|
+
module Runner
|
4
|
+
|
5
|
+
class StoryMediator
|
6
|
+
def initialize(step_group, runner, options={})
|
7
|
+
@step_group = step_group
|
8
|
+
@stories = []
|
9
|
+
@runner = runner
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def stories
|
14
|
+
@stories.collect { |p| p.to_proc }
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_story(title, narrative)
|
18
|
+
@stories << Story.new(title, narrative, @step_group, @options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_scenario(title)
|
22
|
+
current_story.add_scenario Scenario.new(title)
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_given(name)
|
26
|
+
current_scenario.add_step Step.new('Given', name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_given_scenario(name)
|
30
|
+
current_scenario.add_step Step.new('GivenScenario', name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_when(name)
|
34
|
+
current_scenario.add_step Step.new('When', name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_then(name)
|
38
|
+
current_scenario.add_step Step.new('Then', name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def last_step
|
42
|
+
current_scenario.last_step
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_to_last(name)
|
46
|
+
last_step.name << name
|
47
|
+
end
|
48
|
+
|
49
|
+
def run_stories
|
50
|
+
stories.each { |story| @runner.instance_eval(&story) }
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def current_story
|
55
|
+
@stories.last
|
56
|
+
end
|
57
|
+
|
58
|
+
def current_scenario
|
59
|
+
current_story.current_scenario
|
60
|
+
end
|
61
|
+
|
62
|
+
class Story
|
63
|
+
def initialize(title, narrative, step_group, options)
|
64
|
+
@title = title
|
65
|
+
@narrative = narrative
|
66
|
+
@scenarios = []
|
67
|
+
@step_group = step_group
|
68
|
+
@options = options
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_proc
|
72
|
+
title = @title
|
73
|
+
narrative = @narrative
|
74
|
+
scenarios = @scenarios.collect { |scenario| scenario.to_proc }
|
75
|
+
options = @options.merge(:steps_for => @step_group)
|
76
|
+
lambda do
|
77
|
+
Story title, narrative, options do
|
78
|
+
scenarios.each { |scenario| instance_eval(&scenario) }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_scenario(scenario)
|
84
|
+
@scenarios << scenario
|
85
|
+
end
|
86
|
+
|
87
|
+
def current_scenario
|
88
|
+
@scenarios.last
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Scenario
|
93
|
+
def initialize(name)
|
94
|
+
@name = name
|
95
|
+
@steps = []
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_proc
|
99
|
+
name = @name
|
100
|
+
steps = @steps.collect { |step| step.to_proc }
|
101
|
+
lambda do
|
102
|
+
Scenario name do
|
103
|
+
steps.each { |step| instance_eval(&step) }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def add_step(step)
|
109
|
+
@steps << step
|
110
|
+
end
|
111
|
+
|
112
|
+
def last_step
|
113
|
+
@steps.last
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Step
|
118
|
+
attr_reader :name
|
119
|
+
|
120
|
+
def initialize(type, name)
|
121
|
+
@type = type
|
122
|
+
@name = name
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_proc
|
126
|
+
type = @type
|
127
|
+
name = @name
|
128
|
+
lambda do
|
129
|
+
send(type, name)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|