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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90aa17e423f74f9397944f3e941fc539833f4a86
4
- data.tar.gz: c18f24941a1ed1c7880140decfe262a88b37f3aa
3
+ metadata.gz: e82947e11b28a1768717833153778b493f638129
4
+ data.tar.gz: eb74d70ed1a8ddd968aa24db3ec1903d29d332d8
5
5
  SHA512:
6
- metadata.gz: de15b7a7878ba85fe1da7f7e55bb418b499a31c3a7f3556bd66be0667e077a7c69ca2f02ce8f5066b5274a98639ca4c101f94c28c9bc113a5ba14a70876bb4eb
7
- data.tar.gz: 9d3fd63e5bec19aae9234bf8a13a889934fd97202e4c30cdf85bd518176034c95c81a8bbd623298d728d706c46c51086375f162a1364f6c69de90ce8642af5e0
6
+ metadata.gz: 4144ba3401348e02bfda53f0eff38f7f018b3e8eef93a157bd727ab4bc499a1c645440263bbc405e07da213c553ea9f1bc697c40fcfcb0e79b72cf1bf6dd1a6d
7
+ data.tar.gz: c80f18c84bf32c1be09220253e438afb8ca249c63c3d68d3ba15bf0669ab71b1b79f6c926affaa3064194cee37be91f5c74e4c290ab82ac277388795c8f35621
data/.gitignore CHANGED
@@ -16,4 +16,4 @@ TODO
16
16
  alto
17
17
  .rspec
18
18
  .bundle
19
-
19
+ tags
@@ -1,8 +1,25 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 3.9.0 (not yet released)
4
-
5
- * deprecated old aasm_* class methods (old-style DSL), in preparation for AASM v4.0.0
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
 
@@ -0,0 +1,7 @@
1
+ # Planned changes for AASM 4.1
2
+
3
+ * remove support for `:on_transition` callback
4
+
5
+ # Planned changes for AASM 4.0
6
+
7
+ * nothing left
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 => Proc.new { do_afterwards } do
98
- transitions :from => :sleeping, :to => :running, :on_transition => Proc.new {|obj, *args| obj.set_process(*args) }
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 do_afterwards
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 `do_afterwards` is called after the transition `run` (from `sleeping` to `running`)
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
- event:before
135
- previous_state:before_exit
136
- new_state:before_enter
137
- ...update state...
138
- previous_state:after_exit
139
- new_state:after_enter
140
- event:after
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 Job
215
+ class Cleaner
200
216
  include AASM
201
217
 
202
218
  aasm do
203
- state :sleeping, :initial => true
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 => :running, :to => :cleaning
223
+ transitions :from => :idle, :to => :cleaning, :guard => :cleaning_needed?
213
224
  end
214
225
 
215
- event :sleep do
216
- transitions :from => :running, :to => :sleeping, :guard => :cleaning_needed?
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 = Job.new
227
- job.run
228
- job.may_sleep? # => false
229
- job.sleep # => raises AASM::InvalidTransition
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 already use"
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 already use"
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, :after_commit => :notify_about_running_job
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 permissible.
595
+ AASM supports a couple of methods to find out which states or events are provided or permitted.
506
596
 
507
- Given the `Job` class from above:
597
+ Given this `Job` class:
508
598
 
509
599
  ```ruby
510
- job = Job.new
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.aasm.states(:permissible => true).map(&:name)
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(:permissible => true).map(&:name)
610
+ job.aasm.states(:permitted => true).map(&:name)
519
611
  => [:cleaning, :sleeping]
520
612
 
521
- job.aasm.events
522
- => [:run, :clean, :sleep]
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
+ ```