simple_machine 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -15,16 +15,24 @@ After requiring 'simple_machine' inject state machine in your class like this:
15
15
 
16
16
  class Phone
17
17
  include SimpleMachine
18
- implement_state_machine_for :my_state do
19
- initial_state :off
20
- other_states :ready, :dialing, :busy
21
- allow_transition :turn_on, :from => :off, :to => :ready
22
- allow_transition :dial, :from => :ready, :to => :dialing
23
- allow_transition :hangup, :from => :dialing, :to => :ready
24
- allow_transition :hangup, :from => :busy, :to => :ready
25
- allow_transition :turn_off, :from => :ready, :to => :off
26
- end
27
- end
18
+ implement_state_machine_for :my_state do
19
+ initial_state :off
20
+ other_states :ready, :dialing, :busy
21
+ allow_transition :turn_on, :from => :off, :to => :ready
22
+ allow_transition :dial, :from => :ready, :to => :dialing
23
+ allow_transition :hangup, :from => :dialing, :to => :ready
24
+ allow_transition :hangup, :from => :busy, :to => :ready
25
+ allow_transition :turn_off, :from => :ready, :to => :off do
26
+ # self is set to phone instance
27
+ puts my_state #=> :ready
28
+ # do something before transition ...
29
+ true # state will be changed to :off if block returns true
30
+ end
31
+ after_transition do
32
+ # self is set to phone instance
33
+ puts "New state is '#{my_state}'"
34
+ end
35
+ end
28
36
  end
29
37
 
30
38
  This chunk of code produces following effects:
@@ -56,11 +64,16 @@ In this case even if +dial+ is allowed transition from +ready+ state this is wha
56
64
 
57
65
  === Callback
58
66
 
59
- After each transition callback is invoked in parent class if it is defined:
67
+ After each transition callback is invoked if it is defined:
60
68
 
61
69
  class Phone
62
- def after_my_state_changed
63
- puts "New my state is '#{self.my_state}'"
70
+ include SimpleMachine
71
+ implement_state_machine_for :my_state do
72
+ # ...
73
+ after_transition do
74
+ # self is set to phone instance
75
+ puts "New state is '#{my_state}'"
76
+ end
64
77
  end
65
78
  end
66
79
 
@@ -38,7 +38,10 @@ module SimpleMachine
38
38
  result = @defined_transitions[from_state].inject(false) { |sum, hash| sum || hash[:transition] == transition }
39
39
  return result
40
40
  end
41
- def allow_transition(transition, options)
41
+ def after_transition(&block)
42
+ @after_transition = block
43
+ end
44
+ def allow_transition(transition, options, &block)
42
45
  @defined_transitions ||= {}
43
46
  raise "Unknown source state #{options[:from]}. Please define it first as initial_state or in other_states." unless all_states.include?(options[:from])
44
47
  raise "Unknown target state #{options[:to]}. Please define it first as initial_state or in other_states." unless all_states.include?(options[:to])
@@ -47,6 +50,8 @@ module SimpleMachine
47
50
  @defined_transitions[options[:from]] = [] unless @defined_transitions.has_key?(options[:from])
48
51
  @defined_transitions[options[:from]] << { :transition => transition, :target_state => options[:to] }
49
52
 
53
+ transition_block = block_given? ? block : false
54
+
50
55
  class_eval do
51
56
  define_method "can_#{transition}?" do
52
57
  allowed_transitions.include? transition
@@ -58,11 +63,17 @@ module SimpleMachine
58
63
  else
59
64
  raise "Invalid transition ##{transition.to_s.gsub '_', ' '} from '#{current_state}' state"
60
65
  end unless allowed_transitions.include? transition
