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 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
+ ```