state-fu 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
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