61
- variable = "@#{self.class.parents_state_field_name}"
62
- result = @owner.instance_eval { instance_variable_set variable, options[:to] }
63
- callback_method = "after_#{self.class.parents_state_field_name}_changed".to_sym
64
- @owner.send callback_method if @owner.respond_to? callback_method
65
- result
66
+
67
+ if !transition_block or @owner.instance_eval( &transition_block )
68
+ variable = "@#{self.class.parents_state_field_name}"
69
+ result = @owner.instance_eval { instance_variable_set variable, options[:to] }
70
+ # after_transition callback
71
+ after_transition_block = self.class.instance_eval { @after_transition }
72
+ @owner.instance_eval &after_transition_block if after_transition_block
73
+ result
74
+ else
75
+ false
76
+ end
66
77
  end
67
78
  end
68
79
  end
@@ -70,7 +81,7 @@ module SimpleMachine
70
81
  end
71
82
 
72
83
  module ClassMethods
73
-
84
+
74
85
  def implement_state_machine_for(state_field_name, &block)
75
86
  machine_property_name = "#{state_field_name}_machine".to_sym
76
87
  default_state_field_name = "#{state_field_name}_default_state".to_sym
@@ -108,9 +108,50 @@ describe SimpleMachine, "for :dispatch_state field on Job, when state machine is
108
108
 
109
109
  job.dispatch_state.should be(:assigned)
110
110
  end
111
- it "calls job#after_dispatch_state_changed callback if defined" do
112
- Job.any_instance.expects(:after_dispatch_state_changed)
113
- Job.new.dispatch_state_machine.assign
111
+ it "calls after_transition callback if defined" do
112
+ rspec = self
113
+ job = Job.new
114
+ @inner_state_machine_class.after_transition do
115
+ puts "After transition"
116
+ dispatch_state.should rspec.be(:assigned)
117
+ self.should rspec.be(job)
118
+ end
119
+ job.dispatch_state_machine.assign.should be(:assigned)
120
+
121
+ @inner_state_machine_class.instance_eval { instance_variable_set :@after_transition, nil }
122
+ end
123
+ context "when block is given" do
124
+ before :each do
125
+ @inner_state_machine_class.expects(:defined_transition?).with(:assign, :waiting).returns(false)
126
+ end
127
+ it "executes block" do
128
+ rspec = self
129
+ job = Job.new
130
+ @inner_state_machine_class.allow_transition :assign, :from => :waiting, :to => :assigned do
131
+ dispatch_state.should rspec.be(:waiting)
132
+ self.should rspec.be(job)
133
+ true
134
+ end
135
+
136
+ job.dispatch_state_machine.assign.should be(:assigned)
137
+ end
138
+ it "changes state if block returned true" do
139
+ @inner_state_machine_class.allow_transition :assign, :from => :waiting, :to => :assigned do
140
+ true
141
+ end
142
+
143
+ job = Job.new
144
+ job.dispatch_state_machine.assign.should be(:assigned)
145
+ end
146
+ it "doesn't change state if block returned false" do
147
+ @inner_state_machine_class.allow_transition :assign, :from => :waiting, :to => :assigned do
148
+ false
149
+ end
150
+
151
+ job = Job.new
152
+ job.dispatch_state_machine.assign.should be(false)
153
+ job.dispatch_state.should be(:waiting)
154
+ end
114
155
  end
115
156
  end
116
157
  context "when it is not valid transition from current state" do
@@ -159,7 +200,10 @@ describe SimpleMachine, "for :dispatch_state field on Job, when state machine is
159
200
  end
160
201
  context "when there are more job instances" do
161
202
  before :all do
203
+ Job.new.dispatch_state_machine.class.expects(:defined_transition?).with(:assign, :waiting).returns false
204
+ Job.new.dispatch_state_machine.class.expects(:defined_transition?).with(:accept, :assigned)
162
205
  Job.implement_state_machine_for :dispatch_state do
206
+ allow_transition :assign, :from => :waiting, :to => :assigned
163
207
  allow_transition :accept, :from => :assigned, :to => :accepted
164
208
  end
165
209
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_machine
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 0
10
- version: 1.0.0
9
+ - 1
10
+ version: 1.0.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Milan Burmaja
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-13 00:00:00 +01:00
18
+ date: 2010-12-16 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies: []
21
21