aasm 5.0.8 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +108 -17
- data/lib/aasm/base.rb +44 -12
- data/lib/aasm/configuration.rb +3 -0
- data/lib/aasm/core/event.rb +12 -6
- data/lib/aasm/core/invokers/class_invoker.rb +1 -1
- data/lib/aasm/core/invokers/literal_invoker.rb +3 -1
- data/lib/aasm/core/state.rb +6 -5
- data/lib/aasm/core/transition.rb +1 -1
- data/lib/aasm/dsl_helper.rb +24 -22
- data/lib/aasm/errors.rb +1 -0
- data/lib/aasm/instance_base.rb +15 -4
- data/lib/aasm/localizer.rb +13 -3
- data/lib/aasm/persistence/active_record_persistence.rb +18 -0
- data/lib/aasm/persistence/base.rb +13 -2
- data/lib/aasm/persistence/orm.rb +23 -19
- data/lib/aasm/version.rb +1 -1
- data/lib/aasm.rb +0 -2
- data/lib/generators/aasm/orm_helpers.rb +1 -1
- data/lib/generators/active_record/templates/migration.rb +1 -1
- metadata +9 -397
- data/.document +0 -6
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -27
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- data/.gitignore +0 -20
- data/.travis.yml +0 -100
- data/API +0 -34
- data/Appraisals +0 -71
- data/CHANGELOG.md +0 -431
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -24
- data/Dockerfile +0 -44
- data/Gemfile +0 -6
- data/Gemfile.lock_old +0 -151
- 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 -37
- data/callbacks.txt +0 -51
- data/docker-compose.yml +0 -40
- data/gemfiles/norails.gemfile +0 -10
- data/gemfiles/rails_3.2.gemfile +0 -14
- data/gemfiles/rails_4.2.gemfile +0 -16
- data/gemfiles/rails_4.2_mongoid_5.gemfile +0 -11
- data/gemfiles/rails_4.2_nobrainer.gemfile +0 -9
- data/gemfiles/rails_5.0.gemfile +0 -13
- data/gemfiles/rails_5.0_nobrainer.gemfile +0 -9
- data/gemfiles/rails_5.1.gemfile +0 -13
- data/gemfiles/rails_5.2.gemfile +0 -13
- data/spec/database.rb +0 -59
- 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 -53
- data/spec/generators/mongoid_generator_spec.rb +0 -31
- data/spec/generators/no_brainer_generator_spec.rb +0 -29
- 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/instance_level_skip_validation_example.rb +0 -19
- 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/person.rb +0 -23
- 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 -32
- data/spec/models/active_record/thief.rb +0 -29
- data/spec/models/active_record/transactor.rb +0 -124
- 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/work.rb +0 -3
- 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 -34
- data/spec/models/callbacks/with_state_arg_multiple.rb +0 -29
- 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/nobrainer/complex_no_brainer_example.rb +0 -36
- data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +0 -39
- data/spec/models/nobrainer/no_scope_no_brainer.rb +0 -21
- data/spec/models/nobrainer/nobrainer_relationships.rb +0 -25
- data/spec/models/nobrainer/silent_persistor_no_brainer.rb +0 -39
- data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +0 -25
- data/spec/models/nobrainer/simple_no_brainer.rb +0 -23
- data/spec/models/nobrainer/validator_no_brainer.rb +0 -98
- 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 -23
- data/spec/models/simple_example_with_guard_args.rb +0 -17
- 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 -36
- data/spec/spec_helpers/active_record.rb +0 -8
- data/spec/spec_helpers/dynamoid.rb +0 -35
- data/spec/spec_helpers/mongoid.rb +0 -26
- data/spec/spec_helpers/nobrainer.rb +0 -15
- data/spec/spec_helpers/redis.rb +0 -18
- data/spec/spec_helpers/remove_warnings.rb +0 -1
- data/spec/spec_helpers/sequel.rb +0 -7
- data/spec/unit/abstract_class_spec.rb +0 -27
- 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 -304
- data/spec/unit/callbacks_spec.rb +0 -521
- data/spec/unit/complex_example_spec.rb +0 -93
- data/spec/unit/complex_multiple_example_spec.rb +0 -115
- 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 -394
- 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/invoker_spec.rb +0 -189
- data/spec/unit/invokers/base_invoker_spec.rb +0 -72
- data/spec/unit/invokers/class_invoker_spec.rb +0 -95
- data/spec/unit/invokers/literal_invoker_spec.rb +0 -86
- data/spec/unit/invokers/proc_invoker_spec.rb +0 -86
- 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 -773
- 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 -200
- data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -165
- data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +0 -198
- data/spec/unit/persistence/no_brainer_persistence_spec.rb +0 -158
- 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 -88
- data/spec/unit/simple_custom_example_spec.rb +0 -39
- data/spec/unit/simple_example_spec.rb +0 -57
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a088e1285940890f3c1a87b6cf0534e80702a96d2fb6eb42b43ff0cf50e6c57d
|
4
|
+
data.tar.gz: b96c64d2458639d217540b8787b1f718d80cd8e64b2661825435a795d5796a93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85a0532a6257477070680b2864c27191f618a61091fe9ba0c2c0672e99aa53c4b61968e4987607c4e5747bb42686cff6667966c90cf4592e87ad0324175e711c
|
7
|
+
data.tar.gz: 1abfc89101b1cdf7440a82446b6c600d65fe6f2bfc83575a85dc2ef0edb04968cc509c9ff88ec2ce68f4aa8b72283f75301a0fcfefffa16fc0347cb31f11c492
|
data/README.md
CHANGED
@@ -20,6 +20,7 @@
|
|
20
20
|
- [Extending AASM](#extending-aasm)
|
21
21
|
- [ActiveRecord](#activerecord)
|
22
22
|
- [Bang events](#bang-events)
|
23
|
+
- [Timestamps](#timestamps)
|
23
24
|
- [ActiveRecord enums](#activerecord-enums)
|
24
25
|
- [Sequel](#sequel)
|
25
26
|
- [Dynamoid](#dynamoid)
|
@@ -212,15 +213,28 @@ class LogRunTime
|
|
212
213
|
end
|
213
214
|
```
|
214
215
|
|
215
|
-
|
216
|
+
#### Parameters
|
217
|
+
You can pass parameters to events:
|
216
218
|
|
217
219
|
```ruby
|
218
220
|
job = Job.new
|
219
221
|
job.run(:defragmentation)
|
220
222
|
```
|
221
223
|
|
222
|
-
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.
|
223
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 guards 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
|
224
238
|
In case of an error during the event processing the error is rescued and passed to `:error`
|
225
239
|
callback, which can handle it or re-raise it for further propagation.
|
226
240
|
|
@@ -260,8 +274,8 @@ begin
|
|
260
274
|
new_state enter
|
261
275
|
...update state...
|
262
276
|
event before_success # if persist successful
|
263
|
-
transition success # if persist successful
|
264
|
-
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
|
265
279
|
old_state after_exit
|
266
280
|
new_state after_enter
|
267
281
|
event after
|
@@ -275,6 +289,8 @@ ensure
|
|
275
289
|
end
|
276
290
|
```
|
277
291
|
|
292
|
+
Use event's `after_commit` callback if it should be fired after database update.
|
293
|
+
|
278
294
|
#### The current event triggered
|
279
295
|
|
280
296
|
While running the callbacks you can easily retrieve the name of the event triggered
|
@@ -305,7 +321,7 @@ and then
|
|
305
321
|
Let's assume you want to allow particular transitions only if a defined condition is
|
306
322
|
given. For this you can set up a guard per transition, which will run before actually
|
307
323
|
running the transition. If the guard returns `false` the transition will be
|
308
|
-
denied (raising `AASM::InvalidTransition`
|
324
|
+
denied (raising `AASM::InvalidTransition`):
|
309
325
|
|
310
326
|
```ruby
|
311
327
|
class Cleaner
|
@@ -348,7 +364,7 @@ job.clean # => raises AASM::InvalidTransition
|
|
348
364
|
job.may_clean_if_needed? # => true
|
349
365
|
job.clean_if_needed! # idle
|
350
366
|
|
351
|
-
job.clean_if_dirty(:clean) # =>
|
367
|
+
job.clean_if_dirty(:clean) # => raises AASM::InvalidTransition
|
352
368
|
job.clean_if_dirty(:dirty) # => true
|
353
369
|
```
|
354
370
|
|
@@ -384,7 +400,7 @@ If you prefer a more Ruby-like guard syntax, you can use `if` and `unless` as we
|
|
384
400
|
end
|
385
401
|
```
|
386
402
|
|
387
|
-
You can invoke a Class instead a method
|
403
|
+
You can invoke a Class instead of a method if the Class responds to `call`
|
388
404
|
|
389
405
|
```ruby
|
390
406
|
event :sleep do
|
@@ -432,11 +448,38 @@ job.stage1_completed
|
|
432
448
|
job.aasm.current_state # stage3
|
433
449
|
```
|
434
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
|
+
```
|
435
478
|
|
436
479
|
### Multiple state machines per class
|
437
480
|
|
438
481
|
Multiple state machines per class are supported. Be aware though that _AASM_ has been
|
439
|
-
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.
|
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.
|
440
483
|
|
441
484
|
```ruby
|
442
485
|
class SimpleMultipleExample
|
@@ -474,13 +517,13 @@ simple = SimpleMultipleExample.new
|
|
474
517
|
|
475
518
|
simple.aasm(:move).current_state
|
476
519
|
# => :standing
|
477
|
-
simple.aasm(:work).
|
520
|
+
simple.aasm(:work).current_state
|
478
521
|
# => :sleeping
|
479
522
|
|
480
523
|
simple.start
|
481
524
|
simple.aasm(:move).current_state
|
482
525
|
# => :standing
|
483
|
-
simple.aasm(:work).
|
526
|
+
simple.aasm(:work).current_state
|
484
527
|
# => :processing
|
485
528
|
|
486
529
|
```
|
@@ -677,6 +720,8 @@ end
|
|
677
720
|
AASM comes with support for ActiveRecord and allows automatic persisting of the object's
|
678
721
|
state in the database.
|
679
722
|
|
723
|
+
Add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile.
|
724
|
+
|
680
725
|
```ruby
|
681
726
|
class Job < ActiveRecord::Base
|
682
727
|
include AASM
|
@@ -713,7 +758,7 @@ job.aasm.fire!(:run) # saved
|
|
713
758
|
|
714
759
|
Saving includes running all validations on the `Job` class. If
|
715
760
|
`whiny_persistence` flag is set to `true`, exception is raised in case of
|
716
|
-
failure. If `whiny_persistence` flag is set to false
|
761
|
+
failure. If `whiny_persistence` flag is set to `false`, methods with a bang return
|
717
762
|
`true` if the state transition is successful or `false` if an error occurs.
|
718
763
|
|
719
764
|
If you want make sure the state gets saved without running validations (and
|
@@ -741,7 +786,7 @@ class Job < ActiveRecord::Base
|
|
741
786
|
end
|
742
787
|
```
|
743
788
|
|
744
|
-
Also
|
789
|
+
Also, you can skip the validation at instance level with `some_event_name_without_validation!` method.
|
745
790
|
With this you have the flexibility of having validation for all your transitions by default and then skip it wherever required.
|
746
791
|
Please note that only state column will be updated as mentioned in the above example.
|
747
792
|
|
@@ -777,6 +822,37 @@ job.aasm_state = :running # => raises AASM::NoDirectAssignmentError
|
|
777
822
|
job.aasm_state # => 'sleeping'
|
778
823
|
```
|
779
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
|
+
|
780
856
|
#### ActiveRecord enums
|
781
857
|
|
782
858
|
You can use
|
@@ -981,6 +1057,12 @@ job.run
|
|
981
1057
|
job.save! #notify_about_running_job is not run
|
982
1058
|
```
|
983
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
|
+
|
984
1066
|
If you want to encapsulate state changes within an own transaction, the behavior
|
985
1067
|
of this nested transaction might be confusing. Take a look at
|
986
1068
|
[ActiveRecord Nested Transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html)
|
@@ -1002,7 +1084,7 @@ end
|
|
1002
1084
|
|
1003
1085
|
which then leads to `transaction(requires_new: false)`, the Rails default.
|
1004
1086
|
|
1005
|
-
Additionally, if you do not want any of your
|
1087
|
+
Additionally, if you do not want any of your ActiveRecord actions to be
|
1006
1088
|
wrapped in a transaction, you can specify the `use_transactions` flag. This can
|
1007
1089
|
be useful if you want want to persist things to the database that happen as a
|
1008
1090
|
result of a transaction or callback, even when some error occurs. The
|
@@ -1022,7 +1104,7 @@ end
|
|
1022
1104
|
|
1023
1105
|
### Pessimistic Locking
|
1024
1106
|
|
1025
|
-
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.
|
1026
1108
|
|
1027
1109
|
| Option | Purpose |
|
1028
1110
|
| ------ | ------- |
|
@@ -1076,7 +1158,10 @@ end
|
|
1076
1158
|
```
|
1077
1159
|
|
1078
1160
|
Whatever column name is used, make sure to add a migration to provide this column
|
1079
|
-
(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.
|
1080
1165
|
|
1081
1166
|
```ruby
|
1082
1167
|
class AddJobState < ActiveRecord::Migration
|
@@ -1092,7 +1177,7 @@ end
|
|
1092
1177
|
|
1093
1178
|
### Log State Changes
|
1094
1179
|
|
1095
|
-
Logging state change can be done using [paper_trail](https://github.com/paper-trail-gem/paper_trail) gem
|
1180
|
+
Logging state change can be done using [paper_trail](https://github.com/paper-trail-gem/paper_trail) gem
|
1096
1181
|
|
1097
1182
|
Example of implementation can be found here [https://github.com/nitsujri/aasm-papertrail-example](https://github.com/nitsujri/aasm-papertrail-example)
|
1098
1183
|
|
@@ -1376,7 +1461,7 @@ After installing AASM you can run generator:
|
|
1376
1461
|
```
|
1377
1462
|
Replace NAME with the Model name, COLUMN_NAME is optional(default is 'aasm_state').
|
1378
1463
|
This will create a model (if one does not exist) and configure it with aasm block.
|
1379
|
-
For
|
1464
|
+
For ActiveRecord orm a migration file is added to add aasm state column to table.
|
1380
1465
|
|
1381
1466
|
### Docker
|
1382
1467
|
|
@@ -1406,6 +1491,12 @@ Feel free to
|
|
1406
1491
|
* [Anil Maurya](http://github.com/anilmaurya) (since 2016)
|
1407
1492
|
|
1408
1493
|
|
1494
|
+
|
1495
|
+
## Stargazers over time
|
1496
|
+
|
1497
|
+
[![Stargazers over time](https://starchart.cc/aasm/aasm.svg)](https://starchart.cc/aasm/aasm)
|
1498
|
+
|
1499
|
+
|
1409
1500
|
## [Contributing](CONTRIBUTING.md)
|
1410
1501
|
|
1411
1502
|
## Warranty ##
|
data/lib/aasm/base.rb
CHANGED
@@ -37,6 +37,9 @@ module AASM
|
|
37
37
|
# string for a specific lock type i.e. FOR UPDATE NOWAIT
|
38
38
|
configure :requires_lock, false
|
39
39
|
|
40
|
+
# automatically set `"#{state_name}_at" = ::Time.now` on state changes
|
41
|
+
configure :timestamps, false
|
42
|
+
|
40
43
|
# set to true to forbid direct assignment of aasm_state column (in ActiveRecord)
|
41
44
|
configure :no_direct_assignment, false
|
42
45
|
|
@@ -51,19 +54,12 @@ module AASM
|
|
51
54
|
# Configure a logger, with default being a Logger to STDERR
|
52
55
|
configure :logger, Logger.new(STDERR)
|
53
56
|
|
57
|
+
# setup timestamp-setting callback if enabled
|
58
|
+
setup_timestamps(@name)
|
59
|
+
|
54
60
|
# make sure to raise an error if no_direct_assignment is enabled
|
55
61
|
# and attribute is directly assigned though
|
56
|
-
|
57
|
-
|
58
|
-
if @state_machine.config.no_direct_assignment
|
59
|
-
@klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name|
|
60
|
-
if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
|
61
|
-
raise AASM::NoDirectAssignmentError.new('direct assignment of AASM column has been disabled (see AASM configuration for this class)')
|
62
|
-
else
|
63
|
-
super(state_name)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
62
|
+
setup_no_direct_assignment(@name)
|
67
63
|
end
|
68
64
|
|
69
65
|
# This method is both a getter and a setter
|
@@ -227,7 +223,20 @@ module AASM
|
|
227
223
|
end
|
228
224
|
end
|
229
225
|
|
230
|
-
klass.send(:define_method, method_name, method_definition)
|
226
|
+
klass.send(:define_method, method_name, method_definition).tap do |sym|
|
227
|
+
apply_ruby2_keyword(klass, sym)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def apply_ruby2_keyword(klass, sym)
|
232
|
+
if RUBY_VERSION >= '2.7.1'
|
233
|
+
if klass.instance_method(sym).parameters.find { |type, _| type.to_s.start_with?('rest') }
|
234
|
+
# If there is a place where you are receiving in *args, do ruby2_keywords.
|
235
|
+
klass.module_eval do
|
236
|
+
ruby2_keywords sym
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
231
240
|
end
|
232
241
|
|
233
242
|
def namespace?
|
@@ -267,5 +276,28 @@ module AASM
|
|
267
276
|
end
|
268
277
|
end
|
269
278
|
|
279
|
+
def setup_timestamps(aasm_name)
|
280
|
+
return unless @state_machine.config.timestamps
|
281
|
+
|
282
|
+
after_all_transitions do
|
283
|
+
if self.class.aasm(:"#{aasm_name}").state_machine.config.timestamps
|
284
|
+
ts_setter = "#{aasm(aasm_name).to_state}_at="
|
285
|
+
respond_to?(ts_setter) && send(ts_setter, ::Time.now)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def setup_no_direct_assignment(aasm_name)
|
291
|
+
return unless @state_machine.config.no_direct_assignment
|
292
|
+
|
293
|
+
@klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name|
|
294
|
+
if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
|
295
|
+
raise AASM::NoDirectAssignmentError.new('direct assignment of AASM column has been disabled (see AASM configuration for this class)')
|
296
|
+
else
|
297
|
+
super(state_name)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
270
302
|
end
|
271
303
|
end
|
data/lib/aasm/configuration.rb
CHANGED
@@ -24,6 +24,9 @@ module AASM
|
|
24
24
|
# for ActiveRecord: use pessimistic locking
|
25
25
|
attr_accessor :requires_lock
|
26
26
|
|
27
|
+
# automatically set `"#{state_name}_at" = ::Time.now` on state changes
|
28
|
+
attr_accessor :timestamps
|
29
|
+
|
27
30
|
# forbid direct assignment in aasm_state column (in ActiveRecord)
|
28
31
|
attr_accessor :no_direct_assignment
|
29
32
|
|
data/lib/aasm/core/event.rb
CHANGED
@@ -2,17 +2,18 @@
|
|
2
2
|
|
3
3
|
module AASM::Core
|
4
4
|
class Event
|
5
|
-
include DslHelper
|
5
|
+
include AASM::DslHelper
|
6
6
|
|
7
|
-
attr_reader :name, :state_machine, :options
|
7
|
+
attr_reader :name, :state_machine, :options, :default_display_name
|
8
8
|
|
9
9
|
def initialize(name, state_machine, options = {}, &block)
|
10
10
|
@name = name
|
11
11
|
@state_machine = state_machine
|
12
12
|
@transitions = []
|
13
|
-
@valid_transitions = {}
|
13
|
+
@valid_transitions = Hash.new { |h, k| h[k] = {} }
|
14
14
|
@guards = Array(options[:guard] || options[:guards] || options[:if])
|
15
15
|
@unless = Array(options[:unless]) #TODO: This could use a better name
|
16
|
+
@default_display_name = name.to_s.gsub(/_/, ' ').capitalize
|
16
17
|
|
17
18
|
# from aasm4
|
18
19
|
@options = options # QUESTION: .dup ?
|
@@ -78,8 +79,9 @@ module AASM::Core
|
|
78
79
|
|
79
80
|
def fire_transition_callbacks(obj, *args)
|
80
81
|
from_state = obj.aasm(state_machine.name).current_state
|
81
|
-
transition = @valid_transitions[from_state]
|
82
|
-
|
82
|
+
transition = @valid_transitions[obj.object_id][from_state]
|
83
|
+
transition.invoke_success_callbacks(obj, *args) if transition
|
84
|
+
@valid_transitions.delete(obj.object_id)
|
83
85
|
end
|
84
86
|
|
85
87
|
def ==(event)
|
@@ -109,6 +111,10 @@ module AASM::Core
|
|
109
111
|
transitions.flat_map(&:failures)
|
110
112
|
end
|
111
113
|
|
114
|
+
def to_s
|
115
|
+
name.to_s
|
116
|
+
end
|
117
|
+
|
112
118
|
private
|
113
119
|
|
114
120
|
def attach_event_guards(definitions)
|
@@ -148,7 +154,7 @@ module AASM::Core
|
|
148
154
|
result = transition
|
149
155
|
else
|
150
156
|
result = to_state || Array(transition.to).first
|
151
|
-
Array(transition.to).each {|to| @valid_transitions[to] = transition }
|
157
|
+
Array(transition.to).each {|to| @valid_transitions[obj.object_id][to] = transition }
|
152
158
|
transition.execute(obj, *args)
|
153
159
|
end
|
154
160
|
|
@@ -30,7 +30,9 @@ module AASM
|
|
30
30
|
raise(*record_error) unless record.respond_to?(subject, true)
|
31
31
|
return record.__send__(subject) if subject_arity.zero?
|
32
32
|
return record.__send__(subject, *args) if subject_arity < 0
|
33
|
-
|
33
|
+
req_args = args[0..(subject_arity - 1)]
|
34
|
+
return record.__send__(subject, **req_args[0]) if req_args[0].is_a?(Hash)
|
35
|
+
record.__send__(subject, *req_args)
|
34
36
|
end
|
35
37
|
# rubocop:enable Metrics/AbcSize
|
36
38
|
|
data/lib/aasm/core/state.rb
CHANGED
@@ -2,12 +2,13 @@
|
|
2
2
|
|
3
3
|
module AASM::Core
|
4
4
|
class State
|
5
|
-
attr_reader :name, :state_machine, :options
|
5
|
+
attr_reader :name, :state_machine, :options, :default_display_name
|
6
6
|
|
7
7
|
def initialize(name, klass, state_machine, options={})
|
8
8
|
@name = name
|
9
9
|
@klass = klass
|
10
10
|
@state_machine = state_machine
|
11
|
+
@default_display_name = name.to_s.gsub(/_/, ' ').capitalize
|
11
12
|
update(options)
|
12
13
|
end
|
13
14
|
|
@@ -54,11 +55,11 @@ module AASM::Core
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def display_name
|
57
|
-
@display_name
|
58
|
+
@display_name = begin
|
58
59
|
if Module.const_defined?(:I18n)
|
59
60
|
localized_name
|
60
61
|
else
|
61
|
-
|
62
|
+
@default_display_name
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
@@ -75,8 +76,8 @@ module AASM::Core
|
|
75
76
|
private
|
76
77
|
|
77
78
|
def update(options = {})
|
78
|
-
if options.key?(:display)
|
79
|
-
@
|
79
|
+
if options.key?(:display)
|
80
|
+
@default_display_name = options.delete(:display)
|
80
81
|
end
|
81
82
|
@options = options
|
82
83
|
self
|
data/lib/aasm/core/transition.rb
CHANGED
data/lib/aasm/dsl_helper.rb
CHANGED
@@ -1,30 +1,32 @@
|
|
1
|
-
module
|
1
|
+
module AASM
|
2
|
+
module DslHelper
|
2
3
|
|
3
|
-
|
4
|
-
|
4
|
+
class Proxy
|
5
|
+
attr_accessor :options
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def initialize(options, valid_keys, source)
|
8
|
+
@valid_keys = valid_keys
|
9
|
+
@source = source
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
@options = options
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def method_missing(name, *args, &block)
|
15
|
+
if @valid_keys.include?(name)
|
16
|
+
options[name] = Array(options[name])
|
17
|
+
options[name] << block if block
|
18
|
+
options[name] += Array(args)
|
19
|
+
else
|
20
|
+
@source.send name, *args, &block
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
22
|
-
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
def add_options_from_dsl(options, valid_keys, &block)
|
26
|
+
proxy = Proxy.new(options, valid_keys, self)
|
27
|
+
proxy.instance_eval(&block)
|
28
|
+
proxy.options
|
29
|
+
end
|
29
30
|
|
30
|
-
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/aasm/errors.rb
CHANGED
data/lib/aasm/instance_base.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module AASM
|
2
2
|
class InstanceBase
|
3
|
-
|
4
3
|
attr_accessor :from_state, :to_state, :current_event
|
5
4
|
|
6
5
|
def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
|
@@ -28,7 +27,7 @@ module AASM
|
|
28
27
|
end
|
29
28
|
|
30
29
|
def human_state
|
31
|
-
|
30
|
+
state_object_for_name(current_state).display_name
|
32
31
|
end
|
33
32
|
|
34
33
|
def states(options={}, *args)
|
@@ -115,12 +114,15 @@ module AASM
|
|
115
114
|
end
|
116
115
|
|
117
116
|
def fire(event_name, *args, &block)
|
117
|
+
event_exists?(event_name)
|
118
|
+
|
118
119
|
@instance.send(event_name, *args, &block)
|
119
120
|
end
|
120
121
|
|
121
122
|
def fire!(event_name, *args, &block)
|
122
|
-
event_name
|
123
|
-
|
123
|
+
event_exists?(event_name, true)
|
124
|
+
bang_event_name = "#{event_name}!".to_sym
|
125
|
+
@instance.send(bang_event_name, *args, &block)
|
124
126
|
end
|
125
127
|
|
126
128
|
def set_current_state_with_persistence(state)
|
@@ -129,5 +131,14 @@ module AASM
|
|
129
131
|
save_success
|
130
132
|
end
|
131
133
|
|
134
|
+
private
|
135
|
+
|
136
|
+
def event_exists?(event_name, bang = false)
|
137
|
+
event = @instance.class.aasm(@name).state_machine.events[event_name.to_sym]
|
138
|
+
return true if event
|
139
|
+
|
140
|
+
event_error = bang ? "#{event_name}!" : event_name
|
141
|
+
raise AASM::UndefinedEvent, "Event :#{event_error} doesn't exist" if event.nil?
|
142
|
+
end
|
132
143
|
end
|
133
144
|
end
|
data/lib/aasm/localizer.rb
CHANGED
@@ -5,7 +5,7 @@ module AASM
|
|
5
5
|
list << :"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
|
6
6
|
list
|
7
7
|
end
|
8
|
-
translate_queue(checklist) || I18n.translate(checklist.shift, :default => event
|
8
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => default_display_name(event))
|
9
9
|
end
|
10
10
|
|
11
11
|
def human_state_name(klass, state)
|
@@ -14,7 +14,7 @@ module AASM
|
|
14
14
|
list << item_for(klass, state, ancestor, :old_style => true)
|
15
15
|
list
|
16
16
|
end
|
17
|
-
translate_queue(checklist) || I18n.translate(checklist.shift, :default => state
|
17
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => default_display_name(state))
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
@@ -46,8 +46,18 @@ module AASM
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def ancestors_list(klass)
|
49
|
+
has_active_record_base = defined?(::ActiveRecord::Base)
|
49
50
|
klass.ancestors.select do |ancestor|
|
50
|
-
|
51
|
+
not_active_record_base = has_active_record_base ? (ancestor != ::ActiveRecord::Base) : true
|
52
|
+
ancestor.respond_to?(:model_name) && not_active_record_base
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_display_name(object) # Can use better arguement name
|
57
|
+
if object.respond_to?(:default_display_name)
|
58
|
+
object.default_display_name
|
59
|
+
else
|
60
|
+
object.to_s.gsub(/_/, ' ').capitalize
|
51
61
|
end
|
52
62
|
end
|
53
63
|
end
|
@@ -61,6 +61,24 @@ module AASM
|
|
61
61
|
|
62
62
|
private
|
63
63
|
|
64
|
+
def aasm_execute_after_commit
|
65
|
+
begin
|
66
|
+
require 'after_commit_everywhere'
|
67
|
+
raise LoadError unless Gem::Version.new(::AfterCommitEverywhere::VERSION) >= Gem::Version.new('0.1.5')
|
68
|
+
|
69
|
+
self.extend ::AfterCommitEverywhere
|
70
|
+
after_commit do
|
71
|
+
yield
|
72
|
+
end
|
73
|
+
rescue LoadError
|
74
|
+
warn <<-MSG
|
75
|
+
[DEPRECATION] :after_commit AASM callback is not safe in terms of race conditions and redundant calls.
|
76
|
+
Please add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile in order to fix that.
|
77
|
+
MSG
|
78
|
+
yield
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
64
82
|
def aasm_raise_invalid_record
|
65
83
|
raise ActiveRecord::RecordInvalid.new(self)
|
66
84
|
end
|