aasm 4.12.2 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE +1 -1
- data/README.md +393 -116
- data/lib/aasm/aasm.rb +30 -27
- data/lib/aasm/base.rb +64 -11
- data/lib/aasm/configuration.rb +6 -0
- data/lib/aasm/core/event.rb +26 -30
- data/lib/aasm/core/invoker.rb +129 -0
- data/lib/aasm/core/invokers/base_invoker.rb +75 -0
- data/lib/aasm/core/invokers/class_invoker.rb +52 -0
- data/lib/aasm/core/invokers/literal_invoker.rb +49 -0
- data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
- data/lib/aasm/core/state.rb +16 -14
- data/lib/aasm/core/transition.rb +8 -69
- data/lib/aasm/dsl_helper.rb +24 -22
- data/lib/aasm/errors.rb +5 -3
- data/lib/aasm/instance_base.rb +34 -3
- data/lib/aasm/localizer.rb +13 -3
- data/lib/aasm/persistence/active_record_persistence.rb +25 -5
- data/lib/aasm/persistence/base.rb +14 -3
- data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
- data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
- data/lib/aasm/persistence/mongoid_persistence.rb +1 -1
- data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
- data/lib/aasm/persistence/orm.rb +26 -14
- data/lib/aasm/persistence/plain_persistence.rb +2 -1
- data/lib/aasm/persistence/redis_persistence.rb +1 -1
- data/lib/aasm/persistence/sequel_persistence.rb +0 -1
- data/lib/aasm/persistence.rb +3 -0
- data/lib/aasm/rspec/allow_event.rb +5 -1
- data/lib/aasm/rspec/allow_transition_to.rb +5 -1
- data/lib/aasm/rspec/transition_from.rb +5 -1
- data/lib/aasm/version.rb +1 -1
- data/lib/aasm.rb +5 -2
- data/lib/generators/aasm/orm_helpers.rb +7 -1
- data/lib/generators/active_record/aasm_generator.rb +3 -1
- data/lib/generators/active_record/templates/migration.rb +1 -1
- data/lib/generators/nobrainer/aasm_generator.rb +28 -0
- data/lib/motion-aasm.rb +1 -0
- metadata +42 -343
- data/.document +0 -6
- data/.gitignore +0 -20
- data/.travis.yml +0 -52
- data/API +0 -34
- data/Appraisals +0 -43
- data/CHANGELOG.md +0 -365
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -24
- data/Gemfile +0 -7
- data/HOWTO +0 -12
- data/PLANNED_CHANGES.md +0 -11
- data/README_FROM_VERSION_3_TO_4.md +0 -240
- data/Rakefile +0 -31
- data/TESTING.md +0 -25
- data/aasm.gemspec +0 -35
- data/callbacks.txt +0 -51
- data/gemfiles/rails_3.2.gemfile +0 -13
- data/gemfiles/rails_4.0.gemfile +0 -15
- data/gemfiles/rails_4.2.gemfile +0 -16
- data/gemfiles/rails_4.2_mongoid_5.gemfile +0 -11
- data/gemfiles/rails_5.0.gemfile +0 -14
- data/spec/database.rb +0 -44
- data/spec/database.yml +0 -3
- data/spec/en.yml +0 -12
- data/spec/en_deprecated_style.yml +0 -10
- data/spec/generators/active_record_generator_spec.rb +0 -47
- data/spec/generators/mongoid_generator_spec.rb +0 -31
- data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +0 -25
- data/spec/models/active_record/complex_active_record_example.rb +0 -37
- data/spec/models/active_record/derivate_new_dsl.rb +0 -7
- data/spec/models/active_record/false_state.rb +0 -35
- data/spec/models/active_record/gate.rb +0 -39
- data/spec/models/active_record/invalid_persistor.rb +0 -29
- data/spec/models/active_record/localizer_test_model.rb +0 -34
- data/spec/models/active_record/no_direct_assignment.rb +0 -21
- data/spec/models/active_record/no_scope.rb +0 -21
- data/spec/models/active_record/persisted_state.rb +0 -12
- data/spec/models/active_record/provided_and_persisted_state.rb +0 -24
- data/spec/models/active_record/reader.rb +0 -7
- data/spec/models/active_record/readme_job.rb +0 -21
- data/spec/models/active_record/silent_persistor.rb +0 -29
- data/spec/models/active_record/simple_new_dsl.rb +0 -17
- data/spec/models/active_record/thief.rb +0 -29
- data/spec/models/active_record/transactor.rb +0 -99
- data/spec/models/active_record/transient.rb +0 -6
- data/spec/models/active_record/validator.rb +0 -118
- data/spec/models/active_record/with_enum.rb +0 -39
- data/spec/models/active_record/with_enum_without_column.rb +0 -38
- data/spec/models/active_record/with_false_enum.rb +0 -31
- data/spec/models/active_record/with_true_enum.rb +0 -39
- data/spec/models/active_record/worker.rb +0 -2
- data/spec/models/active_record/writer.rb +0 -6
- data/spec/models/basic_two_state_machines_example.rb +0 -25
- data/spec/models/callbacks/basic.rb +0 -98
- data/spec/models/callbacks/basic_multiple.rb +0 -75
- data/spec/models/callbacks/guard_within_block.rb +0 -67
- data/spec/models/callbacks/guard_within_block_multiple.rb +0 -66
- data/spec/models/callbacks/multiple_transitions_transition_guard.rb +0 -66
- data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +0 -65
- data/spec/models/callbacks/private_method.rb +0 -44
- data/spec/models/callbacks/private_method_multiple.rb +0 -44
- data/spec/models/callbacks/with_args.rb +0 -62
- data/spec/models/callbacks/with_args_multiple.rb +0 -61
- data/spec/models/callbacks/with_state_arg.rb +0 -30
- data/spec/models/callbacks/with_state_arg_multiple.rb +0 -26
- data/spec/models/complex_example.rb +0 -222
- data/spec/models/conversation.rb +0 -93
- data/spec/models/default_state.rb +0 -12
- data/spec/models/double_definer.rb +0 -21
- data/spec/models/dynamoid/complex_dynamoid_example.rb +0 -37
- data/spec/models/dynamoid/dynamoid_multiple.rb +0 -18
- data/spec/models/dynamoid/dynamoid_simple.rb +0 -18
- data/spec/models/foo.rb +0 -106
- data/spec/models/foo_callback_multiple.rb +0 -45
- data/spec/models/guard_arguments_check.rb +0 -17
- data/spec/models/guard_with_params.rb +0 -24
- data/spec/models/guard_with_params_multiple.rb +0 -18
- data/spec/models/guardian.rb +0 -58
- data/spec/models/guardian_multiple.rb +0 -48
- data/spec/models/guardian_without_from_specified.rb +0 -18
- data/spec/models/initial_state_proc.rb +0 -31
- data/spec/models/mongoid/complex_mongoid_example.rb +0 -37
- data/spec/models/mongoid/invalid_persistor_mongoid.rb +0 -39
- data/spec/models/mongoid/mongoid_relationships.rb +0 -26
- data/spec/models/mongoid/no_scope_mongoid.rb +0 -21
- data/spec/models/mongoid/silent_persistor_mongoid.rb +0 -39
- data/spec/models/mongoid/simple_mongoid.rb +0 -23
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +0 -25
- data/spec/models/mongoid/validator_mongoid.rb +0 -100
- data/spec/models/multi_transitioner.rb +0 -34
- data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +0 -31
- data/spec/models/namespaced_multiple_example.rb +0 -42
- data/spec/models/no_initial_state.rb +0 -25
- data/spec/models/not_auto_loaded/process.rb +0 -21
- data/spec/models/parametrised_event.rb +0 -42
- data/spec/models/parametrised_event_multiple.rb +0 -29
- data/spec/models/process_with_new_dsl.rb +0 -31
- data/spec/models/provided_state.rb +0 -24
- data/spec/models/redis/complex_redis_example.rb +0 -40
- data/spec/models/redis/redis_multiple.rb +0 -20
- data/spec/models/redis/redis_simple.rb +0 -20
- data/spec/models/sequel/complex_sequel_example.rb +0 -46
- data/spec/models/sequel/invalid_persistor.rb +0 -52
- data/spec/models/sequel/sequel_multiple.rb +0 -25
- data/spec/models/sequel/sequel_simple.rb +0 -26
- data/spec/models/sequel/silent_persistor.rb +0 -50
- data/spec/models/sequel/transactor.rb +0 -112
- data/spec/models/sequel/validator.rb +0 -93
- data/spec/models/sequel/worker.rb +0 -12
- data/spec/models/silencer.rb +0 -27
- data/spec/models/simple_custom_example.rb +0 -53
- data/spec/models/simple_example.rb +0 -15
- data/spec/models/simple_multiple_example.rb +0 -42
- data/spec/models/state_machine_with_failed_event.rb +0 -20
- data/spec/models/states_on_one_line_example.rb +0 -8
- data/spec/models/sub_class.rb +0 -41
- data/spec/models/sub_class_with_more_states.rb +0 -18
- data/spec/models/sub_classing.rb +0 -3
- data/spec/models/super_class.rb +0 -46
- data/spec/models/this_name_better_not_be_in_use.rb +0 -11
- data/spec/models/valid_state_name.rb +0 -23
- data/spec/spec_helper.rb +0 -26
- data/spec/spec_helpers/active_record.rb +0 -7
- data/spec/spec_helpers/dynamoid.rb +0 -33
- data/spec/spec_helpers/mongoid.rb +0 -7
- data/spec/spec_helpers/redis.rb +0 -15
- data/spec/spec_helpers/remove_warnings.rb +0 -1
- data/spec/spec_helpers/sequel.rb +0 -7
- data/spec/unit/api_spec.rb +0 -100
- data/spec/unit/basic_two_state_machines_example_spec.rb +0 -10
- data/spec/unit/callback_multiple_spec.rb +0 -300
- data/spec/unit/callbacks_spec.rb +0 -491
- data/spec/unit/complex_example_spec.rb +0 -84
- data/spec/unit/complex_multiple_example_spec.rb +0 -99
- data/spec/unit/edge_cases_spec.rb +0 -16
- data/spec/unit/event_multiple_spec.rb +0 -73
- data/spec/unit/event_naming_spec.rb +0 -16
- data/spec/unit/event_spec.rb +0 -381
- data/spec/unit/exception_spec.rb +0 -11
- data/spec/unit/guard_arguments_check_spec.rb +0 -9
- data/spec/unit/guard_multiple_spec.rb +0 -60
- data/spec/unit/guard_spec.rb +0 -89
- data/spec/unit/guard_with_params_multiple_spec.rb +0 -10
- data/spec/unit/guard_with_params_spec.rb +0 -14
- data/spec/unit/guard_without_from_specified_spec.rb +0 -10
- data/spec/unit/initial_state_multiple_spec.rb +0 -15
- data/spec/unit/initial_state_spec.rb +0 -12
- data/spec/unit/inspection_multiple_spec.rb +0 -201
- data/spec/unit/inspection_spec.rb +0 -149
- data/spec/unit/localizer_spec.rb +0 -78
- data/spec/unit/memory_leak_spec.rb +0 -38
- data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +0 -14
- data/spec/unit/namespaced_multiple_example_spec.rb +0 -75
- data/spec/unit/new_dsl_spec.rb +0 -12
- data/spec/unit/override_warning_spec.rb +0 -94
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +0 -618
- data/spec/unit/persistence/active_record_persistence_spec.rb +0 -721
- data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +0 -135
- data/spec/unit/persistence/dynamoid_persistence_spec.rb +0 -84
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -204
- data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -169
- data/spec/unit/persistence/redis_persistence_multiple_spec.rb +0 -88
- data/spec/unit/persistence/redis_persistence_spec.rb +0 -53
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +0 -148
- data/spec/unit/persistence/sequel_persistence_spec.rb +0 -368
- data/spec/unit/readme_spec.rb +0 -41
- data/spec/unit/reloading_spec.rb +0 -15
- data/spec/unit/rspec_matcher_spec.rb +0 -79
- data/spec/unit/simple_custom_example_spec.rb +0 -39
- data/spec/unit/simple_example_spec.rb +0 -42
- data/spec/unit/simple_multiple_example_spec.rb +0 -91
- data/spec/unit/state_spec.rb +0 -89
- data/spec/unit/states_on_one_line_example_spec.rb +0 -16
- data/spec/unit/subclassing_multiple_spec.rb +0 -74
- data/spec/unit/subclassing_spec.rb +0 -46
- data/spec/unit/transition_spec.rb +0 -436
- data/test/minitest_helper.rb +0 -57
- data/test/unit/minitest_matcher_test.rb +0 -80
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
|
-
|
13
|
-
and [Mongoid](http://mongoid.org/) 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, :
|
76
|
+
state :sleeping, initial: true
|
31
77
|
state :running, :cleaning
|
32
78
|
|
33
79
|
event :run do
|
34
|
-
transitions :
|
80
|
+
transitions from: :sleeping, to: :running
|
35
81
|
end
|
36
82
|
|
37
83
|
event :clean do
|
38
|
-
transitions :
|
84
|
+
transitions from: :running, to: :cleaning
|
39
85
|
end
|
40
86
|
|
41
87
|
event :sleep do
|
42
|
-
transitions :
|
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 :
|
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
|
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, :
|
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, :
|
149
|
+
event :run, after: :notify_somebody do
|
104
150
|
before do
|
105
151
|
log('Preparing to run')
|
106
152
|
end
|
107
153
|
|
108
|
-
transitions :
|
109
|
-
transitions :
|
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 :
|
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,23 +213,34 @@ class LogRunTime
|
|
165
213
|
end
|
166
214
|
```
|
167
215
|
|
168
|
-
|
216
|
+
#### Parameters
|
217
|
+
You can pass parameters to events:
|
169
218
|
|
170
219
|
```ruby
|
171
220
|
job = Job.new
|
172
|
-
job.run(:
|
221
|
+
job.run(:defragmentation)
|
173
222
|
```
|
174
223
|
|
175
|
-
In this case
|
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
|
-
|
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
|
|
182
241
|
Also, you can define a method that will be called if any event fails:
|
183
242
|
|
184
|
-
```
|
243
|
+
```ruby
|
185
244
|
def aasm_event_failed(event_name, old_state_name)
|
186
245
|
# use custom exception/messages, report metrics, etc
|
187
246
|
end
|
@@ -215,8 +274,8 @@ begin
|
|
215
274
|
new_state enter
|
216
275
|
...update state...
|
217
276
|
event before_success # if persist successful
|
218
|
-
transition success # if persist successful
|
219
|
-
event success # if persist successful
|
277
|
+
transition success # if persist successful, database update not guaranteed
|
278
|
+
event success # if persist successful, database update not guaranteed
|
220
279
|
old_state after_exit
|
221
280
|
new_state after_enter
|
222
281
|
event after
|
@@ -230,6 +289,8 @@ ensure
|
|
230
289
|
end
|
231
290
|
```
|
232
291
|
|
292
|
+
Use event's `after_commit` callback if it should be fired after database update.
|
293
|
+
|
233
294
|
#### The current event triggered
|
234
295
|
|
235
296
|
While running the callbacks you can easily retrieve the name of the event triggered
|
@@ -260,38 +321,38 @@ and then
|
|
260
321
|
Let's assume you want to allow particular transitions only if a defined condition is
|
261
322
|
given. For this you can set up a guard per transition, which will run before actually
|
262
323
|
running the transition. If the guard returns `false` the transition will be
|
263
|
-
denied (raising `AASM::InvalidTransition`
|
324
|
+
denied (raising `AASM::InvalidTransition`):
|
264
325
|
|
265
326
|
```ruby
|
266
327
|
class Cleaner
|
267
328
|
include AASM
|
268
329
|
|
269
330
|
aasm do
|
270
|
-
state :idle, :
|
331
|
+
state :idle, initial: true
|
271
332
|
state :cleaning
|
272
333
|
|
273
334
|
event :clean do
|
274
|
-
transitions :
|
335
|
+
transitions from: :idle, to: :cleaning, guard: :cleaning_needed?
|
275
336
|
end
|
276
337
|
|
277
338
|
event :clean_if_needed do
|
278
|
-
transitions :
|
339
|
+
transitions from: :idle, to: :cleaning do
|
279
340
|
guard do
|
280
341
|
cleaning_needed?
|
281
342
|
end
|
282
343
|
end
|
283
|
-
transitions :
|
344
|
+
transitions from: :idle, to: :idle
|
284
345
|
end
|
285
|
-
|
346
|
+
|
286
347
|
event :clean_if_dirty do
|
287
|
-
transitions :
|
348
|
+
transitions from: :idle, to: :cleaning, guard: :if_dirty?
|
288
349
|
end
|
289
350
|
end
|
290
351
|
|
291
352
|
def cleaning_needed?
|
292
353
|
false
|
293
354
|
end
|
294
|
-
|
355
|
+
|
295
356
|
def if_dirty?(status)
|
296
357
|
status == :dirty
|
297
358
|
end
|
@@ -303,7 +364,7 @@ job.clean # => raises AASM::InvalidTransition
|
|
303
364
|
job.may_clean_if_needed? # => true
|
304
365
|
job.clean_if_needed! # idle
|
305
366
|
|
306
|
-
job.clean_if_dirty(:clean) # =>
|
367
|
+
job.clean_if_dirty(:clean) # => raises AASM::InvalidTransition
|
307
368
|
job.clean_if_dirty(:dirty) # => true
|
308
369
|
```
|
309
370
|
|
@@ -313,16 +374,16 @@ You can even provide a number of guards, which all have to succeed to proceed
|
|
313
374
|
def walked_the_dog?; ...; end
|
314
375
|
|
315
376
|
event :sleep do
|
316
|
-
transitions :
|
377
|
+
transitions from: :running, to: :sleeping, guards: [:cleaning_needed?, :walked_the_dog?]
|
317
378
|
end
|
318
379
|
```
|
319
380
|
|
320
381
|
If you want to provide guards for all transitions within an event, you can use event guards
|
321
382
|
|
322
383
|
```ruby
|
323
|
-
event :sleep, :
|
324
|
-
transitions :
|
325
|
-
transitions :
|
384
|
+
event :sleep, guards: [:walked_the_dog?] do
|
385
|
+
transitions from: :running, to: :sleeping, guards: [:cleaning_needed?]
|
386
|
+
transitions from: :cleaning, to: :sleeping
|
326
387
|
end
|
327
388
|
```
|
328
389
|
|
@@ -330,15 +391,30 @@ If you prefer a more Ruby-like guard syntax, you can use `if` and `unless` as we
|
|
330
391
|
|
331
392
|
```ruby
|
332
393
|
event :clean do
|
333
|
-
transitions :
|
394
|
+
transitions from: :running, to: :cleaning, if: :cleaning_needed?
|
334
395
|
end
|
335
396
|
|
336
397
|
event :sleep do
|
337
|
-
transitions :
|
398
|
+
transitions from: :running, to: :sleeping, unless: :cleaning_needed?
|
338
399
|
end
|
339
400
|
end
|
340
401
|
```
|
341
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
|
+
```
|
342
418
|
|
343
419
|
### Transitions
|
344
420
|
|
@@ -351,7 +427,7 @@ class Job
|
|
351
427
|
include AASM
|
352
428
|
|
353
429
|
aasm do
|
354
|
-
state :stage1, :
|
430
|
+
state :stage1, initial: true
|
355
431
|
state :stage2
|
356
432
|
state :stage3
|
357
433
|
state :completed
|
@@ -372,40 +448,67 @@ job.stage1_completed
|
|
372
448
|
job.aasm.current_state # stage3
|
373
449
|
```
|
374
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
|
+
```
|
375
478
|
|
376
479
|
### Multiple state machines per class
|
377
480
|
|
378
481
|
Multiple state machines per class are supported. Be aware though that _AASM_ has been
|
379
|
-
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.
|
380
483
|
|
381
484
|
```ruby
|
382
485
|
class SimpleMultipleExample
|
383
486
|
include AASM
|
384
|
-
aasm(:move) do
|
385
|
-
state :standing, :
|
487
|
+
aasm(:move, column: 'move_state') do
|
488
|
+
state :standing, initial: true
|
386
489
|
state :walking
|
387
490
|
state :running
|
388
491
|
|
389
492
|
event :walk do
|
390
|
-
transitions :
|
493
|
+
transitions from: :standing, to: :walking
|
391
494
|
end
|
392
495
|
event :run do
|
393
|
-
transitions :
|
496
|
+
transitions from: [:standing, :walking], to: :running
|
394
497
|
end
|
395
498
|
event :hold do
|
396
|
-
transitions :
|
499
|
+
transitions from: [:walking, :running], to: :standing
|
397
500
|
end
|
398
501
|
end
|
399
502
|
|
400
|
-
aasm(:work) do
|
401
|
-
state :sleeping, :
|
503
|
+
aasm(:work, column: 'work_state') do
|
504
|
+
state :sleeping, initial: true
|
402
505
|
state :processing
|
403
506
|
|
404
507
|
event :start do
|
405
|
-
transitions :
|
508
|
+
transitions from: :sleeping, to: :processing
|
406
509
|
end
|
407
510
|
event :stop do
|
408
|
-
transitions :
|
511
|
+
transitions from: :processing, to: :sleeping
|
409
512
|
end
|
410
513
|
end
|
411
514
|
end
|
@@ -414,26 +517,72 @@ simple = SimpleMultipleExample.new
|
|
414
517
|
|
415
518
|
simple.aasm(:move).current_state
|
416
519
|
# => :standing
|
417
|
-
simple.aasm(:work).
|
520
|
+
simple.aasm(:work).current_state
|
418
521
|
# => :sleeping
|
419
522
|
|
420
523
|
simple.start
|
421
524
|
simple.aasm(:move).current_state
|
422
525
|
# => :standing
|
423
|
-
simple.aasm(:work).
|
526
|
+
simple.aasm(:work).current_state
|
424
527
|
# => :processing
|
425
528
|
|
426
529
|
```
|
427
530
|
|
428
|
-
|
429
|
-
|
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:
|
430
536
|
`SimpleMultipleExample: overriding method 'run'!`.
|
431
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
|
+
|
432
581
|
All _AASM_ class- and instance-level `aasm` methods accept a state machine selector.
|
433
582
|
So, for example, to use inspection on a class level, you have to use
|
434
583
|
|
435
584
|
```ruby
|
436
|
-
SimpleMultipleExample.aasm(:
|
585
|
+
SimpleMultipleExample.aasm(:move).states.map(&:name)
|
437
586
|
# => [:standing, :walking, :running]
|
438
587
|
```
|
439
588
|
|
@@ -445,26 +594,26 @@ class Example
|
|
445
594
|
include AASM
|
446
595
|
|
447
596
|
aasm(:work) do
|
448
|
-
state :sleeping, :
|
597
|
+
state :sleeping, initial: true
|
449
598
|
state :processing
|
450
599
|
|
451
600
|
event :start do
|
452
|
-
transitions :
|
601
|
+
transitions from: :sleeping, to: :processing
|
453
602
|
end
|
454
603
|
event :stop do
|
455
|
-
transitions :
|
604
|
+
transitions from: :processing, to: :sleeping
|
456
605
|
end
|
457
606
|
end
|
458
607
|
|
459
608
|
aasm(:question) do
|
460
|
-
state :answered, :
|
609
|
+
state :answered, initial: true
|
461
610
|
state :asked
|
462
611
|
|
463
|
-
event :ask, :
|
464
|
-
transitions :
|
612
|
+
event :ask, binding_event: :start do
|
613
|
+
transitions from: :answered, to: :asked
|
465
614
|
end
|
466
|
-
event :answer, :
|
467
|
-
transitions :
|
615
|
+
event :answer, binding_event: :stop do
|
616
|
+
transitions from: :asked, to: :answered
|
468
617
|
end
|
469
618
|
end
|
470
619
|
end
|
@@ -510,7 +659,7 @@ class CustomAASMBase < AASM::Base
|
|
510
659
|
# A custom transiton that we want available across many AASM models.
|
511
660
|
def count_transitions!
|
512
661
|
klass.class_eval do
|
513
|
-
aasm :
|
662
|
+
aasm with_klass: CustomAASMBase do
|
514
663
|
after_all_transitions :increment_transition_count
|
515
664
|
end
|
516
665
|
end
|
@@ -540,26 +689,26 @@ class CustomAASMBase < AASM::Base
|
|
540
689
|
end
|
541
690
|
```
|
542
691
|
|
543
|
-
When we declare our model that has an AASM state machine, we simply declare the AASM block with a `:
|
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.
|
544
693
|
|
545
694
|
```ruby
|
546
695
|
class SimpleCustomExample
|
547
696
|
include AASM
|
548
697
|
|
549
698
|
# Let's build an AASM state machine with our custom class.
|
550
|
-
aasm :
|
699
|
+
aasm with_klass: CustomAASMBase do
|
551
700
|
requires_guards!
|
552
701
|
count_transitions!
|
553
702
|
|
554
|
-
state :initialised, :
|
703
|
+
state :initialised, initial: true
|
555
704
|
state :filled_out
|
556
705
|
state :authorised
|
557
706
|
|
558
707
|
event :fill_out do
|
559
|
-
transitions :
|
708
|
+
transitions from: :initialised, to: :filled_out, guard: :fillable?
|
560
709
|
end
|
561
710
|
event :authorise do
|
562
|
-
transitions :
|
711
|
+
transitions from: :filled_out, to: :authorised, guard: :authorizable?
|
563
712
|
end
|
564
713
|
end
|
565
714
|
end
|
@@ -571,20 +720,22 @@ end
|
|
571
720
|
AASM comes with support for ActiveRecord and allows automatic persisting of the object's
|
572
721
|
state in the database.
|
573
722
|
|
723
|
+
Add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile.
|
724
|
+
|
574
725
|
```ruby
|
575
726
|
class Job < ActiveRecord::Base
|
576
727
|
include AASM
|
577
728
|
|
578
729
|
aasm do # default column: aasm_state
|
579
|
-
state :sleeping, :
|
730
|
+
state :sleeping, initial: true
|
580
731
|
state :running
|
581
732
|
|
582
733
|
event :run do
|
583
|
-
transitions :
|
734
|
+
transitions from: :sleeping, to: :running
|
584
735
|
end
|
585
736
|
|
586
737
|
event :sleep do
|
587
|
-
transitions :
|
738
|
+
transitions from: :running, to: :sleeping
|
588
739
|
end
|
589
740
|
end
|
590
741
|
|
@@ -599,11 +750,15 @@ You can tell AASM to auto-save the object or leave it unsaved
|
|
599
750
|
job = Job.new
|
600
751
|
job.run # not saved
|
601
752
|
job.run! # saved
|
753
|
+
|
754
|
+
# or
|
755
|
+
job.aasm.fire(:run) # not saved
|
756
|
+
job.aasm.fire!(:run) # saved
|
602
757
|
```
|
603
758
|
|
604
759
|
Saving includes running all validations on the `Job` class. If
|
605
760
|
`whiny_persistence` flag is set to `true`, exception is raised in case of
|
606
|
-
failure. If `whiny_persistence` flag is set to false
|
761
|
+
failure. If `whiny_persistence` flag is set to `false`, methods with a bang return
|
607
762
|
`true` if the state transition is successful or `false` if an error occurs.
|
608
763
|
|
609
764
|
If you want make sure the state gets saved without running validations (and
|
@@ -615,22 +770,30 @@ be updated in the database (just like ActiveRecord `update_column` is working).
|
|
615
770
|
class Job < ActiveRecord::Base
|
616
771
|
include AASM
|
617
772
|
|
618
|
-
aasm :
|
619
|
-
state :sleeping, :
|
773
|
+
aasm skip_validation_on_save: true do
|
774
|
+
state :sleeping, initial: true
|
620
775
|
state :running
|
621
776
|
|
622
777
|
event :run do
|
623
|
-
transitions :
|
778
|
+
transitions from: :sleeping, to: :running
|
624
779
|
end
|
625
780
|
|
626
781
|
event :sleep do
|
627
|
-
transitions :
|
782
|
+
transitions from: :running, to: :sleeping
|
628
783
|
end
|
629
784
|
end
|
630
785
|
|
631
786
|
end
|
632
787
|
```
|
633
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
|
+
|
634
797
|
If you want to make sure that the _AASM_ column for storing the state is not directly assigned,
|
635
798
|
configure _AASM_ to not allow direct assignment, like this:
|
636
799
|
|
@@ -638,12 +801,12 @@ configure _AASM_ to not allow direct assignment, like this:
|
|
638
801
|
class Job < ActiveRecord::Base
|
639
802
|
include AASM
|
640
803
|
|
641
|
-
aasm :
|
642
|
-
state :sleeping, :
|
804
|
+
aasm no_direct_assignment: true do
|
805
|
+
state :sleeping, initial: true
|
643
806
|
state :running
|
644
807
|
|
645
808
|
event :run do
|
646
|
-
transitions :
|
809
|
+
transitions from: :sleeping, to: :running
|
647
810
|
end
|
648
811
|
end
|
649
812
|
|
@@ -659,6 +822,37 @@ job.aasm_state = :running # => raises AASM::NoDirectAssignmentError
|
|
659
822
|
job.aasm_state # => 'sleeping'
|
660
823
|
```
|
661
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
|
+
|
662
856
|
#### ActiveRecord enums
|
663
857
|
|
664
858
|
You can use
|
@@ -674,8 +868,8 @@ class Job < ActiveRecord::Base
|
|
674
868
|
running: 99
|
675
869
|
}
|
676
870
|
|
677
|
-
aasm :
|
678
|
-
state :sleeping, :
|
871
|
+
aasm column: :state, enum: true do
|
872
|
+
state :sleeping, initial: true
|
679
873
|
state :running
|
680
874
|
end
|
681
875
|
end
|
@@ -731,6 +925,23 @@ class Job
|
|
731
925
|
end
|
732
926
|
```
|
733
927
|
|
928
|
+
### NoBrainer
|
929
|
+
|
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.
|
933
|
+
|
934
|
+
```ruby
|
935
|
+
class Job
|
936
|
+
include NoBrainer::Document
|
937
|
+
include AASM
|
938
|
+
field :aasm_state
|
939
|
+
aasm do
|
940
|
+
...
|
941
|
+
end
|
942
|
+
end
|
943
|
+
```
|
944
|
+
|
734
945
|
### Redis
|
735
946
|
|
736
947
|
AASM also supports persistence in Redis via
|
@@ -757,7 +968,7 @@ class Job < ActiveRecord::Base
|
|
757
968
|
include AASM
|
758
969
|
|
759
970
|
aasm do
|
760
|
-
state :sleeping, :
|
971
|
+
state :sleeping, initial: true
|
761
972
|
state :running
|
762
973
|
state :cleaning
|
763
974
|
end
|
@@ -786,8 +997,8 @@ defining the `AASM` states, like this:
|
|
786
997
|
class Job < ActiveRecord::Base
|
787
998
|
include AASM
|
788
999
|
|
789
|
-
aasm :
|
790
|
-
state :sleeping, :
|
1000
|
+
aasm create_scopes: false do
|
1001
|
+
state :sleeping, initial: true
|
791
1002
|
state :running
|
792
1003
|
state :cleaning
|
793
1004
|
end
|
@@ -820,11 +1031,11 @@ class Job < ActiveRecord::Base
|
|
820
1031
|
include AASM
|
821
1032
|
|
822
1033
|
aasm do
|
823
|
-
state :sleeping, :
|
1034
|
+
state :sleeping, initial: true
|
824
1035
|
state :running
|
825
1036
|
|
826
|
-
event :run, :
|
827
|
-
transitions :
|
1037
|
+
event :run, after_commit: :notify_about_running_job do
|
1038
|
+
transitions from: :sleeping, to: :running
|
828
1039
|
end
|
829
1040
|
end
|
830
1041
|
|
@@ -846,18 +1057,24 @@ job.run
|
|
846
1057
|
job.save! #notify_about_running_job is not run
|
847
1058
|
```
|
848
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
|
+
|
849
1066
|
If you want to encapsulate state changes within an own transaction, the behavior
|
850
1067
|
of this nested transaction might be confusing. Take a look at
|
851
1068
|
[ActiveRecord Nested Transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html)
|
852
1069
|
if you want to know more about this. Nevertheless, AASM by default requires a new transaction
|
853
|
-
`transaction(:
|
1070
|
+
`transaction(requires_new: true)`. You can override this behavior by changing
|
854
1071
|
the configuration
|
855
1072
|
|
856
1073
|
```ruby
|
857
1074
|
class Job < ActiveRecord::Base
|
858
1075
|
include AASM
|
859
1076
|
|
860
|
-
aasm :
|
1077
|
+
aasm requires_new_transaction: false do
|
861
1078
|
...
|
862
1079
|
end
|
863
1080
|
|
@@ -865,11 +1082,29 @@ class Job < ActiveRecord::Base
|
|
865
1082
|
end
|
866
1083
|
```
|
867
1084
|
|
868
|
-
which then leads to `transaction(:
|
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
|
+
```
|
869
1104
|
|
870
1105
|
### Pessimistic Locking
|
871
1106
|
|
872
|
-
AASM supports [
|
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.
|
873
1108
|
|
874
1109
|
| Option | Purpose |
|
875
1110
|
| ------ | ------- |
|
@@ -882,7 +1117,7 @@ AASM supports [Active Record pessimistic locking via `with_lock`](http://api.rub
|
|
882
1117
|
class Job < ActiveRecord::Base
|
883
1118
|
include AASM
|
884
1119
|
|
885
|
-
aasm :
|
1120
|
+
aasm requires_lock: true do
|
886
1121
|
...
|
887
1122
|
end
|
888
1123
|
|
@@ -894,7 +1129,7 @@ end
|
|
894
1129
|
class Job < ActiveRecord::Base
|
895
1130
|
include AASM
|
896
1131
|
|
897
|
-
aasm :
|
1132
|
+
aasm requires_lock: 'FOR UPDATE NOWAIT' do
|
898
1133
|
...
|
899
1134
|
end
|
900
1135
|
|
@@ -912,18 +1147,21 @@ this by defining your favorite column name, using `:column` like this:
|
|
912
1147
|
class Job < ActiveRecord::Base
|
913
1148
|
include AASM
|
914
1149
|
|
915
|
-
aasm :
|
1150
|
+
aasm column: :my_state do
|
916
1151
|
...
|
917
1152
|
end
|
918
1153
|
|
919
|
-
aasm :another_state_machine, column:
|
1154
|
+
aasm :another_state_machine, column: :second_state do
|
920
1155
|
...
|
921
1156
|
end
|
922
1157
|
end
|
923
1158
|
```
|
924
1159
|
|
925
1160
|
Whatever column name is used, make sure to add a migration to provide this column
|
926
|
-
(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.
|
927
1165
|
|
928
1166
|
```ruby
|
929
1167
|
class AddJobState < ActiveRecord::Migration
|
@@ -937,6 +1175,13 @@ class AddJobState < ActiveRecord::Migration
|
|
937
1175
|
end
|
938
1176
|
```
|
939
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
|
+
|
940
1185
|
### Inspection
|
941
1186
|
|
942
1187
|
AASM supports query methods for states and events
|
@@ -948,19 +1193,19 @@ class Job
|
|
948
1193
|
include AASM
|
949
1194
|
|
950
1195
|
aasm do
|
951
|
-
state :sleeping, :
|
1196
|
+
state :sleeping, initial: true
|
952
1197
|
state :running, :cleaning
|
953
1198
|
|
954
1199
|
event :run do
|
955
|
-
transitions :
|
1200
|
+
transitions from: :sleeping, to: :running
|
956
1201
|
end
|
957
1202
|
|
958
1203
|
event :clean do
|
959
|
-
transitions :
|
1204
|
+
transitions from: :running, to: :cleaning, guard: :cleaning_needed?
|
960
1205
|
end
|
961
1206
|
|
962
1207
|
event :sleep do
|
963
|
-
transitions :
|
1208
|
+
transitions from: [:running, :cleaning], to: :sleeping
|
964
1209
|
end
|
965
1210
|
end
|
966
1211
|
|
@@ -978,15 +1223,19 @@ Job.aasm.states.map(&:name)
|
|
978
1223
|
job = Job.new
|
979
1224
|
|
980
1225
|
# show all permitted states (from initial state)
|
981
|
-
job.aasm.states(:
|
1226
|
+
job.aasm.states(permitted: true).map(&:name)
|
982
1227
|
#=> [:running]
|
983
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
|
+
|
984
1233
|
job.run
|
985
|
-
job.aasm.states(:
|
1234
|
+
job.aasm.states(permitted: true).map(&:name)
|
986
1235
|
#=> [:sleeping]
|
987
1236
|
|
988
1237
|
# show all non permitted states
|
989
|
-
job.aasm.states(:
|
1238
|
+
job.aasm.states(permitted: false).map(&:name)
|
990
1239
|
#=> [:cleaning]
|
991
1240
|
|
992
1241
|
# show all possible (triggerable) events from the current state
|
@@ -994,23 +1243,23 @@ job.aasm.events.map(&:name)
|
|
994
1243
|
#=> [:clean, :sleep]
|
995
1244
|
|
996
1245
|
# show all permitted events
|
997
|
-
job.aasm.events(:
|
1246
|
+
job.aasm.events(permitted: true).map(&:name)
|
998
1247
|
#=> [:sleep]
|
999
1248
|
|
1000
1249
|
# show all non permitted events
|
1001
|
-
job.aasm.events(:
|
1250
|
+
job.aasm.events(permitted: false).map(&:name)
|
1002
1251
|
#=> [:clean]
|
1003
1252
|
|
1004
1253
|
# show all possible events except a specific one
|
1005
|
-
job.aasm.events(:
|
1254
|
+
job.aasm.events(reject: :sleep).map(&:name)
|
1006
1255
|
#=> [:clean]
|
1007
1256
|
|
1008
1257
|
# list states for select
|
1009
1258
|
Job.aasm.states_for_select
|
1010
|
-
|
1259
|
+
#=> [["Sleeping", "sleeping"], ["Running", "running"], ["Cleaning", "cleaning"]]
|
1011
1260
|
|
1012
1261
|
# show permitted states with guard parameter
|
1013
|
-
job.aasm.states({:
|
1262
|
+
job.aasm.states({permitted: true}, guard_parameter).map(&:name)
|
1014
1263
|
```
|
1015
1264
|
|
1016
1265
|
|
@@ -1023,7 +1272,7 @@ use
|
|
1023
1272
|
class Job
|
1024
1273
|
include AASM
|
1025
1274
|
|
1026
|
-
aasm :
|
1275
|
+
aasm logger: Rails.logger do
|
1027
1276
|
...
|
1028
1277
|
end
|
1029
1278
|
end
|
@@ -1047,7 +1296,15 @@ the 'instance method symbol / string' way whenever possible when defining guardi
|
|
1047
1296
|
|
1048
1297
|
#### RSpec
|
1049
1298
|
|
1050
|
-
AASM provides some matchers for [RSpec](http://rspec.info):
|
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:
|
1051
1308
|
|
1052
1309
|
```ruby
|
1053
1310
|
# classes with only the default state machine
|
@@ -1060,7 +1317,7 @@ expect(job).to allow_event :run
|
|
1060
1317
|
expect(job).to_not allow_event :clean
|
1061
1318
|
expect(job).to allow_transition_to(:running)
|
1062
1319
|
expect(job).to_not allow_transition_to(:cleaning)
|
1063
|
-
# on_event also accept arguments
|
1320
|
+
# on_event also accept multiple arguments
|
1064
1321
|
expect(job).to transition_from(:sleeping).to(:running).on_event(:run, :defragmentation)
|
1065
1322
|
|
1066
1323
|
# classes with multiple state machine
|
@@ -1081,6 +1338,9 @@ expect(multiple).to allow_event(:start).on(:move)
|
|
1081
1338
|
expect(multiple).to_not allow_event(:stop).on(:move)
|
1082
1339
|
expect(multiple).to allow_transition_to(:processing).on(:move)
|
1083
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
|
+
|
1084
1344
|
```
|
1085
1345
|
|
1086
1346
|
#### Minitest
|
@@ -1091,7 +1351,10 @@ AASM provides assertions and rspec-like expectations for [Minitest](https://gith
|
|
1091
1351
|
|
1092
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`.
|
1093
1353
|
|
1094
|
-
|
1354
|
+
|
1355
|
+
##### Examples Of Usage (Minitest):
|
1356
|
+
|
1357
|
+
Add `require 'aasm/minitest'` to your `test_helper.rb` file and use them like this:
|
1095
1358
|
|
1096
1359
|
```ruby
|
1097
1360
|
# classes with only the default state machine
|
@@ -1198,7 +1461,15 @@ After installing AASM you can run generator:
|
|
1198
1461
|
```
|
1199
1462
|
Replace NAME with the Model name, COLUMN_NAME is optional(default is 'aasm_state').
|
1200
1463
|
This will create a model (if one does not exist) and configure it with aasm block.
|
1201
|
-
For
|
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
|
+
```
|
1202
1473
|
|
1203
1474
|
## Latest changes ##
|
1204
1475
|
|
@@ -1220,6 +1491,12 @@ Feel free to
|
|
1220
1491
|
* [Anil Maurya](http://github.com/anilmaurya) (since 2016)
|
1221
1492
|
|
1222
1493
|
|
1494
|
+
|
1495
|
+
## Stargazers over time
|
1496
|
+
|
1497
|
+
[![Stargazers over time](https://starchart.cc/aasm/aasm.svg)](https://starchart.cc/aasm/aasm)
|
1498
|
+
|
1499
|
+
|
1223
1500
|
## [Contributing](CONTRIBUTING.md)
|
1224
1501
|
|
1225
1502
|
## Warranty ##
|
@@ -1231,7 +1508,7 @@ purpose.
|
|
1231
1508
|
|
1232
1509
|
## License ##
|
1233
1510
|
|
1234
|
-
Copyright (c) 2006-
|
1511
|
+
Copyright (c) 2006-2017 Scott Barron
|
1235
1512
|
|
1236
1513
|
Permission is hereby granted, free of charge, to any person obtaining
|
1237
1514
|
a copy of this software and associated documentation files (the
|