finite_machine 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile +2 -2
- data/README.md +13 -3
- data/lib/finite_machine.rb +2 -1
- data/lib/finite_machine/events_chain.rb +12 -5
- data/lib/finite_machine/observer.rb +7 -5
- data/lib/finite_machine/state_machine.rb +3 -0
- data/lib/finite_machine/threadable.rb +2 -3
- data/lib/finite_machine/transition.rb +24 -8
- data/lib/finite_machine/transition_event.rb +18 -2
- data/lib/finite_machine/two_phase_lock.rb +31 -0
- data/lib/finite_machine/version.rb +1 -1
- data/spec/unit/callbacks_spec.rb +86 -0
- data/spec/unit/choice_spec.rb +76 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64cf549e74a02a5036aec535a7be9784a10224bf
|
4
|
+
data.tar.gz: e7733f5805b8989033e229109200b7f69feebe3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db31c094295a9ed657daca0960b3299d00ee131a3149ce99750d14b44555e0e5375e5f0e3b5c0d70ecf955a895cd4addafc1b9e99576dd03a21df9ca2b271547
|
7
|
+
data.tar.gz: bff4f5c620276cf7cc664662d0c3aa66e555d1e09fd242e9d22ab5577f0425ee94c4db7bf804e4102a2e99d759eb1180fc04988ccff3fca2368879d4cc02ab1d
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
0.9.2 (September 27, 2014)
|
2
|
+
|
3
|
+
* Removes use of class variable to share Sync by @reggieb
|
4
|
+
* Fix observer to differentiate between any state and any event
|
5
|
+
* [#23] Fix transition to correctly set :from and :to parameters for :any state
|
6
|
+
* [#25] Fix passing parameters to choice events with same named events
|
7
|
+
* Fix choice pseudostate to work with :any state
|
8
|
+
|
1
9
|
0.9.1 (August 10, 2014)
|
2
10
|
|
3
11
|
* Add TransitionBuilder to internally build transitions from states
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1217,15 +1217,23 @@ car.reverse_lights_on? # => true
|
|
1217
1217
|
|
1218
1218
|
### 8.2 ActiveRecord
|
1219
1219
|
|
1220
|
-
In order to integrate **FiniteMachine** with ActiveRecord use the `target` helper to reference the current class
|
1220
|
+
In order to integrate **FiniteMachine** with ActiveRecord simply add a method with state machine definition. You can also define the state machine in separate module to aid reusability. Once the state machine is defined use the `target` helper to reference the current class. Having defined `target` you call ActiveRecord methods inside the callbacks to persist the state.
|
1221
|
+
|
1222
|
+
You can use the `restore!` method to specify which state the **FininteMachine** should be put back into as follows:
|
1221
1223
|
|
1222
1224
|
```ruby
|
1223
1225
|
class Account < ActiveRecord::Base
|
1224
1226
|
validates :state, presence: true
|
1225
1227
|
|
1228
|
+
before_validation :set_initial_state, on: :create
|
1229
|
+
|
1230
|
+
def set_initial_state
|
1231
|
+
self.state = manage.current
|
1232
|
+
end
|
1233
|
+
|
1226
1234
|
def initialize(attrs = {})
|
1227
1235
|
super
|
1228
|
-
|
1236
|
+
manage.restore!(state.to_sym) if state.present?
|
1229
1237
|
end
|
1230
1238
|
|
1231
1239
|
def manage
|
@@ -1241,7 +1249,7 @@ class Account < ActiveRecord::Base
|
|
1241
1249
|
}
|
1242
1250
|
|
1243
1251
|
callbacks {
|
1244
|
-
|
1252
|
+
on_enter do |event|
|
1245
1253
|
target.state = event.to
|
1246
1254
|
target.save
|
1247
1255
|
end
|
@@ -1258,6 +1266,8 @@ account.manage.authorize
|
|
1258
1266
|
account.state # => :access
|
1259
1267
|
```
|
1260
1268
|
|
1269
|
+
Please note that you do not need to call `target.save` inside callback, it is enought to just set the state. It is much more prefereable to let the `ActiveRecord` object to persist when it makes sense for the application and thus keep the state machine focused on managing the state transitions.
|
1270
|
+
|
1261
1271
|
### 8.3 Transactions
|
1262
1272
|
|
1263
1273
|
When using **FiniteMachine** with ActiveRecord it advisable to trigger state changes inside transactions to ensure integrity of the database. Given Account example from section 8.2 one can run event in transaction in the following way:
|
data/lib/finite_machine.rb
CHANGED
@@ -31,6 +31,7 @@ require "finite_machine/subscribers"
|
|
31
31
|
require "finite_machine/state_parser"
|
32
32
|
require "finite_machine/observer"
|
33
33
|
require "finite_machine/listener"
|
34
|
+
require "finite_machine/two_phase_lock"
|
34
35
|
|
35
36
|
module FiniteMachine
|
36
37
|
# Default state name
|
@@ -43,7 +44,7 @@ module FiniteMachine
|
|
43
44
|
ANY_STATE = :any
|
44
45
|
|
45
46
|
# Describe any event name
|
46
|
-
ANY_EVENT = :
|
47
|
+
ANY_EVENT = :any_event
|
47
48
|
|
48
49
|
# Returned when transition has successfully performed
|
49
50
|
SUCCEEDED = 1
|
@@ -16,7 +16,7 @@ module FiniteMachine
|
|
16
16
|
|
17
17
|
# Initialize a EventsChain
|
18
18
|
#
|
19
|
-
# @param [
|
19
|
+
# @param [StateMachine] machine
|
20
20
|
# the state machine
|
21
21
|
#
|
22
22
|
# @api public
|
@@ -30,7 +30,7 @@ module FiniteMachine
|
|
30
30
|
# @param [Symbol] name
|
31
31
|
# the event name
|
32
32
|
#
|
33
|
-
# @param [
|
33
|
+
# @param [Transition]
|
34
34
|
#
|
35
35
|
# @return [nil]
|
36
36
|
#
|
@@ -63,7 +63,7 @@ module FiniteMachine
|
|
63
63
|
# @param [Symbol] name
|
64
64
|
# the event name
|
65
65
|
#
|
66
|
-
# @return [
|
66
|
+
# @return [Transition]
|
67
67
|
#
|
68
68
|
# @api public
|
69
69
|
def select_transition(name, *args)
|
@@ -72,12 +72,19 @@ module FiniteMachine
|
|
72
72
|
|
73
73
|
# Examine choice transitions to find one matching condition
|
74
74
|
#
|
75
|
-
# @
|
75
|
+
# @param [Symbol] name
|
76
|
+
# the event name
|
77
|
+
#
|
78
|
+
# @param [Symbol] from_state
|
79
|
+
# the current context from_state
|
80
|
+
#
|
81
|
+
# @return [Transition]
|
76
82
|
# The choice transition that matches
|
77
83
|
#
|
78
84
|
# @api public
|
79
|
-
def select_choice_transition(name, *args, &block)
|
85
|
+
def select_choice_transition(name, from_state, *args, &block)
|
80
86
|
chain[name].state_transitions.find do |trans|
|
87
|
+
[from_state, ANY_STATE].include?(trans.from_state) &&
|
81
88
|
trans.check_conditions(*args, &block)
|
82
89
|
end
|
83
90
|
end
|
@@ -41,7 +41,9 @@ module FiniteMachine
|
|
41
41
|
def on(event_type = HookEvent, *args, &callback)
|
42
42
|
sync_exclusive do
|
43
43
|
name, async, _ = args
|
44
|
-
|
44
|
+
if name.nil?
|
45
|
+
name = event_type < HookEvent::Anyaction ? ANY_EVENT : ANY_STATE
|
46
|
+
end
|
45
47
|
async = false if async.nil?
|
46
48
|
ensure_valid_callback_name!(event_type, name)
|
47
49
|
callback.extend(Async) if async == :async
|
@@ -52,7 +54,7 @@ module FiniteMachine
|
|
52
54
|
# Unregister callback for a given event
|
53
55
|
#
|
54
56
|
# @api public
|
55
|
-
def off(event_type
|
57
|
+
def off(event_type, name = ANY_STATE, &callback)
|
56
58
|
sync_exclusive do
|
57
59
|
hooks.unregister event_type, name, callback
|
58
60
|
end
|
@@ -107,8 +109,8 @@ module FiniteMachine
|
|
107
109
|
# @api public
|
108
110
|
def trigger(event, *args, &block)
|
109
111
|
sync_exclusive do
|
110
|
-
[event.type
|
111
|
-
[event.name, ANY_STATE].each do |event_name|
|
112
|
+
[event.type].each do |event_type|
|
113
|
+
[event.name, ANY_STATE, ANY_EVENT].each do |event_name|
|
112
114
|
hooks.call(event_type, event_name) do |hook|
|
113
115
|
handle_callback(hook, event)
|
114
116
|
off(event_type, event_name, &hook) if hook.is_a?(Once)
|
@@ -163,7 +165,7 @@ module FiniteMachine
|
|
163
165
|
#
|
164
166
|
# @api private
|
165
167
|
def callback_names
|
166
|
-
machine.states + machine.event_names + [ANY_EVENT]
|
168
|
+
machine.states + machine.event_names + [ANY_EVENT, ANY_STATE]
|
167
169
|
end
|
168
170
|
|
169
171
|
# Forward the message to observer
|
@@ -4,7 +4,6 @@ module FiniteMachine
|
|
4
4
|
# A mixin to allow instance methods to be synchronized
|
5
5
|
module Threadable
|
6
6
|
module InstanceMethods
|
7
|
-
@@sync = Sync.new
|
8
7
|
|
9
8
|
# Exclusive lock
|
10
9
|
#
|
@@ -12,7 +11,7 @@ module FiniteMachine
|
|
12
11
|
#
|
13
12
|
# @api public
|
14
13
|
def sync_exclusive(&block)
|
15
|
-
|
14
|
+
TwoPhaseLock.synchronize(:EX, &block)
|
16
15
|
end
|
17
16
|
|
18
17
|
# Shared lock
|
@@ -21,7 +20,7 @@ module FiniteMachine
|
|
21
20
|
#
|
22
21
|
# @api public
|
23
22
|
def sync_shared(&block)
|
24
|
-
|
23
|
+
TwoPhaseLock.synchronize(:SH, &block)
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
@@ -63,12 +63,12 @@ module FiniteMachine
|
|
63
63
|
#
|
64
64
|
# @api public
|
65
65
|
def self.create(machine, attrs = {})
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
transition = new(machine, attrs)
|
67
|
+
transition.update_transitions
|
68
|
+
transition.define_state_methods
|
69
69
|
|
70
70
|
builder = EventBuilder.new(machine)
|
71
|
-
builder.call(
|
71
|
+
builder.call(transition)
|
72
72
|
end
|
73
73
|
|
74
74
|
# Decide :to state from available transitions for this event
|
@@ -78,10 +78,11 @@ module FiniteMachine
|
|
78
78
|
# @api public
|
79
79
|
def to_state(*args)
|
80
80
|
if transition_choice?
|
81
|
-
found_trans = machine.select_choice_transition(name, *args)
|
82
|
-
found_trans.map[from_state]
|
81
|
+
found_trans = machine.select_choice_transition(name, from_state, *args)
|
82
|
+
found_trans.map[from_state] || found_trans.map[ANY_STATE]
|
83
83
|
else
|
84
|
-
machine.transitions[name]
|
84
|
+
available_trans = machine.transitions[name]
|
85
|
+
available_trans[from_state] || available_trans[ANY_STATE]
|
85
86
|
end
|
86
87
|
end
|
87
88
|
|
@@ -165,7 +166,7 @@ module FiniteMachine
|
|
165
166
|
# @api private
|
166
167
|
def update_transitions
|
167
168
|
from_states.each do |from|
|
168
|
-
if value = machine.transitions[name][from]
|
169
|
+
if (value = machine.transitions[name][from])
|
169
170
|
machine.transitions[name][from] = [value, map[from]].flatten
|
170
171
|
else
|
171
172
|
machine.transitions[name][from] = map[from] || ANY_STATE
|
@@ -205,6 +206,20 @@ module FiniteMachine
|
|
205
206
|
end
|
206
207
|
end
|
207
208
|
|
209
|
+
# Find latest from state
|
210
|
+
#
|
211
|
+
# Note that for the exit hook the call hasn't happened yet so
|
212
|
+
# we need to find previous to state when the from is :any.
|
213
|
+
#
|
214
|
+
# @return [Object] from_state
|
215
|
+
#
|
216
|
+
# @api private
|
217
|
+
def latest_from_state
|
218
|
+
sync_shared do
|
219
|
+
from_state == ANY_STATE ? machine.previous_state : from_state
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
208
223
|
# Execute current transition
|
209
224
|
#
|
210
225
|
# @return [nil]
|
@@ -215,6 +230,7 @@ module FiniteMachine
|
|
215
230
|
return if cancelled
|
216
231
|
self.from_state = machine.state
|
217
232
|
update_state(*args)
|
233
|
+
machine.previous_state = machine.state
|
218
234
|
machine.initial_state = machine.state if from_state == DEFAULT_STATE
|
219
235
|
end
|
220
236
|
end
|
@@ -2,12 +2,28 @@
|
|
2
2
|
|
3
3
|
module FiniteMachine
|
4
4
|
# A class representing a callback transition event
|
5
|
+
#
|
6
|
+
# Used internally by {Observer}
|
7
|
+
#
|
8
|
+
# @api private
|
5
9
|
class TransitionEvent
|
6
|
-
|
10
|
+
# This event from state name
|
11
|
+
#
|
12
|
+
# @return [Object]
|
13
|
+
#
|
14
|
+
# @api public
|
7
15
|
attr_accessor :from
|
8
16
|
|
17
|
+
# This event to state name
|
18
|
+
#
|
19
|
+
# @return [Object]
|
20
|
+
#
|
21
|
+
# @api public
|
9
22
|
attr_accessor :to
|
10
23
|
|
24
|
+
# This event name
|
25
|
+
#
|
26
|
+
# @api public
|
11
27
|
attr_accessor :name
|
12
28
|
|
13
29
|
# Build a transition event
|
@@ -20,7 +36,7 @@ module FiniteMachine
|
|
20
36
|
def self.build(transition, *data)
|
21
37
|
instance = new
|
22
38
|
instance.name = transition.name
|
23
|
-
instance.from = transition.
|
39
|
+
instance.from = transition.latest_from_state
|
24
40
|
instance.to = transition.to_state(*data)
|
25
41
|
instance
|
26
42
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FiniteMachine
|
4
|
+
# Mixin to provide lock to a {Threadable}
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
module TwoPhaseLock
|
8
|
+
# Create synchronization lock
|
9
|
+
#
|
10
|
+
# @return [Sync]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def sync
|
14
|
+
@sync ||= Sync.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Synchronize given block of code
|
18
|
+
#
|
19
|
+
# @param [Symbol] mode
|
20
|
+
# the synchronization mode out of :SH and :EX
|
21
|
+
#
|
22
|
+
# @return [nil]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
def synchronize(mode, &block)
|
26
|
+
sync.synchronize(mode, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
module_function :sync, :synchronize
|
30
|
+
end # TwoPhaseLock
|
31
|
+
end # FiniteMachine
|
data/spec/unit/callbacks_spec.rb
CHANGED
@@ -429,6 +429,92 @@ describe FiniteMachine, 'callbacks' do
|
|
429
429
|
fsm.go(nil, nil)
|
430
430
|
end
|
431
431
|
|
432
|
+
it "sets callback parameters correctly for transition from :any state" do
|
433
|
+
expected = {name: :init, from: :none, to: :green, a: nil, b: nil, c: nil }
|
434
|
+
|
435
|
+
callback = Proc.new { |event, a, b, c|
|
436
|
+
target.expect(event.from).to target.eql(expected[:from])
|
437
|
+
target.expect(event.to).to target.eql(expected[:to])
|
438
|
+
target.expect(event.name).to target.eql(expected[:name])
|
439
|
+
target.expect(a).to target.eql(expected[:a])
|
440
|
+
target.expect(b).to target.eql(expected[:b])
|
441
|
+
target.expect(c).to target.eql(expected[:c])
|
442
|
+
}
|
443
|
+
|
444
|
+
context = self
|
445
|
+
|
446
|
+
fsm = FiniteMachine.define do
|
447
|
+
initial :red
|
448
|
+
|
449
|
+
target context
|
450
|
+
|
451
|
+
events {
|
452
|
+
event :power_on, :off => :red
|
453
|
+
event :power_off, :any => :off
|
454
|
+
event :go, :red => :green
|
455
|
+
event :slow, :green => :yellow
|
456
|
+
event :stop, :yellow => :red
|
457
|
+
}
|
458
|
+
|
459
|
+
callbacks {
|
460
|
+
# generic state callbacks
|
461
|
+
on_enter(&callback)
|
462
|
+
on_transition(&callback)
|
463
|
+
on_exit(&callback)
|
464
|
+
|
465
|
+
# generic event callbacks
|
466
|
+
on_before(&callback)
|
467
|
+
on_after(&callback)
|
468
|
+
|
469
|
+
# state callbacks
|
470
|
+
on_enter :green, &callback
|
471
|
+
on_enter :yellow, &callback
|
472
|
+
on_enter :red, &callback
|
473
|
+
on_enter :off, &callback
|
474
|
+
on_enter :off, &callback
|
475
|
+
|
476
|
+
on_transition :green, &callback
|
477
|
+
on_transition :yellow, &callback
|
478
|
+
on_transition :red, &callback
|
479
|
+
on_transition :off, &callback
|
480
|
+
on_transition :off, &callback
|
481
|
+
|
482
|
+
on_exit :green, &callback
|
483
|
+
on_exit :yellow, &callback
|
484
|
+
on_exit :red, &callback
|
485
|
+
on_exit :off, &callback
|
486
|
+
on_exit :off, &callback
|
487
|
+
|
488
|
+
# event callbacks
|
489
|
+
on_before :power_on, &callback
|
490
|
+
on_before :power_off, &callback
|
491
|
+
on_before :go, &callback
|
492
|
+
on_before :slow, &callback
|
493
|
+
on_before :stop, &callback
|
494
|
+
|
495
|
+
on_after :power_on, &callback
|
496
|
+
on_after :power_off, &callback
|
497
|
+
on_after :go, &callback
|
498
|
+
on_after :slow, &callback
|
499
|
+
on_after :stop, &callback
|
500
|
+
}
|
501
|
+
end
|
502
|
+
|
503
|
+
expect(fsm.current).to eq(:red)
|
504
|
+
|
505
|
+
expected = {name: :go, from: :red, to: :green, a: 1, b: 2, c: 3 }
|
506
|
+
fsm.go(1, 2, 3)
|
507
|
+
|
508
|
+
expected = {name: :slow, from: :green, to: :yellow, a: 4, b: 5, c: 6}
|
509
|
+
fsm.slow(4, 5, 6)
|
510
|
+
|
511
|
+
expected = {name: :stop, from: :yellow, to: :red, a: 7, b: 8, c: 9}
|
512
|
+
fsm.stop(7, 8, 9)
|
513
|
+
|
514
|
+
expected = {name: :power_off, from: :red, to: :off, a: 10, b: 11, c: 12}
|
515
|
+
fsm.power_off(10, 11, 12)
|
516
|
+
end
|
517
|
+
|
432
518
|
it "raises an error with invalid callback name" do
|
433
519
|
expect {
|
434
520
|
FiniteMachine.define do
|
data/spec/unit/choice_spec.rb
CHANGED
@@ -184,4 +184,80 @@ describe FiniteMachine, '#choice' do
|
|
184
184
|
fsm.next
|
185
185
|
expect(fsm.current).to eq(:green)
|
186
186
|
end
|
187
|
+
|
188
|
+
it "sets callback properties correctly" do
|
189
|
+
expected = {name: :init, from: :none, to: :red, a: nil, b: nil, c: nil }
|
190
|
+
|
191
|
+
callback = Proc.new { |event, a, b, c|
|
192
|
+
target.expect(event.from).to target.eql(expected[:from])
|
193
|
+
target.expect(event.to).to target.eql(expected[:to])
|
194
|
+
target.expect(event.name).to target.eql(expected[:name])
|
195
|
+
target.expect(a).to target.eql(expected[:a])
|
196
|
+
target.expect(b).to target.eql(expected[:b])
|
197
|
+
target.expect(c).to target.eql(expected[:c])
|
198
|
+
}
|
199
|
+
|
200
|
+
context = self
|
201
|
+
|
202
|
+
fsm = FiniteMachine.define do
|
203
|
+
initial :red
|
204
|
+
|
205
|
+
target context
|
206
|
+
|
207
|
+
events {
|
208
|
+
event :next, from: :red do
|
209
|
+
choice :green, if: -> { false }
|
210
|
+
choice :yellow
|
211
|
+
end
|
212
|
+
|
213
|
+
event :next, from: :yellow do
|
214
|
+
choice :green, if: -> { true }
|
215
|
+
choice :yellow
|
216
|
+
end
|
217
|
+
|
218
|
+
event :finish, from: :any do
|
219
|
+
choice :green, if: -> { false }
|
220
|
+
choice :red
|
221
|
+
end
|
222
|
+
}
|
223
|
+
|
224
|
+
callbacks {
|
225
|
+
# generic state callbacks
|
226
|
+
on_enter(&callback)
|
227
|
+
on_transition(&callback)
|
228
|
+
on_exit(&callback)
|
229
|
+
|
230
|
+
# generic event callbacks
|
231
|
+
on_before(&callback)
|
232
|
+
on_after(&callback)
|
233
|
+
|
234
|
+
# state callbacks
|
235
|
+
on_enter :green, &callback
|
236
|
+
on_enter :yellow, &callback
|
237
|
+
on_enter :red, &callback
|
238
|
+
|
239
|
+
on_transition :green, &callback
|
240
|
+
on_transition :yellow, &callback
|
241
|
+
on_transition :red, &callback
|
242
|
+
|
243
|
+
on_exit :green, &callback
|
244
|
+
on_exit :yellow, &callback
|
245
|
+
on_exit :red, &callback
|
246
|
+
|
247
|
+
# event callbacks
|
248
|
+
on_before :next, &callback
|
249
|
+
on_after :next, &callback
|
250
|
+
}
|
251
|
+
end
|
252
|
+
expect(fsm.current).to eq(:red)
|
253
|
+
|
254
|
+
expected = {name: :next, from: :red, to: :yellow, a: 1, b: 2, c: 3}
|
255
|
+
fsm.next(1, 2, 3)
|
256
|
+
|
257
|
+
expected = {name: :next, from: :yellow, to: :green, a: 4, b: 5, c: 6}
|
258
|
+
fsm.next(4, 5, 6)
|
259
|
+
|
260
|
+
expected = {name: :finish, from: :green, to: :red, a: 7, b: 8, c: 9}
|
261
|
+
fsm.finish(7, 8, 9)
|
262
|
+
end
|
187
263
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: finite_machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- lib/finite_machine/transition.rb
|
73
73
|
- lib/finite_machine/transition_builder.rb
|
74
74
|
- lib/finite_machine/transition_event.rb
|
75
|
+
- lib/finite_machine/two_phase_lock.rb
|
75
76
|
- lib/finite_machine/version.rb
|
76
77
|
- spec/spec_helper.rb
|
77
78
|
- spec/unit/async_events_spec.rb
|