state-fu 0.13.4 → 0.13.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/blueprint.rb +82 -0
- data/lib/lathe.rb +1 -3
- data/lib/machine.rb +15 -1
- data/lib/sprocket.rb +4 -0
- data/lib/state-fu.rb +1 -0
- data/lib/support/exceptions.rb +2 -2
- data/lib/transition.rb +1 -1
- data/spec/features/serialization_spec.rb +120 -0
- data/spec/integration/transition_spec.rb +56 -102
- data/spec/spec_helper.rb +1 -3
- data/spec/units/exceptions_spec.rb +46 -27
- data/spec/units/machine_spec.rb +21 -0
- data/spec/units/sprocket_spec.rb +23 -0
- metadata +5 -2
data/lib/blueprint.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module StateFu
|
2
|
+
module Blueprint
|
3
|
+
|
4
|
+
def self.load_yaml(yaml)
|
5
|
+
hash = YAML.load(yaml)
|
6
|
+
returning Machine.new(hash[:options] || {}) do |machine|
|
7
|
+
add_states machine, hash
|
8
|
+
add_events machine, hash
|
9
|
+
hash[:requirement_messages] &&
|
10
|
+
hash[:requirement_messages].each { |k, v| machine.requirement_messages[k] = v }
|
11
|
+
hash[:initial_state] &&
|
12
|
+
machine.initial_state = hash[:initial_state]
|
13
|
+
# TODO tools, helpers here
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.to_yaml(machine)
|
18
|
+
to_hash(machine).to_yaml
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# serialization
|
24
|
+
|
25
|
+
def self.to_hash(machine)
|
26
|
+
raise TypeError unless machine.serializable?
|
27
|
+
{
|
28
|
+
:states => machine.states.map{ |s| state s },
|
29
|
+
:events => machine.events.map{ |e| event e },
|
30
|
+
:options => machine.options,
|
31
|
+
:helpers => machine.helpers,
|
32
|
+
:tools => machine.tools,
|
33
|
+
:requirement_messages => machine.requirement_messages,
|
34
|
+
:initial_state => machine.initial_state.name
|
35
|
+
}.delete_if {|k, v| v == [] || v.nil?}
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.state(state)
|
39
|
+
{
|
40
|
+
:name => state.name,
|
41
|
+
:hooks => state.hooks.dup.delete_if {|k,v| v == []},
|
42
|
+
:requirements => state.requirements,
|
43
|
+
:options => state.options
|
44
|
+
}.delete_if {|k,v| v == [] || v == {}}
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.event(event)
|
48
|
+
{
|
49
|
+
:name => event.name,
|
50
|
+
:origins => event.origins.names,
|
51
|
+
:targets => event.targets.names,
|
52
|
+
:hooks => event.hooks.dup.delete_if {|k,v| v == []},
|
53
|
+
:requirements => event.requirements,
|
54
|
+
:options => event.options
|
55
|
+
}.delete_if {|k,v| v == [] || v == {}}
|
56
|
+
end
|
57
|
+
|
58
|
+
# deserialization
|
59
|
+
|
60
|
+
def self.add_states(machine, hash)
|
61
|
+
hash[:states].each do |h|
|
62
|
+
s = State.new(machine, h[:name], h[:options] || {})
|
63
|
+
# cheap hacks to get around the data structures used for hooks and requirements
|
64
|
+
h[:hooks].each { |k, hooks| hooks.each { |hook| s.hooks[k] << hook }} if h[:hooks]
|
65
|
+
h[:requirements].each { |r| s.requirements << r } if h[:requirements]
|
66
|
+
machine.states << s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.add_events(machine, hash)
|
71
|
+
hash[:events].each do |h|
|
72
|
+
e = Event.new(machine, h[:name], h[:options] || {})
|
73
|
+
e.origins = h[:origins]
|
74
|
+
e.targets = h[:targets]
|
75
|
+
# cheap hacks to get around the data structures used for hooks and requirements
|
76
|
+
h[:hooks].each { |k, hooks| hooks.each { |hook| e.hooks[k] << hook }} if h[:hooks]
|
77
|
+
h[:requirements].each { |r| e.requirements << r } if h[:requirements]
|
78
|
+
machine.events << e
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/lathe.rb
CHANGED
@@ -176,9 +176,6 @@ module StateFu
|
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
179
|
-
|
180
|
-
#####################################
|
181
|
-
|
182
179
|
# define an event or state requirement.
|
183
180
|
# options:
|
184
181
|
# :on => :entry|:exit|array (state only) - check requirement on state entry, exit or both?
|
@@ -283,6 +280,7 @@ module StateFu
|
|
283
280
|
|
284
281
|
# set the target state(s) of an event
|
285
282
|
# to :destination
|
283
|
+
# to :target_a, :target_b
|
286
284
|
# to [:end, :finale, :intermission]
|
287
285
|
def to *args, &block
|
288
286
|
valid_in_context Event
|
data/lib/machine.rb
CHANGED
@@ -34,9 +34,10 @@ module StateFu
|
|
34
34
|
options[:define_methods] = (name == DEFAULT) unless options.symbolize_keys!.has_key?(:define_methods)
|
35
35
|
options[:field_name] ||= Persistence.default_field_name(name)
|
36
36
|
options[:singleton] = true unless owner.is_a?(Class)
|
37
|
-
# define an accessor method with the given name
|
38
37
|
if options[:singleton]
|
39
38
|
_binding = StateFu::Binding.new machine, owner, name, options
|
39
|
+
|
40
|
+
# define an accessor method with the given name
|
40
41
|
MethodFactory.define_singleton_method(owner, name) { _binding }
|
41
42
|
if alias_name = options[:alias] || options[:as]
|
42
43
|
MethodFactory.define_singleton_method(owner, alias_name) { _binding }
|
@@ -191,5 +192,18 @@ module StateFu
|
|
191
192
|
@graphviz ||= Plotter.new(self).output
|
192
193
|
end
|
193
194
|
|
195
|
+
def to_yaml
|
196
|
+
StateFu::Blueprint.to_yaml(self)
|
197
|
+
end
|
198
|
+
|
199
|
+
# TODO simplify this by adding serializable? to state & event
|
200
|
+
def serializable?
|
201
|
+
named_procs.empty? && (states + events).all?(&:serializable?)
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.load_yaml(yaml)
|
205
|
+
StateFu::Blueprint.load_yaml(yaml)
|
206
|
+
end
|
207
|
+
|
194
208
|
end
|
195
209
|
end
|
data/lib/sprocket.rb
CHANGED
data/lib/state-fu.rb
CHANGED
data/lib/support/exceptions.rb
CHANGED
@@ -36,7 +36,7 @@ module StateFu
|
|
36
36
|
attr_reader :transition
|
37
37
|
|
38
38
|
def initialize transition, message=nil, options={}
|
39
|
-
raise caller.inspect unless transition.is_a?(Transition)
|
39
|
+
# raise caller.inspect unless transition.is_a?(Transition)
|
40
40
|
@transition = transition
|
41
41
|
super transition.binding, message, options
|
42
42
|
end
|
@@ -99,7 +99,7 @@ module StateFu
|
|
99
99
|
attr_reader :legal_transitions
|
100
100
|
|
101
101
|
def initialize transition, message=nil, valid_transitions=nil, options={}
|
102
|
-
@
|
102
|
+
@legal_transitions = valid_transitions
|
103
103
|
super transition, message, options
|
104
104
|
end
|
105
105
|
end
|
data/lib/transition.rb
CHANGED
@@ -0,0 +1,120 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
2
|
+
|
3
|
+
describe "serializing a state machine" do
|
4
|
+
|
5
|
+
before do
|
6
|
+
make_pristine_class('Document') do
|
7
|
+
machine do
|
8
|
+
states :draft, :published, :meta => {:isbn => 'snoo-factory'}
|
9
|
+
|
10
|
+
state :draft do
|
11
|
+
requires :working_title, :message => "What's it called?"
|
12
|
+
on_exit :bump_version
|
13
|
+
end
|
14
|
+
|
15
|
+
event :publish, :from => :draft, :to => :published, :meta => :wibble do
|
16
|
+
execute :generate_publicity
|
17
|
+
requires :title, :message => "It needs a title"
|
18
|
+
end
|
19
|
+
initial_state :blank
|
20
|
+
end
|
21
|
+
end
|
22
|
+
@machine = Document.machine
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should respond to #to_yaml' do
|
26
|
+
@machine.should respond_to(:to_yaml)
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "=> to_yaml" do
|
30
|
+
before do
|
31
|
+
@yaml = YAML.load(@machine.to_yaml)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should have states" do
|
35
|
+
@yaml[:states].map{|s| s[:name] }.should == [:draft, :published, :blank]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should have an initial_state" do
|
39
|
+
@yaml[:initial_state].should == :blank
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should have events" do
|
43
|
+
@yaml[:events].map{|e| e[:name] }.should == [:publish]
|
44
|
+
@yaml[:events][0][:origins].should == [:draft]
|
45
|
+
@yaml[:events][0][:targets].should == [:published]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have state requirements" do
|
49
|
+
@yaml[:states][0][:requirements].should == [:working_title]
|
50
|
+
end
|
51
|
+
it "should have event requirements" do
|
52
|
+
@yaml[:events][0][:requirements].should == [:title]
|
53
|
+
end
|
54
|
+
it "should have state hooks" do
|
55
|
+
@yaml[:states][0][:hooks].should == {:exit=>[:bump_version]}
|
56
|
+
end
|
57
|
+
it "should have event hooks" do
|
58
|
+
@yaml[:events][0][:hooks].should == {:execute=>[:generate_publicity]}
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should have helpers"
|
62
|
+
it "should have tools"
|
63
|
+
|
64
|
+
it "should have requirement_messages" do
|
65
|
+
@yaml[:requirement_messages][:title].should == "It needs a title"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should have arbitrary options for states" do
|
69
|
+
@yaml[:states][0][:options].should == {:meta => {:isbn=>"snoo-factory"}}
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should have arbitrary options for events" do
|
73
|
+
@yaml[:events][0][:options].should == {:meta => :wibble}
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should have options" do
|
77
|
+
@yaml[:options].should == {:field_name=>:state_fu_field, :define_methods=>true}
|
78
|
+
end
|
79
|
+
|
80
|
+
it "raise TypeError if it is not serializable?" do
|
81
|
+
other_machine = StateFu::Machine.new do
|
82
|
+
named_proc(:do_stuff) { puts "I has a proc" }
|
83
|
+
end
|
84
|
+
lambda do
|
85
|
+
other_machine.to_yaml
|
86
|
+
end.should raise_error(TypeError)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "StateFu::Machine.load_yaml" do
|
91
|
+
before do
|
92
|
+
@loaded = StateFu::Machine.load_yaml(@machine.to_yaml)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should return a machine" do
|
96
|
+
@loaded.should be_kind_of(StateFu::Machine)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should have states with the same names" do
|
100
|
+
@loaded.states.names.should == @machine.states.names
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should have events with the same names" do
|
104
|
+
@loaded.events.names.should == @machine.events.names
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should have event hooks" do
|
108
|
+
@loaded.events.first.hooks.should == {:before=>[], :after=>[], :execute=>[:generate_publicity]}
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should have state hooks" do
|
112
|
+
@loaded.states.first.hooks.should == {:exit=>[:bump_version], :entry=>[], :accepted=>[]}
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should return the same yaml if serialized again" do
|
116
|
+
YAML.load(@loaded.to_yaml).should == YAML.load(@machine.to_yaml)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -525,42 +525,37 @@ describe StateFu::Transition do
|
|
525
525
|
|
526
526
|
describe "fire! calling hooks" do
|
527
527
|
before do
|
528
|
-
@t
|
528
|
+
@t = @obj.state_fu.transition(:go)
|
529
529
|
end
|
530
530
|
|
531
|
-
it "should
|
532
|
-
@binding
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
mock( @obj ).entering_b( @t ) { @t.should_not be_accepted }
|
540
|
-
mock( @obj ).after_go(@t) { @t.should be_accepted }
|
541
|
-
mock( @obj ).accepted_b(@t) { @t.should be_accepted }
|
531
|
+
it "should change state between state:entering and event:after" do
|
532
|
+
@binding = @obj.state_fu
|
533
|
+
@obj.should_receive(:entering_b).and_return do
|
534
|
+
@obj.current_state.name.should == :a
|
535
|
+
end
|
536
|
+
@obj.should_receive(:after_go).and_return do
|
537
|
+
@obj.current_state.name.should == :b
|
538
|
+
end
|
542
539
|
@t.fire!
|
543
540
|
end
|
544
541
|
|
545
542
|
it "should call the method for each hook on @obj in order, with the transition" do
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
mock( @obj ).accepted_b(@t) { @called << :accepted_b }
|
553
|
-
|
543
|
+
hooks = [:before_go, :exiting_a, :execute_go, :entering_b, :after_go, :accepted_b]
|
544
|
+
hooks.each do |hook|
|
545
|
+
@obj.should_receive(hook).and_return do
|
546
|
+
@obj.called hook
|
547
|
+
end
|
548
|
+
end
|
554
549
|
@t.fire!()
|
550
|
+
@obj.calls.should == hooks
|
555
551
|
end
|
556
552
|
|
557
553
|
describe "adding an anonymous hook for event.hooks[:execute]" do
|
558
554
|
before do
|
559
|
-
called = @called # get us a ref for the closure
|
560
555
|
Klass.state_fu_machine do
|
561
556
|
event( :go ) do
|
562
557
|
execute do |ctx|
|
563
|
-
called
|
558
|
+
called :execute_proc
|
564
559
|
end
|
565
560
|
end
|
566
561
|
end
|
@@ -581,76 +576,63 @@ describe StateFu::Transition do
|
|
581
576
|
end
|
582
577
|
|
583
578
|
it "should be replace the previous proc for a slot if redefined" do
|
584
|
-
pending
|
585
579
|
called = @called # get us a ref for the closure
|
586
580
|
Klass.state_fu_machine do
|
587
581
|
event( :go ) do
|
588
582
|
execute do |ctx|
|
589
|
-
called
|
583
|
+
called(:updated)
|
590
584
|
end
|
591
585
|
end
|
592
586
|
end
|
593
|
-
|
594
|
-
@
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
:execute_go,
|
602
|
-
:execute_proc_2,
|
603
|
-
:entering_b,
|
604
|
-
:after_go,
|
605
|
-
:accepted_b ]
|
587
|
+
@t.fire!
|
588
|
+
@obj.calls.should == [:before_go,
|
589
|
+
:exiting_a,
|
590
|
+
:execute_go,
|
591
|
+
:updated,
|
592
|
+
:entering_b,
|
593
|
+
:after_go,
|
594
|
+
:accepted_b]
|
606
595
|
end
|
607
596
|
end # anonymous hook
|
608
597
|
|
609
598
|
describe "adding a named hook with a block" do
|
610
599
|
describe "with arity of -1/0" do
|
611
600
|
it "should call the block in the context of the transition" do
|
612
|
-
pending
|
613
|
-
called = @called # get us a ref for the closure
|
614
601
|
Klass.state_fu_machine do
|
615
602
|
event( :go ) do
|
616
603
|
execute(:named_execute) do
|
617
|
-
|
618
|
-
called << :execute_named_proc
|
604
|
+
called :execute_named_proc
|
619
605
|
end
|
620
606
|
end
|
621
607
|
end
|
622
608
|
@t.fire!()
|
623
|
-
@
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
609
|
+
@obj.calls.should == [ :before_go,
|
610
|
+
:exiting_a,
|
611
|
+
:execute_go,
|
612
|
+
:execute_named_proc,
|
613
|
+
:entering_b,
|
614
|
+
:after_go,
|
615
|
+
:accepted_b ]
|
630
616
|
end
|
631
617
|
end # arity 0
|
632
618
|
|
633
619
|
describe "with arity of 1" do
|
634
620
|
it "should call the proc in the context of the object, passing the transition as the argument" do
|
635
|
-
pending
|
636
|
-
called = @called # get us a ref for the closure
|
637
621
|
Klass.state_fu_machine do
|
638
622
|
event( :go ) do
|
639
|
-
execute(:named_execute) do |
|
640
|
-
|
641
|
-
raise self.class.inspect unless self.is_a?( Klass )
|
642
|
-
called << :execute_named_proc
|
623
|
+
execute(:named_execute) do |t|
|
624
|
+
called [:execute_named_proc, t]
|
643
625
|
end
|
644
626
|
end
|
645
627
|
end
|
646
628
|
@t.fire!()
|
647
|
-
@
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
629
|
+
@obj.calls.should == [:before_go,
|
630
|
+
:exiting_a,
|
631
|
+
:execute_go,
|
632
|
+
[:execute_named_proc, @t],
|
633
|
+
:entering_b,
|
634
|
+
:after_go,
|
635
|
+
:accepted_b]
|
654
636
|
end
|
655
637
|
end # arity 1
|
656
638
|
end # named proc
|
@@ -714,7 +696,6 @@ describe StateFu::Transition do
|
|
714
696
|
@event = @machine.events[:go]
|
715
697
|
@a = @machine.states[:a]
|
716
698
|
@b = @machine.states[:b]
|
717
|
-
# stub(@obj).ok? { true }
|
718
699
|
end
|
719
700
|
|
720
701
|
describe "when no block is supplied for the requirement" do
|
@@ -735,8 +716,6 @@ describe StateFu::Transition do
|
|
735
716
|
|
736
717
|
|
737
718
|
it "should contain the event in @binding.valid_events if @obj.ok? is true" do
|
738
|
-
# stub( @binding ).ok?() { true }
|
739
|
-
# set_method_arity(@binding,:ok, 0)
|
740
719
|
@obj.ok = true
|
741
720
|
@binding.current_state.should == @machine.initial_state
|
742
721
|
@binding.events.should == @machine.events
|
@@ -744,14 +723,12 @@ describe StateFu::Transition do
|
|
744
723
|
end
|
745
724
|
|
746
725
|
it "should not contain :go in @binding.valid_events if !@obj.ok?" do
|
747
|
-
# stub( @binding ).ok?() { false }
|
748
726
|
@obj.ok = false
|
749
727
|
@binding.events.should == @machine.events
|
750
728
|
@binding.valid_events.should == []
|
751
729
|
end
|
752
730
|
|
753
731
|
it "should raise a RequirementError if requirements are not satisfied" do
|
754
|
-
#stub( @binding ).ok? { false }
|
755
732
|
@obj.ok = false
|
756
733
|
lambda do
|
757
734
|
@obj.state_fu.fire_transition!( :go )
|
@@ -814,14 +791,12 @@ describe StateFu::Transition do
|
|
814
791
|
end
|
815
792
|
|
816
793
|
it "should be invalid if @obj.entry_ok? is false" do
|
817
|
-
#mock( @obj ).entry_ok? { false }
|
818
794
|
@obj.entry_ok = false
|
819
795
|
@b.entry_requirements.should == [:entry_ok?]
|
820
796
|
@binding.valid_next_states.should == []
|
821
797
|
end
|
822
798
|
|
823
799
|
it "should be valid if @obj.entry_ok? is true" do
|
824
|
-
# mock( @obj ).entry_ok? { true }
|
825
800
|
@obj.entry_ok = true
|
826
801
|
@binding.valid_next_states.should == [@b]
|
827
802
|
end
|
@@ -863,18 +838,6 @@ describe StateFu::Transition do
|
|
863
838
|
|
864
839
|
describe "a method defined on the stateful object" do
|
865
840
|
|
866
|
-
it "should be able to conditionally execute code based on whether the transition is a test" do
|
867
|
-
pending
|
868
|
-
testing = nil
|
869
|
-
@obj.__define_singleton_method(:run_exec) do
|
870
|
-
testing = t.testing?
|
871
|
-
end
|
872
|
-
@obj.state_fu.fire! :run do |t|
|
873
|
-
t.test_only = true
|
874
|
-
end
|
875
|
-
testing.should == true
|
876
|
-
end
|
877
|
-
|
878
841
|
it "should be able to call methods on the transition mixed in via machine.helper" do
|
879
842
|
t1 = @obj.state_fu.transition( :run)
|
880
843
|
t1.should_not respond_to(:my_rad_method)
|
@@ -901,31 +864,25 @@ describe StateFu::Transition do
|
|
901
864
|
end
|
902
865
|
|
903
866
|
it "should be able to access the args / options passed to fire! via transition.args" do
|
904
|
-
pending
|
905
867
|
# NOTE a trailing hash gets munged into options - not args
|
906
868
|
args = [:a, :b, { 'c' => :d }]
|
907
|
-
|
908
|
-
|
909
|
-
|
869
|
+
Klass.state_fu_machine do
|
870
|
+
event(:run, :from => :start, :to => :finish ) do
|
871
|
+
execute( :run_exec ) do |t|
|
872
|
+
t.args.should == [:a, :b, {'c' => :d}]
|
873
|
+
t.options.should == {:c => :d} # options are symbolized
|
874
|
+
end
|
875
|
+
end
|
910
876
|
end
|
911
|
-
trans = @obj.state_fu.
|
877
|
+
trans = @obj.state_fu.run!( *args )
|
912
878
|
trans.should be_accepted
|
913
879
|
end
|
914
880
|
end # method defined on object
|
915
881
|
|
916
882
|
describe "a block passed to binding.transition" do
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
t.args.should == ['who','yo','daddy?']
|
921
|
-
t.options.should == {:hi => :mum}
|
922
|
-
end
|
923
|
-
trans = @obj.state_fu.transition( :run ) do
|
924
|
-
@args = %w/ who yo daddy? /
|
925
|
-
@options = {:hi => :mum}
|
926
|
-
|
927
|
-
end
|
928
|
-
trans.fire!()
|
883
|
+
|
884
|
+
it "should not be possible unless it's made less confusing" do
|
885
|
+
pending "wtf is the use case?"
|
929
886
|
end
|
930
887
|
end
|
931
888
|
|
@@ -934,7 +891,6 @@ describe StateFu::Transition do
|
|
934
891
|
describe "next_transition" do
|
935
892
|
describe "when there are multiple events but only one is fireable?" do
|
936
893
|
before do
|
937
|
-
pending
|
938
894
|
reset!
|
939
895
|
make_pristine_class("Klass")
|
940
896
|
@machine = Klass.state_fu_machine do
|
@@ -954,8 +910,6 @@ describe StateFu::Transition do
|
|
954
910
|
@obj = Klass.new()
|
955
911
|
@binding = @obj.state_fu
|
956
912
|
@binding.events.length.should == 2
|
957
|
-
#@machine.events[:impossibility].fireable_by?( @binding ).should == false
|
958
|
-
#@machine.events[:inevitability].fireable_by?( @binding ).should == true
|
959
913
|
end
|
960
914
|
|
961
915
|
describe "when the fireable? event has only one target" do
|
@@ -1022,8 +976,8 @@ describe StateFu::Transition do
|
|
1022
976
|
t.should be_nil
|
1023
977
|
end
|
1024
978
|
|
1025
|
-
it "should raise
|
1026
|
-
lambda { @binding.next! }.should raise_error( StateFu::
|
979
|
+
it "should raise TransitionNotFound if next! is called" do
|
980
|
+
lambda { @binding.next! }.should raise_error( StateFu::TransitionNotFound )
|
1027
981
|
end
|
1028
982
|
end
|
1029
983
|
|
data/spec/spec_helper.rb
CHANGED
@@ -93,9 +93,7 @@ module MySpecHelper
|
|
93
93
|
RelaxDB.replicate_db "relaxdb_spec_base", "relaxdb_spec"
|
94
94
|
RelaxDB.enable_view_creation
|
95
95
|
rescue => e
|
96
|
-
puts "\n===== Run rake create_base_db before the first spec run ====="
|
97
|
-
puts
|
98
|
-
exit!
|
96
|
+
puts "\n===== Run rake create_base_db in the relaxdb gem folder before the first spec run ====="
|
99
97
|
end
|
100
98
|
#
|
101
99
|
end
|
@@ -1,30 +1,56 @@
|
|
1
1
|
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
2
2
|
|
3
|
+
module MockTransitionHelper
|
4
|
+
def mock_transition
|
5
|
+
transition = Object.new()
|
6
|
+
# hmm, seems we have to stub #binding ourselves ...
|
7
|
+
transition.instance_eval do
|
8
|
+
(class << self; self; end).class_eval do
|
9
|
+
def binding
|
10
|
+
@binding ||= Object.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
transition
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
3
18
|
describe StateFu::RequirementError do
|
19
|
+
include MockTransitionHelper
|
4
20
|
|
5
21
|
describe "constructor" do
|
6
22
|
before do
|
7
|
-
@transition =
|
23
|
+
@transition = mock_transition
|
8
24
|
end
|
9
25
|
|
26
|
+
it "should create an exception given a transition" do
|
27
|
+
e = StateFu::RequirementError.new(@transition)
|
28
|
+
e.should be_kind_of(StateFu::RequirementError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should become an array of requirement messages with #to_a" do
|
32
|
+
e = StateFu::RequirementError.new(@transition)
|
33
|
+
e.should be_kind_of(StateFu::RequirementError)
|
34
|
+
@transition.should_receive(:unmet_requirement_messages).and_return(['oshit'])
|
35
|
+
e.to_a.should == ['oshit']
|
36
|
+
end
|
10
37
|
end
|
11
38
|
end
|
12
39
|
|
13
40
|
describe StateFu::TransitionHalted do
|
41
|
+
include MockTransitionHelper
|
14
42
|
|
15
43
|
describe "constructor" do
|
16
44
|
before do
|
17
|
-
@transition =
|
45
|
+
@transition = mock_transition
|
18
46
|
end
|
19
47
|
|
20
48
|
it "should create a TransitionHalted given a transition" do
|
21
|
-
pending
|
22
49
|
e = StateFu::TransitionHalted.new( @transition )
|
23
50
|
e.should be_kind_of( StateFu::TransitionHalted )
|
24
51
|
end
|
25
52
|
|
26
53
|
it "should allow a custom message" do
|
27
|
-
pending
|
28
54
|
msg = 'helo'
|
29
55
|
e = StateFu::TransitionHalted.new( @transition, msg )
|
30
56
|
e.should be_kind_of( StateFu::TransitionHalted )
|
@@ -32,14 +58,12 @@ describe StateFu::TransitionHalted do
|
|
32
58
|
end
|
33
59
|
|
34
60
|
it "should allow a message to be omitted" do
|
35
|
-
pending
|
36
61
|
e = StateFu::TransitionHalted.new( @transition )
|
37
62
|
e.should be_kind_of( StateFu::TransitionHalted )
|
38
|
-
e.message.should == StateFu::TransitionHalted
|
63
|
+
e.message.should == "StateFu::TransitionHalted"
|
39
64
|
end
|
40
65
|
|
41
66
|
it "should allow access to the transition" do
|
42
|
-
pending
|
43
67
|
e = StateFu::TransitionHalted.new( @transition )
|
44
68
|
e.transition.should == @transition
|
45
69
|
end
|
@@ -47,36 +71,31 @@ describe StateFu::TransitionHalted do
|
|
47
71
|
end
|
48
72
|
|
49
73
|
describe StateFu::IllegalTransition do
|
74
|
+
include MockTransitionHelper
|
75
|
+
|
50
76
|
before do
|
51
|
-
@
|
52
|
-
@origin = Object.new
|
53
|
-
@event = Object.new
|
54
|
-
@target = Object.new
|
77
|
+
@transition = mock_transition
|
55
78
|
end
|
56
79
|
|
57
80
|
describe "constructor" do
|
58
|
-
it "should create an IllegalTransition given a
|
59
|
-
|
60
|
-
e = StateFu::IllegalTransition.new( @binding, @event, @origin, @target )
|
81
|
+
it "should create an IllegalTransition given a transition" do
|
82
|
+
e = StateFu::IllegalTransition.new( @transition)
|
61
83
|
e.should be_kind_of( StateFu::IllegalTransition )
|
62
|
-
e.
|
63
|
-
|
84
|
+
e.transition.should == @transition
|
85
|
+
e.message.should == "StateFu::IllegalTransition"
|
86
|
+
end
|
64
87
|
|
65
88
|
it "should allow a custom message" do
|
66
|
-
|
67
|
-
msg = 'helo'
|
68
|
-
e = StateFu::IllegalTransition.new( @binding, @event, @origin, @target, msg )
|
89
|
+
e = StateFu::IllegalTransition.new( @transition, 'danger' )
|
69
90
|
e.should be_kind_of( StateFu::IllegalTransition )
|
70
|
-
e.
|
91
|
+
e.transition.should == @transition
|
92
|
+
e.message.should == 'danger'
|
71
93
|
end
|
72
94
|
|
73
|
-
it "should allow access to
|
74
|
-
|
75
|
-
e
|
76
|
-
e.
|
77
|
-
e.event.should == @event
|
78
|
-
e.origin.should == @origin
|
79
|
-
e.target.should == @target
|
95
|
+
it "should allow access to a list of valid transitions if provided" do
|
96
|
+
e = StateFu::IllegalTransition.new( @transition, 'danger', [:a, :b] )
|
97
|
+
e.should be_kind_of( StateFu::IllegalTransition )
|
98
|
+
e.legal_transitions.should == [:a, :b]
|
80
99
|
end
|
81
100
|
end
|
82
101
|
end
|
data/spec/units/machine_spec.rb
CHANGED
@@ -222,5 +222,26 @@ describe StateFu::Machine do
|
|
222
222
|
|
223
223
|
end # named_procs
|
224
224
|
|
225
|
+
describe "#serializable?" do
|
226
|
+
it "should be true if the machine has no procs / lambdas" do
|
227
|
+
StateFu::Machine.new.should be_serializable
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should be false if it has a named_proc" do
|
231
|
+
other_machine = StateFu::Machine.new do
|
232
|
+
named_proc(:do_stuff) { puts "I has a proc" }
|
233
|
+
end
|
234
|
+
other_machine.serializable?.should == false
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should be false if any states / events are not seralizable" do
|
238
|
+
other_machine = StateFu::Machine.new do
|
239
|
+
state :red
|
240
|
+
end
|
241
|
+
other_machine.states.first.should_receive(:serializable?).and_return(false)
|
242
|
+
other_machine.serializable?.should == false
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
225
246
|
end # instance methods
|
226
247
|
end
|
data/spec/units/sprocket_spec.rb
CHANGED
@@ -65,5 +65,28 @@ describe "Common features / functionality for StateFu::State & StateFu::Event" d
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
describe "#serializable?" do
|
69
|
+
it "should be true if hooks contains no procs and options are empty" do
|
70
|
+
@sprocket.hooks.values.flatten.map(&:class).include?(Proc).should == false
|
71
|
+
@sprocket.options = {}
|
72
|
+
@sprocket.serializable?.should == true
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should be false if hooks contains any procs" do
|
76
|
+
pending
|
77
|
+
# ...
|
78
|
+
@sprocket.serializable?.should == false
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should be false if options contains anything which cannot be turned into yaml" do
|
82
|
+
@sprocket.serializable?.should == true
|
83
|
+
@sprocket.options[:whut] = Class
|
84
|
+
lambda do
|
85
|
+
@sprocket.options.to_yaml
|
86
|
+
end.should raise_error
|
87
|
+
@sprocket.serializable?.should == false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
68
91
|
end
|
69
92
|
end
|
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.13.
|
4
|
+
version: 0.13.5
|
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:
|
12
|
+
date: 2010-01-08 00:00:00 +11:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -25,6 +25,7 @@ extra_rdoc_files:
|
|
25
25
|
files:
|
26
26
|
- Rakefile
|
27
27
|
- lib/binding.rb
|
28
|
+
- lib/blueprint.rb
|
28
29
|
- lib/event.rb
|
29
30
|
- lib/executioner.rb
|
30
31
|
- lib/hooks.rb
|
@@ -76,6 +77,7 @@ files:
|
|
76
77
|
- spec/features/machine_alias_spec.rb
|
77
78
|
- spec/features/not_requirements_spec.rb
|
78
79
|
- spec/features/plotter_spec.rb
|
80
|
+
- spec/features/serialization_spec.rb
|
79
81
|
- spec/features/shared_log_spec.rb
|
80
82
|
- spec/features/singleton_machine_spec.rb
|
81
83
|
- spec/features/state_and_array_options_accessor_spec.rb
|
@@ -143,6 +145,7 @@ test_files:
|
|
143
145
|
- spec/features/machine_alias_spec.rb
|
144
146
|
- spec/features/not_requirements_spec.rb
|
145
147
|
- spec/features/plotter_spec.rb
|
148
|
+
- spec/features/serialization_spec.rb
|
146
149
|
- spec/features/shared_log_spec.rb
|
147
150
|
- spec/features/singleton_machine_spec.rb
|
148
151
|
- spec/features/state_and_array_options_accessor_spec.rb
|