state-fu 0.12.3 → 0.13.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.
- 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
|