state-fu 0.11.1

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 (85) hide show
  1. data/LICENSE +40 -0
  2. data/README.textile +293 -0
  3. data/Rakefile +114 -0
  4. data/lib/binding.rb +292 -0
  5. data/lib/event.rb +192 -0
  6. data/lib/executioner.rb +120 -0
  7. data/lib/hooks.rb +39 -0
  8. data/lib/interface.rb +132 -0
  9. data/lib/lathe.rb +538 -0
  10. data/lib/machine.rb +184 -0
  11. data/lib/method_factory.rb +243 -0
  12. data/lib/persistence.rb +116 -0
  13. data/lib/persistence/active_record.rb +34 -0
  14. data/lib/persistence/attribute.rb +47 -0
  15. data/lib/persistence/base.rb +100 -0
  16. data/lib/persistence/relaxdb.rb +23 -0
  17. data/lib/persistence/session.rb +7 -0
  18. data/lib/sprocket.rb +58 -0
  19. data/lib/state-fu.rb +56 -0
  20. data/lib/state.rb +48 -0
  21. data/lib/support/active_support_lite/array.rb +9 -0
  22. data/lib/support/active_support_lite/array/access.rb +60 -0
  23. data/lib/support/active_support_lite/array/conversions.rb +202 -0
  24. data/lib/support/active_support_lite/array/extract_options.rb +21 -0
  25. data/lib/support/active_support_lite/array/grouping.rb +109 -0
  26. data/lib/support/active_support_lite/array/random_access.rb +13 -0
  27. data/lib/support/active_support_lite/array/wrapper.rb +25 -0
  28. data/lib/support/active_support_lite/blank.rb +67 -0
  29. data/lib/support/active_support_lite/cattr_reader.rb +57 -0
  30. data/lib/support/active_support_lite/keys.rb +57 -0
  31. data/lib/support/active_support_lite/misc.rb +59 -0
  32. data/lib/support/active_support_lite/module.rb +1 -0
  33. data/lib/support/active_support_lite/module/delegation.rb +130 -0
  34. data/lib/support/active_support_lite/object.rb +9 -0
  35. data/lib/support/active_support_lite/string.rb +38 -0
  36. data/lib/support/active_support_lite/symbol.rb +16 -0
  37. data/lib/support/applicable.rb +41 -0
  38. data/lib/support/arrays.rb +197 -0
  39. data/lib/support/core_ext.rb +90 -0
  40. data/lib/support/exceptions.rb +106 -0
  41. data/lib/support/has_options.rb +16 -0
  42. data/lib/support/logger.rb +165 -0
  43. data/lib/support/methodical.rb +17 -0
  44. data/lib/support/no_stdout.rb +55 -0
  45. data/lib/support/plotter.rb +62 -0
  46. data/lib/support/vizier.rb +300 -0
  47. data/lib/tasks/spec_last.rake +55 -0
  48. data/lib/tasks/state_fu.rake +57 -0
  49. data/lib/transition.rb +338 -0
  50. data/lib/transition_query.rb +224 -0
  51. data/spec/custom_formatter.rb +49 -0
  52. data/spec/features/binding_and_transition_helper_mixin_spec.rb +111 -0
  53. data/spec/features/method_missing_only_once_spec.rb +28 -0
  54. data/spec/features/not_requirements_spec.rb +118 -0
  55. data/spec/features/plotter_spec.rb +97 -0
  56. data/spec/features/shared_log_spec.rb +7 -0
  57. data/spec/features/singleton_machine_spec.rb +39 -0
  58. data/spec/features/state_and_array_options_accessor_spec.rb +47 -0
  59. data/spec/features/transition_boolean_comparison_spec.rb +101 -0
  60. data/spec/helper.rb +13 -0
  61. data/spec/integration/active_record_persistence_spec.rb +202 -0
  62. data/spec/integration/binding_extension_spec.rb +41 -0
  63. data/spec/integration/class_accessor_spec.rb +117 -0
  64. data/spec/integration/event_definition_spec.rb +74 -0
  65. data/spec/integration/example_01_document_spec.rb +133 -0
  66. data/spec/integration/example_02_string_spec.rb +88 -0
  67. data/spec/integration/instance_accessor_spec.rb +97 -0
  68. data/spec/integration/lathe_extension_spec.rb +67 -0
  69. data/spec/integration/machine_duplication_spec.rb +101 -0
  70. data/spec/integration/relaxdb_persistence_spec.rb +97 -0
  71. data/spec/integration/requirement_reflection_spec.rb +270 -0
  72. data/spec/integration/state_definition_spec.rb +163 -0
  73. data/spec/integration/transition_spec.rb +1033 -0
  74. data/spec/spec.opts +9 -0
  75. data/spec/spec_helper.rb +132 -0
  76. data/spec/state_fu_spec.rb +948 -0
  77. data/spec/units/binding_spec.rb +192 -0
  78. data/spec/units/event_spec.rb +214 -0
  79. data/spec/units/exceptions_spec.rb +82 -0
  80. data/spec/units/lathe_spec.rb +570 -0
  81. data/spec/units/machine_spec.rb +229 -0
  82. data/spec/units/method_factory_spec.rb +366 -0
  83. data/spec/units/sprocket_spec.rb +69 -0
  84. data/spec/units/state_spec.rb +59 -0
  85. metadata +171 -0
