state-fu 0.12.3 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +1 -1
- data/lib/binding.rb +10 -5
- data/lib/executioner.rb +23 -90
- data/lib/lathe.rb +1 -1
- data/lib/machine.rb +19 -16
- data/lib/method_factory.rb +100 -142
- data/lib/persistence.rb +2 -2
- data/lib/persistence/active_record.rb +3 -3
- data/lib/persistence/attribute.rb +4 -4
- data/lib/persistence/base.rb +4 -20
- data/lib/state-fu.rb +4 -2
- data/lib/support/core_ext.rb +22 -44
- data/lib/support/{logger.rb → logging.rb} +5 -5
- data/lib/transition.rb +11 -13
- data/spec/features/active_record_auto_save_spec.rb +22 -0
- data/spec/features/not_requirements_spec.rb +37 -75
- data/spec/features/shared_log_spec.rb +1 -1
- data/spec/integration/active_record_persistence_spec.rb +21 -13
- data/spec/integration/example_01_document_spec.rb +5 -4
- data/spec/integration/requirement_reflection_spec.rb +5 -4
- data/spec/integration/transition_spec.rb +2 -2
- data/spec/spec_helper.rb +2 -2
- data/spec/state_fu_spec.rb +45 -52
- data/spec/units/binding_spec.rb +1 -1
- metadata +5 -6
- data/lib/support/methodical.rb +0 -17
- data/spec/features/method_missing_only_once_spec.rb +0 -28
@@ -4,7 +4,7 @@ describe "an ActiveRecord model with StateFu included:" do
|
|
4
4
|
|
5
5
|
include MySpecHelper
|
6
6
|
|
7
|
-
before(:
|
7
|
+
before(:all) do
|
8
8
|
reset!
|
9
9
|
prepare_active_record() do
|
10
10
|
def self.up
|
@@ -48,7 +48,8 @@ describe "an ActiveRecord model with StateFu included:" do
|
|
48
48
|
before do
|
49
49
|
ExampleRecord.class_eval do
|
50
50
|
state_fu_machine do
|
51
|
-
|
51
|
+
state :initial do
|
52
|
+
# an after transition hook saves the record
|
52
53
|
event( :change, :to => :final ) { after :save! }
|
53
54
|
end
|
54
55
|
end
|
@@ -84,9 +85,9 @@ describe "an ActiveRecord model with StateFu included:" do
|
|
84
85
|
it "should return false for ExampleRecord, :not_a_column" do
|
85
86
|
StateFu::Persistence.active_record_column?( ExampleRecord, :not_a_column ).should == false
|
86
87
|
end
|
88
|
+
|
87
89
|
it "should not clobber activerecord accessors" do
|
88
90
|
@ex.noodle! rescue nil
|
89
|
-
# lambda { @ex.description }.should_not raise_error()
|
90
91
|
@ex.description.should be_nil
|
91
92
|
@ex.description= 'foo'
|
92
93
|
@ex.description.should == 'foo'
|
@@ -122,12 +123,6 @@ describe "an ActiveRecord model with StateFu included:" do
|
|
122
123
|
@ex.save!
|
123
124
|
end
|
124
125
|
|
125
|
-
it "should fail to save if state_fu! does not instantiate the binding before create" do
|
126
|
-
mock( @ex ).state_fu!.at_least( 1 ) { }
|
127
|
-
lambda { @ex.save! }.should raise_error( ActiveRecord::StatementInvalid )
|
128
|
-
@ex.state_fu_field.should == nil
|
129
|
-
end
|
130
|
-
|
131
126
|
it "should create a record given only a name, with the field set to the initial state" do
|
132
127
|
ex = ExampleRecord.new( :name => "exemplar" )
|
133
128
|
ex.should be_valid
|
@@ -166,7 +161,7 @@ describe "an ActiveRecord model with StateFu included:" do
|
|
166
161
|
end
|
167
162
|
end # saved record after transition
|
168
163
|
|
169
|
-
describe "when a second machine named :status is defined with :field_name => 'status'
|
164
|
+
describe "when a second machine named :status is defined with :field_name => 'status'" do
|
170
165
|
before do
|
171
166
|
ExampleRecord.state_fu_machine(:status, :field_name => 'status') do
|
172
167
|
event( :go, :from => :initial, :to => :final )
|
@@ -196,7 +191,20 @@ describe "an ActiveRecord model with StateFu included:" do
|
|
196
191
|
@ex.status= 'damp'
|
197
192
|
lambda { @ex.status }.should raise_error( StateFu::InvalidStateName )
|
198
193
|
end
|
194
|
+
end # second machine
|
195
|
+
|
196
|
+
describe "coexisting with an attribute-backed machine" do
|
197
|
+
it "should get along merrily" do
|
198
|
+
ExampleRecord.machine(:temporary, :field_name => 'temp') do
|
199
|
+
state :new
|
200
|
+
end
|
201
|
+
@ex = ExampleRecord.new()
|
202
|
+
@ex.temporary.should == :new
|
203
|
+
@ex.instance_variable_get("@temp").should == 'new'
|
204
|
+
@ex.temporary.persister.class.should == StateFu::Persistence::Attribute
|
205
|
+
end
|
199
206
|
end
|
200
|
-
|
201
|
-
|
202
|
-
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -106,12 +106,13 @@ describe "Document" do
|
|
106
106
|
# lambda { @doc.status_field }.should raise_error()
|
107
107
|
# end
|
108
108
|
|
109
|
-
it "should be
|
110
|
-
@doc.send(
|
109
|
+
it "should be nil before state_fu is called" do
|
110
|
+
@doc.send(:status_field).should == nil
|
111
111
|
end
|
112
112
|
|
113
|
-
it "should have an initial value of 'draft'" do
|
114
|
-
@doc.
|
113
|
+
it "should have an initial value of 'draft' once the binding is referenced" do
|
114
|
+
@doc.status # initialize the machine
|
115
|
+
@doc.send(:status_field).should == 'draft'
|
115
116
|
end
|
116
117
|
|
117
118
|
it "should be set to 'published' after publish! is called successfully" do
|
@@ -172,9 +172,10 @@ describe "Transition requirement reflection" do
|
|
172
172
|
stub( @obj ).spacesuit?() { false }
|
173
173
|
stub( @obj ).fuel?() { false }
|
174
174
|
end
|
175
|
+
|
175
176
|
describe "when the arity of the proc is 1" do
|
176
177
|
before do
|
177
|
-
@msg = lambda { |
|
178
|
+
@msg = lambda { |transition| "No #{transition.target.name} for you!" }
|
178
179
|
@machine.requirement_messages[:spacesuit?] = @msg
|
179
180
|
end
|
180
181
|
|
@@ -186,14 +187,14 @@ describe "Transition requirement reflection" do
|
|
186
187
|
messages.length.should == 2
|
187
188
|
messages.strings.length.should == 1
|
188
189
|
messages.strings.first.should be_kind_of( String )
|
189
|
-
messages.strings.first.should == "
|
190
|
+
messages.strings.first.should == "No moon for you!"
|
190
191
|
messages.symbols.first.should == :fuel?
|
191
192
|
end
|
192
193
|
end # arity 1
|
193
194
|
|
194
195
|
describe "when the arity of the proc is 0" do
|
195
196
|
before do
|
196
|
-
@msg = lambda { "No
|
197
|
+
@msg = lambda { "No soup for you!" }
|
197
198
|
@machine.requirement_messages[:spacesuit?] = @msg
|
198
199
|
end
|
199
200
|
|
@@ -205,7 +206,7 @@ describe "Transition requirement reflection" do
|
|
205
206
|
messages.length.should == 2
|
206
207
|
messages.strings.length.should == 1
|
207
208
|
messages.strings.first.should be_kind_of( String )
|
208
|
-
messages.strings.first.should == "No
|
209
|
+
messages.strings.first.should == "No soup for you!"
|
209
210
|
messages.symbols.first.should == :fuel?
|
210
211
|
end
|
211
212
|
end # arity 1
|
data/spec/spec_helper.rb
CHANGED
@@ -12,8 +12,8 @@ require 'spec'
|
|
12
12
|
# record the log output on each run
|
13
13
|
LOGFILE = File.join('log', 'spec.log') unless Object.const_defined?('LOGFILE')
|
14
14
|
FileUtils.rm LOGFILE if File.exists?(LOGFILE)
|
15
|
-
StateFu::
|
16
|
-
StateFu::
|
15
|
+
StateFu::Logging.level = Logger::INFO
|
16
|
+
StateFu::Logging.logger = Logger.new(LOGFILE)
|
17
17
|
|
18
18
|
module MySpecHelper
|
19
19
|
include NoStdout
|
data/spec/state_fu_spec.rb
CHANGED
@@ -79,41 +79,35 @@ describe "A door which opens and shuts:" do
|
|
79
79
|
|
80
80
|
describe "magic event methods" do
|
81
81
|
|
82
|
-
it "doesn't normally have a method #shut!" do
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
it "will define #shut! when method_missing is called for the first time" do
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
it "will keep any existing methods when method_missing is triggered" do
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
it "
|
105
|
-
@door.respond_to?(:
|
106
|
-
@door.respond_to?(:open).should
|
107
|
-
@door.respond_to?(:can_shut?).should
|
108
|
-
@door.respond_to?(:can_open?).should
|
109
|
-
@door.respond_to?(:shut!).should
|
110
|
-
@door.respond_to?(:open!).should
|
111
|
-
@door.can_shut?.should == true # call one of them (triggers method_missing)
|
112
|
-
@door.respond_to?(:open).should == false # a private method: Kernel#open
|
113
|
-
@door.respond_to?(:can_shut?).should == true # but these are all newly defined public methods
|
114
|
-
@door.respond_to?(:can_open?).should == true
|
115
|
-
@door.respond_to?(:shut!).should == true
|
116
|
-
@door.respond_to?(:open!).should == true
|
82
|
+
# it "doesn't normally have a method #shut!" do
|
83
|
+
# @door.respond_to?(:shut!).should == false
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# it "will define #shut! when method_missing is called for the first time" do
|
87
|
+
# begin
|
88
|
+
# @door.play_the_ukelele
|
89
|
+
# rescue NoMethodError
|
90
|
+
# end
|
91
|
+
# @door.respond_to?(:shut!).should == true
|
92
|
+
# end
|
93
|
+
|
94
|
+
# it "will keep any existing methods when method_missing is triggered" do
|
95
|
+
# @door.respond_to?(:shut).should == true
|
96
|
+
# @door.respond_to?(:can_shut?).should == false
|
97
|
+
# @door.shut.should == "I don't know how to shut!"
|
98
|
+
# @door.can_shut?.should == true # triggers method_missing
|
99
|
+
# @door.respond_to?(:shut).should == true
|
100
|
+
# @door.respond_to?(:can_shut?).should == true # new methods defined
|
101
|
+
# @door.shut.should == "I don't know how to shut!" # old method retained
|
102
|
+
# end
|
103
|
+
|
104
|
+
it "TODO RENAME THIS SPEC" do
|
105
|
+
@door.respond_to?(:open).should == false # a private method: Kernel#open
|
106
|
+
@door.respond_to?(:open, true).should == true
|
107
|
+
@door.respond_to?(:can_shut?).should == true # but these are all newly defined public methods
|
108
|
+
@door.respond_to?(:can_open?).should == true
|
109
|
+
@door.respond_to?(:shut!).should == true
|
110
|
+
@door.respond_to?(:open!).should == true
|
117
111
|
end
|
118
112
|
|
119
113
|
it "retains any previously defined method_missing" do
|
@@ -160,9 +154,6 @@ describe "A door which opens and shuts:" do
|
|
160
154
|
|
161
155
|
describe "magic state methods" do
|
162
156
|
it "should be defined for each state by method_missing voodoo" do
|
163
|
-
@door.should_not respond_to(:closed?)
|
164
|
-
@door.should_not respond_to(:open?)
|
165
|
-
@door.open?.should == true
|
166
157
|
@door.should respond_to(:closed?)
|
167
158
|
@door.should respond_to(:open?)
|
168
159
|
end
|
@@ -181,7 +172,6 @@ describe "A door which opens and shuts:" do
|
|
181
172
|
|
182
173
|
it "#can_shut? when the current state is open" do
|
183
174
|
@door.current_state.should == :open
|
184
|
-
# @door.state_fu.valid_transitions.map(&:destination).inspect
|
185
175
|
@door.can_shut?.should == true
|
186
176
|
@door.can_open?.should == nil # not a valid transition from this state -> nil
|
187
177
|
end
|
@@ -208,7 +198,7 @@ describe "A door which opens and shuts:" do
|
|
208
198
|
it "raises StateFu::RequirementError if #open! is called when it is locked" do
|
209
199
|
@door.shut!
|
210
200
|
@door.locked = true
|
211
|
-
lambda { @door.open! }.should raise_error(StateFu::RequirementError)
|
201
|
+
lambda { @door.open! }.should raise_error( StateFu::RequirementError )
|
212
202
|
end
|
213
203
|
|
214
204
|
it "tells you why it won't open if you ask nicely" do
|
@@ -551,15 +541,14 @@ describe "arguments given to different method signatures" do
|
|
551
541
|
def b2(t,a=nil) received[:b2] = [t,a] end
|
552
542
|
def c2(t,*a) received[:c2] = [t,a] end
|
553
543
|
|
554
|
-
# these method signatures
|
555
|
-
# and the object which owns the machine
|
544
|
+
# these method signatures take too many arguments and will cause an ArgumentError
|
556
545
|
def a3(t,a,o) received[:a3] = [t,a,o] end
|
557
546
|
def b3(t,a,o=nil) received[:b3] = [t,a,o] end
|
558
547
|
def c3(t,a,*o) received[:c3] = [t,a,o] end
|
559
548
|
|
560
549
|
machine do
|
561
550
|
cycle :state => :observing, :on => :observe do
|
562
|
-
trigger :a1, :b1, :c1, :a2, :b2, :c2
|
551
|
+
trigger :a1, :b1, :c1, :a2, :b2, :c2
|
563
552
|
end
|
564
553
|
end
|
565
554
|
|
@@ -572,7 +561,7 @@ describe "arguments given to different method signatures" do
|
|
572
561
|
end
|
573
562
|
|
574
563
|
it "have a list of execute hooks" do
|
575
|
-
Recorder.machine.events[:observe].hooks[:execute].should == [:a1, :b1, :c1, :a2, :b2, :c2
|
564
|
+
Recorder.machine.events[:observe].hooks[:execute].should == [:a1, :b1, :c1, :a2, :b2, :c2]
|
576
565
|
end
|
577
566
|
end
|
578
567
|
|
@@ -595,7 +584,7 @@ describe "arguments given to different method signatures" do
|
|
595
584
|
end
|
596
585
|
|
597
586
|
it "call the event's :execute hooks on #observe!" do
|
598
|
-
@results.keys.should =~ [:a1, :b1, :c1, :a2, :b2, :c2
|
587
|
+
@results.keys.should =~ [:a1, :b1, :c1, :a2, :b2, :c2]
|
599
588
|
end
|
600
589
|
|
601
590
|
describe "methods which expect one argument" do
|
@@ -615,10 +604,13 @@ describe "arguments given to different method signatures" do
|
|
615
604
|
end
|
616
605
|
|
617
606
|
describe "methods which expect three arguments" do
|
618
|
-
it "
|
619
|
-
|
620
|
-
|
621
|
-
|
607
|
+
it "raise an ArgumentError" do
|
608
|
+
[:a1, :b1, :c1, :a2, :b2, :c2].each do |meth|
|
609
|
+
@t.call(meth)
|
610
|
+
end
|
611
|
+
[:a3, :b3, :c3].each do |meth|
|
612
|
+
lambda { @t.call(:a3) }.should raise_error(ArgumentError)
|
613
|
+
end
|
622
614
|
end
|
623
615
|
end
|
624
616
|
end
|
@@ -734,7 +726,8 @@ describe "sitting at a poker machine" do
|
|
734
726
|
|
735
727
|
event :stop_spinning, :to => :ready do
|
736
728
|
requires :wheels_stopped?
|
737
|
-
execute :payout do
|
729
|
+
execute :payout do |transition|
|
730
|
+
wheel_states = transition.call(:wheel_states)
|
738
731
|
if wheel_states == wheel_states.uniq
|
739
732
|
self.credits += wheel_states.first[:value]
|
740
733
|
end
|
@@ -756,7 +749,7 @@ describe "sitting at a poker machine" do
|
|
756
749
|
execute do
|
757
750
|
silly_noises_inflicted << :spinning_noise
|
758
751
|
end
|
759
|
-
after do
|
752
|
+
after do |transition, *args|
|
760
753
|
if rand(3) == 0
|
761
754
|
# we use binding.stop! rather than self.stop! here
|
762
755
|
# to disambiguate which machine we're sending the event to.
|
@@ -769,7 +762,7 @@ describe "sitting at a poker machine" do
|
|
769
762
|
# (the first one, becuase it was defined first, and automatically
|
770
763
|
# defined methods never clobber any pre-existing methods) -
|
771
764
|
# which isn't what we want here.
|
772
|
-
binding.stop!([:bomb, :cherry, :smiley, :gold].rand)
|
765
|
+
transition.binding.stop!([:bomb, :cherry, :smiley, :gold].rand)
|
773
766
|
end
|
774
767
|
end
|
775
768
|
end
|
data/spec/units/binding_spec.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: state-fu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Lee
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-17 00:00:00 +11:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -62,8 +62,7 @@ files:
|
|
62
62
|
- lib/support/core_ext.rb
|
63
63
|
- lib/support/exceptions.rb
|
64
64
|
- lib/support/has_options.rb
|
65
|
-
- lib/support/
|
66
|
-
- lib/support/methodical.rb
|
65
|
+
- lib/support/logging.rb
|
67
66
|
- lib/support/no_stdout.rb
|
68
67
|
- lib/support/plotter.rb
|
69
68
|
- lib/support/vizier.rb
|
@@ -72,9 +71,9 @@ files:
|
|
72
71
|
- lib/transition.rb
|
73
72
|
- lib/transition_query.rb
|
74
73
|
- spec/custom_formatter.rb
|
74
|
+
- spec/features/active_record_auto_save_spec.rb
|
75
75
|
- spec/features/binding_and_transition_helper_mixin_spec.rb
|
76
76
|
- spec/features/machine_alias_spec.rb
|
77
|
-
- spec/features/method_missing_only_once_spec.rb
|
78
77
|
- spec/features/not_requirements_spec.rb
|
79
78
|
- spec/features/plotter_spec.rb
|
80
79
|
- spec/features/shared_log_spec.rb
|
@@ -139,9 +138,9 @@ specification_version: 3
|
|
139
138
|
summary: A rich library for state-oriented programming with state machines / workflows
|
140
139
|
test_files:
|
141
140
|
- spec/custom_formatter.rb
|
141
|
+
- spec/features/active_record_auto_save_spec.rb
|
142
142
|
- spec/features/binding_and_transition_helper_mixin_spec.rb
|
143
143
|
- spec/features/machine_alias_spec.rb
|
144
|
-
- spec/features/method_missing_only_once_spec.rb
|
145
144
|
- spec/features/not_requirements_spec.rb
|
146
145
|
- spec/features/plotter_spec.rb
|
147
146
|
- spec/features/shared_log_spec.rb
|
data/lib/support/methodical.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
module StateFu
|
2
|
-
module Methodical
|
3
|
-
|
4
|
-
def self.__define_method( method_name, &block )
|
5
|
-
self.class.class_eval do
|
6
|
-
define_method method_name, &block
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
def __define_singleton_method( method_name, &block )
|
11
|
-
(class << object; self; end).class_eval do
|
12
|
-
define_method method_name, &block
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
@@ -1,28 +0,0 @@
|
|
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
|