aasm 4.11.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.travis.yml +56 -23
  5. data/Appraisals +67 -0
  6. data/CHANGELOG.md +112 -0
  7. data/CONTRIBUTING.md +24 -0
  8. data/Dockerfile +44 -0
  9. data/Gemfile +3 -21
  10. data/Gemfile.lock_old +151 -0
  11. data/LICENSE +1 -1
  12. data/README.md +540 -139
  13. data/Rakefile +6 -1
  14. data/TESTING.md +25 -0
  15. data/aasm.gemspec +5 -0
  16. data/docker-compose.yml +40 -0
  17. data/gemfiles/norails.gemfile +10 -0
  18. data/gemfiles/rails_4.2.gemfile +13 -11
  19. data/gemfiles/rails_4.2_mongoid_5.gemfile +8 -11
  20. data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
  21. data/gemfiles/rails_5.0.gemfile +11 -18
  22. data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
  23. data/gemfiles/rails_5.1.gemfile +14 -0
  24. data/gemfiles/rails_5.2.gemfile +14 -0
  25. data/lib/aasm/aasm.rb +40 -29
  26. data/lib/aasm/base.rb +61 -11
  27. data/lib/aasm/configuration.rb +10 -0
  28. data/lib/aasm/core/event.rb +45 -37
  29. data/lib/aasm/core/invoker.rb +129 -0
  30. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  31. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  32. data/lib/aasm/core/invokers/literal_invoker.rb +47 -0
  33. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  34. data/lib/aasm/core/state.rb +22 -13
  35. data/lib/aasm/core/transition.rb +17 -69
  36. data/lib/aasm/dsl_helper.rb +24 -22
  37. data/lib/aasm/errors.rb +4 -6
  38. data/lib/aasm/instance_base.rb +22 -4
  39. data/lib/aasm/localizer.rb +13 -3
  40. data/lib/aasm/minitest/allow_event.rb +13 -0
  41. data/lib/aasm/minitest/allow_transition_to.rb +13 -0
  42. data/lib/aasm/minitest/have_state.rb +13 -0
  43. data/lib/aasm/minitest/transition_from.rb +21 -0
  44. data/lib/aasm/minitest.rb +5 -0
  45. data/lib/aasm/minitest_spec.rb +15 -0
  46. data/lib/aasm/persistence/active_record_persistence.rb +49 -105
  47. data/lib/aasm/persistence/base.rb +20 -5
  48. data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
  49. data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
  50. data/lib/aasm/persistence/mongoid_persistence.rb +26 -32
  51. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  52. data/lib/aasm/persistence/orm.rb +154 -0
  53. data/lib/aasm/persistence/plain_persistence.rb +2 -1
  54. data/lib/aasm/persistence/redis_persistence.rb +16 -11
  55. data/lib/aasm/persistence/sequel_persistence.rb +36 -64
  56. data/lib/aasm/persistence.rb +3 -3
  57. data/lib/aasm/rspec/allow_event.rb +5 -1
  58. data/lib/aasm/rspec/allow_transition_to.rb +5 -1
  59. data/lib/aasm/rspec/transition_from.rb +5 -1
  60. data/lib/aasm/state_machine.rb +4 -2
  61. data/lib/aasm/state_machine_store.rb +5 -2
  62. data/lib/aasm/version.rb +1 -1
  63. data/lib/aasm.rb +5 -2
  64. data/lib/generators/aasm/orm_helpers.rb +6 -0
  65. data/lib/generators/active_record/aasm_generator.rb +3 -1
  66. data/lib/generators/active_record/templates/migration.rb +1 -1
  67. data/lib/generators/active_record/templates/migration_existing.rb +1 -1
  68. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  69. data/lib/motion-aasm.rb +3 -1
  70. data/spec/database.rb +20 -7
  71. data/spec/en.yml +0 -3
  72. data/spec/generators/active_record_generator_spec.rb +49 -40
  73. data/spec/generators/mongoid_generator_spec.rb +4 -6
  74. data/spec/generators/no_brainer_generator_spec.rb +29 -0
  75. data/spec/{en_deprecated_style.yml → localizer_test_model_deprecated_style.yml} +6 -3
  76. data/spec/localizer_test_model_new_style.yml +11 -0
  77. data/spec/models/active_record/active_record_callback.rb +93 -0
  78. data/spec/models/active_record/complex_active_record_example.rb +5 -1
  79. data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
  80. data/spec/models/{invalid_persistor.rb → active_record/invalid_persistor.rb} +0 -2
  81. data/spec/models/active_record/localizer_test_model.rb +11 -3
  82. data/spec/models/active_record/namespaced.rb +16 -0
  83. data/spec/models/active_record/person.rb +23 -0
  84. data/spec/models/{silent_persistor.rb → active_record/silent_persistor.rb} +0 -2
  85. data/spec/models/active_record/simple_new_dsl.rb +15 -0
  86. data/spec/models/active_record/timestamp_example.rb +16 -0
  87. data/spec/models/{transactor.rb → active_record/transactor.rb} +25 -2
  88. data/spec/models/{validator.rb → active_record/validator.rb} +0 -2
  89. data/spec/models/active_record/work.rb +3 -0
  90. data/spec/models/{worker.rb → active_record/worker.rb} +0 -0
  91. data/spec/models/callbacks/basic.rb +5 -2
  92. data/spec/models/callbacks/with_state_arg.rb +5 -1
  93. data/spec/models/callbacks/with_state_arg_multiple.rb +4 -1
  94. data/spec/models/default_state.rb +1 -1
  95. data/spec/models/guard_arguments_check.rb +17 -0
  96. data/spec/models/guard_with_params.rb +1 -1
  97. data/spec/models/guardian_without_from_specified.rb +18 -0
  98. data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
  99. data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
  100. data/spec/models/mongoid/timestamp_example_mongoid.rb +20 -0
  101. data/spec/models/mongoid/validator_mongoid.rb +100 -0
  102. data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
  103. data/spec/models/namespaced_multiple_example.rb +14 -0
  104. data/spec/models/nobrainer/complex_no_brainer_example.rb +36 -0
  105. data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
  106. data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
  107. data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
  108. data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
  109. data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
  110. data/spec/models/{mongo_mapper/simple_mongo_mapper.rb → nobrainer/simple_no_brainer.rb} +8 -8
  111. data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
  112. data/spec/models/parametrised_event.rb +7 -0
  113. data/spec/models/{mongo_mapper/complex_mongo_mapper_example.rb → redis/complex_redis_example.rb} +8 -5
  114. data/spec/models/redis/redis_multiple.rb +20 -0
  115. data/spec/models/redis/redis_simple.rb +20 -0
  116. data/spec/models/sequel/complex_sequel_example.rb +4 -3
  117. data/spec/models/sequel/invalid_persistor.rb +52 -0
  118. data/spec/models/sequel/sequel_multiple.rb +13 -13
  119. data/spec/models/sequel/sequel_simple.rb +13 -12
  120. data/spec/models/sequel/silent_persistor.rb +50 -0
  121. data/spec/models/sequel/transactor.rb +112 -0
  122. data/spec/models/sequel/validator.rb +93 -0
  123. data/spec/models/sequel/worker.rb +12 -0
  124. data/spec/models/simple_example.rb +8 -0
  125. data/spec/models/simple_example_with_guard_args.rb +17 -0
  126. data/spec/models/simple_multiple_example.rb +12 -0
  127. data/spec/models/sub_class.rb +34 -0
  128. data/spec/models/timestamps_example.rb +19 -0
  129. data/spec/models/timestamps_with_named_machine_example.rb +13 -0
  130. data/spec/spec_helper.rb +15 -33
  131. data/spec/spec_helpers/active_record.rb +8 -0
  132. data/spec/spec_helpers/dynamoid.rb +35 -0
  133. data/spec/spec_helpers/mongoid.rb +26 -0
  134. data/spec/spec_helpers/nobrainer.rb +15 -0
  135. data/spec/spec_helpers/redis.rb +18 -0
  136. data/spec/spec_helpers/remove_warnings.rb +1 -0
  137. data/spec/spec_helpers/sequel.rb +7 -0
  138. data/spec/unit/abstract_class_spec.rb +27 -0
  139. data/spec/unit/api_spec.rb +79 -72
  140. data/spec/unit/callback_multiple_spec.rb +7 -3
  141. data/spec/unit/callbacks_spec.rb +37 -2
  142. data/spec/unit/complex_example_spec.rb +12 -3
  143. data/spec/unit/complex_multiple_example_spec.rb +20 -4
  144. data/spec/unit/event_multiple_spec.rb +1 -1
  145. data/spec/unit/event_spec.rb +29 -4
  146. data/spec/unit/exception_spec.rb +1 -1
  147. data/spec/unit/guard_arguments_check_spec.rb +9 -0
  148. data/spec/unit/guard_spec.rb +17 -0
  149. data/spec/unit/guard_with_params_spec.rb +4 -0
  150. data/spec/unit/guard_without_from_specified_spec.rb +10 -0
  151. data/spec/unit/inspection_multiple_spec.rb +9 -5
  152. data/spec/unit/inspection_spec.rb +7 -3
  153. data/spec/unit/invoker_spec.rb +189 -0
  154. data/spec/unit/invokers/base_invoker_spec.rb +72 -0
  155. data/spec/unit/invokers/class_invoker_spec.rb +95 -0
  156. data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
  157. data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
  158. data/spec/unit/localizer_spec.rb +85 -52
  159. data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
  160. data/spec/unit/namespaced_multiple_example_spec.rb +22 -0
  161. data/spec/unit/override_warning_spec.rb +8 -0
  162. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +468 -447
  163. data/spec/unit/persistence/active_record_persistence_spec.rb +639 -486
  164. data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +4 -9
  165. data/spec/unit/persistence/dynamoid_persistence_spec.rb +4 -9
  166. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +83 -13
  167. data/spec/unit/persistence/mongoid_persistence_spec.rb +97 -13
  168. data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
  169. data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
  170. data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
  171. data/spec/unit/persistence/redis_persistence_spec.rb +8 -32
  172. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +6 -11
  173. data/spec/unit/persistence/sequel_persistence_spec.rb +278 -10
  174. data/spec/unit/rspec_matcher_spec.rb +9 -0
  175. data/spec/unit/simple_example_spec.rb +15 -0
  176. data/spec/unit/simple_multiple_example_spec.rb +28 -0
  177. data/spec/unit/state_spec.rb +23 -7
  178. data/spec/unit/subclassing_multiple_spec.rb +37 -2
  179. data/spec/unit/subclassing_spec.rb +17 -2
  180. data/spec/unit/timestamps_spec.rb +32 -0
  181. data/spec/unit/transition_spec.rb +1 -1
  182. data/test/minitest_helper.rb +57 -0
  183. data/test/unit/minitest_matcher_test.rb +80 -0
  184. metadata +213 -37
  185. data/callbacks.txt +0 -51
  186. data/gemfiles/rails_3.2_stable.gemfile +0 -15
  187. data/gemfiles/rails_4.0.gemfile +0 -16
  188. data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -16
  189. data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -17
  190. data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -163
  191. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
  192. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
  193. data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -149
  194. data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -96
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,19 +73,19 @@ class Job
27
73
  include AASM
