aasm 4.5.1 → 5.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.md +809 -129
  4. data/lib/aasm/aasm.rb +74 -37
  5. data/lib/aasm/base.rb +188 -41
  6. data/lib/aasm/configuration.rb +27 -2
  7. data/lib/aasm/core/event.rb +75 -47
  8. data/lib/aasm/core/invoker.rb +129 -0
  9. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  10. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  11. data/lib/aasm/core/invokers/literal_invoker.rb +49 -0
  12. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  13. data/lib/aasm/core/state.rb +22 -13
  14. data/lib/aasm/core/transition.rb +30 -23
  15. data/lib/aasm/dsl_helper.rb +24 -22
  16. data/lib/aasm/errors.rb +8 -5
  17. data/lib/aasm/instance_base.rb +63 -15
  18. data/lib/aasm/localizer.rb +13 -3
  19. data/lib/aasm/minitest/allow_event.rb +13 -0
  20. data/lib/aasm/minitest/allow_transition_to.rb +13 -0
  21. data/lib/aasm/minitest/have_state.rb +13 -0
  22. data/lib/aasm/minitest/transition_from.rb +21 -0
  23. data/lib/aasm/minitest.rb +5 -0
  24. data/lib/aasm/minitest_spec.rb +15 -0
  25. data/lib/aasm/persistence/active_record_persistence.rb +87 -79
  26. data/lib/aasm/persistence/base.rb +30 -30
  27. data/lib/aasm/persistence/core_data_query_persistence.rb +94 -0
  28. data/lib/aasm/persistence/dynamoid_persistence.rb +92 -0
  29. data/lib/aasm/persistence/mongoid_persistence.rb +49 -35
  30. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  31. data/lib/aasm/persistence/orm.rb +154 -0
  32. data/lib/aasm/persistence/plain_persistence.rb +2 -1
  33. data/lib/aasm/persistence/redis_persistence.rb +112 -0
  34. data/lib/aasm/persistence/sequel_persistence.rb +37 -67
  35. data/lib/aasm/persistence.rb +20 -5
  36. data/lib/aasm/rspec/allow_event.rb +5 -1
  37. data/lib/aasm/rspec/allow_transition_to.rb +5 -1
  38. data/lib/aasm/rspec/transition_from.rb +8 -4
  39. data/lib/aasm/state_machine.rb +6 -12
  40. data/lib/aasm/state_machine_store.rb +76 -0
  41. data/lib/aasm/version.rb +1 -1
  42. data/lib/aasm.rb +8 -2
  43. data/lib/generators/aasm/aasm_generator.rb +16 -0
  44. data/lib/generators/aasm/orm_helpers.rb +41 -0
  45. data/lib/generators/active_record/aasm_generator.rb +40 -0
  46. data/lib/generators/active_record/templates/migration.rb +8 -0
  47. data/lib/generators/active_record/templates/migration_existing.rb +5 -0
  48. data/lib/generators/mongoid/aasm_generator.rb +28 -0
  49. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  50. data/lib/motion-aasm.rb +37 -0
  51. metadata +104 -259
  52. data/.document +0 -6
  53. data/.gitignore +0 -19
  54. data/.travis.yml +0 -37
  55. data/API +0 -34
  56. data/CHANGELOG.md +0 -272
  57. data/CODE_OF_CONDUCT.md +0 -13
  58. data/Gemfile +0 -15
  59. data/HOWTO +0 -12
  60. data/PLANNED_CHANGES.md +0 -11
  61. data/README_FROM_VERSION_3_TO_4.md +0 -240
  62. data/Rakefile +0 -26
  63. data/aasm.gemspec +0 -31
  64. data/callbacks.txt +0 -51
  65. data/gemfiles/rails_3.2.gemfile +0 -14
  66. data/gemfiles/rails_4.0.gemfile +0 -12
  67. data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -14
  68. data/gemfiles/rails_4.1.gemfile +0 -12
  69. data/gemfiles/rails_4.1_mongo_mapper.gemfile +0 -14
  70. data/gemfiles/rails_4.2.gemfile +0 -12
  71. data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -14
  72. data/gemfiles/rails_4.2_mongoid_5.gemfile +0 -12
  73. data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -157
  74. data/spec/database.rb +0 -63
  75. data/spec/database.yml +0 -3
  76. data/spec/en.yml +0 -9
  77. data/spec/en_deprecated_style.yml +0 -10
  78. data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +0 -25
  79. data/spec/models/active_record/complex_active_record_example.rb +0 -33
  80. data/spec/models/active_record/derivate_new_dsl.rb +0 -7
  81. data/spec/models/active_record/false_state.rb +0 -35
  82. data/spec/models/active_record/gate.rb +0 -39
  83. data/spec/models/active_record/localizer_test_model.rb +0 -34
  84. data/spec/models/active_record/no_direct_assignment.rb +0 -21
  85. data/spec/models/active_record/no_scope.rb +0 -21
  86. data/spec/models/active_record/persisted_state.rb +0 -12
  87. data/spec/models/active_record/provided_and_persisted_state.rb +0 -24
  88. data/spec/models/active_record/reader.rb +0 -7
  89. data/spec/models/active_record/readme_job.rb +0 -21
  90. data/spec/models/active_record/simple_new_dsl.rb +0 -17
  91. data/spec/models/active_record/thief.rb +0 -29
  92. data/spec/models/active_record/transient.rb +0 -6
  93. data/spec/models/active_record/with_enum.rb +0 -39
  94. data/spec/models/active_record/with_false_enum.rb +0 -31
  95. data/spec/models/active_record/with_true_enum.rb +0 -39
  96. data/spec/models/active_record/writer.rb +0 -6
  97. data/spec/models/basic_two_state_machines_example.rb +0 -25
  98. data/spec/models/callbacks/basic.rb +0 -78
  99. data/spec/models/callbacks/basic_multiple.rb +0 -75
  100. data/spec/models/callbacks/guard_within_block.rb +0 -66
  101. data/spec/models/callbacks/guard_within_block_multiple.rb +0 -66
  102. data/spec/models/callbacks/multiple_transitions_transition_guard.rb +0 -65
  103. data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +0 -65
  104. data/spec/models/callbacks/private_method.rb +0 -44
  105. data/spec/models/callbacks/private_method_multiple.rb +0 -44
  106. data/spec/models/callbacks/with_args.rb +0 -61
  107. data/spec/models/callbacks/with_args_multiple.rb +0 -61
  108. data/spec/models/callbacks/with_state_arg.rb +0 -26
  109. data/spec/models/callbacks/with_state_arg_multiple.rb +0 -26
  110. data/spec/models/complex_example.rb +0 -222
  111. data/spec/models/conversation.rb +0 -93
  112. data/spec/models/default_state.rb +0 -12
  113. data/spec/models/double_definer.rb +0 -21
  114. data/spec/models/foo.rb +0 -92
  115. data/spec/models/foo_callback_multiple.rb +0 -45
  116. data/spec/models/guardian.rb +0 -48
  117. data/spec/models/guardian_multiple.rb +0 -48
  118. data/spec/models/initial_state_proc.rb +0 -31
  119. data/spec/models/invalid_persistor.rb +0 -31
  120. data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +0 -37
  121. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
  122. data/spec/models/mongo_mapper/simple_mongo_mapper.rb +0 -23
  123. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
  124. data/spec/models/mongoid/complex_mongoid_example.rb +0 -37
  125. data/spec/models/mongoid/no_scope_mongoid.rb +0 -21
  126. data/spec/models/mongoid/simple_mongoid.rb +0 -23
  127. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +0 -25
  128. data/spec/models/no_initial_state.rb +0 -25
  129. data/spec/models/not_auto_loaded/process.rb +0 -21
  130. data/spec/models/parametrised_event.rb +0 -29
  131. data/spec/models/parametrised_event_multiple.rb +0 -29
  132. data/spec/models/process_with_new_dsl.rb +0 -31
  133. data/spec/models/provided_state.rb +0 -24
  134. data/spec/models/sequel/complex_sequel_example.rb +0 -45
  135. data/spec/models/sequel/sequel_multiple.rb +0 -25
  136. data/spec/models/sequel/sequel_simple.rb +0 -25
  137. data/spec/models/silencer.rb +0 -27
  138. data/spec/models/simple_example.rb +0 -15
  139. data/spec/models/simple_multiple_example.rb +0 -30
  140. data/spec/models/state_machine_with_failed_event.rb +0 -12
  141. data/spec/models/sub_class.rb +0 -7
  142. data/spec/models/sub_class_with_more_states.rb +0 -18
  143. data/spec/models/sub_classing.rb +0 -3
  144. data/spec/models/super_class.rb +0 -46
  145. data/spec/models/this_name_better_not_be_in_use.rb +0 -11
  146. data/spec/models/transactor.rb +0 -53
  147. data/spec/models/valid_state_name.rb +0 -23
  148. data/spec/models/validator.rb +0 -79
  149. data/spec/models/worker.rb +0 -2
  150. data/spec/spec_helper.rb +0 -25
  151. data/spec/unit/api_spec.rb +0 -77
  152. data/spec/unit/basic_two_state_machines_example_spec.rb +0 -10
  153. data/spec/unit/callback_multiple_spec.rb +0 -295
  154. data/spec/unit/callbacks_spec.rb +0 -296
  155. data/spec/unit/complex_example_spec.rb +0 -84
  156. data/spec/unit/complex_multiple_example_spec.rb +0 -99
  157. data/spec/unit/edge_cases_spec.rb +0 -16
  158. data/spec/unit/event_multiple_spec.rb +0 -73
  159. data/spec/unit/event_naming_spec.rb +0 -11
  160. data/spec/unit/event_spec.rb +0 -322
  161. data/spec/unit/guard_multiple_spec.rb +0 -60
  162. data/spec/unit/guard_spec.rb +0 -60
  163. data/spec/unit/initial_state_multiple_spec.rb +0 -15
  164. data/spec/unit/initial_state_spec.rb +0 -12
  165. data/spec/unit/inspection_multiple_spec.rb +0 -201
  166. data/spec/unit/inspection_spec.rb +0 -111
  167. data/spec/unit/localizer_spec.rb +0 -76
  168. data/spec/unit/memory_leak_spec.rb +0 -38
  169. data/spec/unit/new_dsl_spec.rb +0 -12
  170. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +0 -573
  171. data/spec/unit/persistence/active_record_persistence_spec.rb +0 -552
  172. data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -146
  173. data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -93
  174. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -127
  175. data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -79
  176. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +0 -153
  177. data/spec/unit/persistence/sequel_persistence_spec.rb +0 -100
  178. data/spec/unit/readme_spec.rb +0 -42
  179. data/spec/unit/reloading_spec.rb +0 -15
  180. data/spec/unit/rspec_matcher_spec.rb +0 -79
  181. data/spec/unit/simple_example_spec.rb +0 -42
  182. data/spec/unit/simple_multiple_example_spec.rb +0 -63
  183. data/spec/unit/state_spec.rb +0 -89
  184. data/spec/unit/subclassing_multiple_spec.rb +0 -39
  185. data/spec/unit/subclassing_spec.rb +0 -31
  186. data/spec/unit/transition_spec.rb +0 -291
