davidlee-state-fu 0.3.1 → 0.10.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 (90) hide show
  1. data/README.textile +124 -34
  2. data/Rakefile +36 -30
  3. data/lib/no_stdout.rb +1 -1
  4. data/lib/state-fu.rb +9 -8
  5. data/lib/state_fu/active_support_lite/array/access.rb +12 -5
  6. data/lib/state_fu/active_support_lite/array/conversions.rb +10 -4
  7. data/lib/state_fu/active_support_lite/array/extract_options.rb +5 -4
  8. data/lib/state_fu/active_support_lite/array/grouping.rb +7 -4
  9. data/lib/state_fu/active_support_lite/array/random_access.rb +4 -3
  10. data/lib/state_fu/active_support_lite/array/wrapper.rb +4 -3
  11. data/lib/state_fu/active_support_lite/array.rb +3 -1
  12. data/lib/state_fu/active_support_lite/blank.rb +18 -9
  13. data/lib/state_fu/active_support_lite/cattr_reader.rb +4 -1
  14. data/lib/state_fu/active_support_lite/keys.rb +8 -3
  15. data/lib/state_fu/active_support_lite/misc.rb +6 -4
  16. data/lib/state_fu/active_support_lite/module/delegation.rb +130 -0
  17. data/lib/state_fu/active_support_lite/module.rb +1 -0
  18. data/lib/state_fu/active_support_lite/object.rb +5 -2
  19. data/lib/state_fu/active_support_lite/string.rb +6 -1
  20. data/lib/state_fu/active_support_lite/symbol.rb +2 -1
  21. data/lib/state_fu/applicable.rb +41 -0
  22. data/lib/state_fu/{helper.rb → arrays.rb} +45 -121
  23. data/lib/state_fu/binding.rb +136 -159
  24. data/lib/state_fu/core_ext.rb +78 -10
  25. data/lib/state_fu/event.rb +112 -48
  26. data/lib/state_fu/exceptions.rb +80 -34
  27. data/lib/state_fu/executioner.rb +149 -0
  28. data/lib/state_fu/has_options.rb +16 -0
  29. data/lib/state_fu/hooks.rb +21 -16
  30. data/lib/state_fu/interface.rb +80 -83
  31. data/lib/state_fu/lathe.rb +361 -148
  32. data/lib/state_fu/logger.rb +122 -45
  33. data/lib/state_fu/machine.rb +60 -32
  34. data/lib/state_fu/method_factory.rb +180 -72
  35. data/lib/state_fu/methodical.rb +17 -0
  36. data/lib/state_fu/persistence/active_record.rb +6 -1
  37. data/lib/state_fu/persistence/attribute.rb +1 -0
  38. data/lib/state_fu/persistence/base.rb +8 -6
  39. data/lib/state_fu/persistence.rb +94 -23
  40. data/lib/state_fu/sprocket.rb +26 -11
  41. data/lib/state_fu/state.rb +8 -27
  42. data/lib/state_fu/transition.rb +207 -98
  43. data/lib/state_fu/transition_query.rb +214 -0
  44. data/lib/state_fu.rb +1 -0
  45. data/lib/tasks/spec_last.rake +46 -0
  46. data/lib/tasks/state_fu.rake +57 -0
  47. data/lib/vizier.rb +61 -61
  48. data/spec/custom_formatter.rb +49 -0
  49. data/spec/features/binding_and_transition_helper_mixin_spec.rb +2 -2
  50. data/spec/features/method_missing_only_once_spec.rb +28 -0
  51. data/spec/features/not_requirements_spec.rb +83 -46
  52. data/spec/features/plotter_spec.rb +97 -0
  53. data/spec/features/shared_log_spec.rb +7 -0
  54. data/spec/features/singleton_machine_spec.rb +39 -0
  55. data/spec/features/state_and_array_options_accessor_spec.rb +1 -1
  56. data/spec/features/{transition_boolean_comparison.rb → transition_boolean_comparison_spec.rb} +29 -18
  57. data/spec/helper.rb +6 -117
  58. data/spec/integration/active_record_persistence_spec.rb +18 -4
  59. data/spec/integration/binding_extension_spec.rb +1 -1
  60. data/spec/integration/class_accessor_spec.rb +49 -59
  61. data/spec/integration/event_definition_spec.rb +20 -20
  62. data/spec/integration/example_01_document_spec.rb +13 -8
  63. data/spec/integration/example_02_string_spec.rb +3 -2
  64. data/spec/integration/instance_accessor_spec.rb +16 -19
  65. data/spec/integration/lathe_extension_spec.rb +2 -2
  66. data/spec/integration/machine_duplication_spec.rb +59 -37
  67. data/spec/integration/relaxdb_persistence_spec.rb +6 -3
  68. data/spec/integration/requirement_reflection_spec.rb +66 -57
  69. data/spec/integration/state_definition_spec.rb +72 -66
  70. data/spec/integration/transition_spec.rb +169 -173
  71. data/spec/spec.opts +5 -3
  72. data/spec/spec_helper.rb +132 -0
  73. data/spec/state_fu_spec.rb +870 -0
  74. data/spec/units/binding_spec.rb +33 -22
  75. data/spec/units/event_spec.rb +3 -22
  76. data/spec/units/exceptions_spec.rb +7 -0
  77. data/spec/units/lathe_spec.rb +7 -7
  78. data/spec/units/machine_spec.rb +67 -75
  79. data/spec/units/method_factory_spec.rb +55 -48
  80. data/spec/units/sprocket_spec.rb +5 -7
  81. data/spec/units/state_spec.rb +33 -24
  82. metadata +31 -19
  83. data/lib/state_fu/active_support_lite/inheritable_attributes.rb +0 -1
  84. data/lib/state_fu/fu_space.rb +0 -51
  85. data/lib/state_fu/mock_transition.rb +0 -38
  86. data/spec/BDD/plotter_spec.rb +0 -115
  87. data/spec/integration/dynamic_requirement_spec.rb +0 -160
  88. data/spec/integration/ex_machine_for_accounts_spec.rb +0 -79
  89. data/spec/integration/sanity_spec.rb +0 -31
  90. data/spec/units/fu_space_spec.rb +0 -95
