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.
Files changed (47) hide show
  1. data/CHANGELOG.txt +5 -0
  2. data/Manifest.txt +44 -17
  3. data/NOTES.txt +2 -7
  4. data/behaviour/everything.rb +1 -1
  5. data/behaviour/examples/{everything.rb → examples.rb} +0 -0
  6. data/behaviour/examples/rbehave/documenter/plain_text_documenter_behaviour.rb +1 -0
  7. data/behaviour/examples/rbehave/given_scenario_behaviour.rb +26 -0
  8. data/behaviour/examples/rbehave/runner/options_behaviour.rb +39 -0
  9. data/behaviour/examples/rbehave/runner/runner_behaviour.rb +39 -0
  10. data/behaviour/examples/rbehave/runner/story_runner_behaviour.rb +58 -2
  11. data/behaviour/examples/rbehave/simple_step_behaviour.rb +35 -0
  12. data/behaviour/examples/rbehave/step_mother_behaviour.rb +32 -0
  13. data/behaviour/examples/rbehave/world_behaviour.rb +44 -5
  14. data/behaviour/examples/rspec_adapter.rb +3 -1
  15. data/examples/game-of-life/.loadpath +5 -0
  16. data/examples/game-of-life/.project +18 -0
  17. data/examples/game-of-life/README.txt +21 -0
  18. data/examples/game-of-life/behaviour/everything.rb +2 -0
  19. data/examples/game-of-life/behaviour/examples/examples.rb +2 -0
  20. data/examples/game-of-life/behaviour/examples/game_behaviour.rb +17 -0
  21. data/examples/game-of-life/behaviour/examples/grid_behaviour.rb +68 -0
  22. data/examples/game-of-life/behaviour/examples/helper.rb +2 -0
  23. data/examples/game-of-life/behaviour/stories/CellsWithLessThanTwoNeighboursDie.story +21 -0
  24. data/examples/game-of-life/behaviour/stories/CellsWithMoreThanThreeNeighboursDie.story +21 -0
  25. data/examples/game-of-life/behaviour/stories/EmptySpacesWithThreeNeighboursCreateACell.story +42 -0
  26. data/examples/game-of-life/behaviour/stories/ICanCreateACell.story +42 -0
  27. data/examples/game-of-life/behaviour/stories/ICanKillACell.story +17 -0
  28. data/examples/game-of-life/behaviour/stories/TheGridWraps.story +53 -0
  29. data/examples/game-of-life/behaviour/stories/create_a_cell.rb +58 -0
  30. data/examples/game-of-life/behaviour/stories/stories.txt +22 -0
  31. data/examples/game-of-life/life.rb +2 -0
  32. data/examples/game-of-life/life/game.rb +6 -0
  33. data/examples/game-of-life/life/grid.rb +38 -0
  34. data/lib/rbehave.rb +6 -71
  35. data/lib/rbehave/documenter/plain_text_documenter.rb +2 -2
  36. data/lib/rbehave/exceptions.rb +3 -3
  37. data/lib/rbehave/given_scenario.rb +12 -0
  38. data/lib/rbehave/runner.rb +52 -0
  39. data/lib/rbehave/runner/options.rb +26 -0
  40. data/lib/rbehave/runner/story_runner.rb +26 -5
  41. data/lib/rbehave/simple_step.rb +14 -0
  42. data/lib/rbehave/step_mother.rb +19 -0
  43. data/lib/rbehave/story.rb +8 -3
  44. data/lib/rbehave/version.rb +1 -1
  45. data/lib/rbehave/world.rb +31 -23
  46. metadata +47 -19
  47. data/Rakefile +0 -54
@@ -0,0 +1,2 @@
1
+ require 'life/game'
2
+ require 'life/grid'
@@ -0,0 +1,6 @@
1
+ class Game
2
+ attr_accessor :grid
3
+ def initialize(rows,cols)
4
+ @grid = Grid.new(rows, cols)
5
+ end
6
+ end
@@ -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[File.join(File.dirname(__FILE__), 'rbehave/**/*.rb')].sort.each { |lib| require lib }
1
+ Dir.chdir(File.dirname(__FILE__)) do
2
+ Dir['rbehave/**/*.rb'].each { |f| require f }
3
+ end
2
4
 
3
- module RBehave
4
- def Story(title, narrative, &body)
5
- RBehave::Runner::story_runner.Story(title, narrative, &body)
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)
@@ -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,12 @@
1
+ module RBehave
2
+ class GivenScenario
3
+ def initialize name
4
+ @name = name
5
+ end
6
+
7
+ def perform(instance, *args)
8
+ scenario = Runner::StoryRunner.scenario_from_current_story @name
9
+ Runner::ScenarioRunner.new.run(scenario, instance)
10
+ end
11
+ end
12
+ end
@@ -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
- attr_accessor :stories, :scenarios
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
- @scenario_runner.run(scenario, @world_creator.create)
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 run_in(container)
12
- container.instance_eval(&@body)
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
@@ -1,7 +1,7 @@
1
1
  module Rbehave #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 1
4
+ MINOR = 2
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
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 and to
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