28
74
 
29
75
  aasm do
30
- state :sleeping, :initial => true
76
+ state :sleeping, initial: true
31
77
  state :running, :cleaning
32
78
 
33
79
  event :run do
34
- transitions :from => :sleeping, :to => :running
80
+ transitions from: :sleeping, to: :running
35
81
  end
36
82
 
37
83
  event :clean do
38
- transitions :from => :running, :to => :cleaning
84
+ transitions from: :running, to: :cleaning
39
85
  end
40
86
 
41
87
  event :sleep do
42
- transitions :from => [:running, :cleaning], :to => :sleeping
88
+ transitions from: [:running, :cleaning], to: :sleeping
43
89
  end
44
90
  end
45
91
 
@@ -65,7 +111,7 @@ AASM not to be *whiny*:
65
111
  ```ruby
66
112
  class Job
67
113
  ...
68
- aasm :whiny_transitions => false do
114
+ aasm whiny_transitions: false do
69
115
  ...
70
116
  end
71
117
  end
@@ -86,27 +132,27 @@ the transition succeeds :
86
132
 
87
133
  ### Callbacks
88
134
 
89
- You can define a number of callbacks for your transitions. These methods will be
90
- 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:
91
137
 
92
138
  ```ruby
93
139
  class Job
94
140
  include AASM
