rbehave 0.1.0 → 0.2.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/CHANGELOG.txt +5 -0
- data/Manifest.txt +44 -17
- data/NOTES.txt +2 -7
- data/behaviour/everything.rb +1 -1
- data/behaviour/examples/{everything.rb → examples.rb} +0 -0
- data/behaviour/examples/rbehave/documenter/plain_text_documenter_behaviour.rb +1 -0
- data/behaviour/examples/rbehave/given_scenario_behaviour.rb +26 -0
- data/behaviour/examples/rbehave/runner/options_behaviour.rb +39 -0
- data/behaviour/examples/rbehave/runner/runner_behaviour.rb +39 -0
- data/behaviour/examples/rbehave/runner/story_runner_behaviour.rb +58 -2
- data/behaviour/examples/rbehave/simple_step_behaviour.rb +35 -0
- data/behaviour/examples/rbehave/step_mother_behaviour.rb +32 -0
- data/behaviour/examples/rbehave/world_behaviour.rb +44 -5
- data/behaviour/examples/rspec_adapter.rb +3 -1
- data/examples/game-of-life/.loadpath +5 -0
- data/examples/game-of-life/.project +18 -0
- data/examples/game-of-life/README.txt +21 -0
- data/examples/game-of-life/behaviour/everything.rb +2 -0
- data/examples/game-of-life/behaviour/examples/examples.rb +2 -0
- data/examples/game-of-life/behaviour/examples/game_behaviour.rb +17 -0
- data/examples/game-of-life/behaviour/examples/grid_behaviour.rb +68 -0
- data/examples/game-of-life/behaviour/examples/helper.rb +2 -0
- data/examples/game-of-life/behaviour/stories/CellsWithLessThanTwoNeighboursDie.story +21 -0
- data/examples/game-of-life/behaviour/stories/CellsWithMoreThanThreeNeighboursDie.story +21 -0
- data/examples/game-of-life/behaviour/stories/EmptySpacesWithThreeNeighboursCreateACell.story +42 -0
- data/examples/game-of-life/behaviour/stories/ICanCreateACell.story +42 -0
- data/examples/game-of-life/behaviour/stories/ICanKillACell.story +17 -0
- data/examples/game-of-life/behaviour/stories/TheGridWraps.story +53 -0
- data/examples/game-of-life/behaviour/stories/create_a_cell.rb +58 -0
- data/examples/game-of-life/behaviour/stories/stories.txt +22 -0
- data/examples/game-of-life/life.rb +2 -0
- data/examples/game-of-life/life/game.rb +6 -0
- data/examples/game-of-life/life/grid.rb +38 -0
- data/lib/rbehave.rb +6 -71
- data/lib/rbehave/documenter/plain_text_documenter.rb +2 -2
- data/lib/rbehave/exceptions.rb +3 -3
- data/lib/rbehave/given_scenario.rb +12 -0
- data/lib/rbehave/runner.rb +52 -0
- data/lib/rbehave/runner/options.rb +26 -0
- data/lib/rbehave/runner/story_runner.rb +26 -5
- data/lib/rbehave/simple_step.rb +14 -0
- data/lib/rbehave/step_mother.rb +19 -0
- data/lib/rbehave/story.rb +8 -3
- data/lib/rbehave/version.rb +1 -1
- data/lib/rbehave/world.rb +31 -23
- metadata +47 -19
- data/Rakefile +0 -54
@@ -0,0 +1,38 @@
|
|
1
|
+
class Grid
|
2
|
+
|
3
|
+
attr_accessor :contents
|
4
|
+
|
5
|
+
def initialize(rows, cols)
|
6
|
+
@contents = []
|
7
|
+
rows.times do @contents << [0] * cols end
|
8
|
+
end
|
9
|
+
|
10
|
+
def rows
|
11
|
+
@contents.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def columns
|
15
|
+
@contents[0].size
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
self.contents == other.contents
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_at(row,col)
|
23
|
+
@contents[row][col] = 1
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.from_s(str)
|
27
|
+
row_strings = str.split
|
28
|
+
grid = Grid.new(row_strings.size, row_strings[0].size)
|
29
|
+
|
30
|
+
row_strings.each_with_index do |row, r|
|
31
|
+
row.split(//).each_with_index do |col, c|
|
32
|
+
grid.create_at(r,c) if col == 'X'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
return grid
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/rbehave.rb
CHANGED
@@ -1,72 +1,7 @@
|
|
1
|
-
Dir
|
1
|
+
Dir.chdir(File.dirname(__FILE__)) do
|
2
|
+
Dir['rbehave/**/*.rb'].each { |f| require f }
|
3
|
+
end
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.included(into)
|
9
|
-
RBehave::Runner.register_instance
|
10
|
-
end
|
11
|
-
|
12
|
-
module Runner
|
13
|
-
class << self
|
14
|
-
def story_runner
|
15
|
-
unless @story_runner
|
16
|
-
scenario_runner = ScenarioRunner.new
|
17
|
-
world_creator = World
|
18
|
-
@story_runner = StoryRunner.new(scenario_runner, world_creator)
|
19
|
-
if ARGV.empty?
|
20
|
-
reporter = RBehave::Reporter::PlainTextReporter.new($stdout)
|
21
|
-
scenario_runner.add_listener(reporter)
|
22
|
-
@story_runner.add_listener(reporter)
|
23
|
-
else
|
24
|
-
documenter = RBehave::Documenter::PlainTextDocumenter.new($stdout)
|
25
|
-
scenario_runner.add_listener(documenter)
|
26
|
-
@story_runner.add_listener(documenter)
|
27
|
-
world_creator.add_listener(documenter)
|
28
|
-
self.dry_run = true
|
29
|
-
end
|
30
|
-
end
|
31
|
-
@story_runner
|
32
|
-
end
|
33
|
-
|
34
|
-
def register_instance
|
35
|
-
return if registered?
|
36
|
-
unless registered?
|
37
|
-
registered = true
|
38
|
-
Exception.add_backtrace_filter(/lib\/rbehave/)
|
39
|
-
Exception.add_backtrace_filter(/gems\/rspec/)
|
40
|
-
at_exit do
|
41
|
-
exit RBehave::Runner.run unless $! || RBehave::Runner.run?
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def registered?
|
47
|
-
@registered ||= false
|
48
|
-
end
|
49
|
-
|
50
|
-
def run?
|
51
|
-
@run ||= false
|
52
|
-
end
|
53
|
-
|
54
|
-
def run=(value)
|
55
|
-
@run = value
|
56
|
-
end
|
57
|
-
|
58
|
-
def run
|
59
|
-
story_runner.run_stories
|
60
|
-
self.run = true
|
61
|
-
end
|
62
|
-
|
63
|
-
def dry_run?
|
64
|
-
@dry_run ||= false
|
65
|
-
end
|
66
|
-
|
67
|
-
def dry_run=(value)
|
68
|
-
@dry_run = value
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
5
|
+
def Story(title, narrative, params = {}, &body)
|
6
|
+
RBehave::Runner.story_runner.Story(title, narrative, params, &body)
|
7
|
+
end
|
@@ -17,8 +17,8 @@ module RBehave
|
|
17
17
|
@out << "\nScenario: #{scenario_name}\n"
|
18
18
|
end
|
19
19
|
|
20
|
-
def found_step(name, description)
|
21
|
-
@out << " #{name.to_s.capitalize} #{description}\n"
|
20
|
+
def found_step(name, description, *args)
|
21
|
+
@out << " #{name.to_s.capitalize} #{description} #{args.join ','}\n"
|
22
22
|
end
|
23
23
|
|
24
24
|
def method_missing(meth, *args, &block)
|
data/lib/rbehave/exceptions.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module RBehave
|
2
|
-
class PendingException < StandardError
|
3
|
-
end
|
2
|
+
class PendingException < StandardError; end
|
3
|
+
class UnknownStepException < StandardError; end
|
4
4
|
end
|
5
5
|
|
6
6
|
class Exception
|
@@ -14,7 +14,7 @@ class Exception
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def filtered_backtrace
|
17
|
-
trace = backtrace || []
|
17
|
+
return trace = backtrace || []
|
18
18
|
trace.reject do |line|
|
19
19
|
Exception.backtrace_filters.inject(false) do |already_matched, filter|
|
20
20
|
already_matched || line =~ filter
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module RBehave
|
2
|
+
module Runner
|
3
|
+
class << self
|
4
|
+
def options
|
5
|
+
@options ||= Options.new.parse ARGV
|
6
|
+
end
|
7
|
+
|
8
|
+
def story_runner
|
9
|
+
unless @story_runner
|
10
|
+
scenario_runner = ScenarioRunner.new
|
11
|
+
RBehave::Runner.register_exit_hook
|
12
|
+
world_creator = World
|
13
|
+
@story_runner = StoryRunner.new(scenario_runner, world_creator)
|
14
|
+
unless options.dry_run
|
15
|
+
reporter = RBehave::Reporter::PlainTextReporter.new($stdout)
|
16
|
+
scenario_runner.add_listener(reporter)
|
17
|
+
@story_runner.add_listener(reporter)
|
18
|
+
end
|
19
|
+
case options.format
|
20
|
+
when :simple then documenter = RBehave::Documenter::PlainTextDocumenter.new($stdout)
|
21
|
+
when nil ;
|
22
|
+
else raise "Unimplemented format - #{options.format.to_s}"
|
23
|
+
end
|
24
|
+
if documenter
|
25
|
+
scenario_runner.add_listener(documenter)
|
26
|
+
@story_runner.add_listener(documenter)
|
27
|
+
world_creator.add_listener(documenter)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@story_runner
|
31
|
+
end
|
32
|
+
|
33
|
+
def register_exit_hook
|
34
|
+
Exception.add_backtrace_filter(/lib\/rbehave/)
|
35
|
+
Exception.add_backtrace_filter(/gems\/rbehave/)
|
36
|
+
Exception.add_backtrace_filter(/gems\/rspec/)
|
37
|
+
at_exit do
|
38
|
+
RBehave::Runner.story_runner.run_stories unless $!
|
39
|
+
# TODO exit with non-zero status if run fails
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def dry_run?
|
44
|
+
@dry_run ||= false
|
45
|
+
end
|
46
|
+
|
47
|
+
def dry_run=(value)
|
48
|
+
@dry_run = value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module RBehave
|
4
|
+
module Runner
|
5
|
+
class Options
|
6
|
+
attr_accessor :format, :dry_run
|
7
|
+
|
8
|
+
def parse(args)
|
9
|
+
parser = OptionParser.new do |p|
|
10
|
+
p.banner = "Usage: ruby your_stories.rb [options]"
|
11
|
+
p.separator ""
|
12
|
+
p.separator "Options:"
|
13
|
+
p.on "-n", "--dry-run", "dry run - no execution" do
|
14
|
+
@dry_run = true
|
15
|
+
end
|
16
|
+
formats = [:simple, :rdoc, :html, :xml]
|
17
|
+
p.on "-f", "--format ", formats, "format (#{formats.join(',')})" do |fmt|
|
18
|
+
@format = fmt.to_sym
|
19
|
+
end
|
20
|
+
end
|
21
|
+
parser.parse(args)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,9 +1,22 @@
|
|
1
1
|
module RBehave
|
2
2
|
module Runner
|
3
3
|
class StoryRunner
|
4
|
-
|
4
|
+
class << self
|
5
|
+
attr_accessor :current_story_runner
|
6
|
+
|
7
|
+
def current_story
|
8
|
+
current_story_runner.current_story
|
9
|
+
end
|
10
|
+
|
11
|
+
def scenario_from_current_story(scenario_name)
|
12
|
+
current_story_runner.scenario_from_current_story(scenario_name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_accessor :stories, :scenarios, :current_story
|
5
17
|
|
6
18
|
def initialize(scenario_runner, world_creator = World)
|
19
|
+
StoryRunner.current_story_runner = self
|
7
20
|
@scenario_runner = scenario_runner
|
8
21
|
@world_creator = world_creator
|
9
22
|
@stories = []
|
@@ -12,8 +25,8 @@ module RBehave
|
|
12
25
|
@listeners = []
|
13
26
|
end
|
14
27
|
|
15
|
-
def Story(title, narrative, &body)
|
16
|
-
story = Story.new(title, narrative, &body)
|
28
|
+
def Story(title, narrative, params = {}, &body)
|
29
|
+
story = Story.new(title, narrative, params, &body)
|
17
30
|
@stories << story
|
18
31
|
|
19
32
|
# collect scenarios
|
@@ -24,12 +37,16 @@ module RBehave
|
|
24
37
|
end
|
25
38
|
|
26
39
|
def run_stories
|
40
|
+
return if @stories.empty?
|
27
41
|
@listeners.each { |l| l.run_started(scenarios.size) }
|
28
42
|
@stories.each do |story|
|
43
|
+
@current_story = story
|
29
44
|
@listeners.each { |l| l.story_started(story.title, story.narrative) }
|
30
45
|
scenarios = @scenarios_by_story[story.title]
|
31
46
|
scenarios.each do |scenario|
|
32
|
-
|
47
|
+
type = story[:type] || Object
|
48
|
+
args = story[:args] || []
|
49
|
+
@scenario_runner.run(scenario, @world_creator.create(type, *args))
|
33
50
|
end
|
34
51
|
@listeners.each { |l| l.story_ended(story.title, story.narrative) }
|
35
52
|
end
|
@@ -39,6 +56,10 @@ module RBehave
|
|
39
56
|
def add_listener(listener)
|
40
57
|
@listeners << listener
|
41
58
|
end
|
59
|
+
|
60
|
+
def scenario_from_current_story(scenario_name)
|
61
|
+
@scenarios_by_story[@current_story.title].find {|s| s.name == scenario_name }
|
62
|
+
end
|
42
63
|
end
|
43
64
|
end
|
44
|
-
end
|
65
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RBehave
|
2
|
+
class SimpleStep
|
3
|
+
def initialize(name, &block)
|
4
|
+
@name = name
|
5
|
+
@mod = Module.new
|
6
|
+
@mod.__send__(:define_method, @name, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def perform(instance, *args)
|
10
|
+
instance.extend(@mod)
|
11
|
+
instance.__send__(@name, *args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RBehave
|
2
|
+
class StepMother
|
3
|
+
def initialize
|
4
|
+
@steps = Hash.new do |hsh,key|
|
5
|
+
hsh[key] = Hash.new do |hsh,key|
|
6
|
+
raise UnknownStepException, key
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def store(type, name, step)
|
12
|
+
@steps[type][name] = step
|
13
|
+
end
|
14
|
+
|
15
|
+
def find(type, name)
|
16
|
+
@steps[type][name]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/rbehave/story.rb
CHANGED
@@ -2,14 +2,19 @@ module RBehave
|
|
2
2
|
class Story
|
3
3
|
attr_reader :title, :narrative
|
4
4
|
|
5
|
-
def initialize(title, narrative, &body)
|
5
|
+
def initialize(title, narrative, params = {}, &body)
|
6
6
|
@body = body
|
7
7
|
@title = title
|
8
8
|
@narrative = narrative
|
9
|
+
@params = params
|
9
10
|
end
|
10
11
|
|
11
|
-
def
|
12
|
-
|
12
|
+
def [](key)
|
13
|
+
@params[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def run_in(obj)
|
17
|
+
obj.instance_eval(&@body)
|
13
18
|
end
|
14
19
|
end
|
15
20
|
end
|
data/lib/rbehave/version.rb
CHANGED
data/lib/rbehave/world.rb
CHANGED
@@ -9,22 +9,11 @@ module RBehave
|
|
9
9
|
=end
|
10
10
|
module World
|
11
11
|
# store steps and listeners in the singleton metaclass.
|
12
|
-
# This serves both to keep them out of the way
|
13
|
-
# make them available to all instances.
|
12
|
+
# This serves both to keep them out of the way of runtime Worlds
|
13
|
+
# and to make them available to all instances.
|
14
14
|
class << self
|
15
|
-
def create(cls = Object)
|
16
|
-
cls.new.extend(World)
|
17
|
-
end
|
18
|
-
|
19
|
-
def store_and_call(instance, type, name, &block)
|
20
|
-
@step_mother ||= Hash.new {|hsh,key| hsh[key] = Hash.new }
|
21
|
-
if block_given?
|
22
|
-
@step_mother[type][name] = block
|
23
|
-
else
|
24
|
-
block = @step_mother[type][name]
|
25
|
-
end
|
26
|
-
listeners.each { |l| l.found_step(type, name) }
|
27
|
-
instance.instance_eval(&block) unless RBehave::Runner.dry_run?
|
15
|
+
def create(cls = Object, *args)
|
16
|
+
cls.new(*args).extend(World)
|
28
17
|
end
|
29
18
|
|
30
19
|
def listeners
|
@@ -32,24 +21,43 @@ module RBehave
|
|
32
21
|
end
|
33
22
|
|
34
23
|
def add_listener(listener)
|
35
|
-
listeners << listener
|
24
|
+
listeners() << listener
|
25
|
+
end
|
26
|
+
|
27
|
+
def step_mother
|
28
|
+
@step_mother ||= StepMother.new
|
36
29
|
end
|
30
|
+
|
31
|
+
def store_and_call(instance, type, name, *args, &block)
|
32
|
+
if block_given?
|
33
|
+
step_mother.store(type, name, SimpleStep.new(name, &block))
|
34
|
+
end
|
35
|
+
step = step_mother.find(type, name)
|
36
|
+
listeners.each { |l| l.found_step(type, name, *args) }
|
37
|
+
step.perform(instance, *args) unless RBehave::Runner.dry_run?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def GivenScenario(name)
|
42
|
+
step = GivenScenario.new name
|
43
|
+
World.step_mother.store(:given_scenario, name, step)
|
44
|
+
World.store_and_call self, :given_scenario, name
|
37
45
|
end
|
38
46
|
|
39
|
-
def Given(name, &block)
|
40
|
-
World.store_and_call self, :given, name, &block
|
47
|
+
def Given(name, *args, &block)
|
48
|
+
World.store_and_call self, :given, name, *args, &block
|
41
49
|
end
|
42
50
|
|
43
|
-
def When(name, &block)
|
44
|
-
World.store_and_call self, :when, name, &block
|
51
|
+
def When(name, *args, &block)
|
52
|
+
World.store_and_call self, :when, name, *args, &block
|
45
53
|
end
|
46
54
|
|
47
|
-
def Then(name, &block)
|
48
|
-
World.store_and_call self, :then, name, &block
|
55
|
+
def Then(name, *args, &block)
|
56
|
+
World.store_and_call self, :then, name, *args, &block
|
49
57
|
end
|
50
58
|
|
51
59
|
def pending(message = 'todo')
|
52
|
-
raise PendingException, message
|
60
|
+
raise PendingException, message unless RBehave::Runner.dry_run?
|
53
61
|
end
|
54
62
|
end
|
55
63
|
end
|