@@ -1,115 +0,0 @@
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.machine(:drawme) do
13
- chain 'clean -tarnish-> dirty -fester-> putrid'
14
- end
15
- @machine = Klass.machine(:drawme)
16
- end
17
-
18
- it "deleteme" do
19
- @machine.graphviz.save_as('/tmp/1.dot')
20
- end
21
-
22
- describe "example machine" do
23
- it "should have 3 states" do
24
- @machine.states.length.should == 3
25
- end
26
-
27
- it "should have 2 simple events" do
28
- @machine.events.length.should == 2
29
- @machine.events.each{ |e| e.should be_simple }
30
- end
31
-
32
- end
33
-
34
- describe StateFu::Plotter do
35
- describe "class methods" do
36
- describe ".new" do
37
- it "should expect a StateFu::Machine and return a Plotter" do
38
- @plotter = StateFu::Plotter.new( @machine )
39
- @plotter.should be_kind_of(StateFu::Plotter)
40
- @plotter.machine.should == @machine
41
- lambda { StateFu::Plotter.new( "abracadabra" ) }.should raise_error(RuntimeError)
42
- end
43
- end
44
-
45
- describe "a new plotter" do
46
- before do
47
- @plotter = StateFu::Plotter.new( @machine )
48
- end
49
-
50
- it "should have an empty hash of states" do
51
- @plotter = StateFu::Plotter.new( @machine )
52
- @plotter.states.should == {}
53
- end
54
-
55
- end
56
- end # class methods
57
-
58
- describe "instance methods" do
59
- before do
60
- @plotter = StateFu::Plotter.new( @machine )
61
- end
62
-
63
- describe ".generate" do
64
-
65
- it "should call generate_dot!" do
66
- mock( @plotter ).generate_dot!() { "dot" }
67
- @plotter.generate
68
- end
69
-
70
- it "should store the result in the dot attribute" do
71
- mock( @plotter).generate_dot!() { "dot" }
72
- @plotter.generate
73
- @plotter.dot.should == "dot"
74
- end
75
-
76
- it "should return a Vizier::Graph" # .graph
77
-
78
- describe ".save_as(filename)" do
79
- it "should save the string to a file" do
80
- mock( File).open( 'filename', 'w' ).yields( @fh = Object.new() )
81
- mock( @fh ).write( @plotter.output )
82
- @plotter.output.save_as( 'filename' )
83
- end
84
- end
85
- describe ".save!" do
86
- it "should save the string in a tempfile and return the path" do
87
- mock(@tempfile = Object.new).path {"path"}.subject
88
- mock(Tempfile).new(['state_fu_graph','.dot']).yields( @fh = Object.new() ) { @tempfile }
89
- mock( @fh ).write( @plotter.output )
90
- @plotter.output.save!.should == 'path'
91
- end
92
- end
93
- end
94
-
95
- describe "output" do
96
- it "should return the result of .generate" do
97
- @plotter.output.should == @plotter.generate
98
- end
99
- end
100
-
101
- describe "generate_dot!" do
102
-
103
- it "should return a string" do
104
- @plotter.generate_dot!.should be_kind_of(String)
105
- end
106
-
107
- it "should extend the string to respond_to save_as" do
108
- @plotter.output.should respond_to(:save_as)
109
- end
110
- end
111
-
112
- end
113
- end
114
- end
115
-
@@ -1,160 +0,0 @@
1
- require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
-
3
- module MySpecHelper
4
- module DynamicTransitionObjectInstanceMethods
5
- def method_which_expects_nothing_passed_to_it()
6
- true
7
- end
8
-
9
- def method_which_requires_one_arg( t )
10
- t.args.length == 1
11
- end
12
-
13
- def given_any_arg?( t = nil )
14
- t.is_a?( StateFu::Transition ) && !t.args.empty?
15
- end
16
-
17
- def falsey?( t = nil )
18
- t.is_a?( StateFu::Transition ) && !t.args.empty? && t.args.first == false
19
- end
20
-
21
- def truthy?( t = nil )
22
- t.is_a?( StateFu::Transition ) && !t.args.empty? && t.args.first == true
23
- end
24
-
25
- def method_which_always_returns_true( t = nil )
26
- true
27
- end
28
-
29
- def method_which_always_returns_false( t = nil )
30
- false
31
- end
32
- end
33
- end
34
-
35
- describe "Transition requirement evaluation with dynamic conditions" do
36
- include MySpecHelper
37
-
38
- before do
39
- reset!
40
- make_pristine_class("Klass")
41
-
42
- Klass.send :include, MySpecHelper::DynamicTransitionObjectInstanceMethods
43
-
44
- @machine = Klass.machine do
45
-
46
- state :default do
47
- requires :method_which_requires_one_arg, :on => [:entry, :exit]
48
-
49
- cycle do
50
- requires :method_which_requires_one_arg
51
- end
52
-
53
- event :truthify, :to => :truth do
54
- requires :truthy?
55
- end
56
-
57
- event :falsify, :to => :falsehood do
58
- requires :falsey?
59
- requires :method_which_expects_nothing_passed_to_it
60
- end
61
-
62
- #event :impossify, :to => :impossible do
63
- # requires :method_which_always_returns_false
64
- #end
65
-
66
- end
67
- end
68
- @obj = Klass.new
69
- @fu = @obj.state_fu # binding
70
- # Because RR changes the arity of the method on @obj we need to do this:
71
- # stub( @fu ).limit_arguments( anything ) { |t| t }
72
- end
73
-
74
- describe "a requirement that the transition was given one arg" do
75
-
76
- describe "object.cycle?()" do
77
-
78
- it "should return false given no args" do
79
- @fu.cycle?().should == false
80
- end
81
-
82
- it "should be true given one arg" do
83
- @fu.cycle?(1).should == true
84
- end
85
-
86
- it "should be false given two args" do
87
- @fu.cycle?(1,2).should == false
88
- end
89
- end
90
-
91
- it "should evaluate the requirement by passing it a transition when requirements_met? is called" do
92
- t = @fu.cycle()
93
- # mock.proxy( @fu ).evaluate_requirement_with_transition(:method_which_requires_one_arg, t ).at_least(2)
94
- t.requirements_met?.should == false
95
- t.args = [1]
96
- t.requirements_met?.should == true
97
- end
98
-
99
- describe "binding.evaluate_requirement_with_args" do
100
-
101
- end
102
-
103
- describe "valid_transitions" do
104
- describe "given no arguments" do
105
- it "should return {}" do
106
- @fu.valid_transitions.should == {}
107
- end
108
- end
109
-
110
- it "should pass each requirement method a transition object with no args" do
111
- end
112
-
113
- it "should call method_which_requires_one_arg given call_on_object_with_optional_args(:method_which_requires_one_arg .. )" do
114
- t = @fu.blank_mock_transition( :first_arg )
115
- @obj.respond_to?(:method_which_requires_one_arg).should be_true
116
- meth = @obj.method(:method_which_requires_one_arg)
117
- meth.arity.should == 1
118
- @fu.limit_arguments( meth, t ).should == [t]
119
- @fu.call_on_object_with_optional_args( :method_which_requires_one_arg, t )
120
- end
121
-
122
- it "should call method_which_requires_one_arg with a mock transition with one argument" do
123
- t = @fu.blank_mock_transition( :first_arg )
124
- # mock( @obj ).method_which_requires_one_arg( t )
125
- @fu.call_on_object_with_optional_args( :method_which_requires_one_arg, t ).should == true
126
- end
127
-
128
- it "should contain the :cycle_default event only if an arg is supplied" do
129
- @fu.should == :default
130
- ves = @fu.valid_events( )
131
- ves.length.should == 0
132
- ves = @fu.valid_events( 1 )
133
- ves.length.should == 1
134
- ves.first.should == @machine.events[:cycle_default]
135
-
136
- @machine.events[:cycle_default].target.should == @fu.current_state
137
- @fu.evaluate_requirement_with_args( :method_which_requires_one_arg ).should == false
138
- @fu.evaluate_requirement_with_args( :method_which_requires_one_arg, 1 ).should == true
139
- @fu.current_state.enterable_by?( @fu ).should == false
140
- @fu.current_state.enterable_by?( @fu, 1 ).should == true
141
-
142
- vts = @fu.valid_transitions()
143
- vts.should be_kind_of( Hash )
144
- vts.should be_empty
145
- vts = @fu.valid_transitions( 1 )
146
- vts.should_not be_empty
147
- vts.length.should == 1
148
- vts.keys.first.should == @machine.events[:cycle_default]
149
- vts.values.first.should == [@machine.states[:default]]
150
- end
151
-
152
- it "should pass method_which_requires_one_argument() a transition with no arguments" do
153
- # mock.proxy( @obj ).method_which_requires_one_arg( is_a(StateFu::MockTransition) ).times(3)
154
- @fu.valid_transitions( 1 )
155
- end
156
-
157
- end
158
- end
159
-
160
- end
@@ -1,79 +0,0 @@
1
- require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
-
3
- ##
4
- ##
5
- ##
6
-
7
- describe "A simple Machine definition" do
8
-
9
- include MySpecHelper
10
-
11
- describe "When there is a machine describing user logins" do
12
- before(:all) do
13
- reset!
14
- make_pristine_class 'Klass'
15
-
16
- # TODO
17
- #
18
- # Method proxy:
19
- # @obj.om.helpers[:method_name] => proc
20
- # look for methods first on obj
21
- # then in on the helper
22
- # before raising a method_missing error.
23
- #
24
- # when executing methods from the helper,
25
- # use method(mname) to convert them into something
26
- # we can instance_eval in the context of our machinist instance :)
27
- #
28
- # maybe. or maybe that's just sick.
29
-
30
- # @machine_spec = lambda do
31
- Klass.machine( :method_proxy => true ) do
32
-
33
- states :new, :active, :limbo, :expired, :deleted
34
-
35
- event :confirm do
36
- from :new, :to => :confirmed, :auto => true do
37
- needs :email_confirmation
38
- end
39
- end
40
-
41
- state :confirmed do
42
- on_entry :send_welcome_email
43
- end
44
-
45
- event :login, :from => [:confirmed, :active], :to => :active do
46
- execute :handle_login do
47
- halt_unless :password_correct?
48
- halt_if :dodgy_user_agent?
49
- obj.generate_new_cookie!
50
- end
51
- end
52
-
53
- state :active do
54
- on_entry :popup_banner_ads_everywhere
55
- end
56
-
57
- event :delete do
58
- from :ALL, :except => :deleted
59
- to :deleted
60
- after do
61
- obj.destroy!
62
- end
63
- end
64
-
65
- states :ALL do
66
- accepted(:save!)
67
- end
68
-
69
- end
70
- end # machine
71
- # end # before
72
-
73
- # it "parsing it should not throw an error"
74
- # @machine_spec.should_not raise_error()
75
- # @machine_spec.call()
76
-
77
-
78
- end # describe_1
79
- end
@@ -1,31 +0,0 @@
1
- require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
-
3
- class Moo
4
- def arity_1( a )
5
- raise "!"
6
- end
7
-
8
- def arity_0( )
9
- raise "!"
10
- end
11
- end
12
-
13
- describe "sanity check: rr / arity" do
14
- it "should have the expected arity for standard methods" do
15
- m = Moo.new()
16
- m.method(:arity_1).arity.should == 1
17
- m.method(:arity_0).arity.should == 0
18
- end
19
-
20
- it "should have the expected arity when methods are mocked" do
21
- m = Moo.new()
22
- a1 = Object.new
23
- a0 = Object.new
24
- stub( a1 ).arity() { 1 }
25
- stub( a0 ).arity() { 0 }
26
- stub( m ).method(:arity_1) { a1 }
27
- stub( m ).method(:arity_0) { a0 }
28
- m.method(:arity_1).arity.should == 1
29
- m.method(:arity_0).arity.should == 0
30
- end
31
- end
@@ -1,95 +0,0 @@
1
- require File.expand_path("#{File.dirname(__FILE__)}/../helper")
2
-
3
- StateFu::FuSpace.reset!
4
-
5
- ##
6
- ##
7
- ##
8
- describe StateFu::FuSpace do
9
- include MySpecHelper
10
-
11
- before(:each) do
12
- reset!
13
- make_pristine_class 'Klass'
14
- @k = Klass.new()
15
- end
16
-
17
- describe "Before any Machine is defined" do
18
- it "should return {} given StateFu::FuSpace.class_machines()" do
19
- StateFu::FuSpace.should respond_to(:class_machines)
20
- StateFu::FuSpace.class_machines.should == {}
21
- end
22
- end
23
-
24
- describe "Having called Klass.machine() with an empty block:" do
25
- before(:each) do
26
- Klass.machine do
27
- end
28
- StateFu::DEFAULT_MACHINE.should == :state_fu
29
- end
30
-
31
- it "should return { Klass => { ... } } given StateFu::FuSpace.class_machines()" do
32
- StateFu::FuSpace.should respond_to(:class_machines)
33
- machines = StateFu::FuSpace.class_machines()
34
- machines.keys.should == [Klass]
35
- machines.values.first.should be_kind_of( Hash )
36
- end
37
-
38
- it "should return { :state_fu => <StateFu::Machine> } given StateFu::FuSpace.class_machines[Klass]" do
39
- StateFu::FuSpace.should respond_to(:class_machines)
40
- machines = StateFu::FuSpace.class_machines[Klass]
41
- machines.should be_kind_of(Hash)
42
- machines.should_not be_empty
43
- machines.length.should == 1
44
- machines.keys.should == [:state_fu]
45
- machines.values.first.should be_kind_of( StateFu::Machine )
46
- end
47
-
48
- it "should return { Klass => { ... } } given StateFu::FuSpace.field_names()" do
49
- StateFu::FuSpace.should respond_to(:field_names)
50
- fields = StateFu::FuSpace.field_names()
51
- fields.keys.should == [Klass]
52
- fields.values.first.should be_kind_of( Hash )
53
- end
54
-
55
- it "should return { :state_fu => :state_fu_state } given StateFu::FuSpace.field_names[Klass]" do
56
- StateFu::FuSpace.should respond_to(:field_names)
57
- fields = StateFu::FuSpace.field_names[Klass]
58
- fields.should be_kind_of(Hash)
59
- fields.should_not be_empty
60
- fields.length.should == 1
61
- fields.keys.should == [:state_fu]
62
- fields.values.should == [:state_fu_field]
63
- end
64
-
65
- describe "Having called Klass.machine(:two) with an empty block:" do
66
- before(:each) do
67
- # Klass.machine.should_not be_nil
68
- Klass.machine(:two) do
69
- end
70
- end
71
-
72
- it "should return { :state_fu => <StateFu::Machine>, :two => <StateFu::Machine> } given StateFu::FuSpace.class_machines()" do
73
- StateFu::FuSpace.should respond_to(:class_machines)
74
- machines = StateFu::FuSpace.class_machines[Klass]
75
- machines.should be_kind_of(Hash)
76
- machines.should_not be_empty
77
- machines.length.should == 2
78
- machines.keys.sort.should == [:state_fu, :two]
79
- machines.values.each { |v| v.should be_kind_of( StateFu::Machine ) }
80
- end
81
-
82
- describe "Having called StateFu::FuSpace.reset!" do
83
- before(:each) do
84
- StateFu::FuSpace.reset!
85
- end
86
- it "should return {} given StateFu::FuSpace.class_machines()" do
87
- StateFu::FuSpace.should respond_to(:class_machines)
88
- StateFu::FuSpace.class_machines.should == {}
89
- end
90
- end
91
-
92
- end
93
- end
94
- end
95
-