95
141
 
96
142
  aasm do
97
- state :sleeping, :initial => true, :before_enter => :do_something
98
- state :running
143
+ state :sleeping, initial: true, before_enter: :do_something
144
+ state :running, before_enter: Proc.new { do_something && notify_somebody }
99
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) }
109
- transitions :from => :running, :to => :finished, :after => LogRunTime
154
+ transitions from: :sleeping, to: :running, after: Proc.new {|*args| set_process(*args) }
155
+ transitions from: :running, to: :finished, after: LogRunTime
110
156
  end
111
157
 
112
158
  event :sleep do
@@ -116,7 +162,7 @@ class Job
116
162
  error do |e|
117
163
  ...
118
164
  end
119
- transitions :from => :running, :to => :sleeping
165
+ transitions from: :running, to: :sleeping
120
166
  end
121
167
  end
122
168
 
@@ -151,6 +197,8 @@ is finished.
151
197
 
152
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:
153
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.
201
+
154
202
  ```ruby
155
203
  class LogRunTime
156
204
  # optional args parameter can be omitted, but if you define initialize
@@ -165,6 +213,51 @@ class LogRunTime
165
213
  end
166
214
  ```
167
215
 
216
+ #### Parameters
217
+ You can pass parameters to events:
218
+
219
+ ```ruby
220
+ job = Job.new
221
+ job.run(:defragmentation)
222
+ ```
223
+
224
+ All guards and after callbacks will receive these parameters. In this case `set_process` would be called with
225
+ `:defragmentation` argument.
226
+
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 gaurds and callbacks
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
238
+ In case of an error during the event processing the error is rescued and passed to `:error`
239
+ callback, which can handle it or re-raise it for further propagation.
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
+
249
+ During the transition's `:after` callback (and reliably only then, or in the global
250
+ `after_all_transitions` callback) you can access the originating state (the from-state)
251
+ and the target state (the to state), like this:
252
+
253
+ ```ruby
254
+ def set_process(name)
255
+ logger.info "from #{aasm.from_state} to #{aasm.to_state}"
256
+ end
257
+ ```
258
+
259
+ #### Lifecycle
260
+
168
261
  Here you can see a list of all possible callbacks, together with their order of calling:
169
262
 
