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.
@@ -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
@@ -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
@@ -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
@@ -53,6 +53,10 @@ module StateFu
53
53
  self.to_sym === other.to_sym || super(other)
54
54
  end
55
55
 
56
+ def serializable?
57
+ !hooks.values.flatten.map(&:class).include?(Proc) && !!(options.to_yaml rescue false)
58
+ end
59
+
56
60
  end
57
61
  end
58
62
 
@@ -29,6 +29,7 @@ end
29
29
  'support/exceptions',
30
30
  'executioner',
31
31
  'machine',
32
+ 'blueprint',
32
33
  'lathe',
33
34
  'method_factory',
34
35
  'binding',
@@ -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
- @valid_transitions = valid_transitions
102
+ @legal_transitions = valid_transitions
103
103
  super transition, message, options
104
104
  end
105
105
  end
@@ -61,7 +61,7 @@ module StateFu
61
61
 
62
62
  machine.inject_helpers_into( self )
63
63
  self.args = argument_list
64
- apply!(argument_list, &block )
64
+ apply!(argument_list, &block ) # initialize arguments and eval block last
65
65
  end
66
66
 
67
67
 
@@ -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 = @obj.state_fu.transition( :go )
528
+ @t = @obj.state_fu.transition(:go)
529
529
  end
530
530
 
531
- it "should update the object's state after state:entering and before event:after" do
532
- @binding = @obj.state_fu
533
- pending
534
- @t.fire!
535
- end
536
-
537
- it "should be accepted after state:entering and before event:after" do
538
- pending
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
- pending
547
- mock( @obj ).before_go(@t) { @called << :before_go }
548
- mock( @obj ).exiting_a(@t) { @called << :exiting_a }
549
- mock( @obj ).execute_go(@t) { @called << :execute_go }
550
- mock( @obj ).entering_b(@t) { @called << :entering_b }
551
- mock( @obj ).after_go(@t) { @called << :after_go }
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( :execute_proc )
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 << :execute_proc_2
583
+ called(:updated)
590
584
  end
591
585
  end
592
586
  end
593
-
594
- @event.hooks[:execute].length.should == 2
595
- @event.hooks[:execute].first.class.should == Symbol
596
- @event.hooks[:execute].last.class.should == Proc
597
-
598
- @t.fire!()
599
- @called.should == [ :before_go,
600
- :exiting_a,
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
- raise self.class.inspect unless self.is_a?( StateFu::Transition )
618
- called << :execute_named_proc
604
+ called :execute_named_proc
619
605
  end
620
606
  end
621
607
  end
622
608
  @t.fire!()
623
- @called.should == [ :before_go,
624
- :exiting_a,
625
- :execute_go,
626
- :execute_named_proc,
627
- :entering_b,
628
- :after_go,
629
- :accepted_b ]
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 |ctx|
640
- raise ctx.class.inspect unless ctx.is_a?( StateFu::Transition )
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
- @called.should == [ :before_go,
648
- :exiting_a,
649
- :execute_go,
650
- :execute_named_proc,
651
- :entering_b,
652
- :after_go,
653
- :accepted_b ]
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
- @obj.__define_singleton_method(:run_exec) do
908
- t.args.should == [:a, :b,{'c' => :d}]
909
- t.options.should == {}
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.fire!( :run, *args )
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
- it "should execute in the context of the transition initializer after it's set up" do
918
- pending
919
- @obj.__define_singleton_method(:run_exec) do
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 an IllegalTransition if next! is called" do
1026
- lambda { @binding.next! }.should raise_error( StateFu::IllegalTransition )
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
 
@@ -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 = Object.new()
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 = Object.new()
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::DEFAULT_MESSAGE
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
- @binding = Object.new
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 binding, event, origin & target" do
59
- pending
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.message.should == StateFu::IllegalTransition::DEFAULT_MESSAGE
63
- end
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
- pending
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.message.should == msg
91
+ e.transition.should == @transition
92
+ e.message.should == 'danger'
71
93
  end
72
94
 
73
- it "should allow access to the binding, event, origin, and target" do
74
- pending
75
- e = StateFu::IllegalTransition.new( @binding, @event, @origin, @target )
76
- e.binding.should == @binding
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
@@ -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
@@ -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
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: 2009-12-13 00:00:00 +11:00
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