aasm 5.1.1 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Appraisals +5 -5
- data/CHANGELOG.md +13 -2
- data/README.md +59 -6
- data/aasm.gemspec +1 -1
- data/gemfiles/rails_4.2.gemfile +1 -1
- data/gemfiles/rails_4.2_mongoid_5.gemfile +1 -1
- data/gemfiles/rails_5.0.gemfile +1 -1
- data/gemfiles/rails_5.1.gemfile +1 -1
- data/gemfiles/rails_5.2.gemfile +1 -1
- data/lib/aasm/base.rb +30 -11
- data/lib/aasm/configuration.rb +3 -0
- data/lib/aasm/core/event.rb +7 -2
- 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/localizer.rb +13 -3
- data/lib/aasm/persistence/active_record_persistence.rb +1 -1
- data/lib/aasm/persistence/base.rb +13 -2
- data/lib/aasm/version.rb +1 -1
- data/spec/database.rb +9 -11
- data/spec/localizer_test_model_deprecated_style.yml +7 -0
- data/spec/localizer_test_model_new_style.yml +6 -0
- data/spec/models/active_record/localizer_test_model.rb +8 -0
- data/spec/models/active_record/namespaced.rb +16 -0
- data/spec/models/active_record/timestamp_example.rb +16 -0
- data/spec/models/mongoid/timestamp_example_mongoid.rb +20 -0
- data/spec/models/timestamps_example.rb +19 -0
- data/spec/models/timestamps_with_named_machine_example.rb +13 -0
- data/spec/unit/localizer_spec.rb +40 -8
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +17 -0
- data/spec/unit/persistence/active_record_persistence_spec.rb +12 -0
- data/spec/unit/persistence/mongoid_persistence_spec.rb +12 -0
- data/spec/unit/timestamps_spec.rb +32 -0
- metadata +16 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e3ec7032fd9c8368ddfb4bed4e931edc5f9d96c02a69f89f7ee26913f7d8698
|
4
|
+
data.tar.gz: ddb4ef39501440da1869426eda3b4359e37eeef67b464c9e0d660e9f353510f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c294de071cad6569f855af1455c9d0281622ca5c9a3df0466baacaca569d8974718eb46d4c06e674f54629a77e2634d69829ee2910612ba146b4aaf786891544
|
7
|
+
data.tar.gz: a7b0c176cec262ad9d89538fe43e6df484f4d79328f85e853dcc9fe6b6ce9a9629f802e7de14659e5c91135a3a4df09a6066011983b1936eff06b17657c24cc5
|
data/Appraisals
CHANGED
@@ -8,7 +8,7 @@ appraise 'rails_4.2' do
|
|
8
8
|
gem 'aws-sdk', '~> 2', platforms: :ruby
|
9
9
|
gem 'redis-objects'
|
10
10
|
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
|
11
|
-
gem "after_commit_everywhere", "~>
|
11
|
+
gem "after_commit_everywhere", "~> 1.0"
|
12
12
|
end
|
13
13
|
|
14
14
|
appraise 'rails_4.2_nobrainer' do
|
@@ -21,7 +21,7 @@ appraise 'rails_4.2_mongoid_5' do
|
|
21
21
|
gem 'rails', '4.2.5'
|
22
22
|
gem 'mongoid', '~> 5.0'
|
23
23
|
gem 'activerecord-jdbcsqlite3-adapter', '1.3.24', platforms: :jruby
|
24
|
-
gem "after_commit_everywhere", "~>
|
24
|
+
gem "after_commit_everywhere", "~> 1.0"
|
25
25
|
end
|
26
26
|
|
27
27
|
appraise 'rails_5.0' do
|
@@ -31,7 +31,7 @@ appraise 'rails_5.0' do
|
|
31
31
|
gem 'dynamoid', '~> 1.3', platforms: :ruby
|
32
32
|
gem 'aws-sdk', '~> 2', platforms: :ruby
|
33
33
|
gem 'redis-objects'
|
34
|
-
gem "after_commit_everywhere", "~>
|
34
|
+
gem "after_commit_everywhere", "~> 1.0"
|
35
35
|
end
|
36
36
|
|
37
37
|
appraise 'rails_5.0_nobrainer' do
|
@@ -46,7 +46,7 @@ appraise 'rails_5.1' do
|
|
46
46
|
gem 'dynamoid', '~> 1.3', platforms: :ruby
|
47
47
|
gem 'aws-sdk', '~>2', platforms: :ruby
|
48
48
|
gem 'redis-objects'
|
49
|
-
gem "after_commit_everywhere", "~>
|
49
|
+
gem "after_commit_everywhere", "~> 1.0"
|
50
50
|
end
|
51
51
|
|
52
52
|
appraise 'rails_5.2' do
|
@@ -56,7 +56,7 @@ appraise 'rails_5.2' do
|
|
56
56
|
gem 'dynamoid', '~>2.2', platforms: :ruby
|
57
57
|
gem 'aws-sdk', '~>2', platforms: :ruby
|
58
58
|
gem 'redis-objects'
|
59
|
-
gem "after_commit_everywhere", "~>
|
59
|
+
gem "after_commit_everywhere", "~> 1.0"
|
60
60
|
end
|
61
61
|
|
62
62
|
appraise 'norails' do
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
3
|
## unreleased
|
4
|
-
|
5
|
-
|
4
|
+
## 5.2.0
|
5
|
+
|
6
|
+
* fix: timestamp will work with named machine [#739](https://github.com/aasm/aasm/pull/739), thanks to [RolandStuder](https://github.com/RolandStuder)
|
7
|
+
* Create namespaced scopes in PR [#735](https://github.com/aasm/aasm/pull/735), thanks to [caiohsramos](https://github.com/caiohsramos)
|
8
|
+
* Fix multiple state machines example per class on README in PR [#732](https://github.com/aasm/aasm/pull/732), thanks to [RodrigoVitiello](https://github.com/RodrigoVitiello)
|
9
|
+
* Update version in recommendation to add after_commit_everywhere in PR [#729](https://github.com/aasm/aasm/pull/729), thanks to [Envek](https://github.com/Envek)
|
10
|
+
* Fix i18n Event translations failing [#721](https://github.com/aasm/aasm/issues/721) in PR [#723](https://github.com/aasm/aasm/pull/723), thanks to [the-spectator](https://github.com/the-spectator)
|
11
|
+
* Add documentation to the Readme about how parameters are handled in AASM events in PR [#722](https://github.com/aasm/aasm/pull/722), thanks to [dstuebe](https://github.com/dstuebe)
|
12
|
+
* Fix human_state cached across locales [#709](https://github.com/aasm/aasm/issues/709) in PR [716](https://github.com/aasm/aasm/pull/716), thanks to [the-spectator](https://github.com/the-spectator)
|
13
|
+
* Relocate DslHelper from root namespace to under AASM namespace in PR [#711](https://github.com/aasm/aasm/pull/711) thank to [yujideveloper ](https://github.com/yujideveloper )
|
14
|
+
* Document how to define transitions from any state in in PR [#699](https://github.com/aasm/aasm/pull/699) thanks to [hedgesky](https://github.com/hedgesky)
|
15
|
+
* Add simple option for auto-generated timestamps in PR [#677](https://github.com/aasm/aasm/pull/677), thanks to [jaynetics](https://github.com/jaynetics)
|
16
|
+
## 5.1.1
|
6
17
|
|
7
18
|
* Fix Depreciation message for after_commit_everywhere [#695](https://github.com/aasm/aasm/issues/695) in PR [#696](https://github.com/aasm/aasm/pull/696)
|
8
19
|
* Fix human_state to use display option [#684](https://github.com/aasm/aasm/issues/684) in PR [#697](https://github.com/aasm/aasm/pull/697)
|
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 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
|
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
|
|
@@ -434,6 +448,14 @@ job.stage1_completed
|
|
434
448
|
job.aasm.current_state # stage3
|
435
449
|
```
|
436
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
|
+
|
437
459
|
### Display name for state
|
438
460
|
|
439
461
|
You can define display name for state using :display option
|
@@ -495,13 +517,13 @@ simple = SimpleMultipleExample.new
|
|
495
517
|
|
496
518
|
simple.aasm(:move).current_state
|
497
519
|
# => :standing
|
498
|
-
simple.aasm(:work).
|
520
|
+
simple.aasm(:work).current_state
|
499
521
|
# => :sleeping
|
500
522
|
|
501
523
|
simple.start
|
502
524
|
simple.aasm(:move).current_state
|
503
525
|
# => :standing
|
504
|
-
simple.aasm(:work).
|
526
|
+
simple.aasm(:work).current_state
|
505
527
|
# => :processing
|
506
528
|
|
507
529
|
```
|
@@ -698,7 +720,7 @@ end
|
|
698
720
|
AASM comes with support for ActiveRecord and allows automatic persisting of the object's
|
699
721
|
state in the database.
|
700
722
|
|
701
|
-
Add `gem 'after_commit_everywhere', '~>
|
723
|
+
Add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile.
|
702
724
|
|
703
725
|
```ruby
|
704
726
|
class Job < ActiveRecord::Base
|
@@ -800,6 +822,37 @@ job.aasm_state = :running # => raises AASM::NoDirectAssignmentError
|
|
800
822
|
job.aasm_state # => 'sleeping'
|
801
823
|
```
|
802
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
|
+
|
803
856
|
#### ActiveRecord enums
|
804
857
|
|
805
858
|
You can use
|
@@ -1007,7 +1060,7 @@ job.save! #notify_about_running_job is not run
|
|
1007
1060
|
Please note that `:after_commit` AASM callbacks behaves around custom implementation
|
1008
1061
|
of transaction pattern rather than a real-life DB transaction. This fact still causes
|
1009
1062
|
the race conditions and redundant callback calls within nested transaction. In order
|
1010
|
-
to fix that it's highly recommended to add `gem 'after_commit_everywhere', '~>
|
1063
|
+
to fix that it's highly recommended to add `gem 'after_commit_everywhere', '~> 1.0'`
|
1011
1064
|
to your `Gemfile`.
|
1012
1065
|
|
1013
1066
|
If you want to encapsulate state changes within an own transaction, the behavior
|
data/aasm.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_development_dependency 'generator_spec'
|
25
25
|
s.add_development_dependency 'appraisal'
|
26
26
|
s.add_development_dependency "simplecov"
|
27
|
-
s.add_development_dependency "codecov", ">= 0.1.
|
27
|
+
s.add_development_dependency "codecov", ">= 0.1.21"
|
28
28
|
|
29
29
|
# debugging
|
30
30
|
# s.add_development_dependency 'debugger'
|
data/gemfiles/rails_4.2.gemfile
CHANGED
@@ -12,6 +12,6 @@ gem "dynamoid", "~> 1", platforms: :ruby
|
|
12
12
|
gem "aws-sdk", "~> 2", platforms: :ruby
|
13
13
|
gem "redis-objects"
|
14
14
|
gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
|
15
|
-
gem "after_commit_everywhere", "~>
|
15
|
+
gem "after_commit_everywhere", "~> 1.0"
|
16
16
|
|
17
17
|
gemspec path: "../"
|
@@ -7,6 +7,6 @@ gem "rails", "4.2.5"
|
|
7
7
|
gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
|
8
8
|
gem "mongoid", "~> 5.0"
|
9
9
|
gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
|
10
|
-
gem "after_commit_everywhere", "~>
|
10
|
+
gem "after_commit_everywhere", "~> 1.0"
|
11
11
|
|
12
12
|
gemspec path: "../"
|
data/gemfiles/rails_5.0.gemfile
CHANGED
data/gemfiles/rails_5.1.gemfile
CHANGED
data/gemfiles/rails_5.2.gemfile
CHANGED
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
|
@@ -267,5 +263,28 @@ module AASM
|
|
267
263
|
end
|
268
264
|
end
|
269
265
|
|
266
|
+
def setup_timestamps(aasm_name)
|
267
|
+
return unless @state_machine.config.timestamps
|
268
|
+
|
269
|
+
after_all_transitions do
|
270
|
+
if self.class.aasm(:"#{aasm_name}").state_machine.config.timestamps
|
271
|
+
ts_setter = "#{aasm(aasm_name).to_state}_at="
|
272
|
+
respond_to?(ts_setter) && send(ts_setter, ::Time.now)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def setup_no_direct_assignment(aasm_name)
|
278
|
+
return unless @state_machine.config.no_direct_assignment
|
279
|
+
|
280
|
+
@klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name|
|
281
|
+
if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
|
282
|
+
raise AASM::NoDirectAssignmentError.new('direct assignment of AASM column has been disabled (see AASM configuration for this class)')
|
283
|
+
else
|
284
|
+
super(state_name)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
270
289
|
end
|
271
290
|
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,9 +2,9 @@
|
|
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
|
@@ -13,6 +13,7 @@ module AASM::Core
|
|
13
13
|
@valid_transitions = {}
|
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 ?
|
@@ -109,6 +110,10 @@ module AASM::Core
|
|
109
110
|
transitions.flat_map(&:failures)
|
110
111
|
end
|
111
112
|
|
113
|
+
def to_s
|
114
|
+
name.to_s
|
115
|
+
end
|
116
|
+
|
112
117
|
private
|
113
118
|
|
114
119
|
def attach_event_guards(definitions)
|
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/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
|
@@ -73,7 +73,7 @@ module AASM
|
|
73
73
|
rescue LoadError
|
74
74
|
warn <<-MSG
|
75
75
|
[DEPRECATION] :after_commit AASM callback is not safe in terms of race conditions and redundant calls.
|
76
|
-
Please add `gem 'after_commit_everywhere', '~>
|
76
|
+
Please add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile in order to fix that.
|
77
77
|
MSG
|
78
78
|
yield
|
79
79
|
end
|
@@ -59,7 +59,9 @@ module AASM
|
|
59
59
|
# make sure to create a (named) scope for each state
|
60
60
|
def state_with_scope(*args)
|
61
61
|
names = state_without_scope(*args)
|
62
|
-
names.each
|
62
|
+
names.each do |name|
|
63
|
+
create_scopes(name)
|
64
|
+
end
|
63
65
|
end
|
64
66
|
alias_method :state_without_scope, :state
|
65
67
|
alias_method :state, :state_with_scope
|
@@ -71,7 +73,16 @@ module AASM
|
|
71
73
|
end
|
72
74
|
|
73
75
|
def create_scope(name)
|
74
|
-
@klass.aasm_create_scope(@name, name)
|
76
|
+
@klass.aasm_create_scope(@name, name) if create_scope?(name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_scopes(name)
|
80
|
+
if namespace?
|
81
|
+
# Create default scopes even when namespace? for backward compatibility
|
82
|
+
namepaced_name = "#{namespace}_#{name}"
|
83
|
+
create_scope(namepaced_name)
|
84
|
+
end
|
85
|
+
create_scope(name)
|
75
86
|
end
|
76
87
|
end # Base
|
77
88
|
|
data/lib/aasm/version.rb
CHANGED
data/spec/database.rb
CHANGED
@@ -5,17 +5,10 @@ ActiveRecord::Migration.suppress_messages do
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
t.string "status"
|
13
|
-
end
|
14
|
-
ActiveRecord::Migration.create_table "implemented_abstract_class_dsls", :force => true do |t|
|
15
|
-
t.string "status"
|
16
|
-
end
|
17
|
-
ActiveRecord::Migration.create_table "users", :force => true do |t|
|
18
|
-
t.string "status"
|
8
|
+
%w(simple_new_dsls multiple_simple_new_dsls implemented_abstract_class_dsls users multiple_namespaceds).each do |table_name|
|
9
|
+
ActiveRecord::Migration.create_table table_name, :force => true do |t|
|
10
|
+
t.string "status"
|
11
|
+
end
|
19
12
|
end
|
20
13
|
|
21
14
|
ActiveRecord::Migration.create_table "complex_active_record_examples", :force => true do |t|
|
@@ -56,4 +49,9 @@ ActiveRecord::Migration.suppress_messages do
|
|
56
49
|
t.string "state"
|
57
50
|
t.string "some_string"
|
58
51
|
end
|
52
|
+
|
53
|
+
ActiveRecord::Migration.create_table "timestamp_examples", :force => true do |t|
|
54
|
+
t.string "aasm_state"
|
55
|
+
t.datetime "opened_at"
|
56
|
+
end
|
59
57
|
end
|
@@ -24,11 +24,19 @@ describe 'localized state names' do
|
|
24
24
|
state = LocalizerTestModel.aasm.states.detect {|s| s == :opened}
|
25
25
|
expect(state.localized_name).to eq("It's open now!")
|
26
26
|
expect(state.human_name).to eq("It's open now!")
|
27
|
+
expect(state.display_name).to eq("It's open now!")
|
28
|
+
|
29
|
+
I18n.with_locale(:fr) do
|
30
|
+
expect(state.localized_name).to eq("C'est ouvert maintenant!")
|
31
|
+
expect(state.human_name).to eq("C'est ouvert maintenant!")
|
32
|
+
expect(state.display_name).to eq("C'est ouvert maintenant!")
|
33
|
+
end
|
27
34
|
end
|
28
35
|
|
29
36
|
it 'should use fallback' do
|
30
37
|
state = LocalizerTestModel.aasm.states.detect {|s| s == :closed}
|
31
38
|
expect(state.localized_name).to eq('Closed')
|
32
39
|
expect(state.human_name).to eq('Closed')
|
40
|
+
expect(state.display_name).to eq('Closed')
|
33
41
|
end
|
34
42
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class MultipleNamespaced < ActiveRecord::Base
|
2
|
+
include AASM
|
3
|
+
|
4
|
+
aasm(:status, namespace: :car) do
|
5
|
+
state :unsold, initial: true
|
6
|
+
state :sold
|
7
|
+
|
8
|
+
event :sell do
|
9
|
+
transitions from: :unsold, to: :sold
|
10
|
+
end
|
11
|
+
|
12
|
+
event :return do
|
13
|
+
transitions from: :sold, to: :unsold
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class TimestampExample < ActiveRecord::Base
|
2
|
+
include AASM
|
3
|
+
|
4
|
+
aasm column: :aasm_state, timestamps: true do
|
5
|
+
state :opened
|
6
|
+
state :closed
|
7
|
+
|
8
|
+
event :open do
|
9
|
+
transitions to: :opened
|
10
|
+
end
|
11
|
+
|
12
|
+
event :close do
|
13
|
+
transitions to: :closed
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class TimestampExampleMongoid
|
2
|
+
include Mongoid::Document
|
3
|
+
include AASM
|
4
|
+
|
5
|
+
field :status, type: String
|
6
|
+
field :opened_at, type: Time
|
7
|
+
|
8
|
+
aasm column: :status, timestamps: true do
|
9
|
+
state :opened
|
10
|
+
state :closed
|
11
|
+
|
12
|
+
event :open do
|
13
|
+
transitions to: :opened
|
14
|
+
end
|
15
|
+
|
16
|
+
event :close do
|
17
|
+
transitions to: :closed
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class TimestampsExample
|
2
|
+
include AASM
|
3
|
+
|
4
|
+
attr_accessor :opened_at
|
5
|
+
attr_reader :closed_at
|
6
|
+
|
7
|
+
aasm timestamps: true do
|
8
|
+
state :opened
|
9
|
+
state :closed
|
10
|
+
|
11
|
+
event :open do
|
12
|
+
transitions to: :opened
|
13
|
+
end
|
14
|
+
|
15
|
+
event :close do
|
16
|
+
transitions to: :closed
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/unit/localizer_spec.rb
CHANGED
@@ -29,12 +29,28 @@ if defined?(ActiveRecord)
|
|
29
29
|
end
|
30
30
|
|
31
31
|
context 'aasm.human_event_name' do
|
32
|
-
|
33
|
-
|
32
|
+
context 'with event name' do
|
33
|
+
it 'should return translated event name' do
|
34
|
+
expect(LocalizerTestModel.aasm.human_event_name(:close)).to eq("Let's close it!")
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should return humanized event name' do
|
38
|
+
expect(LocalizerTestModel.aasm.human_event_name(:open)).to eq("Open")
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
|
-
|
37
|
-
|
42
|
+
context 'with event object' do
|
43
|
+
it 'should return translated event name' do
|
44
|
+
event = LocalizerTestModel.aasm.events.detect { |e| e.name == :close }
|
45
|
+
|
46
|
+
expect(LocalizerTestModel.aasm.human_event_name(event)).to eq("Let's close it!")
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should return humanized event name' do
|
50
|
+
event = LocalizerTestModel.aasm.events.detect { |e| e.name == :open }
|
51
|
+
|
52
|
+
expect(LocalizerTestModel.aasm.human_event_name(event)).to eq("Open")
|
53
|
+
end
|
38
54
|
end
|
39
55
|
end
|
40
56
|
end
|
@@ -65,12 +81,28 @@ if defined?(ActiveRecord)
|
|
65
81
|
end
|
66
82
|
|
67
83
|
context 'aasm.human_event_name' do
|
68
|
-
|
69
|
-
|
84
|
+
context 'with event name' do
|
85
|
+
it 'should return translated event name' do
|
86
|
+
expect(LocalizerTestModel.aasm.human_event_name(:close)).to eq("Let's close it!")
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should return humanized event name' do
|
90
|
+
expect(LocalizerTestModel.aasm.human_event_name(:open)).to eq("Open")
|
91
|
+
end
|
70
92
|
end
|
71
93
|
|
72
|
-
|
73
|
-
|
94
|
+
context 'with event object' do
|
95
|
+
it 'should return translated event name' do
|
96
|
+
event = LocalizerTestModel.aasm.events.detect { |e| e.name == :close }
|
97
|
+
|
98
|
+
expect(LocalizerTestModel.aasm.human_event_name(event)).to eq("Let's close it!")
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should return humanized event name' do
|
102
|
+
event = LocalizerTestModel.aasm.events.detect { |e| e.name == :open }
|
103
|
+
|
104
|
+
expect(LocalizerTestModel.aasm.human_event_name(event)).to eq("Open")
|
105
|
+
end
|
74
106
|
end
|
75
107
|
end
|
76
108
|
end
|
@@ -331,6 +331,23 @@ if defined?(ActiveRecord)
|
|
331
331
|
expect(MultipleSimpleNewDsl.unknown_scope).to contain_exactly(dsl2)
|
332
332
|
end
|
333
333
|
end
|
334
|
+
|
335
|
+
context "when namespeced" do
|
336
|
+
it "add namespaced scopes" do
|
337
|
+
expect(MultipleNamespaced).to respond_to(:car_unsold)
|
338
|
+
expect(MultipleNamespaced).to respond_to(:car_sold)
|
339
|
+
|
340
|
+
expect(MultipleNamespaced.car_unsold.is_a?(ActiveRecord::Relation)).to be_truthy
|
341
|
+
expect(MultipleNamespaced.car_sold.is_a?(ActiveRecord::Relation)).to be_truthy
|
342
|
+
end
|
343
|
+
it "add unnamespaced scopes" do
|
344
|
+
expect(MultipleNamespaced).to respond_to(:unsold)
|
345
|
+
expect(MultipleNamespaced).to respond_to(:sold)
|
346
|
+
|
347
|
+
expect(MultipleNamespaced.unsold.is_a?(ActiveRecord::Relation)).to be_truthy
|
348
|
+
expect(MultipleNamespaced.sold.is_a?(ActiveRecord::Relation)).to be_truthy
|
349
|
+
end
|
350
|
+
end
|
334
351
|
end # scopes
|
335
352
|
|
336
353
|
describe "direct assignment" do
|
@@ -837,4 +837,16 @@ if defined?(ActiveRecord)
|
|
837
837
|
expect(example.complete!).to be_falsey
|
838
838
|
end
|
839
839
|
end
|
840
|
+
|
841
|
+
describe 'testing the timestamps option' do
|
842
|
+
let(:example) { TimestampExample.create! }
|
843
|
+
|
844
|
+
it 'should update existing timestamp columns' do
|
845
|
+
expect { example.open! }.to change { example.reload.opened_at }.from(nil).to(instance_of(::Time))
|
846
|
+
end
|
847
|
+
|
848
|
+
it 'should not fail if there is no corresponding timestamp column' do
|
849
|
+
expect { example.close! }.to change { example.reload.aasm_state }
|
850
|
+
end
|
851
|
+
end
|
840
852
|
end
|
@@ -161,5 +161,17 @@ if defined?(Mongoid::Document)
|
|
161
161
|
end
|
162
162
|
end
|
163
163
|
|
164
|
+
describe 'testing the timestamps option' do
|
165
|
+
let(:example) { TimestampExampleMongoid.create }
|
166
|
+
|
167
|
+
it 'should update existing timestamp fields' do
|
168
|
+
expect { example.open! }.to change { example.reload.opened_at }.from(nil).to(instance_of(::Time))
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'should not fail if there is no corresponding timestamp field' do
|
172
|
+
expect { example.close! }.to change { example.reload.status }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
164
176
|
end
|
165
177
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'timestamps option' do
|
4
|
+
it 'calls a timestamp setter based on the state name when entering a new state' do
|
5
|
+
object = TimestampsExample.new
|
6
|
+
expect { object.open }.to change { object.opened_at }.from(nil).to(instance_of(::Time))
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'overwrites any previous timestamp if a state is entered repeatedly' do
|
10
|
+
object = TimestampsExample.new
|
11
|
+
object.opened_at = ::Time.new(2000, 1, 1)
|
12
|
+
expect { object.open }.to change { object.opened_at }
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'does nothing if there is no setter matching the new state' do
|
16
|
+
object = TimestampsExample.new
|
17
|
+
expect { object.close }.not_to change { object.closed_at }
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can be turned off and on' do
|
21
|
+
object = TimestampsExample.new
|
22
|
+
object.class.aasm.state_machine.config.timestamps = false
|
23
|
+
expect { object.open }.not_to change { object.opened_at }
|
24
|
+
object.class.aasm.state_machine.config.timestamps = true
|
25
|
+
expect { object.open }.to change { object.opened_at }
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'calls a timestamp setter when using a named state machine' do
|
29
|
+
object = TimestampsWithNamedMachineExample.new
|
30
|
+
expect { object.open }.to change { object.opened_at }.from(nil).to(instance_of(::Time))
|
31
|
+
end
|
32
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aasm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thorsten Boettger
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-05-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: concurrent-ruby
|
@@ -115,20 +115,14 @@ dependencies:
|
|
115
115
|
requirements:
|
116
116
|
- - ">="
|
117
117
|
- !ruby/object:Gem::Version
|
118
|
-
version: 0.1.
|
119
|
-
- - "<"
|
120
|
-
- !ruby/object:Gem::Version
|
121
|
-
version: 0.1.20
|
118
|
+
version: 0.1.21
|
122
119
|
type: :development
|
123
120
|
prerelease: false
|
124
121
|
version_requirements: !ruby/object:Gem::Requirement
|
125
122
|
requirements:
|
126
123
|
- - ">="
|
127
124
|
- !ruby/object:Gem::Version
|
128
|
-
version: 0.1.
|
129
|
-
- - "<"
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: 0.1.20
|
125
|
+
version: 0.1.21
|
132
126
|
- !ruby/object:Gem::Dependency
|
133
127
|
name: pry
|
134
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -246,6 +240,7 @@ files:
|
|
246
240
|
- spec/models/active_record/instance_level_skip_validation_example.rb
|
247
241
|
- spec/models/active_record/invalid_persistor.rb
|
248
242
|
- spec/models/active_record/localizer_test_model.rb
|
243
|
+
- spec/models/active_record/namespaced.rb
|
249
244
|
- spec/models/active_record/no_direct_assignment.rb
|
250
245
|
- spec/models/active_record/no_scope.rb
|
251
246
|
- spec/models/active_record/persisted_state.rb
|
@@ -256,6 +251,7 @@ files:
|
|
256
251
|
- spec/models/active_record/silent_persistor.rb
|
257
252
|
- spec/models/active_record/simple_new_dsl.rb
|
258
253
|
- spec/models/active_record/thief.rb
|
254
|
+
- spec/models/active_record/timestamp_example.rb
|
259
255
|
- spec/models/active_record/transactor.rb
|
260
256
|
- spec/models/active_record/transient.rb
|
261
257
|
- spec/models/active_record/validator.rb
|
@@ -302,6 +298,7 @@ files:
|
|
302
298
|
- spec/models/mongoid/silent_persistor_mongoid.rb
|
303
299
|
- spec/models/mongoid/simple_mongoid.rb
|
304
300
|
- spec/models/mongoid/simple_new_dsl_mongoid.rb
|
301
|
+
- spec/models/mongoid/timestamp_example_mongoid.rb
|
305
302
|
- spec/models/mongoid/validator_mongoid.rb
|
306
303
|
- spec/models/multi_transitioner.rb
|
307
304
|
- spec/models/multiple_transitions_that_differ_only_by_guard.rb
|
@@ -343,6 +340,8 @@ files:
|
|
343
340
|
- spec/models/sub_classing.rb
|
344
341
|
- spec/models/super_class.rb
|
345
342
|
- spec/models/this_name_better_not_be_in_use.rb
|
343
|
+
- spec/models/timestamps_example.rb
|
344
|
+
- spec/models/timestamps_with_named_machine_example.rb
|
346
345
|
- spec/models/valid_state_name.rb
|
347
346
|
- spec/spec_helper.rb
|
348
347
|
- spec/spec_helpers/active_record.rb
|
@@ -407,6 +406,7 @@ files:
|
|
407
406
|
- spec/unit/states_on_one_line_example_spec.rb
|
408
407
|
- spec/unit/subclassing_multiple_spec.rb
|
409
408
|
- spec/unit/subclassing_spec.rb
|
409
|
+
- spec/unit/timestamps_spec.rb
|
410
410
|
- spec/unit/transition_spec.rb
|
411
411
|
- test/minitest_helper.rb
|
412
412
|
- test/unit/minitest_matcher_test.rb
|
@@ -451,6 +451,7 @@ test_files:
|
|
451
451
|
- spec/models/active_record/instance_level_skip_validation_example.rb
|
452
452
|
- spec/models/active_record/invalid_persistor.rb
|
453
453
|
- spec/models/active_record/localizer_test_model.rb
|
454
|
+
- spec/models/active_record/namespaced.rb
|
454
455
|
- spec/models/active_record/no_direct_assignment.rb
|
455
456
|
- spec/models/active_record/no_scope.rb
|
456
457
|
- spec/models/active_record/persisted_state.rb
|
@@ -461,6 +462,7 @@ test_files:
|
|
461
462
|
- spec/models/active_record/silent_persistor.rb
|
462
463
|
- spec/models/active_record/simple_new_dsl.rb
|
463
464
|
- spec/models/active_record/thief.rb
|
465
|
+
- spec/models/active_record/timestamp_example.rb
|
464
466
|
- spec/models/active_record/transactor.rb
|
465
467
|
- spec/models/active_record/transient.rb
|
466
468
|
- spec/models/active_record/validator.rb
|
@@ -507,6 +509,7 @@ test_files:
|
|
507
509
|
- spec/models/mongoid/silent_persistor_mongoid.rb
|
508
510
|
- spec/models/mongoid/simple_mongoid.rb
|
509
511
|
- spec/models/mongoid/simple_new_dsl_mongoid.rb
|
512
|
+
- spec/models/mongoid/timestamp_example_mongoid.rb
|
510
513
|
- spec/models/mongoid/validator_mongoid.rb
|
511
514
|
- spec/models/multi_transitioner.rb
|
512
515
|
- spec/models/multiple_transitions_that_differ_only_by_guard.rb
|
@@ -548,6 +551,8 @@ test_files:
|
|
548
551
|
- spec/models/sub_classing.rb
|
549
552
|
- spec/models/super_class.rb
|
550
553
|
- spec/models/this_name_better_not_be_in_use.rb
|
554
|
+
- spec/models/timestamps_example.rb
|
555
|
+
- spec/models/timestamps_with_named_machine_example.rb
|
551
556
|
- spec/models/valid_state_name.rb
|
552
557
|
- spec/spec_helper.rb
|
553
558
|
- spec/spec_helpers/active_record.rb
|
@@ -612,6 +617,7 @@ test_files:
|
|
612
617
|
- spec/unit/states_on_one_line_example_spec.rb
|
613
618
|
- spec/unit/subclassing_multiple_spec.rb
|
614
619
|
- spec/unit/subclassing_spec.rb
|
620
|
+
- spec/unit/timestamps_spec.rb
|
615
621
|
- spec/unit/transition_spec.rb
|
616
622
|
- test/minitest_helper.rb
|
617
623
|
- test/unit/minitest_matcher_test.rb
|