data/README.md CHANGED
@@ -2,16 +2,62 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/aasm.svg)](http://badge.fury.io/rb/aasm)
4
4
  [![Build Status](https://travis-ci.org/aasm/aasm.svg?branch=master)](https://travis-ci.org/aasm/aasm)
5
- [![Dependency Status](https://gemnasium.com/aasm/aasm.svg)](https://gemnasium.com/aasm/aasm)
6
5
  [![Code Climate](https://codeclimate.com/github/aasm/aasm/badges/gpa.svg)](https://codeclimate.com/github/aasm/aasm)
6
+ [![codecov](https://codecov.io/gh/aasm/aasm/branch/master/graph/badge.svg)](https://codecov.io/gh/aasm/aasm)
7
+
8
+ ## Index
9
+ - [Upgrade from version 3 to 4](#upgrade-from-version-3-to-4)
10
+ - [Usage](#usage)
11
+ - [Callbacks](#callbacks)
12
+ - [Lifecycle](#lifecycle)
13
+ - [The current event triggered](#the-current-event-triggered)
14
+ - [Guards](#guards)
15
+ - [Transitions](#transitions)
16
+ - [Multiple state machines per class](#multiple-state-machines-per-class)
17
+ - [Handling naming conflicts between multiple state machines](#handling-naming-conflicts-between-multiple-state-machines)
18
+ - [Binding event](#binding-event)
19
+ - [Auto-generated Status Constants](#auto-generated-status-constants)
20
+ - [Extending AASM](#extending-aasm)
21
+ - [ActiveRecord](#activerecord)
22
+ - [Bang events](#bang-events)
23
+ - [Timestamps](#timestamps)
24
+ - [ActiveRecord enums](#activerecord-enums)
25
+ - [Sequel](#sequel)
26
+ - [Dynamoid](#dynamoid)
27
+ - [Mongoid](#mongoid)
28
+ - [Nobrainer](#nobrainer)
29
+ - [Redis](#redis)
30
+ - [Automatic Scopes](#automatic-scopes)
31
+ - [Transaction support](#transaction-support)
32
+ - [Pessimistic Locking](#pessimistic-locking)
33
+ - [Column name & migration](#column-name--migration)
34
+ - [Log State Changes](#log-state-changes)
35
+ - [Inspection](#inspection)
36
+ - [Warning output](#warning-output)
37
+ - [RubyMotion support](#rubymotion-support)
38
+ - [Testing](#testing)
39
+ - [RSpec](#rspec)
40
+ - [Minitest](#minitest)
41
+ - [Assertions](#assertions)
42
+ - [Expectations](#expectations)
43
+ - [Installation](#installation)
44
+ - [Manually from RubyGems.org](#manually-from-rubygemsorg)
45
+ - [Bundler](#or-if-you-are-using-bundler)
46
+ - [Building your own gems](#building-your-own-gems)
47
+ - [Generators](#generators)
48
+ - [Test suite with Docker](#docker)
49
+ - [Latest changes](#latest-changes)
50
+ - [Questions?](#questions)
51
+ - [Maintainers](#maintainers)
52
+ - [Contributing](CONTRIBUTING.md)
53
+ - [Warranty](#warranty)
54
+ - [License](#license)
7
55
 
8
56
  This package contains AASM, a library for adding finite state machines to Ruby classes.
9
57
 
10
58
  AASM started as the *acts_as_state_machine* plugin but has evolved into a more generic library
11
- that no longer targets only ActiveRecord models. It currently provides adapters for
12
- [ActiveRecord](http://api.rubyonrails.org/classes/ActiveRecord/Base.html),
13
- [Mongoid](http://mongoid.org/), and [Mongomapper](http://mongomapper.com/) but it can be used for any Ruby class, no matter what
14
- parent class it has (if any).
59
+ that no longer targets only ActiveRecord models. It currently provides adapters for many
60
+ ORMs but it can be used for any Ruby class, no matter what parent class it has (if any).
15
61
 
16
62
  ## Upgrade from version 3 to 4
17
63
 
@@ -27,20 +73,19 @@ class Job
27
73
  include AASM
28
74
 
29
75
  aasm do
30
- state :sleeping, :initial => true
31
- state :running
32
- state :cleaning
76
+ state :sleeping, initial: true
77
+ state :running, :cleaning
33
78
 
34
79
  event :run do
35
- transitions :from => :sleeping, :to => :running
80
+ transitions from: :sleeping, to: :running
36
81
  end
37
82
 
38
83
  event :clean do
39
- transitions :from => :running, :to => :cleaning
84
+ transitions from: :running, to: :cleaning
40
85
  end
41
86
 
42
87
  event :sleep do
43
- transitions :from => [:running, :cleaning], :to => :sleeping
88
+ transitions from: [:running, :cleaning], to: :sleeping
44
89
  end
45
90
  end
46
91
 
@@ -66,7 +111,7 @@ AASM not to be *whiny*:
66
111
  ```ruby
67
112
  class Job
68
113
  ...
69
- aasm :whiny_transitions => false do
114
+ aasm whiny_transitions: false do
70
115
  ...
71
116
  end
72
117
  end
@@ -87,25 +132,27 @@ the transition succeeds :
87
132
 
88
133
  ### Callbacks
89
134
 
90
- You can define a number of callbacks for your transitions. These methods will be
91
- called, when certain criteria are met, like entering a particular state:
135
+ You can define a number of callbacks for your events, transitions and states. These methods, Procs or classes will be
136
+ called when certain criteria are met, like entering a particular state:
92
137
 
93
138
  ```ruby
94
139
  class Job
95
140
  include AASM
96
141
 
97
142
  aasm do
98
- state :sleeping, :initial => true, :before_enter => :do_something
99
- state :running
143
+ state :sleeping, initial: true, before_enter: :do_something
144
+ state :running, before_enter: Proc.new { do_something && notify_somebody }
145
+ state :finished
100
146
 
101
147
  after_all_transitions :log_status_change
102
148
 
103
- event :run, :after => :notify_somebody do
149
+ event :run, after: :notify_somebody do
104
150
  before do
105
151
  log('Preparing to run')
106
152
  end
107
153
 
108
- transitions :from => :sleeping, :to => :running, :after => Proc.new {|*args| set_process(*args) }
154
+ transitions from: :sleeping, to: :running, after: Proc.new {|*args| set_process(*args) }
155
+ transitions from: :running, to: :finished, after: LogRunTime
109
156
  end
110
157
 
111
158
  event :sleep do
@@ -115,7 +162,7 @@ class Job
115
162
  error do |e|
116
163
  ...
117
164
  end
118
- transitions :from => :running, :to => :sleeping
165
+ transitions from: :running, to: :sleeping
119
166
  end
120
167
  end
121
168
 
@@ -131,54 +178,74 @@ class Job
131
178
  ...
132
179
  end
133
180
 
134
- def notify_somebody(user)
181
+ def notify_somebody
135
182
  ...
136
183
  end
137
184
 
138
185
  end
186
+
187
+ class LogRunTime
188
+ def call
189
+ log "Job was running for X seconds"
190
+ end
191
+ end
139
192
  ```
140
193
 
141
194
  In this case `do_something` is called before actually entering the state `sleeping`,
142
195
  while `notify_somebody` is called after the transition `run` (from `sleeping` to `running`)
143
196
  is finished.
144
197
 
145
- Here you can see a list of all possible callbacks, together with their order of calling:
198
+ AASM will also initialize `LogRunTime` and run the `call` method for you after the transition from `running` to `finished` in the example above. You can pass arguments to the class by defining an initialize method on it, like this:
199
+
200
+ Note that Procs are executed in the context of a record, it means that you don't need to expect the record as an argument, just call the methods you need.
146
201
 
147
202
  ```ruby
148
- begin
149
- event before
150
- event guards
151
- transition guards
152
- old_state before_exit
153
- old_state exit
154
- after_all_transitions
155
- transition after
156
- new_state before_enter
157
- new_state enter
158
- ...update state...
159
- event success # if persist successful
160
- old_state after_exit
161
- new_state after_enter
162
- event after
163
- rescue
164
- event error
203
+ class LogRunTime
204
+ # optional args parameter can be omitted, but if you define initialize
205
+ # you must accept the model instance as the first parameter to it.
206
+ def initialize(job, args = {})
207
+ @job = job
208
+ end
209
+
210
+ def call
211
+ log "Job was running for #{@job.run_time} seconds"
212
+ end
165
213
  end
166
214
  ```
167
215
 
168
- Also, you can pass parameters to events:
216
+ #### Parameters
217
+ You can pass parameters to events:
169
218
 
170
219
  ```ruby
171
220
  job = Job.new
172
- job.run(:running, :defragmentation)
221
+ job.run(:defragmentation)
173
222
  ```
174
223
 
175
- In this case the `set_process` would be called with `:defragmentation` argument.
224
+ All guards and after callbacks will receive these parameters. In this case `set_process` would be called with
225
+ `:defragmentation` argument.
176
226
 
177
- Note that when passing arguments to a state transition, the first argument must be the desired end state. In the above example, we wish to transition to `:running` state and run the callback with `:defragmentation` argument. You can also pass in `nil` as the desired end state, and AASM will try to transition to the first end state defined for that event.
227
+ If the first argument to the event is a state (e.g. `:running` or `:finished`), the first argument is consumed and
228
+ the state machine will attempt to transition to that state. Add comma separated parameter for guards and callbacks
178
229
 
230
+ ```ruby
231
+ job = Job.new
232
+ job.run(:running, :defragmentation)
233
+ ```
234
+ In this case `set_process` won't be called, job will transition to running state and callback will receive
235
+ `:defragmentation` as parameter
236
+
237
+ #### Error Handling
179
238
  In case of an error during the event processing the error is rescued and passed to `:error`
180
239
  callback, which can handle it or re-raise it for further propagation.
181
240
 
241
+ Also, you can define a method that will be called if any event fails:
242
+
243
+ ```ruby
244
+ def aasm_event_failed(event_name, old_state_name)
245
+ # use custom exception/messages, report metrics, etc
246
+ end
247
+ ```
248
+
182
249
  During the transition's `:after` callback (and reliably only then, or in the global
183
250
  `after_all_transitions` callback) you can access the originating state (the from-state)
184
251
  and the target state (the to state), like this:
@@ -189,6 +256,41 @@ and the target state (the to state), like this:
189
256
  end
190
257
  ```
191
258
 
259
+ #### Lifecycle
260
+
261
+ Here you can see a list of all possible callbacks, together with their order of calling:
262
+
263
+ ```ruby
264
+ begin
265
+ event before_all_events
266
+ event before
267
+ event guards
268
+ transition guards
269
+ old_state before_exit
270
+ old_state exit
271
+ after_all_transitions
272
+ transition after
273
+ new_state before_enter
274
+ new_state enter
275
+ ...update state...
276
+ event before_success # if persist successful
277
+ transition success # if persist successful, database update not guaranteed
278
+ event success # if persist successful, database update not guaranteed
279
+ old_state after_exit
280
+ new_state after_enter
281
+ event after
282
+ event after_all_events
283
+ rescue
284
+ event error
285
+ event error_on_all_events
286
+ ensure
287
+ event ensure
288
+ event ensure_on_all_events
289
+ end
290
+ ```
291
+
292
+ Use event's `after_commit` callback if it should be fired after database update.
293
+
192
294
  #### The current event triggered
193
295
 
194
296
  While running the callbacks you can easily retrieve the name of the event triggered
@@ -219,33 +321,41 @@ and then
219
321
  Let's assume you want to allow particular transitions only if a defined condition is
220
322
  given. For this you can set up a guard per transition, which will run before actually
221
323
  running the transition. If the guard returns `false` the transition will be
222
- denied (raising `AASM::InvalidTransition` or returning `false` itself):
324
+ denied (raising `AASM::InvalidTransition`):
223
325
 
224
326
  ```ruby
225
327
  class Cleaner
226
328
  include AASM
227
329
 
228
330
  aasm do
229
- state :idle, :initial => true
331
+ state :idle, initial: true
230
332
  state :cleaning
231
333
 
232
334
  event :clean do
233
- transitions :from => :idle, :to => :cleaning, :guard => :cleaning_needed?
335
+ transitions from: :idle, to: :cleaning, guard: :cleaning_needed?
234
336
  end
235
337
 
236
338
  event :clean_if_needed do
237
- transitions :from => :idle, :to => :cleaning do
339
+ transitions from: :idle, to: :cleaning do
238
340
  guard do
239
341
  cleaning_needed?
240
342
  end
241
343
  end
242
- transitions :from => :idle, :to => :idle
344
+ transitions from: :idle, to: :idle
345
+ end
346
+
347
+ event :clean_if_dirty do
348
+ transitions from: :idle, to: :cleaning, guard: :if_dirty?
243
349
  end
244
350
  end
245
351
 
246
352
  def cleaning_needed?
247
353
  false
248
354
  end
355
+
356
+ def if_dirty?(status)
357
+ status == :dirty
358
+ end
249
359
  end
250
360
 
251
361
  job = Cleaner.new
@@ -253,6 +363,9 @@ job.may_clean? # => false
253
363
  job.clean # => raises AASM::InvalidTransition
254
364
  job.may_clean_if_needed? # => true
255
365
  job.clean_if_needed! # idle
366
+
367
+ job.clean_if_dirty(:clean) # => raises AASM::InvalidTransition
368
+ job.clean_if_dirty(:dirty) # => true
256
369
  ```
257
370
 
258
371
  You can even provide a number of guards, which all have to succeed to proceed
@@ -261,16 +374,16 @@ You can even provide a number of guards, which all have to succeed to proceed
261
374
  def walked_the_dog?; ...; end
262
375
 
263
376
  event :sleep do
264
- transitions :from => :running, :to => :sleeping, :guards => [:cleaning_needed?, :walked_the_dog?]
377
+ transitions from: :running, to: :sleeping, guards: [:cleaning_needed?, :walked_the_dog?]
265
378
  end
266
379
  ```
267
380
 
268
381
  If you want to provide guards for all transitions within an event, you can use event guards
269
382
 
270
383
  ```ruby
271
- event :sleep, :guards => [:walked_the_dog?] do
272
- transitions :from => :running, :to => :sleeping, :guards => [:cleaning_needed?]
273
- transitions :from => :cleaning, :to => :sleeping
384
+ event :sleep, guards: [:walked_the_dog?] do
385
+ transitions from: :running, to: :sleeping, guards: [:cleaning_needed?]
386
+ transitions from: :cleaning, to: :sleeping
274
387
  end
275
388
  ```
276
389
 
@@ -278,15 +391,30 @@ If you prefer a more Ruby-like guard syntax, you can use `if` and `unless` as we
278
391
 
279
392
  ```ruby
280
393
  event :clean do
281
- transitions :from => :running, :to => :cleaning, :if => :cleaning_needed?
394
+ transitions from: :running, to: :cleaning, if: :cleaning_needed?
282
395
  end
283
396
 
284
397
  event :sleep do
285
- transitions :from => :running, :to => :sleeping, :unless => :cleaning_needed?
398
+ transitions from: :running, to: :sleeping, unless: :cleaning_needed?
286
399
  end
287
400
  end
288
401
  ```
289
402
 
403
+ You can invoke a Class instead of a method if the Class responds to `call`
404
+
405
+ ```ruby
406
+ event :sleep do
407
+ transitions from: :running, to: :sleeping, guards: Dog
408
+ end
409
+ ```
410
+ ```ruby
411
+ class Dog
412
+ def call
413
+ cleaning_needed? && walked?
414
+ end
415
+ ...
416
+ end
417
+ ```
290
418
 
291
419
  ### Transitions
292
420
 
@@ -299,7 +427,7 @@ class Job
299
427
  include AASM
300
428
 
301
429
  aasm do
302
- state :stage1, :initial => true
430
+ state :stage1, initial: true
303
431
  state :stage2
304
432
  state :stage3
305
433
  state :completed
@@ -320,40 +448,67 @@ job.stage1_completed
320
448
  job.aasm.current_state # stage3
321
449
  ```
322
450
 
451
+ You can define transition from any defined state by omitting `from`:
452
+
453
+ ```ruby
454
+ event :abort do
455
+ transitions to: :aborted
456
+ end
457
+ ```
458
+
459
+ ### Display name for state
460
+
461
+ You can define display name for state using :display option
462
+
463
+ ```ruby
464
+ class Job
465
+ include AASM
466
+
467
+ aasm do
468
+ state :stage1, initial: true, display: 'First Stage'
469
+ state :stage2
470
+ state :stage3
471
+ end
472
+ end
473
+
474
+ job = Job.new
475
+ job.aasm.human_state
476
+
477
+ ```
323
478
 
324
479
  ### Multiple state machines per class
325
480
 
326
481
  Multiple state machines per class are supported. Be aware though that _AASM_ has been
327
- built with one state machine per class in mind. Nonetheless, here's how to do it:
482
+ built with one state machine per class in mind. Nonetheless, here's how to do it (see below). Please note that you will need to specify database columns for where your pertinent states will be stored - we have specified two columns `move_state` and `work_state` in the example below. See the [Column name & migration](https://github.com/aasm/aasm#column-name--migration) section for further info.
328
483
 
329
484
  ```ruby
330
485
  class SimpleMultipleExample
331
486
  include AASM
332
- aasm(:move) do
333
- state :standing, :initial => true
487
+ aasm(:move, column: 'move_state') do
488
+ state :standing, initial: true
334
489
  state :walking
335
490
  state :running
336
491
 
337
492
  event :walk do
338
- transitions :from => :standing, :to => :walking
493
+ transitions from: :standing, to: :walking
339
494
  end
340
495
  event :run do
341
- transitions :from => [:standing, :walking], :to => :running
496
+ transitions from: [:standing, :walking], to: :running
342
497
  end
343
498
  event :hold do
344
- transitions :from => [:walking, :running], :to => :standing
499
+ transitions from: [:walking, :running], to: :standing
345
500
  end
346
501
  end
347
502
 
348
- aasm(:work) do
349
- state :sleeping, :initial => true
503
+ aasm(:work, column: 'work_state') do
504
+ state :sleeping, initial: true
350
505
  state :processing
351
506
 
352
507
  event :start do
353
- transitions :from => :sleeping, :to => :processing
508
+ transitions from: :sleeping, to: :processing
354
509
  end
355
510
  event :stop do
356
- transitions :from => :processing, :to => :sleeping
511
+ transitions from: :processing, to: :sleeping
357
512
  end
358
513
  end
359
514
  end
@@ -362,32 +517,202 @@ simple = SimpleMultipleExample.new
362
517
 
363
518
  simple.aasm(:move).current_state
364
519
  # => :standing
365
- simple.aasm(:work).current
520
+ simple.aasm(:work).current_state
366
521
  # => :sleeping
367
522
 
368
523
  simple.start
369
524
  simple.aasm(:move).current_state
370
525
  # => :standing
371
- simple.aasm(:work).current
526
+ simple.aasm(:work).current_state
372
527
  # => :processing
373
528
 
374
529
  ```
375
530
 
376
- _AASM_ doesn't prohibit to define the same event in more than one state machine. The
377
- latest definition "wins" and overrides previous definitions. Nonetheless, a warning is issued:
378
- `SimpleMultipleExample: The aasm event name run is already used!`.
531
+ #### Handling naming conflicts between multiple state machines
532
+
533
+ _AASM_ doesn't prohibit to define the same event in more than one state
534
+ machine. If no namespace is provided, the latest definition "wins" and
535
+ overrides previous definitions. Nonetheless, a warning is issued:
536
+ `SimpleMultipleExample: overriding method 'run'!`.
537
+
538
+ Alternatively, you can provide a namespace for each state machine:
539
+
540
+ ```ruby
541
+ class NamespacedMultipleExample
542
+ include AASM
543
+ aasm(:status) do
544
+ state :unapproved, initial: true
545
+ state :approved
546
+
547
+ event :approve do
548
+ transitions from: :unapproved, to: :approved
549
+ end
550
+
551
+ event :unapprove do
552
+ transitions from: :approved, to: :unapproved
553
+ end
554
+ end
555
+
556
+ aasm(:review_status, namespace: :review) do
557
+ state :unapproved, initial: true
558
+ state :approved
559
+
560
+ event :approve do
561
+ transitions from: :unapproved, to: :approved
562
+ end
563
+
564
+ event :unapprove do
565
+ transitions from: :approved, to: :unapproved
566
+ end
567
+ end
568
+ end
569
+
570
+ namespaced = NamespacedMultipleExample.new
571
+
572
+ namespaced.aasm(:status).current_state
573
+ # => :unapproved
574
+ namespaced.aasm(:review_status).current_state
575
+ # => :unapproved
576
+ namespaced.approve_review
577
+ namespaced.aasm(:review_status).current_state
578
+ # => :approved
579
+ ```
379
580
 
380
581
  All _AASM_ class- and instance-level `aasm` methods accept a state machine selector.
381
582
  So, for example, to use inspection on a class level, you have to use
382
583
 
383
584
  ```ruby
384
- SimpleMultipleExample.aasm(:work).states
585
+ SimpleMultipleExample.aasm(:move).states.map(&:name)
385
586
  # => [:standing, :walking, :running]
386
587
  ```
387
588
 
388
- *Final note*: Support for multiple state machines per class is a pretty new feature
389
- (since version `4.3`), so please bear with us in case it doesn't work as expected.
589
+ ### Binding event
590
+
591
+ Allow an event to be bound to another
592
+ ```ruby
593
+ class Example
594
+ include AASM
595
+
596
+ aasm(:work) do
597
+ state :sleeping, initial: true
598
+ state :processing
599
+
600
+ event :start do
601
+ transitions from: :sleeping, to: :processing
602
+ end
603
+ event :stop do
604
+ transitions from: :processing, to: :sleeping
605
+ end
606
+ end
390
607
 
608
+ aasm(:question) do
609
+ state :answered, initial: true
610
+ state :asked
611
+
612
+ event :ask, binding_event: :start do
613
+ transitions from: :answered, to: :asked
614
+ end
615
+ event :answer, binding_event: :stop do
616
+ transitions from: :asked, to: :answered
617
+ end
618
+ end
619
+ end
620
+
621
+ example = Example.new
622
+ example.aasm(:work).current_state #=> :sleeping
623
+ example.aasm(:question).current_state #=> :answered
624
+ example.ask
625
+ example.aasm(:work).current_state #=> :processing
626
+ example.aasm(:question).current_state #=> :asked
627
+ ```
628
+
629
+ ### Auto-generated Status Constants
630
+
631
+ AASM automatically [generates constants](https://github.com/aasm/aasm/pull/60)
632
+ for each status so you don't have to explicitly define them.
633
+
634
+ ```ruby
635
+ class Foo
636
+ include AASM
637
+
638
+ aasm do
639
+ state :initialized
640
+ state :calculated
641
+ state :finalized
642
+ end
643
+ end
644
+
645
+ > Foo::STATE_INITIALIZED
646
+ #=> :initialized
647
+ > Foo::STATE_CALCULATED
648
+ #=> :calculated
649
+ ```
650
+
651
+ ### Extending AASM
652
+
653
+ AASM allows you to easily extend `AASM::Base` for your own application purposes.
654
+
655
+ Let's suppose we have common logic across many AASM models. We can embody this logic in a sub-class of `AASM::Base`.
656
+
657
+ ```ruby
658
+ class CustomAASMBase < AASM::Base
659
+ # A custom transiton that we want available across many AASM models.
660
+ def count_transitions!
661
+ klass.class_eval do
662
+ aasm with_klass: CustomAASMBase do
663
+ after_all_transitions :increment_transition_count
664
+ end
665
+ end
666
+ end
667
+
668
+ # A custom annotation that we want available across many AASM models.
669
+ def requires_guards!
670
+ klass.class_eval do
671
+ attr_reader :authorizable_called,
672
+ :transition_count,
673
+ :fillable_called
674
+
675
+ def authorizable?
676
+ @authorizable_called = true
677
+ end
678
+
679
+ def fillable?
680
+ @fillable_called = true
681
+ end
682
+
683
+ def increment_transition_count
684
+ @transition_count ||= 0
685
+ @transition_count += 1
686
+ end
687
+ end
688
+ end
689
+ end
690
+ ```
691
+
692
+ When we declare our model that has an AASM state machine, we simply declare the AASM block with a `:with_klass` key to our own class.
693
+
694
+ ```ruby
695
+ class SimpleCustomExample
696
+ include AASM
697
+
698
+ # Let's build an AASM state machine with our custom class.
699
+ aasm with_klass: CustomAASMBase do
700
+ requires_guards!
701
+ count_transitions!
702
+
703
+ state :initialised, initial: true
704
+ state :filled_out
705
+ state :authorised
706
+
707
+ event :fill_out do
708
+ transitions from: :initialised, to: :filled_out, guard: :fillable?
709
+ end
710
+ event :authorise do
711
+ transitions from: :filled_out, to: :authorised, guard: :authorizable?
712
+ end
713
+ end
714
+ end
715
+ ```
391
716
 
392
717
 
393
718
  ### ActiveRecord
@@ -395,60 +720,80 @@ SimpleMultipleExample.aasm(:work).states
395
720
  AASM comes with support for ActiveRecord and allows automatic persisting of the object's
396
721
  state in the database.
397
722
 
723
+ Add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile.
724
+
398
725
  ```ruby
399
726
  class Job < ActiveRecord::Base
400
727
  include AASM
401
728
 
402
729
  aasm do # default column: aasm_state
403
- state :sleeping, :initial => true
730
+ state :sleeping, initial: true
404
731
  state :running
405
732
 
406
733
  event :run do
407
- transitions :from => :sleeping, :to => :running
734
+ transitions from: :sleeping, to: :running
408
735
  end
409
736
 
410
737
  event :sleep do
411
- transitions :from => :running, :to => :sleeping
738
+ transitions from: :running, to: :sleeping
412
739
  end
413
740
  end
414
741
 
415
742
  end
416
743
  ```
417
744
 
745
+ ### Bang events
746
+
418
747
  You can tell AASM to auto-save the object or leave it unsaved
419
748
 
420
749
  ```ruby
421
750
  job = Job.new
422
751
  job.run # not saved
423
752
  job.run! # saved
753
+
754
+ # or
755
+ job.aasm.fire(:run) # not saved
756
+ job.aasm.fire!(:run) # saved
424
757
  ```
425
758
 
426
- Saving includes running all validations on the `Job` class. If you want make sure
427
- the state gets saved without running validations (and thereby maybe persisting an
428
- invalid object state), simply tell AASM to skip the validations. Be aware that
429
- when skipping validations, only the state column will be updated in the database
430
- (just like ActiveRecord `change_column` is working).
759
+ Saving includes running all validations on the `Job` class. If
760
+ `whiny_persistence` flag is set to `true`, exception is raised in case of
761
+ failure. If `whiny_persistence` flag is set to `false`, methods with a bang return
762
+ `true` if the state transition is successful or `false` if an error occurs.
763
+
764
+ If you want make sure the state gets saved without running validations (and
765
+ thereby maybe persisting an invalid object state), simply tell AASM to skip the
766
+ validations. Be aware that when skipping validations, only the state column will
767
+ be updated in the database (just like ActiveRecord `update_column` is working).
431
768
 
432
769
  ```ruby
433
770
  class Job < ActiveRecord::Base
434
771
  include AASM
435
772
 
436
- aasm :skip_validation_on_save => true do
437
- state :sleeping, :initial => true
773
+ aasm skip_validation_on_save: true do
774
+ state :sleeping, initial: true
438
775
  state :running
439
776
 
440
777
  event :run do
441
- transitions :from => :sleeping, :to => :running
778
+ transitions from: :sleeping, to: :running
442
779
  end
443
780
 
444
781
  event :sleep do
445
- transitions :from => :running, :to => :sleeping
782
+ transitions from: :running, to: :sleeping
446
783
  end
447
784
  end
448
785
 
449
786
  end
450
787
  ```
451
788
 
789
+ Also, you can skip the validation at instance level with `some_event_name_without_validation!` method.
790
+ With this you have the flexibility of having validation for all your transitions by default and then skip it wherever required.
791
+ Please note that only state column will be updated as mentioned in the above example.
792
+
793
+ ```ruby
794
+ job.run_without_validation!
795
+ ```
796
+
452
797
  If you want to make sure that the _AASM_ column for storing the state is not directly assigned,
453
798
  configure _AASM_ to not allow direct assignment, like this:
454
799
 
@@ -456,12 +801,12 @@ configure _AASM_ to not allow direct assignment, like this:
456
801
  class Job < ActiveRecord::Base
457
802
  include AASM
458
803
 
459
- aasm :no_direct_assignment => true do
460
- state :sleeping, :initial => true
804
+ aasm no_direct_assignment: true do
805
+ state :sleeping, initial: true
461
806
  state :running
462
807
 
463
808
  event :run do
464
- transitions :from => :sleeping, :to => :running
809
+ transitions from: :sleeping, to: :running
465
810
  end
466
811
  end
467
812
 
@@ -477,6 +822,37 @@ job.aasm_state = :running # => raises AASM::NoDirectAssignmentError
477
822
  job.aasm_state # => 'sleeping'
478
823
  ```
479
824
 
825
+ ### Timestamps
826
+
827
+ You can tell _AASM_ to try to write a timestamp whenever a new state is entered.
828
+ If `timestamps: true` is set, _AASM_ will look for a field named like the new state plus `_at` and try to fill it:
829
+
830
+ ```ruby
831
+ class Job < ActiveRecord::Base
832
+ include AASM
833
+
834
+ aasm timestamps: true do
835
+ state :sleeping, initial: true
836
+ state :running
837
+
838
+ event :run do
839
+ transitions from: :sleeping, to: :running
840
+ end
841
+ end
842
+ end
843
+ ```
844
+
845
+ resulting in this:
846
+
847
+ ```ruby
848
+ job = Job.create
849
+ job.running_at # => nil
850
+ job.run!
851
+ job.running_at # => 2020-02-20 20:00:00
852
+ ```
853
+
854
+ Missing timestamp fields are silently ignored, so it is not necessary to have setters (such as ActiveRecord columns) for *all* states when using this option.
855
+
480
856
  #### ActiveRecord enums
481
857
 
482
858
  You can use
@@ -492,8 +868,8 @@ class Job < ActiveRecord::Base
492
868
  running: 99
493
869
  }
494
870
 
495
- aasm :column => :state, :enum => true do
496
- state :sleeping, :initial => true
871
+ aasm column: :state, enum: true do
872
+ state :sleeping, initial: true
497
873
  state :running
498
874
  end
499
875
  end
@@ -513,7 +889,7 @@ to ```false```.
513
889
 
514
890
  ### Sequel
515
891
 
516
- AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_, _Mongoid_, and _MongoMapper_.
892
+ AASM also supports [Sequel](http://sequel.jeremyevans.net/) besides _ActiveRecord_, and _Mongoid_.
517
893
 
518
894
  ```ruby
519
895
  class Job < Sequel::Model
@@ -528,6 +904,11 @@ end
528
904
  However it's not yet as feature complete as _ActiveRecord_. For example, there are
529
905
  scopes defined yet. See [Automatic Scopes](#automatic-scopes).
530
906
 
907
+ ### Dynamoid
908
+
909
+ Since version `4.8.0` _AASM_ also supports [Dynamoid](http://joshsymonds.com/Dynamoid/) as
910
+ persistence ORM.
911
+
531
912
  ### Mongoid
532
913
 
533
914
  AASM also supports persistence to Mongodb if you're using Mongoid. Make sure
@@ -544,23 +925,40 @@ class Job
544
925
  end
545
926
  ```
546
927
 
547
- ### MongoMapper
928
+ ### NoBrainer
548
929
 
549
- AASM also supports persistence to Mongodb if you're using MongoMapper. Make sure
550
- to include MongoMapper::Document before you include AASM.
930
+ AASM also supports persistence to [RethinkDB](https://www.rethinkdb.com/)
931
+ if you're using [Nobrainer](http://nobrainer.io/).
932
+ Make sure to include NoBrainer::Document before you include AASM.
551
933
 
552
934
  ```ruby
553
935
  class Job
554
- include MongoMapper::Document
936
+ include NoBrainer::Document
555
937
  include AASM
556
-
557
- key :aasm_state, Symbol
938
+ field :aasm_state
558
939
  aasm do
559
940
  ...
560
941
  end
561
942
  end
562
943
  ```
563
944
 
945
+ ### Redis
946
+
947
+ AASM also supports persistence in Redis via
948
+ [Redis::Objects](https://github.com/nateware/redis-objects).
949
+ Make sure to include Redis::Objects before you include AASM. Note that non-bang
950
+ events will work as bang events, persisting the changes on every call.
951
+
952
+ ```ruby
953
+ class User
954
+ include Redis::Objects
955
+ include AASM
956
+
957
+ aasm do
958
+ end
959
+ end
960
+ ```
961
+
564
962
  ### Automatic Scopes
565
963
 
566
964
  AASM will automatically create scope methods for each state in the model.
@@ -570,7 +968,7 @@ class Job < ActiveRecord::Base
570
968
  include AASM
571
969
 
572
970
  aasm do
573
- state :sleeping, :initial => true
971
+ state :sleeping, initial: true
574
972
  state :running
575
973
  state :cleaning
576
974
  end
@@ -599,8 +997,8 @@ defining the `AASM` states, like this:
599
997
  class Job < ActiveRecord::Base
600
998
  include AASM
601
999
 
602
- aasm :create_scopes => false do
603
- state :sleeping, :initial => true
1000
+ aasm create_scopes: false do
1001
+ state :sleeping, initial: true
604
1002
  state :running
605
1003
  state :cleaning
606
1004
  end
@@ -614,6 +1012,17 @@ Since version *3.0.13* AASM supports ActiveRecord transactions. So whenever a tr
614
1012
  callback or the state update fails, all changes to any database record are rolled back.
615
1013
  Mongodb does not support transactions.
616
1014
 
1015
+ There are currently 3 transactional callbacks that can be handled on the event, and 2 transactional callbacks for all events.
1016
+
1017
+ ```ruby
1018
+ event before_all_transactions
1019
+ event before_transaction
1020
+ event aasm_fire_event (within transaction)
1021
+ event after_commit (if event successful)
1022
+ event after_transaction
1023
+ event after_all_transactions
1024
+ ```
1025
+
617
1026
  If you want to make sure a depending action happens only after the transaction is committed,
618
1027
  use the `after_commit` callback along with the auto-save (bang) methods, like this:
619
1028
 
@@ -622,11 +1031,11 @@ class Job < ActiveRecord::Base
622
1031
  include AASM
623
1032
 
624
1033
  aasm do
625
- state :sleeping, :initial => true
1034
+ state :sleeping, initial: true
626
1035
  state :running
627
1036
 
628
- event :run, :after_commit => :notify_about_running_job do
629
- transitions :from => :sleeping, :to => :running
1037
+ event :run, after_commit: :notify_about_running_job do
1038
+ transitions from: :sleeping, to: :running
630
1039
  end
631
1040
  end
632
1041
 
@@ -648,18 +1057,24 @@ job.run
648
1057
  job.save! #notify_about_running_job is not run
649
1058
  ```
650
1059
 
1060
+ Please note that `:after_commit` AASM callbacks behaves around custom implementation
1061
+ of transaction pattern rather than a real-life DB transaction. This fact still causes
1062
+ the race conditions and redundant callback calls within nested transaction. In order
1063
+ to fix that it's highly recommended to add `gem 'after_commit_everywhere', '~> 1.0'`
1064
+ to your `Gemfile`.
1065
+
651
1066
  If you want to encapsulate state changes within an own transaction, the behavior
652
1067
  of this nested transaction might be confusing. Take a look at
653
1068
  [ActiveRecord Nested Transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html)
654
1069
  if you want to know more about this. Nevertheless, AASM by default requires a new transaction
655
- `transaction(:requires_new => true)`. You can override this behavior by changing
1070
+ `transaction(requires_new: true)`. You can override this behavior by changing
656
1071
  the configuration
657
1072
 
658
1073
  ```ruby
659
1074
  class Job < ActiveRecord::Base
660
1075
  include AASM
661
1076
 
662
- aasm :requires_new_transaction => false do
1077
+ aasm requires_new_transaction: false do
663
1078
  ...
664
1079
  end
665
1080
 
@@ -667,7 +1082,60 @@ class Job < ActiveRecord::Base
667
1082
  end
668
1083
  ```
669
1084
 
670
- which then leads to `transaction(:requires_new => false)`, the Rails default.
1085
+ which then leads to `transaction(requires_new: false)`, the Rails default.
1086
+
1087
+ Additionally, if you do not want any of your ActiveRecord actions to be
1088
+ wrapped in a transaction, you can specify the `use_transactions` flag. This can
1089
+ be useful if you want want to persist things to the database that happen as a
1090
+ result of a transaction or callback, even when some error occurs. The
1091
+ `use_transactions` flag is true by default.
1092
+
1093
+ ```ruby
1094
+ class Job < ActiveRecord::Base
1095
+ include AASM
1096
+
1097
+ aasm use_transactions: false do
1098
+ ...
1099
+ end
1100
+
1101
+ ...
1102
+ end
1103
+ ```
1104
+
1105
+ ### Pessimistic Locking
1106
+
1107
+ AASM supports [ActiveRecord pessimistic locking via `with_lock`](http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html#method-i-with_lock) for database persistence layers.
1108
+
1109
+ | Option | Purpose |
1110
+ | ------ | ------- |
1111
+ | `false` (default) | No lock is obtained | |
1112
+ | `true` | Obtain a blocking pessimistic lock e.g. `FOR UPDATE` |
1113
+ | String | Obtain a lock based on the SQL string e.g. `FOR UPDATE NOWAIT` |
1114
+
1115
+
1116
+ ```ruby
1117
+ class Job < ActiveRecord::Base
1118
+ include AASM
1119
+
1120
+ aasm requires_lock: true do
1121
+ ...
1122
+ end
1123
+
1124
+ ...
1125
+ end
1126
+ ```
1127
+
1128
+ ```ruby
1129
+ class Job < ActiveRecord::Base
1130
+ include AASM
1131
+
1132
+ aasm requires_lock: 'FOR UPDATE NOWAIT' do
1133
+ ...
1134
+ end
1135
+
1136
+ ...
1137
+ end
1138
+ ```
671
1139
 
672
1140
 
673
1141
  ### Column name & migration
@@ -679,15 +1147,21 @@ this by defining your favorite column name, using `:column` like this:
679
1147
  class Job < ActiveRecord::Base
680
1148
  include AASM
681
1149
 
682
- aasm :column => 'my_state' do
1150
+ aasm column: :my_state do
683
1151
  ...
684
1152
  end
685
1153
 
1154
+ aasm :another_state_machine, column: :second_state do
1155
+ ...
1156
+ end
686
1157
  end
687
1158
  ```
688
1159
 
689
1160
  Whatever column name is used, make sure to add a migration to provide this column
690
- (of type `string`):
1161
+ (of type `string`).
1162
+ Do not add default value for column at the database level. If you add default
1163
+ value in database then AASM callbacks on the initial state will not be fired upon
1164
+ instantiation of the model.
691
1165
 
692
1166
  ```ruby
693
1167
  class AddJobState < ActiveRecord::Migration
@@ -701,41 +1175,136 @@ class AddJobState < ActiveRecord::Migration
701
1175
  end
702
1176
  ```
703
1177
 
1178
+ ### Log State Changes
1179
+
1180
+ Logging state change can be done using [paper_trail](https://github.com/paper-trail-gem/paper_trail) gem
1181
+
1182
+ Example of implementation can be found here [https://github.com/nitsujri/aasm-papertrail-example](https://github.com/nitsujri/aasm-papertrail-example)
1183
+
1184
+
704
1185
  ### Inspection
705
1186
 
706
- AASM supports a couple of methods to find out which states or events are provided or permitted.
1187
+ AASM supports query methods for states and events
707
1188
 
708
- Given this `Job` class:
1189
+ Given the following `Job` class:
1190
+
1191
+ ```ruby
1192
+ class Job
1193
+ include AASM
1194
+
1195
+ aasm do
1196
+ state :sleeping, initial: true
1197
+ state :running, :cleaning
1198
+
1199
+ event :run do
1200
+ transitions from: :sleeping, to: :running
1201
+ end
1202
+
1203
+ event :clean do
1204
+ transitions from: :running, to: :cleaning, guard: :cleaning_needed?
1205
+ end
1206
+
1207
+ event :sleep do
1208
+ transitions from: [:running, :cleaning], to: :sleeping
1209
+ end
1210
+ end
1211
+
1212
+ def cleaning_needed?
1213
+ false
1214
+ end
1215
+ end
1216
+ ```
709
1217
 
710
1218
  ```ruby
711
1219
  # show all states
712
1220
  Job.aasm.states.map(&:name)
713
- => [:sleeping, :running, :cleaning]
1221
+ #=> [:sleeping, :running, :cleaning]
714
1222
 
715
1223
  job = Job.new
716
1224
 
717
- # show all permitted (reachable / possible) states
718
- job.aasm.states(:permitted => true).map(&:name)
719
- => [:running]
1225
+ # show all permitted states (from initial state)
1226
+ job.aasm.states(permitted: true).map(&:name)
1227
+ #=> [:running]
1228
+
1229
+ # List all the permitted transitions(event and state pairs) from initial state
1230
+ job.aasm.permitted_transitions
1231
+ #=> [{ :event => :run, :state => :running }]
1232
+
720
1233
  job.run
721
- job.aasm.states(:permitted => true).map(&:name)
722
- => [:cleaning, :sleeping]
1234
+ job.aasm.states(permitted: true).map(&:name)
1235
+ #=> [:sleeping]
723
1236
 
724
- # show all possible (triggerable) events (allowed by transitions)
1237
+ # show all non permitted states
1238
+ job.aasm.states(permitted: false).map(&:name)
1239
+ #=> [:cleaning]
1240
+
1241
+ # show all possible (triggerable) events from the current state
725
1242
  job.aasm.events.map(&:name)
726
- => [:sleep]
727
- job.aasm.events(:reject => :sleep).map(&:name)
728
- => []
1243
+ #=> [:clean, :sleep]
1244
+
1245
+ # show all permitted events
1246
+ job.aasm.events(permitted: true).map(&:name)
1247
+ #=> [:sleep]
1248
+
1249
+ # show all non permitted events
1250
+ job.aasm.events(permitted: false).map(&:name)
1251
+ #=> [:clean]
1252
+
1253
+ # show all possible events except a specific one
1254
+ job.aasm.events(reject: :sleep).map(&:name)
1255
+ #=> [:clean]
729
1256
 
730
1257
  # list states for select
731
1258
  Job.aasm.states_for_select
732
- => [["Sleeping", "sleeping"], ["Running", "running"], ["Cleaning", "cleaning"]]
1259
+ #=> [["Sleeping", "sleeping"], ["Running", "running"], ["Cleaning", "cleaning"]]
1260
+
1261
+ # show permitted states with guard parameter
1262
+ job.aasm.states({permitted: true}, guard_parameter).map(&:name)
1263
+ ```
1264
+
1265
+
1266
+ ### Warning output
1267
+
1268
+ Warnings are by default printed to `STDERR`. If you want to log those warnings to another output,
1269
+ use
1270
+
1271
+ ```ruby
1272
+ class Job
1273
+ include AASM
1274
+
1275
+ aasm logger: Rails.logger do
1276
+ ...
1277
+ end
1278
+ end
733
1279
  ```
734
1280
 
1281
+ You can hide warnings by setting `AASM::Configuration.hide_warnings = true`
1282
+
1283
+ ### RubyMotion support
1284
+
1285
+ Now supports [CodeDataQuery](https://github.com/infinitered/cdq.git) !
1286
+ However I'm still in the process of submitting my compatibility updates to their repository.
1287
+ In the meantime you can use [my fork](https://github.com/Infotaku/cdq.git), there may still be some minor issues but I intend to extensively use it myself, so fixes should come fast.
1288
+
1289
+ Warnings:
1290
+ - Due to RubyMotion Proc's lack of 'source_location' method, it may be harder
1291
+ to find out the origin of a "cannot transition from" error. I would recommend using
1292
+ the 'instance method symbol / string' way whenever possible when defining guardians and callbacks.
1293
+
735
1294
 
736
1295
  ### Testing
737
1296
 
738
- AASM provides some matchers for [RSpec](http://rspec.info): `transition_from`, `have_state`, `allow_event` and `allow_transition_to`. Add `require 'aasm/rspec'` to your `spec_helper.rb` file and use them like this
1297
+ #### RSpec
1298
+
1299
+ AASM provides some matchers for [RSpec](http://rspec.info):
1300
+ * `transition_from`,
1301
+ * `have_state`, `allow_event`
1302
+ * and `allow_transition_to`.
1303
+
1304
+ ##### Installation Instructions:
1305
+ * Add `require 'aasm/rspec'` to your `spec_helper.rb` file.
1306
+
1307
+ ##### Examples Of Usage in Rspec:
739
1308
 
740
1309
  ```ruby
741
1310
  # classes with only the default state machine
@@ -748,6 +1317,8 @@ expect(job).to allow_event :run
748
1317
  expect(job).to_not allow_event :clean
749
1318
  expect(job).to allow_transition_to(:running)
750
1319
  expect(job).to_not allow_transition_to(:cleaning)
1320
+ # on_event also accept multiple arguments
1321
+ expect(job).to transition_from(:sleeping).to(:running).on_event(:run, :defragmentation)
751
1322
 
752
1323
  # classes with multiple state machine
753
1324
  multiple = SimpleMultipleExample.new
@@ -767,6 +1338,96 @@ expect(multiple).to allow_event(:start).on(:move)
767
1338
  expect(multiple).to_not allow_event(:stop).on(:move)
768
1339
  expect(multiple).to allow_transition_to(:processing).on(:move)
769
1340
  expect(multiple).to_not allow_transition_to(:sleeping).on(:move)
1341
+ # allow_event also accepts arguments
1342
+ expect(job).to allow_event(:run).with(:defragmentation)
1343
+
1344
+ ```
1345
+
1346
+ #### Minitest
1347
+
1348
+ AASM provides assertions and rspec-like expectations for [Minitest](https://github.com/seattlerb/minitest).
1349
+
1350
+ ##### Assertions
1351
+
1352
+ List of supported assertions: `assert_have_state`, `refute_have_state`, `assert_transitions_from`, `refute_transitions_from`, `assert_event_allowed`, `refute_event_allowed`, `assert_transition_to_allowed`, `refute_transition_to_allowed`.
1353
+
1354
+
1355
+ ##### Examples Of Usage (Minitest):
1356
+
1357
+ Add `require 'aasm/minitest'` to your `test_helper.rb` file and use them like this:
1358
+
1359
+ ```ruby
1360
+ # classes with only the default state machine
1361
+ job = Job.new
1362
+ assert_transitions_from job, :sleeping, to: :running, on_event: :run
1363
+ refute_transitions_from job, :sleeping, to: :cleaning, on_event: :run
1364
+ assert_have_state job, :sleeping
1365
+ refute_have_state job, :running
1366
+ assert_event_allowed job, :run
1367
+ refute_event_allowed job, :clean
1368
+ assert_transition_to_allowed job, :running
1369
+ refute_transition_to_allowed job, :cleaning
1370
+ # on_event also accept arguments
1371
+ assert_transitions_from job, :sleeping, :defragmentation, to: :running, on_event: :run
1372
+
1373
+ # classes with multiple state machine
1374
+ multiple = SimpleMultipleExample.new
1375
+ assert_transitions_from multiple, :standing, to: :walking, on_event: :walk, on: :move
1376
+ refute_transitions_from multiple, :standing, to: :running, on_event: :walk, on: :move
1377
+ assert_have_state multiple, :standing, on: :move
1378
+ refute_have_state multiple, :walking, on: :move
1379
+ assert_event_allowed multiple, :walk, on: :move
1380
+ refute_event_allowed multiple, :hold, on: :move
1381
+ assert_transition_to_allowed multiple, :walking, on: :move
1382
+ refute_transition_to_allowed multiple, :running, on: :move
1383
+ assert_transitions_from multiple, :sleeping, to: :processing, on_event: :start, on: :work
1384
+ refute_transitions_from multiple, :sleeping, to: :sleeping, on_event: :start, on: :work
1385
+ assert_have_state multiple, :sleeping, on: :work
1386
+ refute_have_state multiple, :processing, on: :work
1387
+ assert_event_allowed multiple, :start, on: :move
1388
+ refute_event_allowed multiple, :stop, on: :move
1389
+ assert_transition_to_allowed multiple, :processing, on: :move
1390
+ refute_transition_to_allowed multiple, :sleeping, on: :move
1391
+ ```
1392
+
1393
+ ##### Expectations
1394
+
1395
+ List of supported expectations: `must_transition_from`, `wont_transition_from`, `must_have_state`, `wont_have_state`, `must_allow_event`, `wont_allow_event`, `must_allow_transition_to`, `wont_allow_transition_to`.
1396
+
1397
+ Add `require 'aasm/minitest_spec'` to your `test_helper.rb` file and use them like this:
1398
+
1399
+ ```ruby
1400
+ # classes with only the default state machine
1401
+ job = Job.new
1402
+ job.must_transition_from :sleeping, to: :running, on_event: :run
1403
+ job.wont_transition_from :sleeping, to: :cleaning, on_event: :run
1404
+ job.must_have_state :sleeping
1405
+ job.wont_have_state :running
1406
+ job.must_allow_event :run
1407
+ job.wont_allow_event :clean
1408
+ job.must_allow_transition_to :running
1409
+ job.wont_allow_transition_to :cleaning
1410
+ # on_event also accept arguments
1411
+ job.must_transition_from :sleeping, :defragmentation, to: :running, on_event: :run
1412
+
1413
+ # classes with multiple state machine
1414
+ multiple = SimpleMultipleExample.new
1415
+ multiple.must_transition_from :standing, to: :walking, on_event: :walk, on: :move
1416
+ multiple.wont_transition_from :standing, to: :running, on_event: :walk, on: :move
1417
+ multiple.must_have_state :standing, on: :move
1418
+ multiple.wont_have_state :walking, on: :move
1419
+ multiple.must_allow_event :walk, on: :move
1420
+ multiple.wont_allow_event :hold, on: :move
1421
+ multiple.must_allow_transition_to :walking, on: :move
1422
+ multiple.wont_allow_transition_to :running, on: :move
1423
+ multiple.must_transition_from :sleeping, to: :processing, on_event: :start, on: :work
1424
+ multiple.wont_transition_from :sleeping, to: :sleeping, on_event: :start, on: :work
1425
+ multiple.must_have_state :sleeping, on: :work
1426
+ multiple.wont_have_state :processing, on: :work
1427
+ multiple.must_allow_event :start, on: :move
1428
+ multiple.wont_allow_event :stop, on: :move
1429
+ multiple.must_allow_transition_to :processing, on: :move
1430
+ multiple.wont_allow_transition_to :sleeping, on: :move
770
1431
  ```
771
1432
 
772
1433
  ## <a id="installation">Installation ##
@@ -791,6 +1452,25 @@ gem 'aasm'
791
1452
  % sudo gem install pkg/aasm-x.y.z.gem
792
1453
  ```
793
1454
 
1455
+ ### Generators
1456
+
1457
+ After installing AASM you can run generator:
1458
+
1459
+ ```sh
1460
+ % rails generate aasm NAME [COLUMN_NAME]
1461
+ ```
1462
+ Replace NAME with the Model name, COLUMN_NAME is optional(default is 'aasm_state').
1463
+ This will create a model (if one does not exist) and configure it with aasm block.
1464
+ For ActiveRecord orm a migration file is added to add aasm state column to table.
1465
+
1466
+ ### Docker
1467
+
1468
+ Run test suite easily on docker
1469
+ ```
1470
+ 1. docker-compose build aasm
1471
+ 2. docker-compose run --rm aasm
1472
+ ```
1473
+
794
1474
  ## Latest changes ##
795
1475
 
796
1476
  Take a look at the [CHANGELOG](https://github.com/aasm/aasm/blob/master/CHANGELOG.md) for details about recent changes to the current version.
@@ -808,16 +1488,16 @@ Feel free to
808
1488
  * [Scott Barron](https://github.com/rubyist) (2006–2009, original author)
809
1489
  * [Travis Tilley](https://github.com/ttilley) (2009–2011)
810
1490
  * [Thorsten Böttger](http://github.com/alto) (since 2011)
1491
+ * [Anil Maurya](http://github.com/anilmaurya) (since 2016)
1492
+
1493
+
1494
+
1495
+ ## Stargazers over time
811
1496
 
1497
+ [![Stargazers over time](https://starchart.cc/aasm/aasm.svg)](https://starchart.cc/aasm/aasm)
812
1498
 
813
- ## Contributing ##
814
1499
 
815
- 1. Read the [Contributor Code of Conduct](https://github.com/aasm/aasm/blob/master/CODE_OF_CONDUCT.md)
816
- 2. Fork it
817
- 3. Create your feature branch (git checkout -b my-new-feature)
818
- 4. Commit your changes (git commit -am 'Added some feature')
819
- 5. Push to the branch (git push origin my-new-feature)
820
- 6. Create new Pull Request
1500
+ ## [Contributing](CONTRIBUTING.md)
821
1501
 
822
1502
  ## Warranty ##
823
1503
 
@@ -828,7 +1508,7 @@ purpose.
828
1508
 
829
1509
  ## License ##
830
1510
 
831
- Copyright (c) 2006-2015 Scott Barron
1511
+ Copyright (c) 2006-2017 Scott Barron
832
1512
 
833
1513
  Permission is hereby granted, free of charge, to any person obtaining
834
1514
  a copy of this software and associated documentation files (the