170
263
  ```ruby
@@ -180,8 +273,9 @@ begin
180
273
  new_state before_enter
181
274
  new_state enter
182
275
  ...update state...
183
- transition success # if persist successful
184
- event success # if persist successful
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
185
279
  old_state after_exit
186
280
  new_state after_enter
187
281
  event after
@@ -195,29 +289,7 @@ ensure
195
289
  end
196
290
  ```
197
291
 
198
- Also, you can pass parameters to events:
199
-
200
- ```ruby
201
- job = Job.new
202
- job.run(:running, :defragmentation)
203
- ```
204
-
205
- In this case the `set_process` would be called with `:defragmentation` argument.
206
-
207
- 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.
208
-
209
- In case of an error during the event processing the error is rescued and passed to `:error`
210
- callback, which can handle it or re-raise it for further propagation.
211
-
212
- During the transition's `:after` callback (and reliably only then, or in the global
213
- `after_all_transitions` callback) you can access the originating state (the from-state)
214
- and the target state (the to state), like this:
215
-
216
- ```ruby
217
- def set_process(name)
218
- logger.info "from #{aasm.from_state} to #{aasm.to_state}"
219
- end
220
- ```
292
+ Use event's `after_commit` callback if it should be fired after database update.
221
293
 
222
294
  #### The current event triggered
223
295
 
@@ -256,26 +328,34 @@ class Cleaner
256
328
  include AASM
257
329
 
258
330
  aasm do
259
- state :idle, :initial => true
331
+ state :idle, initial: true
260
332
  state :cleaning
261
333
 
262
334
  event :clean do
263
- transitions :from => :idle, :to => :cleaning, :guard => :cleaning_needed?
335
+ transitions from: :idle, to: :cleaning, guard: :cleaning_needed?
264
336
  end
265
337
 
266
338
  event :clean_if_needed do
267
- transitions :from => :idle, :to => :cleaning do
339
+ transitions from: :idle, to: :cleaning do
268
340
  guard do
269
341
  cleaning_needed?
270
342
  end
271
343
  end
272
- 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?
273
349
  end
274
350
  end
275
351
 
276
352
  def cleaning_needed?
277
353
  false
278
354
  end
355
+
356
+ def if_dirty?(status)
357
+ status == :dirty
358
+ end
279
359
  end
280
360
 
281
361
  job = Cleaner.new
@@ -283,6 +363,9 @@ job.may_clean? # => false
283
363
  job.clean # => raises AASM::InvalidTransition
284
364
  job.may_clean_if_needed? # => true
285
365
  job.clean_if_needed! # idle
366
+
367
+ job.clean_if_dirty(:clean) # => false
368
+ job.clean_if_dirty(:dirty) # => true
286
369
  ```
287
370
 
288
371
  You can even provide a number of guards, which all have to succeed to proceed
@@ -291,16 +374,16 @@ You can even provide a number of guards, which all have to succeed to proceed
291
374
  def walked_the_dog?; ...; end
292
375
 
293
376
  event :sleep do
294
- transitions :from => :running, :to => :sleeping, :guards => [:cleaning_needed?, :walked_the_dog?]
377
+ transitions from: :running, to: :sleeping, guards: [:cleaning_needed?, :walked_the_dog?]
295
378
  end
296
379
  ```
297
380
 
298
381
  If you want to provide guards for all transitions within an event, you can use event guards
299
382
 
300
383
  ```ruby
301
- event :sleep, :guards => [:walked_the_dog?] do
302
- transitions :from => :running, :to => :sleeping, :guards => [:cleaning_needed?]
303
- 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
304
387
  end
305
388
  ```
306
389
 
@@ -308,15 +391,30 @@ If you prefer a more Ruby-like guard syntax, you can use `if` and `unless` as we
308
391
 
309
392
  ```ruby
310
393
  event :clean do
311
- transitions :from => :running, :to => :cleaning, :if => :cleaning_needed?
394
+ transitions from: :running, to: :cleaning, if: :cleaning_needed?
312
395
  end
313
396
 
314
397
  event :sleep do
315
- transitions :from => :running, :to => :sleeping, :unless => :cleaning_needed?
398
+ transitions from: :running, to: :sleeping, unless: :cleaning_needed?
316
399
  end
317
400
  end
318
401
  ```
319
402
 
403
+ You can invoke a Class instead a method since this 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
+ ```
320
418
 
321
419
  ### Transitions
322
420
 
@@ -329,7 +427,7 @@ class Job
329
427
  include AASM
330
428
 
331
429
  aasm do
332
- state :stage1, :initial => true
430
+ state :stage1, initial: true
333
431
  state :stage2
334
432
  state :stage3
335
433
  state :completed
@@ -350,40 +448,67 @@ job.stage1_completed
350
448
  job.aasm.current_state # stage3
351
449
  ```
352
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
+ ```
353
478
 
354
479
  ### Multiple state machines per class
355
480
 
356
481
  Multiple state machines per class are supported. Be aware though that _AASM_ has been
357
- 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.
358
483
 
359
484
  ```ruby
360
485
  class SimpleMultipleExample
361
486
  include AASM