@@ -0,0 +1,49 @@
1
+ require 'spec/runner/formatter/progress_bar_formatter'
2
+ class CustomFormatter < Spec::Runner::Formatter::ProgressBarFormatter
3
+ def add_line(l)
4
+ (@lines||=[]) << l
5
+ end
6
+
7
+ def dump_pending
8
+ unless @pending_examples.empty?
9
+ lpad = @pending_examples.map{|e|e[2].length}.max
10
+ @output.puts
11
+ @output.puts "Pending: #{@pending_examples.length}"
12
+ @pending_examples.each do |pending_example|
13
+ @output.puts yellow("#{pending_example[2].strip.ljust(lpad)} # - #{pending_example[1]}")
14
+ end
15
+ end
16
+ @output.flush
17
+ end
18
+
19
+ # def example_failed(example, counter, failure)
20
+ # failure.instance_eval do
21
+ # (class<<self;self;end).class_eval { attr_accessor :location }
22
+ # end
23
+ # failure.location = example.location
24
+ # super(example,counter,failure)
25
+ # end
26
+
27
+ def dump_summary(duration, example_count, failure_count, pending_count)
28
+ if @lines
29
+ @output.puts "="*72
30
+ @lines.each do |line|
31
+ @output.puts line
32
+ end
33
+ @output.puts "="*72
34
+ end
35
+ super(duration, example_count, failure_count, pending_count)
36
+ end
37
+
38
+ def dump_failure(counter, failure)
39
+ @output.puts
40
+ @output.puts "#{counter.to_s})"
41
+ # @output.puts failure.location
42
+ @output.puts colorize_failure("#{failure.header}\n#{failure.exception.message}", failure.inspect)
43
+ @output.puts format_backtrace(failure.exception.backtrace)
44
+ #failure.exception
45
+ line = failure.exception.backtrace.last rescue failure.exception.inspect
46
+ add_line line
47
+ @output.flush
48
+ end
49
+ end
@@ -0,0 +1,111 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ module MySpecHelper
4
+ module BindingExampleHelper
5
+
6
+ attr_accessor :ok
7
+
8
+ def helper_method
9
+ end
10
+
11
+ def requirement_satisfier?
12
+ true
13
+ end
14
+
15
+ def requirement_satisfier_with_arg?( t )
16
+ end
17
+ end
18
+
19
+ module OtherExampleHelper
20
+ def other_helper_method
21
+ end
22
+
23
+ def other_requirement_satisfier?
24
+ true
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "extending bindings and transitions with Lathe#helper" do
30
+
31
+ include MySpecHelper
32
+
33
+ before(:each) do
34
+ reset!
35
+ make_pristine_class('Klass')
36
+ Klass.class_eval do
37
+ attr_accessor :ok
38
+ end
39
+
40
+ @machine = Klass.state_fu_machine do
41
+ helper MySpecHelper::BindingExampleHelper
42
+ helper 'my_spec_helper/other_example_helper'
43
+
44
+ chain "a -a2b-> b -b2c-> c"
45
+
46
+ events.each do |e|
47
+ e.requires :requirement_satisfier?
48
+ e.requires :requirement_satisfier_with_arg?
49
+ e.requires :other_requirement_satisfier?
50
+ end
51
+ end
52
+
53
+ @other_machine = Klass.state_fu_machine(:other) do
54
+ helper ::MySpecHelper::OtherExampleHelper
55
+ end
56
+ @obj = Klass.new
57
+ @binding = @obj.state_fu
58
+ @other_binding = @obj.other
59
+ @transition = @obj.state_fu.transition(:a2b)
60
+ end # before
61
+
62
+ #
63
+ #
64
+
65
+ describe "binding" do
66
+ describe "instance methods" do
67
+
68
+ it "should respond to helper_method" do
69
+ @binding.should respond_to( :helper_method)
70
+ end
71
+
72
+
73
+ it "should respond to other_helper_method" do
74
+ @binding.should respond_to( :other_helper_method)
75
+ end
76
+
77
+ it "should respond to requirement_satisfier?" do
78
+ @binding.should respond_to( :requirement_satisfier?)
79
+ end
80
+
81
+ it "should respond to other_requirement_satisfier?" do
82
+ @binding.should respond_to( :other_requirement_satisfier?)
83
+ end
84
+
85
+ end
86
+ end
87
+
88
+ describe "transition" do
89
+ describe "instance methods" do
90
+
91
+ it "should respond to helper_method" do
92
+ @transition.should respond_to( :helper_method)
93
+ end
94
+
95
+ it "should respond to other_helper_method" do
96
+ @transition.should respond_to( :other_helper_method)
97
+ end
98
+
99
+ it "should respond to requirement_satisfier?" do
100
+ @transition.should respond_to( :requirement_satisfier?)
101
+ end
102
+
103
+ it "should respond to other_requirement_satisfier?" do
104
+ @transition.should respond_to( :other_requirement_satisfier?)
105
+ end
106
+
107
+ end
108
+ end
109
+
110
+ end
111
+
@@ -0,0 +1,28 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ describe "method_missing" do
4
+ include MySpecHelper
5
+ before do
6
+ make_pristine_class('Klass')
7
+ Klass.state_fu_machine() {}
8
+ @obj = Klass.new
9
+ end
10
+
11
+ it "should revert to the original method_missing after it is called once" do
12
+ mock.proxy( @obj ).state_fu!.times(1)
13
+ mm1 = @obj.method(:method_missing)
14
+ call_snafu = lambda do
15
+ begin
16
+ @obj.snafu!
17
+ rescue NoMethodError
18
+ end
19
+ end
20
+ call_snafu.call()
21
+ mm2 = @obj.method(:method_missing)
22
+ mm1.should_not == mm2
23
+ call_snafu.call()
24
+ mm3 = @obj.method(:method_missing)
25
+ mm3.should == mm2
26
+ # @obj.snafu
27
+ end
28
+ end
@@ -0,0 +1,118 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ module RequirementFeatureHelper
4
+ def account_expired?
5
+ !! account_expired
6
+ end
7
+
8
+ def valid_password?
9
+ !! valid_password
10
+ end
11
+ end
12
+
13
+ # it_should_behave_like "!" do
14
+ shared_examples_for "not requirements" do
15
+ describe "requirements with names beginning with no[t]_" do
16
+
17
+ it "should return the opposite of the requirement name without not_" do
18
+ # @obj.current_state.should == :guest
19
+ @obj.stfu.teleport! :anonymous
20
+ @obj.valid_password = false
21
+ @binding.can_has_valid_password?.should == false
22
+ @binding.can_has_not_valid_password?.should == true
23
+ @binding.can_has_no_valid_password?.should == true
24
+ @obj.valid_password = true
25
+ @binding.can_has_valid_password?.should == true
26
+ @binding.can_has_not_valid_password?.should == false
27
+ @binding.can_has_no_valid_password?.should == false
28
+ end
29
+
30
+ it "should call the method directly if one exists" do
31
+ @obj.valid_password = true
32
+ (class << @obj; self; end).class_eval do
33
+ define_method( :no_valid_password? ) { true }
34
+ end
35
+ @binding.can_has_valid_password?.should == true
36
+ @binding.can_has_not_valid_password?.should == false
37
+ @binding.can_has_no_valid_password?.should == true
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ describe "requirements" do
45
+ before(:all) do
46
+ reset!
47
+ make_pristine_class('Klass')
48
+ Klass.class_eval do
49
+ attr_accessor :valid_password
50
+ attr_accessor :account_expired
51
+ end
52
+ @machine = StateFu::Machine.new do
53
+ initial_state :guest
54
+
55
+ event :has_valid_password, :from => :anonymous, :to => :logged_in do
56
+ requires :valid_password?
57
+ end
58
+
59
+ event :has_not_valid_password, :from => :anonymous, :to => :suspect do
60
+ requires :not_valid_password?
61
+ end
62
+
63
+ event :has_no_valid_password, :from => :anonymous, :to => :suspect do
64
+ requires :no_valid_password?
65
+ end
66
+
67
+ end
68
+ end
69
+
70
+ before :each do
71
+ @obj.valid_password = true
72
+ @obj.account_expired = false
73
+ end
74
+
75
+ describe "requirements defined with a machine helper" do
76
+ before :all do
77
+ @machine.lathe { helper RequirementFeatureHelper }
78
+ @machine.bind!(Klass, :default)
79
+ @obj = Klass.new
80
+ @binding = @obj.state_fu
81
+ end
82
+
83
+ it_should_behave_like "not requirements"
84
+
85
+ it "should not have methods on the object" do
86
+ @obj.respond_to?(:valid_password?).should == false
87
+ @obj.respond_to?(:account_expired?).should == false
88
+ end
89
+
90
+ it "should have methods on the binding" do
91
+ # this is a little misleading because theyre not evaluated on the binding ..
92
+ @binding.respond_to?(:valid_password?).should == true
93
+ @binding.respond_to?(:account_expired?).should == true
94
+ @binding.respond_to?(:not_valid_password?).should == false
95
+ @binding.respond_to?(:not_account_expired?).should == false
96
+ end
97
+ end
98
+
99
+ describe "requirements defined on the object" do
100
+ before :all do
101
+ @machine.bind!(Klass, :default)
102
+ @obj = Klass.new
103
+ @binding = @obj.state_fu
104
+ Klass.class_eval do
105
+ include RequirementFeatureHelper
106
+ end
107
+ end
108
+
109
+ it_should_behave_like "not requirements"
110
+
111
+ it "should have methods on the object" do
112
+ @obj.respond_to?(:valid_password?).should == true
113
+ @obj.respond_to?(:not_valid_password?).should == false
114
+ @obj.respond_to?(:account_expired?).should == true
115
+ @obj.respond_to?(:not_account_expired?).should == false
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,97 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ ##
4
+ ##
5
+ ##
6
+
7
+ describe StateFu::Plotter do
8
+ include MySpecHelper
9
+ before do
10
+ reset!
11
+ make_pristine_class('Klass')
12
+ @machine = Klass.state_fu_machine(:drawme) do
13
+ chain 'clean -tarnish-> dirty -fester-> putrid'
14
+ end
15
+ @machine = Klass.state_fu_machine(:drawme)
16
+ end
17
+
18
+
19
+ describe "class methods" do
20
+ describe ".new" do
21
+ it "should expect a StateFu::Machine and return a Plotter" do
22
+ @plotter = StateFu::Plotter.new( @machine )
23
+ @plotter.should be_kind_of(StateFu::Plotter)
24
+ @plotter.machine.should == @machine
25
+ lambda { StateFu::Plotter.new( "abracadabra" ) }.should raise_error(RuntimeError)
26
+ end
27
+ end
28
+
29
+ describe "a new plotter" do
30
+ before do
31
+ @plotter = StateFu::Plotter.new( @machine )
32
+ end
33
+
34
+ it "should have an empty hash of states" do
35
+ @plotter = StateFu::Plotter.new( @machine )
36
+ @plotter.states.should == {}
37
+ end
38
+
39
+ end
40
+ end # class methods
41
+
42
+ describe "instance methods" do
43
+ before do
44
+ @plotter = StateFu::Plotter.new( @machine )
45
+ end
46
+
47
+ describe ".generate" do
48
+
49
+ it "should call generate_dot!" do
50
+ mock( @plotter ).generate_dot!() { "dot" }
51
+ @plotter.generate
52
+ end
53
+
54
+ it "should store the result in the dot attribute" do
55
+ mock( @plotter).generate_dot!() { "dot" }
56
+ @plotter.generate
57
+ @plotter.dot.should == "dot"
58
+ end
59
+
60
+ describe ".save_as(filename)" do
61
+ it "should save the string to a file" do
62
+ mock( File).open( 'filename', 'w' ).yields( @fh = Object.new() )
63
+ mock( @fh ).write( @plotter.output )
64
+ @plotter.output.save_as( 'filename' )
65
+ end
66
+ end
67
+
68
+ describe ".save!" do
69
+ it "should save the string in a tempfile and return the path" do
70
+ mock(@tempfile = Object.new).path {"path"}.subject
71
+ mock(Tempfile).new(['state_fu_graph','.dot']).yields( @fh = Object.new() ) { @tempfile }
72
+ mock( @fh ).write( @plotter.output )
73
+ @plotter.output.save!.should == 'path'
74
+ end
75
+ end
76
+ end # instance methods
77
+
78
+ describe "output" do
79
+ it "should return the result of .generate" do
80
+ @plotter.output.should == @plotter.generate
81
+ end
82
+ end
83
+
84
+ describe "generate_dot!" do
85
+ it "should return a string" do
86
+ @plotter.generate_dot!.should be_kind_of(String)
87
+ end
88
+
89
+ it "should extend the string to respond_to save_as" do
90
+ @plotter.output.should respond_to(:save_as)
91
+ end
92
+ end # output
93
+
94
+ end
95
+ end
96
+
97
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ describe "using StateFu w/ shared logs" do
4
+ it "should be sane" do
5
+ StateFu::Logger.shared?.should == false
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
+
3
+ describe "singleton machines" do
4
+ before do
5
+ make_pristine_class('Klass')
6
+ @m = StateFu::Machine.new do
7
+ state :a do
8
+ event :beatify, :transitions_to => :b
9
+ end
10
+ end
11
+ @m.events.length.should == 1
12
+ @obj = Klass.new
13
+ @m.bind!( @obj, :my_binding)
14
+ end
15
+
16
+ it "should return a binding to the machine when calling the binding's name" do
17
+ @obj.should respond_to(:my_binding)
18
+ @obj.my_binding.should be_kind_of(StateFu::Binding)
19
+ @obj.my_binding.machine.should == @m
20
+ @obj.my_binding.object.should == @obj
21
+ end
22
+
23
+ it "should have event methods defined" do
24
+ %w/beatify can_beatify? beatify!/.each do |method_name|
25
+ @obj.my_binding.should respond_to(method_name)
26
+ @obj.should respond_to(method_name)
27
+ end
28
+ end
29
+
30
+ it "should transition" do
31
+ @b = @obj.my_binding
32
+ @b.current_state.should == :a
33
+ t = @obj.beatify!
34
+ t.should be_kind_of(StateFu::Transition)
35
+ t.should be_accepted
36
+ @b.current_state.should == :b
37
+ end
38
+
39
+ end