aasm 3.4.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/CHANGELOG.md +20 -3
- data/PLANNED_CHANGES.md +7 -0
- data/README.md +132 -39
- data/README_FROM_VERSION_3_TO_4.md +240 -0
- data/callbacks.txt +51 -0
- data/lib/aasm.rb +2 -0
- data/lib/aasm/aasm.rb +36 -99
- data/lib/aasm/base.rb +18 -7
- data/lib/aasm/configuration.rb +23 -0
- data/lib/aasm/dsl_helper.rb +30 -0
- data/lib/aasm/errors.rb +1 -0
- data/lib/aasm/event.rb +25 -26
- data/lib/aasm/instance_base.rb +15 -15
- data/lib/aasm/persistence/active_record_persistence.rb +2 -2
- data/lib/aasm/state_machine.rb +1 -1
- data/lib/aasm/transition.rb +44 -24
- data/lib/aasm/version.rb +1 -1
- data/spec/database.rb +1 -1
- data/spec/models/auth_machine.rb +10 -6
- data/spec/models/callback_new_dsl.rb +99 -13
- data/spec/models/double_definer.rb +2 -2
- data/spec/models/foo.rb +4 -4
- data/spec/models/parametrised_event.rb +3 -3
- data/spec/models/persistence.rb +16 -1
- data/spec/models/validator.rb +6 -3
- data/spec/unit/callbacks_spec.rb +106 -5
- data/spec/unit/event_spec.rb +4 -4
- data/spec/unit/inspection_spec.rb +7 -7
- data/spec/unit/localizer_spec.rb +1 -0
- data/spec/unit/persistence/active_record_persistence_spec.rb +20 -2
- data/spec/unit/transition_spec.rb +66 -18
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e82947e11b28a1768717833153778b493f638129
|
4
|
+
data.tar.gz: eb74d70ed1a8ddd968aa24db3ec1903d29d332d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4144ba3401348e02bfda53f0eff38f7f018b3e8eef93a157bd727ab4bc499a1c645440263bbc405e07da213c553ea9f1bc697c40fcfcb0e79b72cf1bf6dd1a6d
|
7
|
+
data.tar.gz: c80f18c84bf32c1be09220253e438afb8ca249c63c3d68d3ba15bf0669ab71b1b79f6c926affaa3064194cee37be91f5c74e4c290ab82ac277388795c8f35621
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,25 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
-
##
|
4
|
-
|
5
|
-
*
|
3
|
+
## 4.0.0
|
4
|
+
|
5
|
+
* support `if` and `unless` guard syntax: (see [issue #179](https://github.com/aasm/aasm/issues/179) and [issue #181](https://github.com/aasm/aasm/issues/181)), thanks to [@bigtunacan](https://github.com/bigtunacan)
|
6
|
+
* may configure to not allow direct assignment for persisted AASM models (see [issue #53](https://github.com/aasm/aasm/issues/53))
|
7
|
+
* **DSL change**: callbacks don't require `to_state` parameter anymore, but still support it
|
8
|
+
(closing issues
|
9
|
+
[#11](https://github.com/aasm/aasm/issues/11),
|
10
|
+
[#58](https://github.com/aasm/aasm/issues/58) and
|
11
|
+
[#80](https://github.com/aasm/aasm/issues/80)
|
12
|
+
thanks to [@ejlangev](https://github.com/ejlangev))
|
13
|
+
* **DSL change**: `after_commit` hooks are now event-based (see [issue #112](https://github.com/aasm/aasm/issues/112))
|
14
|
+
* **DSL change**: event and state callbacks have been re-ordered; state callbacks are not run anymore if any guard fails
|
15
|
+
* **DSL change**: `:on_transition` renamed to `:after`
|
16
|
+
* **DSL change**: `:on_transition` renamed to `:after`
|
17
|
+
* **DSL change**: transition `:after` binding changed (see [issue #59](https://github.com/aasm/aasm/issues/59), thanks to [@stiff](https://github.com/stiff))
|
18
|
+
* **DSL change**: instance-based events inspection now returns event instances (instead of the event names as symbol)
|
19
|
+
* **DSL change**: instance-based permissible_events has been removed in favor or events(:permissible => true)
|
20
|
+
* **DSL change**: class-based events now returns a list of Event instances (instead of a hash with event names as keys)
|
21
|
+
* **DSL change**: renamed permissible states and events to permitted states events
|
22
|
+
* removed deprecated methods (mostly the ones prefixed with `aasm_`)
|
6
23
|
|
7
24
|
## 3.4.0
|
8
25
|
|
data/PLANNED_CHANGES.md
ADDED
data/README.md
CHANGED
@@ -94,8 +94,12 @@ class Job
|
|
94
94
|
state :sleeping, :initial => true, :before_enter => :do_something
|
95
95
|
state :running
|
96
96
|
|
97
|
-
event :run, :after =>
|
98
|
-
transitions :from => :sleeping, :to => :running, :
|
97
|
+
event :run, :after => :notify_somebody do
|
98
|
+
transitions :from => :sleeping, :to => :running, :after => Proc.new {|*args| set_process(*args) } do
|
99
|
+
before do
|
100
|
+
log('Preparing to run')
|
101
|
+
end
|
102
|
+
end
|
99
103
|
end
|
100
104
|
|
101
105
|
event :sleep do
|
@@ -117,7 +121,7 @@ class Job
|
|
117
121
|
...
|
118
122
|
end
|
119
123
|
|
120
|
-
def
|
124
|
+
def notify_somebody(user)
|
121
125
|
...
|
122
126
|
end
|
123
127
|
|
@@ -125,19 +129,31 @@ end
|
|
125
129
|
```
|
126
130
|
|
127
131
|
In this case `do_something` is called before actually entering the state `sleeping`,
|
128
|
-
while `
|
132
|
+
while `notify_somebody` is called after the transition `run` (from `sleeping` to `running`)
|
129
133
|
is finished.
|
130
134
|
|
131
135
|
Here you can see a list of all possible callbacks, together with their order of calling:
|
132
136
|
|
133
137
|
```ruby
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
138
|
+
begin
|
139
|
+
event before
|
140
|
+
event guards # test run
|
141
|
+
transition guards # test run
|
142
|
+
old_state before_exit
|
143
|
+
old_state exit
|
144
|
+
new_state before_enter
|
145
|
+
new_state enter
|
146
|
+
event guards
|
147
|
+
transition guards
|
148
|
+
...update state...
|
149
|
+
transition after
|
150
|
+
event success # if persist successful
|
151
|
+
old_state after_exit
|
152
|
+
new_state after_enter
|
153
|
+
event after
|
154
|
+
rescue
|
155
|
+
event error
|
156
|
+
end
|
141
157
|
```
|
142
158
|
|
143
159
|
Also, you can pass parameters to events:
|
@@ -196,37 +212,37 @@ running the transition. If the guard returns `false` the transition will be
|
|
196
212
|
denied (raising `AASM::InvalidTransition` or returning `false` itself):
|
197
213
|
|
198
214
|
```ruby
|
199
|
-
class
|
215
|
+
class Cleaner
|
200
216
|
include AASM
|
201
217
|
|
202
218
|
aasm do
|
203
|
-
state :
|
204
|
-
state :running
|
219
|
+
state :idle, :initial => true
|
205
220
|
state :cleaning
|
206
221
|
|
207
|
-
event :run do
|
208
|
-
transitions :from => :sleeping, :to => :running
|
209
|
-
end
|
210
|
-
|
211
222
|
event :clean do
|
212
|
-
transitions :from => :
|
223
|
+
transitions :from => :idle, :to => :cleaning, :guard => :cleaning_needed?
|
213
224
|
end
|
214
225
|
|
215
|
-
event :
|
216
|
-
transitions :from => :
|
226
|
+
event :clean_if_needed do
|
227
|
+
transitions :from => :idle, :to => :cleaning do
|
228
|
+
guard do
|
229
|
+
cleaning_needed?
|
230
|
+
end
|
231
|
+
end
|
232
|
+
transitions :from => :idle, :to => :idle
|
217
233
|
end
|
218
234
|
end
|
219
235
|
|
220
236
|
def cleaning_needed?
|
221
237
|
false
|
222
238
|
end
|
223
|
-
|
224
239
|
end
|
225
240
|
|
226
|
-
job =
|
227
|
-
job.
|
228
|
-
job.
|
229
|
-
job.
|
241
|
+
job = Cleaner.new
|
242
|
+
job.may_clean? # => false
|
243
|
+
job.clean # => raises AASM::InvalidTransition
|
244
|
+
job.may_clean_if_needed? # => true
|
245
|
+
job.clean_if_needed! # idle
|
230
246
|
```
|
231
247
|
|
232
248
|
You can even provide a number of guards, which all have to succeed to proceed
|
@@ -248,6 +264,52 @@ If you want to provide guards for all transitions within an event, you can use e
|
|
248
264
|
end
|
249
265
|
```
|
250
266
|
|
267
|
+
If you prefer a more Ruby-like guard syntax, you can use `if` and `unless` as well:
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
event :clean do
|
271
|
+
transitions :from => :running, :to => :cleaning, :unless => :cleaning_needed?
|
272
|
+
end
|
273
|
+
|
274
|
+
event :sleep do
|
275
|
+
transitions :from => :running, :to => :sleeping, :if => :cleaning_needed?
|
276
|
+
end
|
277
|
+
end
|
278
|
+
```
|
279
|
+
|
280
|
+
|
281
|
+
### Transitions
|
282
|
+
|
283
|
+
In the event of having multiple transitions for an event, the first transition that successfully completes will stop other transitions in the same event from being processed.
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
require 'aasm'
|
287
|
+
|
288
|
+
class Job
|
289
|
+
include AASM
|
290
|
+
|
291
|
+
aasm do
|
292
|
+
state :stage1, :initial => true
|
293
|
+
state :stage2
|
294
|
+
state :stage3
|
295
|
+
state :completed
|
296
|
+
|
297
|
+
event :stage1_completed do
|
298
|
+
transitions from: :stage1, to: :stage3, guard: :stage2_completed?
|
299
|
+
transitions from: :stage1, to: :stage2
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def stage2_completed?
|
304
|
+
true
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
job = Job.new
|
309
|
+
job.stage1_completed
|
310
|
+
job.aasm.current_state # stage3
|
311
|
+
```
|
312
|
+
|
251
313
|
### ActiveRecord
|
252
314
|
|
253
315
|
AASM comes with support for ActiveRecord and allows automatical persisting of the object's
|
@@ -305,6 +367,34 @@ class Job < ActiveRecord::Base
|
|
305
367
|
end
|
306
368
|
```
|
307
369
|
|
370
|
+
If you want to make sure that the _AASM_ column for storing the state is not directly assigned,
|
371
|
+
configure _AASM_ to not allow direct assignment, like this:
|
372
|
+
|
373
|
+
```ruby
|
374
|
+
class Job < ActiveRecord::Base
|
375
|
+
include AASM
|
376
|
+
|
377
|
+
aasm :no_direct_assignment => true do
|
378
|
+
state :sleeping, :initial => true
|
379
|
+
state :running
|
380
|
+
|
381
|
+
event :run do
|
382
|
+
transitions :from => :sleeping, :to => :running
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|
387
|
+
```
|
388
|
+
|
389
|
+
resulting in this:
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
job = Job.create
|
393
|
+
job.aasm_state # => 'sleeping'
|
394
|
+
job.aasm_state = :running # => raises AASM::NoDirectAssignmentError
|
395
|
+
job.aasm_state # => 'sleeping'
|
396
|
+
```
|
397
|
+
|
308
398
|
#### ActiveRecord enums
|
309
399
|
|
310
400
|
You can use
|
@@ -387,7 +477,7 @@ class Job < ActiveRecord::Base
|
|
387
477
|
end
|
388
478
|
|
389
479
|
def self.sleeping
|
390
|
-
"This method name is in
|
480
|
+
"This method name is already in use"
|
391
481
|
end
|
392
482
|
end
|
393
483
|
```
|
@@ -398,7 +488,7 @@ class JobsController < ApplicationController
|
|
398
488
|
@running_jobs = Job.running
|
399
489
|
@recent_cleaning_jobs = Job.cleaning.where('created_at >= ?', 3.days.ago)
|
400
490
|
|
401
|
-
# @sleeping_jobs = Job.sleeping #=> "This method name is in
|
491
|
+
# @sleeping_jobs = Job.sleeping #=> "This method name is already in use"
|
402
492
|
end
|
403
493
|
end
|
404
494
|
```
|
@@ -434,9 +524,9 @@ class Job < ActiveRecord::Base
|
|
434
524
|
|
435
525
|
aasm do
|
436
526
|
state :sleeping, :initial => true
|
437
|
-
state :running
|
527
|
+
state :running
|
438
528
|
|
439
|
-
event :run do
|
529
|
+
event :run, :after_commit => :notify_about_running_job do
|
440
530
|
transitions :from => :sleeping, :to => :running
|
441
531
|
end
|
442
532
|
end
|
@@ -502,24 +592,27 @@ end
|
|
502
592
|
|
503
593
|
### Inspection
|
504
594
|
|
505
|
-
AASM supports a couple of methods to find out which states or events are provided or
|
595
|
+
AASM supports a couple of methods to find out which states or events are provided or permitted.
|
506
596
|
|
507
|
-
Given
|
597
|
+
Given this `Job` class:
|
508
598
|
|
509
599
|
```ruby
|
510
|
-
|
511
|
-
|
512
|
-
job.aasm.states.map(&:name)
|
600
|
+
# show all states
|
601
|
+
Job.aasm.states.map(&:name)
|
513
602
|
=> [:sleeping, :running, :cleaning]
|
514
603
|
|
515
|
-
job
|
604
|
+
job = Job.new
|
605
|
+
|
606
|
+
# show all permitted (reachable / possible) states
|
607
|
+
job.aasm.states(:permitted => true).map(&:name)
|
516
608
|
=> [:running]
|
517
609
|
job.run
|
518
|
-
job.aasm.states(:
|
610
|
+
job.aasm.states(:permitted => true).map(&:name)
|
519
611
|
=> [:cleaning, :sleeping]
|
520
612
|
|
521
|
-
|
522
|
-
|
613
|
+
# show all possible (triggerable) events (allowed by transitions)
|
614
|
+
job.aasm.events.map(&:name)
|
615
|
+
=> [:sleep]
|
523
616
|
```
|
524
617
|
|
525
618
|
|
@@ -0,0 +1,240 @@
|
|
1
|
+
# Migrating from _AASM_ version 3 to 4
|
2
|
+
|
3
|
+
## Must
|
4
|
+
|
5
|
+
### Callback order has been changed
|
6
|
+
|
7
|
+
The first callback to be run is `:before` of the event. A state's `:before_exit' callback
|
8
|
+
is now run directly before its `:exit` callback. Event-based guards are now run before
|
9
|
+
any of the transition guards are run. And finally, before running any state callbacks,
|
10
|
+
all (event- and transition-based) guards are run to check whether the state callbacks
|
11
|
+
can be run or not.
|
12
|
+
|
13
|
+
|
14
|
+
### Callback `:on_transition` renamed to `:after` and changed its binding
|
15
|
+
|
16
|
+
The transition callback `:on_transition` has been renamed to `:after` in order
|
17
|
+
to make clear it is being called (namely _after_ doing the transition).
|
18
|
+
|
19
|
+
Furthermore, in alignment with the other callbacks, it's not receiving the object
|
20
|
+
at hand as first parameter and binds the current object to self.
|
21
|
+
|
22
|
+
In summary, change from
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
aasm do
|
26
|
+
...
|
27
|
+
transitions :from => :from_state, :to => :to_state, :on_transition => :do_something
|
28
|
+
...
|
29
|
+
end
|
30
|
+
|
31
|
+
...
|
32
|
+
def some_other_method(arg)
|
33
|
+
...
|
34
|
+
end
|
35
|
+
|
36
|
+
def do_something(obj, arg1, arg2)
|
37
|
+
obj.some_other_method(arg1)
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
to
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
aasm do
|
45
|
+
...
|
46
|
+
transitions :from => :from_state, :to => :to_state, :after => :do_something
|
47
|
+
...
|
48
|
+
end
|
49
|
+
|
50
|
+
...
|
51
|
+
def some_other_method(arg)
|
52
|
+
...
|
53
|
+
end
|
54
|
+
|
55
|
+
def do_something(arg1, arg2)
|
56
|
+
some_other_method(arg1) # run on the object as self
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
|
61
|
+
### `after_commit` hooks are now event-based
|
62
|
+
|
63
|
+
The `after_commit` hooks have been move from the state level to the event level.
|
64
|
+
So, if you want some code block to be executed after the _AASM_ state has been
|
65
|
+
saved **AND** committed, change this code
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class Job < ActiveRecord::Base
|
69
|
+
include AASM
|
70
|
+
|
71
|
+
aasm do
|
72
|
+
state :sleeping, :initial => true
|
73
|
+
state :running, :after_commit => :notify_about_running_job
|
74
|
+
|
75
|
+
event :run do
|
76
|
+
transitions :from => :sleeping, :to => :running
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def notify_about_running_job
|
81
|
+
...
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
to
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
class Job < ActiveRecord::Base
|
90
|
+
include AASM
|
91
|
+
|
92
|
+
aasm do
|
93
|
+
state :sleeping, :initial => true
|
94
|
+
state :running
|
95
|
+
|
96
|
+
event :run, :after_commit => :notify_about_running_job do
|
97
|
+
transitions :from => :sleeping, :to => :running
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def notify_about_running_job
|
102
|
+
...
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
|
108
|
+
### Instance-level inspection
|
109
|
+
|
110
|
+
Listing events for the current state now returns Event objects instead of event names (as symbols). So, change from
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
job = Job.new
|
114
|
+
|
115
|
+
job.aasm.events
|
116
|
+
# => [:run]
|
117
|
+
```
|
118
|
+
|
119
|
+
to
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
job = Job.new
|
123
|
+
|
124
|
+
job.aasm.events.map(&:name)
|
125
|
+
# => [:run]
|
126
|
+
```
|
127
|
+
|
128
|
+
Retrieving the list of permitted events has now been integrated into the `events` method. Change from
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
job = Job.new
|
132
|
+
|
133
|
+
job.aasm.permissible_events
|
134
|
+
# => [:run]
|
135
|
+
```
|
136
|
+
|
137
|
+
to
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
job = Job.new
|
141
|
+
|
142
|
+
job.aasm.events(:permitted => true)
|
143
|
+
# => [:run]
|
144
|
+
```
|
145
|
+
|
146
|
+
Class-based events now return a list of `Event` instances. Change from
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
Job.aasm.events.values.map(&:name)
|
150
|
+
# => [:run]
|
151
|
+
```
|
152
|
+
|
153
|
+
to
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
Job.aasm.events.map(&:name)
|
157
|
+
# => [:run]
|
158
|
+
```
|
159
|
+
|
160
|
+
|
161
|
+
## Could
|
162
|
+
|
163
|
+
### Triggering an event without _to_state_
|
164
|
+
|
165
|
+
When providing parameters to callbacks it is not required to provide the `to_state`
|
166
|
+
anymore. So, assuming you have the following class:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
class Job
|
170
|
+
include AASM
|
171
|
+
|
172
|
+
aasm do
|
173
|
+
state :sleeping, :initial => true
|
174
|
+
state :running
|
175
|
+
|
176
|
+
event :run do
|
177
|
+
transitions :from => :sleeping, :to => :running, :after => :log
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def log(message)
|
182
|
+
logger.info message
|
183
|
+
end
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
then you could change from
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
job = Job.new
|
191
|
+
job.run(:running, "we want to run")
|
192
|
+
```
|
193
|
+
|
194
|
+
to this:
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
job = Job.new
|
198
|
+
job.run("we want to run")
|
199
|
+
|
200
|
+
job.run(:running, "we want to run") # still supported to select the target state (the _to_state_)
|
201
|
+
```
|
202
|
+
|
203
|
+
On the other hand, you have to accept the arguments for **all** callback methods (and procs)
|
204
|
+
you provide and use. If you don't want to provide these, you can splat them
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
def before(*args); end
|
208
|
+
# or
|
209
|
+
def before(*_); end # to indicate that you don't want to use the arguments
|
210
|
+
```
|
211
|
+
|
212
|
+
### New configuration option: `no_direct_assignment`
|
213
|
+
|
214
|
+
If you want to make sure that the _AASM_ column for storing the state is not directly assigned,
|
215
|
+
configure _AASM_ to not allow direct assignment, like this:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
class Job < ActiveRecord::Base
|
219
|
+
include AASM
|
220
|
+
|
221
|
+
aasm :no_direct_assignment => true do
|
222
|
+
state :sleeping, :initial => true
|
223
|
+
state :running
|
224
|
+
|
225
|
+
event :run do
|
226
|
+
transitions :from => :sleeping, :to => :running
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
resulting in this:
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
job = Job.create
|
237
|
+
job.aasm_state # => 'sleeping'
|
238
|
+
job.aasm_state = :running # => raises AASM::NoDirectAssignmentError
|
239
|
+
job.aasm_state # => 'sleeping'
|
240
|
+
```
|