362
- aasm(:move) do
363
- state :standing, :initial => true
487
+ aasm(:move, column: 'move_state') do
488
+ state :standing, initial: true
364
489
  state :walking
365
490
  state :running
366
491
 
367
492
  event :walk do
368
- transitions :from => :standing, :to => :walking
493
+ transitions from: :standing, to: :walking
369
494
  end
370
495
  event :run do
371
- transitions :from => [:standing, :walking], :to => :running
496
+ transitions from: [:standing, :walking], to: :running
372
497
  end
373
498
  event :hold do
374
- transitions :from => [:walking, :running], :to => :standing
499
+ transitions from: [:walking, :running], to: :standing
375
500
  end
376
501
  end
377
502
 
378
- aasm(:work) do
379
- state :sleeping, :initial => true
503
+ aasm(:work, column: 'work_state') do
504
+ state :sleeping, initial: true
380
505
  state :processing
381
506
 
382
507
  event :start do
383
- transitions :from => :sleeping, :to => :processing
508
+ transitions from: :sleeping, to: :processing
384
509
  end
385
510
  event :stop do
386
- transitions :from => :processing, :to => :sleeping
511
+ transitions from: :processing, to: :sleeping
387
512
  end
388
513
  end
389
514
  end
@@ -392,31 +517,114 @@ simple = SimpleMultipleExample.new
392
517
 
393
518
  simple.aasm(:move).current_state
394
519
  # => :standing
395
- simple.aasm(:work).current
520
+ simple.aasm(:work).current_state
396
521
  # => :sleeping
397
522
 
398
523
  simple.start
399
524
  simple.aasm(:move).current_state
400
525
  # => :standing
401
- simple.aasm(:work).current
526
+ simple.aasm(:work).current_state
402
527
  # => :processing
403
528
 
404
529
  ```
405
530
 
406
- _AASM_ doesn't prohibit to define the same event in more than one state machine. The
407
- latest definition "wins" and overrides previous definitions. Nonetheless, a warning is issued:
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:
408
536
  `SimpleMultipleExample: overriding method 'run'!`.
409
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
+ ```
580
+
410
581
  All _AASM_ class- and instance-level `aasm` methods accept a state machine selector.
411
582
  So, for example, to use inspection on a class level, you have to use
412
583
 
413
584
  ```ruby
414
- SimpleMultipleExample.aasm(:work).states
585
+ SimpleMultipleExample.aasm(:move).states.map(&:name)
415
586
  # => [:standing, :walking, :running]
416
587
  ```
417
588
 
