davidlee-state-fu 0.0.2 → 0.2.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 +13 -1
- data/lib/state-fu.rb +4 -3
- data/lib/state_fu/active_support_lite/array/access.rb +53 -0
- data/lib/state_fu/active_support_lite/array/conversions.rb +196 -0
- data/lib/state_fu/active_support_lite/array/extract_options.rb +20 -0
- data/lib/state_fu/active_support_lite/array/grouping.rb +106 -0
- data/lib/state_fu/active_support_lite/array/random_access.rb +12 -0
- data/lib/state_fu/active_support_lite/array/wrapper.rb +24 -0
- data/lib/state_fu/active_support_lite/array.rb +7 -0
- data/lib/state_fu/active_support_lite/blank.rb +58 -0
- data/lib/state_fu/active_support_lite/cattr_reader.rb +54 -0
- data/lib/state_fu/active_support_lite/inheritable_attributes.rb +1 -0
- data/lib/state_fu/active_support_lite/keys.rb +52 -0
- data/lib/state_fu/active_support_lite/object.rb +6 -0
- data/lib/state_fu/active_support_lite/string.rb +33 -0
- data/lib/state_fu/active_support_lite/symbol.rb +15 -0
- data/lib/state_fu/binding.rb +113 -58
- data/lib/state_fu/core_ext.rb +12 -13
- data/lib/state_fu/event.rb +4 -4
- data/lib/state_fu/exceptions.rb +12 -0
- data/lib/state_fu/helper.rb +74 -12
- data/lib/state_fu/lathe.rb +15 -0
- data/lib/state_fu/machine.rb +17 -25
- data/lib/state_fu/mock_transition.rb +38 -0
- data/lib/state_fu/persistence/active_record.rb +2 -2
- data/lib/state_fu/persistence/attribute.rb +4 -4
- data/lib/state_fu/persistence/base.rb +1 -1
- data/lib/state_fu/sprocket.rb +4 -0
- data/lib/state_fu/state.rb +4 -4
- data/lib/state_fu/transition.rb +24 -22
- data/spec/helper.rb +5 -3
- data/spec/integration/binding_extension_spec.rb +41 -0
- data/spec/integration/dynamic_requirement_spec.rb +160 -0
- data/spec/integration/example_01_document_spec.rb +2 -1
- data/spec/integration/lathe_extension_spec.rb +67 -0
- data/spec/integration/requirement_reflection_spec.rb +71 -11
- data/spec/integration/temp_spec.rb +17 -0
- data/spec/integration/transition_spec.rb +31 -19
- data/spec/units/binding_spec.rb +6 -0
- data/spec/units/event_spec.rb +6 -5
- data/spec/units/lathe_spec.rb +4 -2
- metadata +40 -17
|
@@ -5,8 +5,7 @@ module StateFu
|
|
|
5
5
|
def self.prepare_field( klass, field_name )
|
|
6
6
|
_field_name = field_name
|
|
7
7
|
klass.send :before_save, :state_fu!
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
# validates_presence_of _field_name
|
|
10
9
|
end
|
|
11
10
|
|
|
12
11
|
private
|
|
@@ -19,6 +18,7 @@ module StateFu
|
|
|
19
18
|
end
|
|
20
19
|
|
|
21
20
|
def write_attribute( string_value )
|
|
21
|
+
# Logger.warn(" :write_attribute,#{ field_name},#{ string_value} \n=========================================================")
|
|
22
22
|
object.send( :write_attribute, field_name, string_value )
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -4,7 +4,7 @@ module StateFu
|
|
|
4
4
|
|
|
5
5
|
def self.prepare_field( klass, field_name )
|
|
6
6
|
# ensure getter exists
|
|
7
|
-
unless klass.instance_methods.include?( field_name )
|
|
7
|
+
unless klass.instance_methods.map(&:to_sym).include?( field_name.to_sym )
|
|
8
8
|
Logger.info "Adding attr_reader :#{field_name} for #{klass}"
|
|
9
9
|
_field_name = field_name
|
|
10
10
|
klass.class_eval do
|
|
@@ -14,7 +14,7 @@ module StateFu
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# ensure setter exists
|
|
17
|
-
unless klass.instance_methods.include?( "#{field_name}=" )
|
|
17
|
+
unless klass.instance_methods.map(&:to_sym).include?( :"#{field_name}=" )
|
|
18
18
|
Logger.info "Adding attr_writer :#{field_name}= for #{klass}"
|
|
19
19
|
_field_name = field_name
|
|
20
20
|
klass.class_eval do
|
|
@@ -31,13 +31,13 @@ module StateFu
|
|
|
31
31
|
|
|
32
32
|
def read_attribute
|
|
33
33
|
string = object.send( field_name )
|
|
34
|
-
Logger.info "Read attribute #{field_name}, got #{string} for #{object}"
|
|
34
|
+
Logger.info "Read attribute #{field_name}, got #{string.inspect} for #{object.inspect}"
|
|
35
35
|
string
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def write_attribute( string_value )
|
|
39
39
|
writer_method = "#{field_name}="
|
|
40
|
-
Logger.info "Writing attribute #{field_name} -> #{string_value} for #{object}"
|
|
40
|
+
Logger.info "Writing attribute #{field_name} -> #{string_value.inspect} for #{object.inspect}"
|
|
41
41
|
object.send( writer_method, string_value )
|
|
42
42
|
end
|
|
43
43
|
|
|
@@ -38,7 +38,7 @@ module StateFu
|
|
|
38
38
|
Logger.info("Machine has no states: #{machine}") if machine.states.empty?
|
|
39
39
|
else
|
|
40
40
|
persist!
|
|
41
|
-
Logger.debug("Object
|
|
41
|
+
# Logger.debug("Object #{object} resuming #{binding.method_name} at #{current_state.name}: #{object.inspect}")
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
data/lib/state_fu/sprocket.rb
CHANGED
data/lib/state_fu/state.rb
CHANGED
|
@@ -25,15 +25,15 @@ module StateFu
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def enterable_by?( binding )
|
|
28
|
+
def enterable_by?( binding, *args )
|
|
29
29
|
entry_requirements.reject do |r|
|
|
30
|
-
res = binding.
|
|
30
|
+
res = binding.evaluate_requirement_with_args( r, *args )
|
|
31
31
|
end.empty?
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def exitable_by?( binding )
|
|
34
|
+
def exitable_by?( binding, *args )
|
|
35
35
|
exit_requirements.reject do |r|
|
|
36
|
-
binding.
|
|
36
|
+
binding.evaluate_requirement_with_args( r, *args )
|
|
37
37
|
end.empty?
|
|
38
38
|
end
|
|
39
39
|
|
data/lib/state_fu/transition.rb
CHANGED
|
@@ -9,6 +9,7 @@ module StateFu
|
|
|
9
9
|
class Transition
|
|
10
10
|
include StateFu::Helper
|
|
11
11
|
include ContextualEval
|
|
12
|
+
|
|
12
13
|
attr_reader( :binding,
|
|
13
14
|
:machine,
|
|
14
15
|
:origin,
|
|
@@ -24,25 +25,18 @@ module StateFu
|
|
|
24
25
|
attr_accessor :test_only, :args, :options
|
|
25
26
|
|
|
26
27
|
def initialize( binding, event, target=nil, *args, &block )
|
|
28
|
+
@binding = binding
|
|
29
|
+
@machine = binding.machine
|
|
30
|
+
@object = binding.object
|
|
31
|
+
@origin = binding.current_state
|
|
32
|
+
|
|
27
33
|
# ensure event is a StateFu::Event
|
|
28
34
|
if event.is_a?( Symbol ) && e = binding.machine.events[ event ]
|
|
29
35
|
event = e
|
|
30
36
|
end
|
|
31
37
|
raise( ArgumentError, "Not an event: #{event}" ) unless event.is_a?( StateFu::Event )
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
case target
|
|
35
|
-
when StateFu::State # good
|
|
36
|
-
when Symbol
|
|
37
|
-
target = binding.machine.states[ target ] ||
|
|
38
|
-
raise( ArgumentError, "target cannot be determined: #{target.inspect}" )
|
|
39
|
-
when NilClass
|
|
40
|
-
unless target = event.target
|
|
41
|
-
raise( ArgumentError, "target cannot be determined: #{target.inspect}" )
|
|
42
|
-
end
|
|
43
|
-
else
|
|
44
|
-
raise ArgumentError.new( target.inspect )
|
|
45
|
-
end
|
|
39
|
+
target = find_event_target( event, target ) || raise( ArgumentError, "target cannot be determined: #{target.inspect}" )
|
|
46
40
|
|
|
47
41
|
# ensure target is valid for the event
|
|
48
42
|
unless event.targets.include?( target )
|
|
@@ -57,10 +51,6 @@ module StateFu
|
|
|
57
51
|
end
|
|
58
52
|
|
|
59
53
|
@options = args.extract_options!.symbolize_keys!
|
|
60
|
-
@binding = binding
|
|
61
|
-
@machine = binding.machine
|
|
62
|
-
@object = binding.object
|
|
63
|
-
@origin = binding.current_state
|
|
64
54
|
@target = target
|
|
65
55
|
@event = event
|
|
66
56
|
@args = args
|
|
@@ -79,18 +69,30 @@ module StateFu
|
|
|
79
69
|
|
|
80
70
|
def unmet_requirements
|
|
81
71
|
requirements.reject do |requirement|
|
|
82
|
-
binding.
|
|
72
|
+
binding.evaluate_requirement_with_transition( requirement, self )
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def evaluate_requirement_message( name )
|
|
77
|
+
msg = machine.requirement_messages[name]
|
|
78
|
+
case msg
|
|
79
|
+
when String, nil
|
|
80
|
+
msg
|
|
81
|
+
when Symbol, Proc
|
|
82
|
+
evaluate_named_proc_or_method( msg, self )
|
|
83
|
+
else
|
|
84
|
+
raise msg.class.to_s
|
|
83
85
|
end
|
|
84
86
|
end
|
|
85
87
|
|
|
86
88
|
def unmet_requirement_messages
|
|
87
|
-
unmet_requirements.map do |
|
|
88
|
-
|
|
89
|
+
unmet_requirements.map do |requirement|
|
|
90
|
+
evaluate_requirement_message( requirement )
|
|
89
91
|
end
|
|
90
92
|
end
|
|
91
93
|
|
|
92
94
|
def check_requirements!
|
|
93
|
-
raise RequirementError.new( unmet_requirements.inspect ) unless requirements_met?
|
|
95
|
+
raise RequirementError.new( self, unmet_requirements.inspect ) unless requirements_met?
|
|
94
96
|
end
|
|
95
97
|
|
|
96
98
|
def requirements_met?
|
|
@@ -117,7 +119,7 @@ module StateFu
|
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
def run_hook( hook )
|
|
120
|
-
evaluate_named_proc_or_method( hook )
|
|
122
|
+
evaluate_named_proc_or_method( hook, self )
|
|
121
123
|
end
|
|
122
124
|
|
|
123
125
|
def halt!( message )
|
data/spec/helper.rb
CHANGED
|
@@ -28,8 +28,9 @@ module MySpecHelper
|
|
|
28
28
|
|
|
29
29
|
def prepare_active_record( options={}, &migration )
|
|
30
30
|
begin
|
|
31
|
+
require 'activesupport'
|
|
31
32
|
require 'active_record'
|
|
32
|
-
rescue
|
|
33
|
+
rescue LoadError => e
|
|
33
34
|
pending "skipping specifications due to load error: #{e}"
|
|
34
35
|
return false
|
|
35
36
|
end
|
|
@@ -81,9 +82,10 @@ module MySpecHelper
|
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
def set_method_arity( object, method_name, needed_arity = 1 )
|
|
84
|
-
a =
|
|
85
|
+
a = Proc.new {}
|
|
85
86
|
stub( a ).arity() { needed_arity }
|
|
86
|
-
stub( object ).method(
|
|
87
|
+
stub( object ).method( anything ) { |x| object.send(x) }
|
|
88
|
+
stub( object ).method( method_name ) { a }
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
|
2
|
+
|
|
3
|
+
module BenchGrinder
|
|
4
|
+
def snark
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "extending StateFu::Lathe" do
|
|
9
|
+
include MySpecHelper
|
|
10
|
+
|
|
11
|
+
describe "helpers" do
|
|
12
|
+
before do
|
|
13
|
+
reset!
|
|
14
|
+
make_pristine_class('Klass')
|
|
15
|
+
@machine = Klass.machine() do
|
|
16
|
+
state :init
|
|
17
|
+
end
|
|
18
|
+
end # before
|
|
19
|
+
|
|
20
|
+
describe "lathe.helper" do
|
|
21
|
+
it "should add the arguments to the machine's collection of helpers" do
|
|
22
|
+
@machine.should respond_to(:helpers)
|
|
23
|
+
@machine.helpers.should be_empty
|
|
24
|
+
@machine.lathe do
|
|
25
|
+
helper :bench_grinder
|
|
26
|
+
end
|
|
27
|
+
@machine.helpers.should_not be_empty
|
|
28
|
+
@machine.helpers.should include(:bench_grinder)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should extend the binding with the helper's methods" do
|
|
32
|
+
@machine.lathe do
|
|
33
|
+
helper :bench_grinder
|
|
34
|
+
end
|
|
35
|
+
@machine.helpers.should include(:bench_grinder)
|
|
36
|
+
@obj = Klass.new
|
|
37
|
+
@obj.state_fu.should respond_to(:snark)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
|
2
|
+
|
|
3
|
+
module MySpecHelper
|
|
4
|
+
module DynamicTransitionObjectInstanceMethods
|
|
5
|
+
def method_which_expects_nothing_passed_to_it()
|
|
6
|
+
true
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def method_which_requires_one_arg( t )
|
|
10
|
+
t.args.length == 1
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def given_any_arg?( t = nil )
|
|
14
|
+
t.is_a?( StateFu::Transition ) && !t.args.empty?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def falsey?( t = nil )
|
|
18
|
+
t.is_a?( StateFu::Transition ) && !t.args.empty? && t.args.first == false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def truthy?( t = nil )
|
|
22
|
+
t.is_a?( StateFu::Transition ) && !t.args.empty? && t.args.first == true
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def method_which_always_returns_true( t = nil )
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def method_which_always_returns_false( t = nil )
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "Transition requirement evaluation with dynamic conditions" do
|
|
36
|
+
include MySpecHelper
|
|
37
|
+
|
|
38
|
+
before do
|
|
39
|
+
reset!
|
|
40
|
+
make_pristine_class("Klass")
|
|
41
|
+
|
|
42
|
+
Klass.send :include, MySpecHelper::DynamicTransitionObjectInstanceMethods
|
|
43
|
+
|
|
44
|
+
@machine = Klass.machine do
|
|
45
|
+
|
|
46
|
+
state :default do
|
|
47
|
+
requires :method_which_requires_one_arg, :on => [:entry, :exit]
|
|
48
|
+
|
|
49
|
+
cycle do
|
|
50
|
+
requires :method_which_requires_one_arg
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
event :truthify, :to => :truth do
|
|
54
|
+
requires :truthy?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
event :falsify, :to => :falsehood do
|
|
58
|
+
requires :falsey?
|
|
59
|
+
requires :method_which_expects_nothing_passed_to_it
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
#event :impossify, :to => :impossible do
|
|
63
|
+
# requires :method_which_always_returns_false
|
|
64
|
+
#end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
@obj = Klass.new
|
|
69
|
+
@fu = @obj.state_fu # binding
|
|
70
|
+
# Because RR changes the arity of the method on @obj we need to do this:
|
|
71
|
+
# stub( @fu ).limit_arguments( anything ) { |t| t }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe "a requirement that the transition was given one arg" do
|
|
75
|
+
|
|
76
|
+
describe "object.cycle?()" do
|
|
77
|
+
|
|
78
|
+
it "should return false given no args" do
|
|
79
|
+
@fu.cycle?().should == false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should be true given one arg" do
|
|
83
|
+
@fu.cycle?(1).should == true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should be false given two args" do
|
|
87
|
+
@fu.cycle?(1,2).should == false
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should evaluate the requirement by passing it a transition when requirements_met? is called" do
|
|
92
|
+
t = @fu.cycle()
|
|
93
|
+
# mock.proxy( @fu ).evaluate_requirement_with_transition(:method_which_requires_one_arg, t ).at_least(2)
|
|
94
|
+
t.requirements_met?.should == false
|
|
95
|
+
t.args = [1]
|
|
96
|
+
t.requirements_met?.should == true
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
describe "binding.evaluate_requirement_with_args" do
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
describe "valid_transitions" do
|
|
104
|
+
describe "given no arguments" do
|
|
105
|
+
it "should return {}" do
|
|
106
|
+
@fu.valid_transitions.should == {}
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should pass each requirement method a transition object with no args" do
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "should call method_which_requires_one_arg given call_on_object_with_optional_args(:method_which_requires_one_arg .. )" do
|
|
114
|
+
t = @fu.blank_mock_transition( :first_arg )
|
|
115
|
+
@obj.respond_to?(:method_which_requires_one_arg).should be_true
|
|
116
|
+
meth = @obj.method(:method_which_requires_one_arg)
|
|
117
|
+
meth.arity.should == 1
|
|
118
|
+
@fu.limit_arguments( meth, t ).should == [t]
|
|
119
|
+
@fu.call_on_object_with_optional_args( :method_which_requires_one_arg, t )
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "should call method_which_requires_one_arg with a mock transition with one argument" do
|
|
123
|
+
t = @fu.blank_mock_transition( :first_arg )
|
|
124
|
+
# mock( @obj ).method_which_requires_one_arg( t )
|
|
125
|
+
@fu.call_on_object_with_optional_args( :method_which_requires_one_arg, t ).should == true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it "should contain the :cycle_default event only if an arg is supplied" do
|
|
129
|
+
@fu.should == :default
|
|
130
|
+
ves = @fu.valid_events( )
|
|
131
|
+
ves.length.should == 0
|
|
132
|
+
ves = @fu.valid_events( 1 )
|
|
133
|
+
ves.length.should == 1
|
|
134
|
+
ves.first.should == @machine.events[:cycle_default]
|
|
135
|
+
|
|
136
|
+
@machine.events[:cycle_default].target.should == @fu.current_state
|
|
137
|
+
@fu.evaluate_requirement_with_args( :method_which_requires_one_arg ).should == false
|
|
138
|
+
@fu.evaluate_requirement_with_args( :method_which_requires_one_arg, 1 ).should == true
|
|
139
|
+
@fu.current_state.enterable_by?( @fu ).should == false
|
|
140
|
+
@fu.current_state.enterable_by?( @fu, 1 ).should == true
|
|
141
|
+
|
|
142
|
+
vts = @fu.valid_transitions()
|
|
143
|
+
vts.should be_kind_of( Hash )
|
|
144
|
+
vts.should be_empty
|
|
145
|
+
vts = @fu.valid_transitions( 1 )
|
|
146
|
+
vts.should_not be_empty
|
|
147
|
+
vts.length.should == 1
|
|
148
|
+
vts.keys.first.should == @machine.events[:cycle_default]
|
|
149
|
+
vts.values.first.should == [@machine.states[:default]]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "should pass method_which_requires_one_argument() a transition with no arguments" do
|
|
153
|
+
# mock.proxy( @obj ).method_which_requires_one_arg( is_a(StateFu::MockTransition) ).times(3)
|
|
154
|
+
@fu.valid_transitions( 1 )
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
|
2
|
+
|
|
3
|
+
module BenchGrinder
|
|
4
|
+
def snark
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
describe "extending StateFu::Lathe" do
|
|
9
|
+
include MySpecHelper
|
|
10
|
+
|
|
11
|
+
describe "helpers" do
|
|
12
|
+
before do
|
|
13
|
+
reset!
|
|
14
|
+
make_pristine_class('Klass')
|
|
15
|
+
@machine = Klass.machine() do
|
|
16
|
+
state :init
|
|
17
|
+
end
|
|
18
|
+
end # before
|
|
19
|
+
|
|
20
|
+
describe "lathe.tool" do
|
|
21
|
+
|
|
22
|
+
it "should add the arguments to the machine's collection of tools" do
|
|
23
|
+
@machine.should respond_to(:tools)
|
|
24
|
+
@machine.tools.should be_empty
|
|
25
|
+
@machine.lathe do
|
|
26
|
+
tool :bench_grinder
|
|
27
|
+
end
|
|
28
|
+
@machine.tools.should_not be_empty
|
|
29
|
+
@machine.tools.should include(:bench_grinder)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should extend the machine's lathe" do
|
|
33
|
+
@machine.lathe do
|
|
34
|
+
tool :bench_grinder
|
|
35
|
+
snark()
|
|
36
|
+
end
|
|
37
|
+
@machine.lathe.should respond_to( :snark )
|
|
38
|
+
@machine.lathe.snark
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should extend the machine's lathe for state and events" do
|
|
42
|
+
@machine.lathe do
|
|
43
|
+
tool :bench_grinder
|
|
44
|
+
snark()
|
|
45
|
+
state :grinding do
|
|
46
|
+
snark()
|
|
47
|
+
event :grind do
|
|
48
|
+
snark()
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should not extend another machine's lathe" do
|
|
55
|
+
@machine.lathe do
|
|
56
|
+
tool :bench_grinder
|
|
57
|
+
snark()
|
|
58
|
+
end
|
|
59
|
+
m2 = Klass.machine(:two) do
|
|
60
|
+
end
|
|
61
|
+
lambda { m2.lathe.snark }.should raise_error( NoMethodError )
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end # tool
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -9,15 +9,15 @@ describe "Transition requirement reflection" do
|
|
|
9
9
|
make_pristine_class("Klass")
|
|
10
10
|
@machine = Klass.machine do
|
|
11
11
|
state :soviet_russia do
|
|
12
|
-
requires( :papers_in_order?,
|
|
13
|
-
|
|
12
|
+
requires( :papers_in_order?, :on => [:entry, :exit] )
|
|
13
|
+
requires( :money_for_bribe?, :on => [:entry, :exit] )
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
state :america do
|
|
17
17
|
requires( :no_turban?,
|
|
18
18
|
:us_visa?,
|
|
19
19
|
:on => :entry )
|
|
20
|
-
requires( :no_arrest_warrant
|
|
20
|
+
requires( :no_arrest_warrant?, :on => [:entry,:exit] )
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
state :moon do
|
|
@@ -66,18 +66,79 @@ describe "Transition requirement reflection" do
|
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
describe "flying from russia to america without one's affairs in order while wearing a turban" do
|
|
70
|
+
before do
|
|
71
|
+
mock( @obj ).us_visa?() { false }
|
|
72
|
+
mock( @obj ).no_turban?() { false }
|
|
73
|
+
mock( @obj ).no_arrest_warrant?() { false }
|
|
74
|
+
mock( @obj ).money_for_bribe?() { false }
|
|
75
|
+
mock( @obj ).papers_in_order?() { false }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe "when no messages are supplied for the requirements" do
|
|
79
|
+
describe "given transition.unmet_requirements" do
|
|
80
|
+
it "should contain a list of failing requirement names as symbols" do
|
|
81
|
+
@obj.state_fu.catch_plane(:america).unmet_requirements.should == [ :papers_in_order?,
|
|
82
|
+
:money_for_bribe?,
|
|
83
|
+
:no_turban?,
|
|
84
|
+
:us_visa?,
|
|
85
|
+
:no_arrest_warrant? ]
|
|
86
|
+
end
|
|
87
|
+
end # unmet requirements
|
|
88
|
+
|
|
89
|
+
describe "given transition.unmet_requirement_messages" do
|
|
90
|
+
it "should return a list of nils" do
|
|
91
|
+
@obj.state_fu.catch_plane(:america).unmet_requirement_messages.should == [nil,nil,nil,nil,nil]
|
|
92
|
+
end
|
|
93
|
+
end # unmet_requirement_messages
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe "when a message is supplied for the money_for_bribe? entry requirement" do
|
|
97
|
+
before do
|
|
98
|
+
Klass.machine do
|
|
99
|
+
state :soviet_russia do
|
|
100
|
+
requires( :money_for_bribe?, :message => "This guard is thirsty! Do you have anything to declare?" )
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe "given transition.unmet_requirements" do
|
|
106
|
+
it "should still contain a list of failing requirement names as symbols" do
|
|
107
|
+
@obj.state_fu.catch_plane(:america).unmet_requirements.should == [ :papers_in_order?,
|
|
108
|
+
:money_for_bribe?,
|
|
109
|
+
:no_turban?,
|
|
110
|
+
:us_visa?,
|
|
111
|
+
:no_arrest_warrant? ]
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
describe "given transition.unmet_requirement_messages" do
|
|
116
|
+
it "should contain a list of nils plus the requirement message for money_for_bribe? as a string" do
|
|
117
|
+
@obj.state_fu.catch_plane(:america).unmet_requirement_messages.should == [ nil,
|
|
118
|
+
"This guard is thirsty! Do you have anything to declare?",
|
|
119
|
+
nil,
|
|
120
|
+
nil,
|
|
121
|
+
nil ]
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end # flying with a turban
|
|
126
|
+
|
|
69
127
|
describe "transition.unmet_requirements" do
|
|
70
128
|
it "should be empty when all requirements are met" do
|
|
71
129
|
@obj.state_fu.fly_spaceship(:moon).unmet_requirements.should == []
|
|
72
130
|
end
|
|
73
131
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
132
|
+
describe "when a message is supplied for the requirement" do
|
|
133
|
+
it "should contain a list of the requirement failure messages as strings" do
|
|
134
|
+
mock( @obj ).spacesuit?() { false }
|
|
135
|
+
mock( @obj ).fuel?() { false }
|
|
136
|
+
@obj.state_fu.fly_spaceship(:moon).unmet_requirements.should == [:spacesuit?, :fuel?]
|
|
137
|
+
end
|
|
78
138
|
end
|
|
79
139
|
end
|
|
80
140
|
|
|
141
|
+
|
|
81
142
|
describe "transition.unmet_requirement_messages" do
|
|
82
143
|
describe "when a string message is defined for one of two unmet_requirements" do
|
|
83
144
|
before do
|
|
@@ -162,7 +223,7 @@ describe "Transition requirement reflection" do
|
|
|
162
223
|
it "should call the method on @obj given transition.evaluate_named_proc_or_method() with the method name" do
|
|
163
224
|
@obj.method( :no_spacesuit_msg_method ).arity.should == 1
|
|
164
225
|
t = @obj.state_fu.fly_spaceship(:moon)
|
|
165
|
-
x =
|
|
226
|
+
x = @obj.state_fu.evaluate_named_proc_or_method(:no_spacesuit_msg_method, t)
|
|
166
227
|
@obj.arg.should == t
|
|
167
228
|
x.should =~ /You can't go to the moon/
|
|
168
229
|
end
|
|
@@ -170,7 +231,7 @@ describe "Transition requirement reflection" do
|
|
|
170
231
|
it "should call t.evaluate_named_proc_or_method(:no_spacesuit_msg_method)" do
|
|
171
232
|
t = @obj.state_fu.fly_spaceship(:moon)
|
|
172
233
|
t.unmet_requirements.length.should == 2
|
|
173
|
-
mock( t ).evaluate_named_proc_or_method(:no_spacesuit_msg_method) { :my_string }
|
|
234
|
+
mock( t ).evaluate_named_proc_or_method(:no_spacesuit_msg_method, t) { :my_string }
|
|
174
235
|
messages = t.unmet_requirement_messages
|
|
175
236
|
messages.should include(:my_string )
|
|
176
237
|
end
|
|
@@ -183,7 +244,7 @@ describe "Transition requirement reflection" do
|
|
|
183
244
|
end
|
|
184
245
|
|
|
185
246
|
it "should return the result of the method execution as the message" do
|
|
186
|
-
t = @obj.state_fu.fly_spaceship(:moon)
|
|
247
|
+
t = @obj.state_fu.fly_spaceship( :moon )
|
|
187
248
|
t.unmet_requirements.length.should == 2
|
|
188
249
|
messages = t.unmet_requirement_messages
|
|
189
250
|
messages.length.should == 2
|
|
@@ -194,7 +255,6 @@ describe "Transition requirement reflection" do
|
|
|
194
255
|
end # no named proc
|
|
195
256
|
end # symbol message
|
|
196
257
|
end # transition.unmet_requirement_messages
|
|
197
|
-
|
|
198
258
|
end
|
|
199
259
|
|
|
200
260
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../helper")
|
|
2
|
+
|
|
3
|
+
describe StateFu::State do
|
|
4
|
+
include MySpecHelper
|
|
5
|
+
|
|
6
|
+
it "should respond to deep_copy" do
|
|
7
|
+
reset!
|
|
8
|
+
make_pristine_class "Klass"
|
|
9
|
+
@machine = Klass.machine do
|
|
10
|
+
state :initial
|
|
11
|
+
end
|
|
12
|
+
@state = @machine.states.first
|
|
13
|
+
@state.should respond_to(:deep_copy)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
|