state-fu 0.13.4 → 0.13.5
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/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
|