418
- *Final note*: Support for multiple state machines per class is a pretty new feature
419
- (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
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
+ ```
420
628
 
421
629
  ### Auto-generated Status Constants
422
630
 
@@ -451,7 +659,7 @@ class CustomAASMBase < AASM::Base
451
659
  # A custom transiton that we want available across many AASM models.
452
660
  def count_transitions!
453
661
  klass.class_eval do
454
- aasm :with_klass => CustomAASMBase do
662
+ aasm with_klass: CustomAASMBase do
455
663
  after_all_transitions :increment_transition_count
456
664
  end
457
665
  end
@@ -481,26 +689,26 @@ class CustomAASMBase < AASM::Base
481
689
  end
482
690
  ```
483
691
 
484
- When we declare our model that has an AASM state machine, we simply declare the AASM block with a `:with` key to our own class.
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.
485
693
 
486
694
  ```ruby
487
695
  class SimpleCustomExample
488
696
  include AASM
489
697
 
490
698
  # Let's build an AASM state machine with our custom class.
491
- aasm :with_klass => CustomAASMBase do
699
+ aasm with_klass: CustomAASMBase do
492
700
  requires_guards!
493
701
  count_transitions!
494
702
 
495
- state :initialised, :initial => true
703
+ state :initialised, initial: true
496
704
  state :filled_out
497
705
  state :authorised
498
706
 
499
707
  event :fill_out do
500
- transitions :from => :initialised, :to => :filled_out, :guard => :fillable?
708
+ transitions from: :initialised, to: :filled_out, guard: :fillable?
501
709
  end
502
710
  event :authorise do
503
- transitions :from => :filled_out, :to => :authorised, :guard => :authorizable?
711
+ transitions from: :filled_out, to: :authorised, guard: :authorizable?
504
712
  end
505
713
  end
506
714
  end
@@ -512,36 +720,46 @@ end
512
720
  AASM comes with support for ActiveRecord and allows automatic persisting of the object's
513
721
  state in the database.
514
722
 
723
+ Add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile.
724
+
515
725
  ```ruby
516
726
  class Job < ActiveRecord::Base
517
727
  include AASM
518
728
 
519
729
  aasm do # default column: aasm_state
520
- state :sleeping, :initial => true
730
+ state :sleeping, initial: true
521
731
  state :running
522
732
 
523
733
  event :run do
524
- transitions :from => :sleeping, :to => :running
734
+ transitions from: :sleeping, to: :running
525
735
  end
526
736
 
527
737
  event :sleep do
528
- transitions :from => :running, :to => :sleeping
738
+ transitions from: :running, to: :sleeping
529
739
  end
530
740
  end
531
741
 
532
742
  end
533
743
  ```
534
744
 
745
+ ### Bang events
746
+
535
747
  You can tell AASM to auto-save the object or leave it unsaved
536
748
 
537
749
  ```ruby
538
750
  job = Job.new
539
751
  job.run # not saved
540
752
  job.run! # saved
753
+
754
+ # or
755
+ job.aasm.fire(:run) # not saved
756
+ job.aasm.fire!(:run) # saved
541
757
  ```
542
758
 
543
- Saving includes running all validations on the `Job` class, and returns `true` if
544
- successful or `false` if errors occur. Exceptions are not raised.
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.
545
763
 
546
764
  If you want make sure the state gets saved without running validations (and
547
765
  thereby maybe persisting an invalid object state), simply tell AASM to skip the
@@ -552,22 +770,30 @@ be updated in the database (just like ActiveRecord `update_column` is working).
552
770
  class Job < ActiveRecord::Base
553
771
  include AASM
554
772
 
555
- aasm :skip_validation_on_save => true do
556
- state :sleeping, :initial => true
773
+ aasm skip_validation_on_save: true do
774
+ state :sleeping, initial: true
557
775
  state :running
558
776
 
559
777
  event :run do
560
- transitions :from => :sleeping, :to => :running
778
+ transitions from: :sleeping, to: :running
561
779
  end
562
780
 
563
781
  event :sleep do
564
- transitions :from => :running, :to => :sleeping
782
+ transitions from: :running, to: :sleeping
565
783
  end
566
784
  end
567
785
 
568
786
  end
569
787
  ```
570
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
+
571
797
  If you want to make sure that the _AASM_ column for storing the state is not directly assigned,
572
798
  configure _AASM_ to not allow direct assignment, like this:
573
799
 
@@ -575,12 +801,12 @@ configure _AASM_ to not allow direct assignment, like this:
575
801
  class Job < ActiveRecord::Base
576
802
  include AASM
577
803
 
578
- aasm :no_direct_assignment => true do
579
- state :sleeping, :initial => true
804
+ aasm no_direct_assignment: true do
805
+ state :sleeping, initial: true
580
806
  state :running
581
807
 
582
808
  event :run do
583
- transitions :from => :sleeping, :to => :running
809
+ transitions from: :sleeping, to: :running
584
810
  end
585
811
  end
586
812
 
@@ -596,6 +822,37 @@ job.aasm_state = :running # => raises AASM::NoDirectAssignmentError
596
822
  job.aasm_state # => 'sleeping'
597
823
  ```
598
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
+
599
856
  #### ActiveRecord enums
600
857
 
601
858
  You can use
@@ -611,8 +868,8 @@ class Job < ActiveRecord::Base
611
868
  running: 99
612
869
  }
613
870
 
614
- aasm :column => :state, :enum => true do
615
- state :sleeping, :initial => true
871
+ aasm column: :state, enum: true do
872
+ state :sleeping, initial: true
616
873
  state :running
617
874
  end
618
875
  end
@@ -632,7 +889,7 @@ to ```false```.
632
889
 
633
890
  ### Sequel
634
891
 
635
- 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_.
636
893
 
637
894
  ```ruby
638
895
  class Job < Sequel::Model
@@ -668,17 +925,17 @@ class Job
668
925
  end
669
926
  ```
670
927
 
671
- ### MongoMapper
928
+ ### NoBrainer
672
929
 
673
- AASM also supports persistence to Mongodb if you're using MongoMapper. Make sure
674
- 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.
675
933
 
676
934
  ```ruby
677
935
  class Job
678
- include MongoMapper::Document
936
+ include NoBrainer::Document
679
937
  include AASM
680
-
681
- key :aasm_state, Symbol
938
+ field :aasm_state
682
939
  aasm do
683
940
  ...
684
941
  end
@@ -687,8 +944,10 @@ end
687
944
 
688
945
  ### Redis
689
946
 
690
- AASM also supports persistence in Redis.
691
- Make sure to include Redis::Objects before you include AASM.
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.
692
951
 
693
952
  ```ruby
694
953
  class User
@@ -709,7 +968,7 @@ class Job < ActiveRecord::Base
709
968
  include AASM
710
969
 
711
970
  aasm do
712
- state :sleeping, :initial => true
971
+ state :sleeping, initial: true
713
972
  state :running
714
973
  state :cleaning
715
974
  end
@@ -738,8 +997,8 @@ defining the `AASM` states, like this:
738
997
  class Job < ActiveRecord::Base
739
998
  include AASM
740
999
 
741
- aasm :create_scopes => false do
742
- state :sleeping, :initial => true
1000
+ aasm create_scopes: false do
1001
+ state :sleeping, initial: true
743
1002
  state :running
744
1003
  state :cleaning
745
1004
  end
@@ -772,11 +1031,11 @@ class Job < ActiveRecord::Base
772
1031
  include AASM
773
1032
 
774
1033
  aasm do
775
- state :sleeping, :initial => true
1034
+ state :sleeping, initial: true
776
1035
  state :running
777
1036
 
778
- event :run, :after_commit => :notify_about_running_job do
779
- transitions :from => :sleeping, :to => :running
1037
+ event :run, after_commit: :notify_about_running_job do
1038
+ transitions from: :sleeping, to: :running
780
1039
  end
781
1040
  end
782
1041
 
@@ -798,18 +1057,24 @@ job.run
798
1057
  job.save! #notify_about_running_job is not run
799
1058
  ```
800
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
+
801
1066
  If you want to encapsulate state changes within an own transaction, the behavior
802
1067
  of this nested transaction might be confusing. Take a look at
803
1068
  [ActiveRecord Nested Transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html)
804
1069
  if you want to know more about this. Nevertheless, AASM by default requires a new transaction
805
- `transaction(:requires_new => true)`. You can override this behavior by changing
1070
+ `transaction(requires_new: true)`. You can override this behavior by changing
806
1071
  the configuration
807
1072
 
808
1073
  ```ruby
809
1074
  class Job < ActiveRecord::Base
810
1075
  include AASM
811
1076
 
812
- aasm :requires_new_transaction => false do
1077
+ aasm requires_new_transaction: false do
813
1078
  ...
814
1079
  end
815
1080
 
@@ -817,7 +1082,25 @@ class Job < ActiveRecord::Base
817
1082
  end
818
1083
  ```
819
1084
 
820
- 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 active record 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
+ ```
821
1104
 
822
1105
  ### Pessimistic Locking
823
1106
 
@@ -834,7 +1117,7 @@ AASM supports [Active Record pessimistic locking via `with_lock`](http://api.rub
834
1117
  class Job < ActiveRecord::Base
835
1118
  include AASM
836
1119
 
837
- aasm :requires_lock => true do
1120
+ aasm requires_lock: true do
838
1121
  ...
839
1122
  end
840
1123
 
@@ -846,7 +1129,7 @@ end
846
1129
  class Job < ActiveRecord::Base
847
1130
  include AASM
848
1131
 
849
- aasm :requires_lock => 'FOR UPDATE NOWAIT' do
1132
+ aasm requires_lock: 'FOR UPDATE NOWAIT' do
850
1133
  ...
851
1134
  end
852
1135
 
@@ -864,15 +1147,21 @@ this by defining your favorite column name, using `:column` like this:
864
1147
  class Job < ActiveRecord::Base
865
1148
  include AASM
866
1149
 
867
- aasm :column => 'my_state' do
1150
+ aasm column: :my_state do
868
1151
  ...
869
1152
  end
870
1153
 
1154
+ aasm :another_state_machine, column: :second_state do
1155
+ ...
1156
+ end
871
1157
  end
872
1158
  ```
873
1159
 
874
1160
  Whatever column name is used, make sure to add a migration to provide this column
875
- (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.
876
1165
 
877
1166
  ```ruby
878
1167
  class AddJobState < ActiveRecord::Migration
@@ -886,6 +1175,13 @@ class AddJobState < ActiveRecord::Migration
886
1175
  end
887
1176
  ```
888
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
+
889
1185
  ### Inspection
890
1186
 
891
1187
  AASM supports query methods for states and events
@@ -897,19 +1193,19 @@ class Job
897
1193
  include AASM
898
1194
 
899
1195
  aasm do
900
- state :sleeping, :initial => true
1196
+ state :sleeping, initial: true
901
1197
  state :running, :cleaning
902
1198
 
903
1199
  event :run do
904
- transitions :from => :sleeping, :to => :running
1200
+ transitions from: :sleeping, to: :running
905
1201
  end
906
1202
 
907
1203
  event :clean do
908
- transitions :from => :running, :to => :cleaning, :guard => :cleaning_needed?
1204
+ transitions from: :running, to: :cleaning, guard: :cleaning_needed?
909
1205
  end
910
1206
 
911
1207
  event :sleep do
912
- transitions :from => [:running, :cleaning], :to => :sleeping
1208
+ transitions from: [:running, :cleaning], to: :sleeping
913
1209
  end
914
1210
  end
915
1211
 
@@ -921,21 +1217,25 @@ end
921
1217
 
922
1218
  ```ruby
923
1219
  # show all states
924
- Job.aasm.states.map(&:name)
1220
+ Job.aasm.states.map(&:name)
925
1221
  #=> [:sleeping, :running, :cleaning]
926
1222
 
927
1223
  job = Job.new
928
1224
 
929
1225
  # show all permitted states (from initial state)
930
- job.aasm.states(:permitted => true).map(&:name)
1226
+ job.aasm.states(permitted: true).map(&:name)
931
1227
  #=> [:running]
932
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
+
933
1233
  job.run
934
- job.aasm.states(:permitted => true).map(&:name)
1234
+ job.aasm.states(permitted: true).map(&:name)
935
1235
  #=> [:sleeping]
936
1236
 
937
1237
  # show all non permitted states
938
- job.aasm.states(:permitted => false).map(&:name)
1238
+ job.aasm.states(permitted: false).map(&:name)
939
1239
  #=> [:cleaning]
940
1240
 
941
1241
  # show all possible (triggerable) events from the current state
@@ -943,23 +1243,23 @@ job.aasm.events.map(&:name)
943
1243
  #=> [:clean, :sleep]
944
1244
 
945
1245
  # show all permitted events
946
- job.aasm.events(:permitted => true).map(&:name)
1246
+ job.aasm.events(permitted: true).map(&:name)
947
1247
  #=> [:sleep]
948
1248
 
949
1249
  # show all non permitted events
950
- job.aasm.events(:permitted => false).map(&:name)
1250
+ job.aasm.events(permitted: false).map(&:name)
951
1251
  #=> [:clean]
952
1252
 
953
1253
  # show all possible events except a specific one
954
- job.aasm.events(:reject => :sleep).map(&:name)
1254
+ job.aasm.events(reject: :sleep).map(&:name)
955
1255
  #=> [:clean]
956
1256
 
957
1257
  # list states for select
958
1258
  Job.aasm.states_for_select
959
- => [["Sleeping", "sleeping"], ["Running", "running"], ["Cleaning", "cleaning"]]
1259
+ #=> [["Sleeping", "sleeping"], ["Running", "running"], ["Cleaning", "cleaning"]]
960
1260
 
961
1261
  # show permitted states with guard parameter
962
- job.aasm.states({:permitted => true}, guard_parameter).map(&:name)
1262
+ job.aasm.states({permitted: true}, guard_parameter).map(&:name)
963
1263
  ```
964
1264
 
965
1265
 
@@ -972,13 +1272,13 @@ use
972
1272
  class Job
973
1273
  include AASM
974
1274
 
975
- aasm :logger => Rails.logger do
1275
+ aasm logger: Rails.logger do
976
1276
  ...
977
1277
  end
978
1278
  end
979
1279
  ```
980
1280
 
981
- Be aware though, that this is not yet released. It will be part of _AASM_ version `4.11.0`.
1281
+ You can hide warnings by setting `AASM::Configuration.hide_warnings = true`
982
1282
 
983
1283
  ### RubyMotion support
984
1284
 
@@ -994,7 +1294,17 @@ the 'instance method symbol / string' way whenever possible when defining guardi
994
1294
 
995
1295
  ### Testing
996
1296
 
997
- 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:
998
1308
 
999
1309
  ```ruby
1000
1310
  # classes with only the default state machine
@@ -1007,7 +1317,7 @@ expect(job).to allow_event :run
1007
1317
  expect(job).to_not allow_event :clean
1008
1318
  expect(job).to allow_transition_to(:running)
1009
1319
  expect(job).to_not allow_transition_to(:cleaning)
1010
- # on_event also accept arguments
1320
+ # on_event also accept multiple arguments
1011
1321
  expect(job).to transition_from(:sleeping).to(:running).on_event(:run, :defragmentation)
1012
1322
 
1013
1323
  # classes with multiple state machine
@@ -1028,6 +1338,96 @@ expect(multiple).to allow_event(:start).on(:move)
1028
1338
  expect(multiple).to_not allow_event(:stop).on(:move)
1029
1339
  expect(multiple).to allow_transition_to(:processing).on(:move)
1030
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
1031
1431
  ```
1032
1432
 
1033
1433
  ## <a id="installation">Installation ##
@@ -1063,6 +1463,14 @@ Replace NAME with the Model name, COLUMN_NAME is optional(default is 'aasm_state
1063
1463
  This will create a model (if one does not exist) and configure it with aasm block.
1064
1464
  For Active record orm a migration file is added to add aasm state column to table.
1065
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
+
1066
1474
  ## Latest changes ##
1067
1475
 
1068
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.
@@ -1083,14 +1491,7 @@ Feel free to
1083
1491
  * [Anil Maurya](http://github.com/anilmaurya) (since 2016)
1084
1492
 
1085
1493
 
1086
- ## Contributing ##
1087
-
1088
- 1. Read the [Contributor Code of Conduct](https://github.com/aasm/aasm/blob/master/CODE_OF_CONDUCT.md)
1089
- 2. Fork it
1090
- 3. Create your feature branch (git checkout -b my-new-feature)
1091
- 4. Commit your changes (git commit -am 'Added some feature')
1092
- 5. Push to the branch (git push origin my-new-feature)
1093
- 6. Create new Pull Request
1494
+ ## [Contributing](CONTRIBUTING.md)
1094
1495
 
1095
1496
  ## Warranty ##
1096
1497
 
@@ -1101,7 +1502,7 @@ purpose.
1101
1502
 
1102
1503
  ## License ##
1103
1504
 
1104
- Copyright (c) 2006-2016 Scott Barron
1505
+ Copyright (c) 2006-2017 Scott Barron
1105
1506
 
1106
1507
  Permission is hereby granted, free of charge, to any person obtaining
1107
1508
  a copy of this software and associated documentation files (the