aasm 4.11.1 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.travis.yml +56 -23
- data/Appraisals +67 -0
- data/CHANGELOG.md +112 -0
- data/CONTRIBUTING.md +24 -0
- data/Dockerfile +44 -0
- data/Gemfile +3 -21
- data/Gemfile.lock_old +151 -0
- data/LICENSE +1 -1
- data/README.md +540 -139
- data/Rakefile +6 -1
- data/TESTING.md +25 -0
- data/aasm.gemspec +5 -0
- data/docker-compose.yml +40 -0
- data/gemfiles/norails.gemfile +10 -0
- data/gemfiles/rails_4.2.gemfile +13 -11
- data/gemfiles/rails_4.2_mongoid_5.gemfile +8 -11
- data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
- data/gemfiles/rails_5.0.gemfile +11 -18
- data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
- data/gemfiles/rails_5.1.gemfile +14 -0
- data/gemfiles/rails_5.2.gemfile +14 -0
- data/lib/aasm/aasm.rb +40 -29
- data/lib/aasm/base.rb +61 -11
- data/lib/aasm/configuration.rb +10 -0
- data/lib/aasm/core/event.rb +45 -37
- 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 +47 -0
- data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
- data/lib/aasm/core/state.rb +22 -13
- data/lib/aasm/core/transition.rb +17 -69
- data/lib/aasm/dsl_helper.rb +24 -22
- data/lib/aasm/errors.rb +4 -6
- data/lib/aasm/instance_base.rb +22 -4
- data/lib/aasm/localizer.rb +13 -3
- data/lib/aasm/minitest/allow_event.rb +13 -0
- data/lib/aasm/minitest/allow_transition_to.rb +13 -0
- data/lib/aasm/minitest/have_state.rb +13 -0
- data/lib/aasm/minitest/transition_from.rb +21 -0
- data/lib/aasm/minitest.rb +5 -0
- data/lib/aasm/minitest_spec.rb +15 -0
- data/lib/aasm/persistence/active_record_persistence.rb +49 -105
- data/lib/aasm/persistence/base.rb +20 -5
- 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 +26 -32
- data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
- data/lib/aasm/persistence/orm.rb +154 -0
- data/lib/aasm/persistence/plain_persistence.rb +2 -1
- data/lib/aasm/persistence/redis_persistence.rb +16 -11
- data/lib/aasm/persistence/sequel_persistence.rb +36 -64
- data/lib/aasm/persistence.rb +3 -3
- 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/state_machine.rb +4 -2
- data/lib/aasm/state_machine_store.rb +5 -2
- data/lib/aasm/version.rb +1 -1
- data/lib/aasm.rb +5 -2
- data/lib/generators/aasm/orm_helpers.rb +6 -0
- data/lib/generators/active_record/aasm_generator.rb +3 -1
- data/lib/generators/active_record/templates/migration.rb +1 -1
- data/lib/generators/active_record/templates/migration_existing.rb +1 -1
- data/lib/generators/nobrainer/aasm_generator.rb +28 -0
- data/lib/motion-aasm.rb +3 -1
- data/spec/database.rb +20 -7
- data/spec/en.yml +0 -3
- data/spec/generators/active_record_generator_spec.rb +49 -40
- data/spec/generators/mongoid_generator_spec.rb +4 -6
- data/spec/generators/no_brainer_generator_spec.rb +29 -0
- data/spec/{en_deprecated_style.yml → localizer_test_model_deprecated_style.yml} +6 -3
- data/spec/localizer_test_model_new_style.yml +11 -0
- data/spec/models/active_record/active_record_callback.rb +93 -0
- data/spec/models/active_record/complex_active_record_example.rb +5 -1
- data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
- data/spec/models/{invalid_persistor.rb → active_record/invalid_persistor.rb} +0 -2
- data/spec/models/active_record/localizer_test_model.rb +11 -3
- data/spec/models/active_record/namespaced.rb +16 -0
- data/spec/models/active_record/person.rb +23 -0
- data/spec/models/{silent_persistor.rb → active_record/silent_persistor.rb} +0 -2
- data/spec/models/active_record/simple_new_dsl.rb +15 -0
- data/spec/models/active_record/timestamp_example.rb +16 -0
- data/spec/models/{transactor.rb → active_record/transactor.rb} +25 -2
- data/spec/models/{validator.rb → active_record/validator.rb} +0 -2
- data/spec/models/active_record/work.rb +3 -0
- data/spec/models/{worker.rb → active_record/worker.rb} +0 -0
- data/spec/models/callbacks/basic.rb +5 -2
- data/spec/models/callbacks/with_state_arg.rb +5 -1
- data/spec/models/callbacks/with_state_arg_multiple.rb +4 -1
- data/spec/models/default_state.rb +1 -1
- data/spec/models/guard_arguments_check.rb +17 -0
- data/spec/models/guard_with_params.rb +1 -1
- data/spec/models/guardian_without_from_specified.rb +18 -0
- data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/timestamp_example_mongoid.rb +20 -0
- data/spec/models/mongoid/validator_mongoid.rb +100 -0
- data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
- data/spec/models/namespaced_multiple_example.rb +14 -0
- data/spec/models/nobrainer/complex_no_brainer_example.rb +36 -0
- data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
- data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
- data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
- data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
- data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
- data/spec/models/{mongo_mapper/simple_mongo_mapper.rb → nobrainer/simple_no_brainer.rb} +8 -8
- data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
- data/spec/models/parametrised_event.rb +7 -0
- data/spec/models/{mongo_mapper/complex_mongo_mapper_example.rb → redis/complex_redis_example.rb} +8 -5
- data/spec/models/redis/redis_multiple.rb +20 -0
- data/spec/models/redis/redis_simple.rb +20 -0
- data/spec/models/sequel/complex_sequel_example.rb +4 -3
- data/spec/models/sequel/invalid_persistor.rb +52 -0
- data/spec/models/sequel/sequel_multiple.rb +13 -13
- data/spec/models/sequel/sequel_simple.rb +13 -12
- data/spec/models/sequel/silent_persistor.rb +50 -0
- data/spec/models/sequel/transactor.rb +112 -0
- data/spec/models/sequel/validator.rb +93 -0
- data/spec/models/sequel/worker.rb +12 -0
- data/spec/models/simple_example.rb +8 -0
- data/spec/models/simple_example_with_guard_args.rb +17 -0
- data/spec/models/simple_multiple_example.rb +12 -0
- data/spec/models/sub_class.rb +34 -0
- data/spec/models/timestamps_example.rb +19 -0
- data/spec/models/timestamps_with_named_machine_example.rb +13 -0
- data/spec/spec_helper.rb +15 -33
- data/spec/spec_helpers/active_record.rb +8 -0
- data/spec/spec_helpers/dynamoid.rb +35 -0
- data/spec/spec_helpers/mongoid.rb +26 -0
- data/spec/spec_helpers/nobrainer.rb +15 -0
- data/spec/spec_helpers/redis.rb +18 -0
- data/spec/spec_helpers/remove_warnings.rb +1 -0
- data/spec/spec_helpers/sequel.rb +7 -0
- data/spec/unit/abstract_class_spec.rb +27 -0
- data/spec/unit/api_spec.rb +79 -72
- data/spec/unit/callback_multiple_spec.rb +7 -3
- data/spec/unit/callbacks_spec.rb +37 -2
- data/spec/unit/complex_example_spec.rb +12 -3
- data/spec/unit/complex_multiple_example_spec.rb +20 -4
- data/spec/unit/event_multiple_spec.rb +1 -1
- data/spec/unit/event_spec.rb +29 -4
- data/spec/unit/exception_spec.rb +1 -1
- data/spec/unit/guard_arguments_check_spec.rb +9 -0
- data/spec/unit/guard_spec.rb +17 -0
- data/spec/unit/guard_with_params_spec.rb +4 -0
- data/spec/unit/guard_without_from_specified_spec.rb +10 -0
- data/spec/unit/inspection_multiple_spec.rb +9 -5
- data/spec/unit/inspection_spec.rb +7 -3
- data/spec/unit/invoker_spec.rb +189 -0
- data/spec/unit/invokers/base_invoker_spec.rb +72 -0
- data/spec/unit/invokers/class_invoker_spec.rb +95 -0
- data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
- data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
- data/spec/unit/localizer_spec.rb +85 -52
- data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
- data/spec/unit/namespaced_multiple_example_spec.rb +22 -0
- data/spec/unit/override_warning_spec.rb +8 -0
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +468 -447
- data/spec/unit/persistence/active_record_persistence_spec.rb +639 -486
- data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +4 -9
- data/spec/unit/persistence/dynamoid_persistence_spec.rb +4 -9
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +83 -13
- data/spec/unit/persistence/mongoid_persistence_spec.rb +97 -13
- data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
- data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
- data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
- data/spec/unit/persistence/redis_persistence_spec.rb +8 -32
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +6 -11
- data/spec/unit/persistence/sequel_persistence_spec.rb +278 -10
- data/spec/unit/rspec_matcher_spec.rb +9 -0
- data/spec/unit/simple_example_spec.rb +15 -0
- data/spec/unit/simple_multiple_example_spec.rb +28 -0
- data/spec/unit/state_spec.rb +23 -7
- data/spec/unit/subclassing_multiple_spec.rb +37 -2
- data/spec/unit/subclassing_spec.rb +17 -2
- data/spec/unit/timestamps_spec.rb +32 -0
- data/spec/unit/transition_spec.rb +1 -1
- data/test/minitest_helper.rb +57 -0
- data/test/unit/minitest_matcher_test.rb +80 -0
- metadata +213 -37
- data/callbacks.txt +0 -51
- data/gemfiles/rails_3.2_stable.gemfile +0 -15
- data/gemfiles/rails_4.0.gemfile +0 -16
- data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -16
- data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -17
- data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -163
- data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
- data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
- data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -149
- data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -96
data/Rakefile
CHANGED
data/TESTING.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
## Install dependency matrix
|
2
|
+
|
3
|
+
appraisal install
|
4
|
+
|
5
|
+
This will re-generate Gemfiles in `gemfile` folder
|
6
|
+
|
7
|
+
Use rvm gemsets or similar to avoid global gem pollution
|
8
|
+
|
9
|
+
## Run specs
|
10
|
+
|
11
|
+
For all supported Rails/ORM combinations:
|
12
|
+
|
13
|
+
appraisal rspec
|
14
|
+
|
15
|
+
Or for specific one:
|
16
|
+
|
17
|
+
appraisal rails_4.2 rspec
|
18
|
+
|
19
|
+
Or for one particular test file
|
20
|
+
|
21
|
+
appraisal rails_4.2_mongoid_5 rspec spec/unit/persistence/mongoid_persistence_multiple_spec.rb
|
22
|
+
|
23
|
+
Or down to one test case
|
24
|
+
|
25
|
+
appraisal rails_4.2_mongoid_5 rspec spec/unit/persistence/mongoid_persistence_multiple_spec.rb:92
|
data/aasm.gemspec
CHANGED
@@ -16,10 +16,15 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.platform = Gem::Platform::RUBY
|
17
17
|
s.required_ruby_version = '>= 1.9.3'
|
18
18
|
|
19
|
+
s.add_dependency 'concurrent-ruby', '~> 1.0'
|
20
|
+
|
19
21
|
s.add_development_dependency 'rake'
|
20
22
|
s.add_development_dependency 'sdoc'
|
21
23
|
s.add_development_dependency 'rspec', ">= 3"
|
22
24
|
s.add_development_dependency 'generator_spec'
|
25
|
+
s.add_development_dependency 'appraisal'
|
26
|
+
s.add_development_dependency "simplecov"
|
27
|
+
s.add_development_dependency "codecov", ">= 0.1.21"
|
23
28
|
|
24
29
|
# debugging
|
25
30
|
# s.add_development_dependency 'debugger'
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
version: "2"
|
2
|
+
|
3
|
+
services:
|
4
|
+
aasm:
|
5
|
+
image: aasm/aasm
|
6
|
+
build: .
|
7
|
+
command: bash -c 'bundle exec appraisal install && bundle exec appraisal rspec'
|
8
|
+
environment:
|
9
|
+
- DYNAMODB_HOST=dynamodb
|
10
|
+
- DYNAMODB_PORT=8000
|
11
|
+
- MONGODB_HOST=mongo
|
12
|
+
- MONGODB_PORT=27017
|
13
|
+
- RAILS_ENV=development
|
14
|
+
- REDIS_HOST=redis
|
15
|
+
- REDIS_PORT=6379
|
16
|
+
- RETHINKDB_DB=rethinkdb_test
|
17
|
+
- RETHINKDB_HOST=rethinkdb
|
18
|
+
- RETHINKDB_PORT=28015
|
19
|
+
depends_on:
|
20
|
+
- dynamodb
|
21
|
+
- mongo
|
22
|
+
- redis
|
23
|
+
- rethinkdb
|
24
|
+
volumes:
|
25
|
+
- .:/application
|
26
|
+
volumes_from:
|
27
|
+
- bundle
|
28
|
+
bundle:
|
29
|
+
image: aasm/aasm
|
30
|
+
command: echo Bundler data container
|
31
|
+
volumes:
|
32
|
+
- /bundle
|
33
|
+
dynamodb:
|
34
|
+
image: cnadiminti/dynamodb-local:2017-02-16
|
35
|
+
mongo:
|
36
|
+
image: mongo:3.6.1
|
37
|
+
redis:
|
38
|
+
image: redis:4.0.6-alpine
|
39
|
+
rethinkdb:
|
40
|
+
image: rethinkdb:2.3.6
|
data/gemfiles/rails_4.2.gemfile
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
|
3
|
-
gem "sqlite3",
|
4
|
-
gem 'rubysl', :platforms => :rbx
|
5
|
-
gem 'rubinius-developer_tools', :platforms => :rbx
|
6
|
-
gem "jruby-openssl", :platforms => :jruby
|
7
|
-
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
|
8
|
-
gem "mime-types", "~> 2" if Gem::Version.create(RUBY_VERSION.dup) <= Gem::Version.create('1.9.3')
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
9
6
|
gem "rails", "4.2.5"
|
10
|
-
gem
|
11
|
-
gem
|
12
|
-
gem
|
13
|
-
gem
|
7
|
+
gem "nokogiri", "1.6.8.1", platforms: [:ruby_19]
|
8
|
+
gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
|
9
|
+
gem "mongoid", "~> 4.0"
|
10
|
+
gem "sequel"
|
11
|
+
gem "dynamoid", "~> 1", platforms: :ruby
|
12
|
+
gem "aws-sdk", "~> 2", platforms: :ruby
|
13
|
+
gem "redis-objects"
|
14
|
+
gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
|
15
|
+
gem "after_commit_everywhere", "~> 1.0"
|
14
16
|
|
15
|
-
gemspec :
|
17
|
+
gemspec path: "../"
|
@@ -1,15 +1,12 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
1
3
|
source "https://rubygems.org"
|
2
4
|
|
3
|
-
gem "sqlite3",
|
4
|
-
gem 'rubysl', :platforms => :rbx
|
5
|
-
gem 'rubinius-developer_tools', :platforms => :rbx
|
6
|
-
gem "jruby-openssl", :platforms => :jruby
|
7
|
-
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
|
8
|
-
gem "mime-types", "~> 2" if Gem::Version.create(RUBY_VERSION.dup) <= Gem::Version.create('1.9.3')
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
9
6
|
gem "rails", "4.2.5"
|
10
|
-
gem
|
11
|
-
gem
|
12
|
-
gem
|
13
|
-
gem
|
7
|
+
gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
|
8
|
+
gem "mongoid", "~> 5.0"
|
9
|
+
gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
|
10
|
+
gem "after_commit_everywhere", "~> 1.0"
|
14
11
|
|
15
|
-
gemspec :
|
12
|
+
gemspec path: "../"
|
data/gemfiles/rails_5.0.gemfile
CHANGED
@@ -1,21 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
gem "sqlite3", :platforms => :ruby
|
4
|
-
gem 'rubysl', :platforms => :rbx
|
5
|
-
gem 'rubinius-developer_tools', :platforms => :rbx
|
6
|
-
gem "jruby-openssl", :platforms => :jruby
|
7
|
-
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
|
8
|
-
|
9
|
-
gem "rails", "5.0.0.beta4"
|
1
|
+
# This file was generated by Appraisal
|
10
2
|
|
11
|
-
|
12
|
-
# gem 'mongoid', '~>4.0' if Gem::Version.create(RUBY_VERSION.dup) >= Gem::Version.create('1.9.3')
|
13
|
-
|
14
|
-
gem 'sequel'
|
15
|
-
|
16
|
-
# dynamoid is not yet Rails 5 compatible
|
17
|
-
# gem 'dynamoid', '~> 1', :platforms => :ruby
|
3
|
+
source "https://rubygems.org"
|
18
4
|
|
19
|
-
gem
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
6
|
+
gem "rails", "5.0.0"
|
7
|
+
gem "mongoid", "~> 6.0"
|
8
|
+
gem "sequel"
|
9
|
+
gem "dynamoid", "~> 1.3", platforms: :ruby
|
10
|
+
gem "aws-sdk", "~> 2", platforms: :ruby
|
11
|
+
gem "redis-objects"
|
12
|
+
gem "after_commit_everywhere", "~> 1.0"
|
20
13
|
|
21
|
-
gemspec :
|
14
|
+
gemspec path: "../"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
6
|
+
gem "rails", "5.1"
|
7
|
+
gem "mongoid", "~>6.0"
|
8
|
+
gem "sequel"
|
9
|
+
gem "dynamoid", "~> 1.3", platforms: :ruby
|
10
|
+
gem "aws-sdk", "~>2", platforms: :ruby
|
11
|
+
gem "redis-objects"
|
12
|
+
gem "after_commit_everywhere", "~> 1.0"
|
13
|
+
|
14
|
+
gemspec path: "../"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
6
|
+
gem "rails", "5.2"
|
7
|
+
gem "mongoid", "~>6.0"
|
8
|
+
gem "sequel"
|
9
|
+
gem "dynamoid", "~>2.2", platforms: :ruby
|
10
|
+
gem "aws-sdk", "~>2", platforms: :ruby
|
11
|
+
gem "redis-objects"
|
12
|
+
gem "after_commit_everywhere", "~> 1.0"
|
13
|
+
|
14
|
+
gemspec path: "../"
|
data/lib/aasm/aasm.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module AASM
|
2
|
+
# this is used internally as an argument default value to represent no value
|
3
|
+
NO_VALUE = :_aasm_no_value
|
2
4
|
|
3
5
|
# provide a state machine for the including class
|
4
6
|
# make sure to load class methods as well
|
@@ -42,7 +44,7 @@ module AASM
|
|
42
44
|
|
43
45
|
raise ArgumentError, "The class #{aasm_klass} must inherit from AASM::Base!" unless aasm_klass.ancestors.include?(AASM::Base)
|
44
46
|
|
45
|
-
@aasm ||=
|
47
|
+
@aasm ||= Concurrent::Map.new
|
46
48
|
if @aasm[state_machine_name]
|
47
49
|
# make sure to use provided options
|
48
50
|
options.each do |key, value|
|
@@ -67,12 +69,12 @@ module AASM
|
|
67
69
|
unless AASM::StateMachineStore.fetch(self.class, true).machine(name)
|
68
70
|
raise AASM::UnknownStateMachineError.new("There is no state machine with the name '#{name}' defined in #{self.class.name}!")
|
69
71
|
end
|
70
|
-
@aasm ||=
|
72
|
+
@aasm ||= Concurrent::Map.new
|
71
73
|
@aasm[name.to_sym] ||= AASM::InstanceBase.new(self, name.to_sym)
|
72
74
|
end
|
73
75
|
|
74
76
|
def initialize_dup(other)
|
75
|
-
@aasm =
|
77
|
+
@aasm = Concurrent::Map.new
|
76
78
|
super
|
77
79
|
end
|
78
80
|
|
@@ -97,25 +99,10 @@ private
|
|
97
99
|
begin
|
98
100
|
old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
|
99
101
|
|
100
|
-
event.
|
101
|
-
:before_all_events,
|
102
|
-
self,
|
103
|
-
*process_args(event, aasm(state_machine_name).current_state, *args)
|
104
|
-
)
|
105
|
-
|
106
|
-
# new event before callback
|
107
|
-
event.fire_callbacks(
|
108
|
-
:before,
|
109
|
-
self,
|
110
|
-
*process_args(event, aasm(state_machine_name).current_state, *args)
|
111
|
-
)
|
102
|
+
fire_default_callbacks(event, *process_args(event, aasm(state_machine_name).current_state, *args))
|
112
103
|
|
113
104
|
if may_fire_to = event.may_fire?(self, *args)
|
114
|
-
old_state
|
115
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
116
|
-
old_state.fire_callbacks(:exit, self,
|
117
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
118
|
-
|
105
|
+
fire_exit_callbacks(old_state, *process_args(event, aasm(state_machine_name).current_state, *args))
|
119
106
|
if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
|
120
107
|
aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
|
121
108
|
else
|
@@ -128,41 +115,65 @@ private
|
|
128
115
|
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
129
116
|
event.fire_global_callbacks(:error_on_all_events, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
130
117
|
raise(e)
|
118
|
+
false
|
131
119
|
ensure
|
132
120
|
event.fire_callbacks(:ensure, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
133
121
|
event.fire_global_callbacks(:ensure_on_all_events, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
134
122
|
end
|
135
123
|
end
|
136
124
|
|
125
|
+
def fire_default_callbacks(event, *processed_args)
|
126
|
+
event.fire_global_callbacks(
|
127
|
+
:before_all_events,
|
128
|
+
self,
|
129
|
+
*processed_args
|
130
|
+
)
|
131
|
+
|
132
|
+
# new event before callback
|
133
|
+
event.fire_callbacks(
|
134
|
+
:before,
|
135
|
+
self,
|
136
|
+
*processed_args
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
def fire_exit_callbacks(old_state, *processed_args)
|
141
|
+
old_state.fire_callbacks(:before_exit, self, *processed_args)
|
142
|
+
old_state.fire_callbacks(:exit, self, *processed_args)
|
143
|
+
end
|
144
|
+
|
137
145
|
def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
|
138
146
|
persist = options[:persist]
|
139
147
|
|
140
148
|
new_state = aasm(state_machine_name).state_object_for_name(new_state_name)
|
149
|
+
callback_args = process_args(event, aasm(state_machine_name).current_state, *args)
|
141
150
|
|
142
|
-
new_state.fire_callbacks(:before_enter, self,
|
143
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
151
|
+
new_state.fire_callbacks(:before_enter, self, *callback_args)
|
144
152
|
|
145
|
-
new_state.fire_callbacks(:enter, self,
|
146
|
-
*process_args(event, aasm(state_machine_name).current_state, *args)) # TODO: remove for AASM 4?
|
153
|
+
new_state.fire_callbacks(:enter, self, *callback_args) # TODO: remove for AASM 4?
|
147
154
|
|
148
155
|
persist_successful = true
|
149
156
|
if persist
|
150
157
|
persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
|
151
158
|
if persist_successful
|
152
159
|
yield if block_given?
|
160
|
+
event.fire_callbacks(:before_success, self, *callback_args)
|
153
161
|
event.fire_transition_callbacks(self, *process_args(event, old_state.name, *args))
|
154
|
-
event.fire_callbacks(:success, self)
|
162
|
+
event.fire_callbacks(:success, self, *callback_args)
|
155
163
|
end
|
156
164
|
else
|
157
165
|
aasm(state_machine_name).current_state = new_state_name
|
158
166
|
yield if block_given?
|
159
167
|
end
|
160
168
|
|
169
|
+
binding_event = event.options[:binding_event]
|
170
|
+
if binding_event
|
171
|
+
__send__("#{binding_event}#{'!' if persist}")
|
172
|
+
end
|
173
|
+
|
161
174
|
if persist_successful
|
162
|
-
old_state.fire_callbacks(:after_exit, self,
|
163
|
-
|
164
|
-
new_state.fire_callbacks(:after_enter, self,
|
165
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
175
|
+
old_state.fire_callbacks(:after_exit, self, *callback_args)
|
176
|
+
new_state.fire_callbacks(:after_enter, self, *callback_args)
|
166
177
|
event.fire_callbacks(
|
167
178
|
:after,
|
168
179
|
self,
|
data/lib/aasm/base.rb
CHANGED
@@ -26,6 +26,9 @@ module AASM
|
|
26
26
|
# raise if the model is invalid (in ActiveRecord)
|
27
27
|
configure :whiny_persistence, false
|
28
28
|
|
29
|
+
# Use transactions (in ActiveRecord)
|
30
|
+
configure :use_transactions, true
|
31
|
+
|
29
32
|
# use requires_new for nested transactions (in ActiveRecord)
|
30
33
|
configure :requires_new_transaction, true
|
31
34
|
|
@@ -34,6 +37,9 @@ module AASM
|
|
34
37
|
# string for a specific lock type i.e. FOR UPDATE NOWAIT
|
35
38
|
configure :requires_lock, false
|
36
39
|
|
40
|
+
# automatically set `"#{state_name}_at" = ::Time.now` on state changes
|
41
|
+
configure :timestamps, false
|
42
|
+
|
37
43
|
# set to true to forbid direct assignment of aasm_state column (in ActiveRecord)
|
38
44
|
configure :no_direct_assignment, false
|
39
45
|
|
@@ -48,18 +54,12 @@ module AASM
|
|
48
54
|
# Configure a logger, with default being a Logger to STDERR
|
49
55
|
configure :logger, Logger.new(STDERR)
|
50
56
|
|
57
|
+
# setup timestamp-setting callback if enabled
|
58
|
+
setup_timestamps(@name)
|
59
|
+
|
51
60
|
# make sure to raise an error if no_direct_assignment is enabled
|
52
61
|
# and attribute is directly assigned though
|
53
|
-
|
54
|
-
klass.send :define_method, "#{@state_machine.config.column}=", ->(state_name) do
|
55
|
-
if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
|
56
|
-
raise AASM::NoDirectAssignmentError.new(
|
57
|
-
'direct assignment of AASM column has been disabled (see AASM configuration for this class)'
|
58
|
-
)
|
59
|
-
else
|
60
|
-
super(state_name)
|
61
|
-
end
|
62
|
-
end
|
62
|
+
setup_no_direct_assignment(@name)
|
63
63
|
end
|
64
64
|
|
65
65
|
# This method is both a getter and a setter
|
@@ -130,6 +130,16 @@ module AASM
|
|
130
130
|
aasm(aasm_name).current_event = event
|
131
131
|
aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block)
|
132
132
|
end
|
133
|
+
|
134
|
+
skip_instance_level_validation(event, name, aasm_name, klass)
|
135
|
+
|
136
|
+
# Create aliases for the event methods. Keep the old names to maintain backwards compatibility.
|
137
|
+
if namespace?
|
138
|
+
klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?")
|
139
|
+
klass.send(:alias_method, "#{name}_#{namespace}!", "#{name}!")
|
140
|
+
klass.send(:alias_method, "#{name}_#{namespace}", name)
|
141
|
+
end
|
142
|
+
|
133
143
|
end
|
134
144
|
|
135
145
|
def after_all_transitions(*callbacks, &block)
|
@@ -208,7 +218,9 @@ module AASM
|
|
208
218
|
klass.defined_enums.values.any?{ |methods|
|
209
219
|
methods.keys{| enum | enum + '?' == method_name }
|
210
220
|
})
|
211
|
-
|
221
|
+
unless AASM::Configuration.hide_warnings
|
222
|
+
@state_machine.config.logger.warn "#{klass.name}: overriding method '#{method_name}'!"
|
223
|
+
end
|
212
224
|
end
|
213
225
|
|
214
226
|
klass.send(:define_method, method_name, method_definition)
|
@@ -236,5 +248,43 @@ module AASM
|
|
236
248
|
end
|
237
249
|
end
|
238
250
|
|
251
|
+
def skip_instance_level_validation(event, name, aasm_name, klass)
|
252
|
+
# Overrides the skip_validation config for an instance (If skip validation is set to false in original config) and
|
253
|
+
# restores it back to the original value after the event is fired.
|
254
|
+
safely_define_method klass, "#{name}_without_validation!", ->(*args, &block) do
|
255
|
+
original_config = AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save
|
256
|
+
begin
|
257
|
+
AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = true unless original_config
|
258
|
+
aasm(aasm_name).current_event = :"#{name}!"
|
259
|
+
aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
|
260
|
+
ensure
|
261
|
+
AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = original_config
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
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
|
+
|
239
289
|
end
|
240
290
|
end
|
data/lib/aasm/configuration.rb
CHANGED
@@ -15,12 +15,18 @@ module AASM
|
|
15
15
|
# for ActiveRecord: store the new state even if the model is invalid and return true
|
16
16
|
attr_accessor :skip_validation_on_save
|
17
17
|
|
18
|
+
# for ActiveRecord: use transactions
|
19
|
+
attr_accessor :use_transactions
|
20
|
+
|
18
21
|
# for ActiveRecord: use requires_new for nested transactions?
|
19
22
|
attr_accessor :requires_new_transaction
|
20
23
|
|
21
24
|
# for ActiveRecord: use pessimistic locking
|
22
25
|
attr_accessor :requires_lock
|
23
26
|
|
27
|
+
# automatically set `"#{state_name}_at" = ::Time.now` on state changes
|
28
|
+
attr_accessor :timestamps
|
29
|
+
|
24
30
|
# forbid direct assignment in aasm_state column (in ActiveRecord)
|
25
31
|
attr_accessor :no_direct_assignment
|
26
32
|
|
@@ -34,5 +40,9 @@ module AASM
|
|
34
40
|
|
35
41
|
# Configure a logger, with default being a Logger to STDERR
|
36
42
|
attr_accessor :logger
|
43
|
+
|
44
|
+
class << self
|
45
|
+
attr_accessor :hide_warnings
|
46
|
+
end
|
37
47
|
end
|
38
48
|
end
|
data/lib/aasm/core/event.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AASM::Core
|
2
4
|
class Event
|
3
|
-
include DslHelper
|
5
|
+
include AASM::DslHelper
|
4
6
|
|
5
|
-
attr_reader :name, :state_machine, :options
|
7
|
+
attr_reader :name, :state_machine, :options, :default_display_name
|
6
8
|
|
7
9
|
def initialize(name, state_machine, options = {}, &block)
|
8
10
|
@name = name
|
@@ -11,6 +13,7 @@ module AASM::Core
|
|
11
13
|
@valid_transitions = {}
|
12
14
|
@guards = Array(options[:guard] || options[:guards] || options[:if])
|
13
15
|
@unless = Array(options[:unless]) #TODO: This could use a better name
|
16
|
+
@default_display_name = name.to_s.gsub(/_/, ' ').capitalize
|
14
17
|
|
15
18
|
# from aasm4
|
16
19
|
@options = options # QUESTION: .dup ?
|
@@ -22,18 +25,29 @@ module AASM::Core
|
|
22
25
|
:before_transaction,
|
23
26
|
:ensure,
|
24
27
|
:error,
|
28
|
+
:before_success,
|
25
29
|
:success,
|
26
30
|
], &block) if block
|
27
31
|
end
|
28
32
|
|
33
|
+
# called internally by Ruby 1.9 after clone()
|
34
|
+
def initialize_copy(orig)
|
35
|
+
super
|
36
|
+
@transitions = @transitions.collect { |transition| transition.clone }
|
37
|
+
@guards = @guards.dup
|
38
|
+
@unless = @unless.dup
|
39
|
+
@options = {}
|
40
|
+
orig.options.each_pair { |name, setting| @options[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
|
41
|
+
end
|
42
|
+
|
29
43
|
# a neutered version of fire - it doesn't actually fire the event, it just
|
30
44
|
# executes the transition guards to determine if a transition is even
|
31
45
|
# an option given current conditions.
|
32
|
-
def may_fire?(obj, to_state
|
46
|
+
def may_fire?(obj, to_state=::AASM::NO_VALUE, *args)
|
33
47
|
_fire(obj, {:test_only => true}, to_state, *args) # true indicates test firing
|
34
48
|
end
|
35
49
|
|
36
|
-
def fire(obj, options={}, to_state
|
50
|
+
def fire(obj, options={}, to_state=::AASM::NO_VALUE, *args)
|
37
51
|
_fire(obj, options, to_state, *args) # false indicates this is not a test (fire!)
|
38
52
|
end
|
39
53
|
|
@@ -85,7 +99,7 @@ module AASM::Core
|
|
85
99
|
@transitions << AASM::Core::Transition.new(self, attach_event_guards(definitions.merge(:from => s.to_sym)), &block)
|
86
100
|
end
|
87
101
|
# Create a transition if :to is specified without :from (transitions from ANY state)
|
88
|
-
if
|
102
|
+
if !definitions[:from] && definitions[:to]
|
89
103
|
@transitions << AASM::Core::Transition.new(self, attach_event_guards(definitions), &block)
|
90
104
|
end
|
91
105
|
end
|
@@ -96,6 +110,10 @@ module AASM::Core
|
|
96
110
|
transitions.flat_map(&:failures)
|
97
111
|
end
|
98
112
|
|
113
|
+
def to_s
|
114
|
+
name.to_s
|
115
|
+
end
|
116
|
+
|
99
117
|
private
|
100
118
|
|
101
119
|
def attach_event_guards(definitions)
|
@@ -110,28 +128,31 @@ module AASM::Core
|
|
110
128
|
definitions
|
111
129
|
end
|
112
130
|
|
113
|
-
def _fire(obj, options={}, to_state
|
131
|
+
def _fire(obj, options={}, to_state=::AASM::NO_VALUE, *args)
|
114
132
|
result = options[:test_only] ? false : nil
|
133
|
+
clear_failed_callbacks
|
115
134
|
transitions = @transitions.select { |t| t.from == obj.aasm(state_machine.name).current_state || t.from == nil}
|
116
135
|
return result if transitions.size == 0
|
117
136
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
137
|
+
if to_state == ::AASM::NO_VALUE
|
138
|
+
to_state = nil
|
139
|
+
elsif !(to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym))
|
140
|
+
# to_state is an argument
|
141
|
+
args.unshift(to_state)
|
142
|
+
to_state = nil
|
125
143
|
end
|
144
|
+
|
145
|
+
# nop, to_state is a valid to-state
|
126
146
|
|
127
147
|
transitions.each do |transition|
|
128
148
|
next if to_state and !Array(transition.to).include?(to_state)
|
129
|
-
if (options.key?(:may_fire) &&
|
149
|
+
if (options.key?(:may_fire) && transition.eql?(options[:may_fire])) ||
|
130
150
|
(!options.key?(:may_fire) && transition.allowed?(obj, *args))
|
131
|
-
|
151
|
+
|
132
152
|
if options[:test_only]
|
133
|
-
|
153
|
+
result = transition
|
134
154
|
else
|
155
|
+
result = to_state || Array(transition.to).first
|
135
156
|
Array(transition.to).each {|to| @valid_transitions[to] = transition }
|
136
157
|
transition.execute(obj, *args)
|
137
158
|
end
|
@@ -142,28 +163,15 @@ module AASM::Core
|
|
142
163
|
result
|
143
164
|
end
|
144
165
|
|
145
|
-
def
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
raise NoMethodError.new("NoMethodError: undefined method `#{code}' for #{record.inspect}:#{record.class}")
|
150
|
-
end
|
151
|
-
arity = record.__send__(:method, code.to_sym).arity
|
152
|
-
record.__send__(code, *(arity < 0 ? args : args[0...arity]))
|
153
|
-
true
|
154
|
-
|
155
|
-
when Proc
|
156
|
-
arity = code.arity
|
157
|
-
record.instance_exec(*(arity < 0 ? args : args[0...arity]), &code)
|
158
|
-
true
|
159
|
-
|
160
|
-
when Array
|
161
|
-
code.each {|a| invoke_callbacks(a, record, args)}
|
162
|
-
true
|
166
|
+
def clear_failed_callbacks
|
167
|
+
# https://github.com/aasm/aasm/issues/383, https://github.com/aasm/aasm/issues/599
|
168
|
+
transitions.each { |transition| transition.failures.clear }
|
169
|
+
end
|
163
170
|
|
164
|
-
|
165
|
-
|
166
|
-
|
171
|
+
def invoke_callbacks(code, record, args)
|
172
|
+
Invoker.new(code, record, args)
|
173
|
+
.with_default_return_value(false)
|
174
|
+
.invoke
|
167
175
|
end
|
168
176
|
end
|
169
177
|
end # AASM
|