simple_machine 1.0.0 → 1.0.1

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.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