aasm 3.4.0 → 4.0.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.
- 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
|
+
```
|