aasm 5.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +6 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.gitignore +20 -0
- data/.travis.yml +100 -0
- data/API +34 -0
- data/Appraisals +71 -0
- data/CHANGELOG.md +431 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/CONTRIBUTING.md +24 -0
- data/Dockerfile +44 -0
- data/Gemfile +6 -0
- data/Gemfile.lock_old +151 -0
- data/HOWTO +12 -0
- data/LICENSE +20 -0
- data/PLANNED_CHANGES.md +11 -0
- data/README.md +1439 -0
- data/README_FROM_VERSION_3_TO_4.md +240 -0
- data/Rakefile +31 -0
- data/TESTING.md +25 -0
- data/aasm.gemspec +37 -0
- data/callbacks.txt +51 -0
- data/docker-compose.yml +40 -0
- data/gemfiles/norails.gemfile +10 -0
- data/gemfiles/rails_3.2.gemfile +14 -0
- data/gemfiles/rails_4.2.gemfile +16 -0
- data/gemfiles/rails_4.2_mongoid_5.gemfile +11 -0
- data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
- data/gemfiles/rails_5.0.gemfile +13 -0
- data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
- data/gemfiles/rails_5.1.gemfile +13 -0
- data/gemfiles/rails_5.2.gemfile +13 -0
- data/lib/aasm.rb +23 -0
- data/lib/aasm/aasm.rb +208 -0
- data/lib/aasm/base.rb +271 -0
- data/lib/aasm/configuration.rb +45 -0
- data/lib/aasm/core/event.rb +172 -0
- 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 +90 -0
- data/lib/aasm/core/transition.rb +83 -0
- data/lib/aasm/dsl_helper.rb +30 -0
- data/lib/aasm/errors.rb +21 -0
- data/lib/aasm/instance_base.rb +133 -0
- data/lib/aasm/localizer.rb +54 -0
- data/lib/aasm/minitest.rb +5 -0
- 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_spec.rb +15 -0
- data/lib/aasm/persistence.rb +54 -0
- data/lib/aasm/persistence/active_record_persistence.rb +165 -0
- data/lib/aasm/persistence/base.rb +78 -0
- data/lib/aasm/persistence/core_data_query_persistence.rb +94 -0
- data/lib/aasm/persistence/dynamoid_persistence.rb +92 -0
- data/lib/aasm/persistence/mongoid_persistence.rb +126 -0
- data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
- data/lib/aasm/persistence/orm.rb +150 -0
- data/lib/aasm/persistence/plain_persistence.rb +26 -0
- data/lib/aasm/persistence/redis_persistence.rb +112 -0
- data/lib/aasm/persistence/sequel_persistence.rb +83 -0
- data/lib/aasm/rspec.rb +5 -0
- data/lib/aasm/rspec/allow_event.rb +26 -0
- data/lib/aasm/rspec/allow_transition_to.rb +26 -0
- data/lib/aasm/rspec/have_state.rb +22 -0
- data/lib/aasm/rspec/transition_from.rb +36 -0
- data/lib/aasm/state_machine.rb +53 -0
- data/lib/aasm/state_machine_store.rb +76 -0
- data/lib/aasm/version.rb +3 -0
- data/lib/generators/aasm/aasm_generator.rb +16 -0
- data/lib/generators/aasm/orm_helpers.rb +41 -0
- data/lib/generators/active_record/aasm_generator.rb +40 -0
- data/lib/generators/active_record/templates/migration.rb +8 -0
- data/lib/generators/active_record/templates/migration_existing.rb +5 -0
- data/lib/generators/mongoid/aasm_generator.rb +28 -0
- data/lib/generators/nobrainer/aasm_generator.rb +28 -0
- data/lib/motion-aasm.rb +37 -0
- data/spec/database.rb +59 -0
- data/spec/database.yml +3 -0
- data/spec/en.yml +12 -0
- data/spec/en_deprecated_style.yml +10 -0
- data/spec/generators/active_record_generator_spec.rb +53 -0
- data/spec/generators/mongoid_generator_spec.rb +31 -0
- data/spec/generators/no_brainer_generator_spec.rb +29 -0
- data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +25 -0
- data/spec/models/active_record/complex_active_record_example.rb +37 -0
- data/spec/models/active_record/derivate_new_dsl.rb +7 -0
- data/spec/models/active_record/false_state.rb +35 -0
- data/spec/models/active_record/gate.rb +39 -0
- data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
- data/spec/models/active_record/invalid_persistor.rb +29 -0
- data/spec/models/active_record/localizer_test_model.rb +34 -0
- data/spec/models/active_record/no_direct_assignment.rb +21 -0
- data/spec/models/active_record/no_scope.rb +21 -0
- data/spec/models/active_record/persisted_state.rb +12 -0
- data/spec/models/active_record/person.rb +23 -0
- data/spec/models/active_record/provided_and_persisted_state.rb +24 -0
- data/spec/models/active_record/reader.rb +7 -0
- data/spec/models/active_record/readme_job.rb +21 -0
- data/spec/models/active_record/silent_persistor.rb +29 -0
- data/spec/models/active_record/simple_new_dsl.rb +32 -0
- data/spec/models/active_record/thief.rb +29 -0
- data/spec/models/active_record/transactor.rb +124 -0
- data/spec/models/active_record/transient.rb +6 -0
- data/spec/models/active_record/validator.rb +118 -0
- data/spec/models/active_record/with_enum.rb +39 -0
- data/spec/models/active_record/with_enum_without_column.rb +38 -0
- data/spec/models/active_record/with_false_enum.rb +31 -0
- data/spec/models/active_record/with_true_enum.rb +39 -0
- data/spec/models/active_record/work.rb +3 -0
- data/spec/models/active_record/worker.rb +2 -0
- data/spec/models/active_record/writer.rb +6 -0
- data/spec/models/basic_two_state_machines_example.rb +25 -0
- data/spec/models/callbacks/basic.rb +98 -0
- data/spec/models/callbacks/basic_multiple.rb +75 -0
- data/spec/models/callbacks/guard_within_block.rb +67 -0
- data/spec/models/callbacks/guard_within_block_multiple.rb +66 -0
- data/spec/models/callbacks/multiple_transitions_transition_guard.rb +66 -0
- data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +65 -0
- data/spec/models/callbacks/private_method.rb +44 -0
- data/spec/models/callbacks/private_method_multiple.rb +44 -0
- data/spec/models/callbacks/with_args.rb +62 -0
- data/spec/models/callbacks/with_args_multiple.rb +61 -0
- data/spec/models/callbacks/with_state_arg.rb +34 -0
- data/spec/models/callbacks/with_state_arg_multiple.rb +29 -0
- data/spec/models/complex_example.rb +222 -0
- data/spec/models/conversation.rb +93 -0
- data/spec/models/default_state.rb +12 -0
- data/spec/models/double_definer.rb +21 -0
- data/spec/models/dynamoid/complex_dynamoid_example.rb +37 -0
- data/spec/models/dynamoid/dynamoid_multiple.rb +18 -0
- data/spec/models/dynamoid/dynamoid_simple.rb +18 -0
- data/spec/models/foo.rb +106 -0
- data/spec/models/foo_callback_multiple.rb +45 -0
- data/spec/models/guard_arguments_check.rb +17 -0
- data/spec/models/guard_with_params.rb +24 -0
- data/spec/models/guard_with_params_multiple.rb +18 -0
- data/spec/models/guardian.rb +58 -0
- data/spec/models/guardian_multiple.rb +48 -0
- data/spec/models/guardian_without_from_specified.rb +18 -0
- data/spec/models/initial_state_proc.rb +31 -0
- data/spec/models/mongoid/complex_mongoid_example.rb +37 -0
- data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/mongoid_relationships.rb +26 -0
- data/spec/models/mongoid/no_scope_mongoid.rb +21 -0
- data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/simple_mongoid.rb +23 -0
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +25 -0
- data/spec/models/mongoid/validator_mongoid.rb +100 -0
- data/spec/models/multi_transitioner.rb +34 -0
- data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
- data/spec/models/namespaced_multiple_example.rb +42 -0
- data/spec/models/no_initial_state.rb +25 -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/nobrainer/simple_no_brainer.rb +23 -0
- data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
- data/spec/models/not_auto_loaded/process.rb +21 -0
- data/spec/models/parametrised_event.rb +42 -0
- data/spec/models/parametrised_event_multiple.rb +29 -0
- data/spec/models/process_with_new_dsl.rb +31 -0
- data/spec/models/provided_state.rb +24 -0
- data/spec/models/redis/complex_redis_example.rb +40 -0
- 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 +46 -0
- data/spec/models/sequel/invalid_persistor.rb +52 -0
- data/spec/models/sequel/sequel_multiple.rb +25 -0
- data/spec/models/sequel/sequel_simple.rb +26 -0
- 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/silencer.rb +27 -0
- data/spec/models/simple_custom_example.rb +53 -0
- data/spec/models/simple_example.rb +23 -0
- data/spec/models/simple_example_with_guard_args.rb +17 -0
- data/spec/models/simple_multiple_example.rb +42 -0
- data/spec/models/state_machine_with_failed_event.rb +20 -0
- data/spec/models/states_on_one_line_example.rb +8 -0
- data/spec/models/sub_class.rb +41 -0
- data/spec/models/sub_class_with_more_states.rb +18 -0
- data/spec/models/sub_classing.rb +3 -0
- data/spec/models/super_class.rb +46 -0
- data/spec/models/this_name_better_not_be_in_use.rb +11 -0
- data/spec/models/valid_state_name.rb +23 -0
- data/spec/spec_helper.rb +36 -0
- 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 +100 -0
- data/spec/unit/basic_two_state_machines_example_spec.rb +10 -0
- data/spec/unit/callback_multiple_spec.rb +304 -0
- data/spec/unit/callbacks_spec.rb +521 -0
- data/spec/unit/complex_example_spec.rb +93 -0
- data/spec/unit/complex_multiple_example_spec.rb +115 -0
- data/spec/unit/edge_cases_spec.rb +16 -0
- data/spec/unit/event_multiple_spec.rb +73 -0
- data/spec/unit/event_naming_spec.rb +16 -0
- data/spec/unit/event_spec.rb +394 -0
- data/spec/unit/exception_spec.rb +11 -0
- data/spec/unit/guard_arguments_check_spec.rb +9 -0
- data/spec/unit/guard_multiple_spec.rb +60 -0
- data/spec/unit/guard_spec.rb +89 -0
- data/spec/unit/guard_with_params_multiple_spec.rb +10 -0
- data/spec/unit/guard_with_params_spec.rb +14 -0
- data/spec/unit/guard_without_from_specified_spec.rb +10 -0
- data/spec/unit/initial_state_multiple_spec.rb +15 -0
- data/spec/unit/initial_state_spec.rb +12 -0
- data/spec/unit/inspection_multiple_spec.rb +201 -0
- data/spec/unit/inspection_spec.rb +149 -0
- 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 +78 -0
- data/spec/unit/memory_leak_spec.rb +38 -0
- data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
- data/spec/unit/namespaced_multiple_example_spec.rb +75 -0
- data/spec/unit/new_dsl_spec.rb +12 -0
- data/spec/unit/override_warning_spec.rb +94 -0
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +618 -0
- data/spec/unit/persistence/active_record_persistence_spec.rb +773 -0
- data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +135 -0
- data/spec/unit/persistence/dynamoid_persistence_spec.rb +84 -0
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +200 -0
- data/spec/unit/persistence/mongoid_persistence_spec.rb +165 -0
- 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 +53 -0
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +148 -0
- data/spec/unit/persistence/sequel_persistence_spec.rb +368 -0
- data/spec/unit/readme_spec.rb +41 -0
- data/spec/unit/reloading_spec.rb +15 -0
- data/spec/unit/rspec_matcher_spec.rb +88 -0
- data/spec/unit/simple_custom_example_spec.rb +39 -0
- data/spec/unit/simple_example_spec.rb +57 -0
- data/spec/unit/simple_multiple_example_spec.rb +91 -0
- data/spec/unit/state_spec.rb +89 -0
- data/spec/unit/states_on_one_line_example_spec.rb +16 -0
- data/spec/unit/subclassing_multiple_spec.rb +74 -0
- data/spec/unit/subclassing_spec.rb +46 -0
- data/spec/unit/transition_spec.rb +436 -0
- data/test/minitest_helper.rb +57 -0
- data/test/unit/minitest_matcher_test.rb +80 -0
- metadata +609 -0
@@ -0,0 +1,16 @@
|
|
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", "4.2.5"
|
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
|
+
|
16
|
+
gemspec path: "../"
|
@@ -0,0 +1,11 @@
|
|
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", "4.2.5"
|
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
|
+
|
11
|
+
gemspec path: "../"
|
@@ -0,0 +1,13 @@
|
|
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.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
|
+
|
13
|
+
gemspec path: "../"
|
@@ -0,0 +1,13 @@
|
|
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
|
+
|
13
|
+
gemspec path: "../"
|
@@ -0,0 +1,13 @@
|
|
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
|
+
|
13
|
+
gemspec path: "../"
|
data/lib/aasm.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
require 'aasm/version'
|
4
|
+
require 'aasm/errors'
|
5
|
+
require 'aasm/configuration'
|
6
|
+
require 'aasm/base'
|
7
|
+
require 'aasm/dsl_helper'
|
8
|
+
require 'aasm/instance_base'
|
9
|
+
require 'aasm/core/transition'
|
10
|
+
require 'aasm/core/event'
|
11
|
+
require 'aasm/core/state'
|
12
|
+
require 'aasm/core/invoker'
|
13
|
+
require 'aasm/core/invokers/base_invoker'
|
14
|
+
require 'aasm/core/invokers/class_invoker'
|
15
|
+
require 'aasm/core/invokers/literal_invoker'
|
16
|
+
require 'aasm/core/invokers/proc_invoker'
|
17
|
+
require 'aasm/localizer'
|
18
|
+
require 'aasm/state_machine_store'
|
19
|
+
require 'aasm/state_machine'
|
20
|
+
require 'aasm/persistence'
|
21
|
+
require 'aasm/persistence/base'
|
22
|
+
require 'aasm/persistence/plain_persistence'
|
23
|
+
require 'aasm/aasm'
|
data/lib/aasm/aasm.rb
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
module AASM
|
2
|
+
# this is used internally as an argument default value to represent no value
|
3
|
+
NO_VALUE = :_aasm_no_value
|
4
|
+
|
5
|
+
# provide a state machine for the including class
|
6
|
+
# make sure to load class methods as well
|
7
|
+
# initialize persistence for the state machine
|
8
|
+
def self.included(base) #:nodoc:
|
9
|
+
base.extend AASM::ClassMethods
|
10
|
+
|
11
|
+
# do not overwrite existing state machines, which could have been created by
|
12
|
+
# inheritance, see class method inherited
|
13
|
+
AASM::StateMachineStore.register(base)
|
14
|
+
|
15
|
+
AASM::Persistence.load_persistence(base)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
# make sure inheritance (aka subclassing) works with AASM
|
21
|
+
def inherited(base)
|
22
|
+
AASM::StateMachineStore.register(base, self)
|
23
|
+
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# this is the entry point for all state and event definitions
|
28
|
+
def aasm(*args, &block)
|
29
|
+
if args[0].is_a?(Symbol) || args[0].is_a?(String)
|
30
|
+
# using custom name
|
31
|
+
state_machine_name = args[0].to_sym
|
32
|
+
options = args[1] || {}
|
33
|
+
else
|
34
|
+
# using the default state_machine_name
|
35
|
+
state_machine_name = :default
|
36
|
+
options = args[0] || {}
|
37
|
+
end
|
38
|
+
|
39
|
+
AASM::StateMachineStore.fetch(self, true).register(state_machine_name, AASM::StateMachine.new(state_machine_name))
|
40
|
+
|
41
|
+
# use a default despite the DSL configuration default.
|
42
|
+
# this is because configuration hasn't been setup for the AASM class but we are accessing a DSL option already for the class.
|
43
|
+
aasm_klass = options[:with_klass] || AASM::Base
|
44
|
+
|
45
|
+
raise ArgumentError, "The class #{aasm_klass} must inherit from AASM::Base!" unless aasm_klass.ancestors.include?(AASM::Base)
|
46
|
+
|
47
|
+
@aasm ||= Concurrent::Map.new
|
48
|
+
if @aasm[state_machine_name]
|
49
|
+
# make sure to use provided options
|
50
|
+
options.each do |key, value|
|
51
|
+
@aasm[state_machine_name].state_machine.config.send("#{key}=", value)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
# create a new base
|
55
|
+
@aasm[state_machine_name] = aasm_klass.new(
|
56
|
+
self,
|
57
|
+
state_machine_name,
|
58
|
+
AASM::StateMachineStore.fetch(self, true).machine(state_machine_name),
|
59
|
+
options
|
60
|
+
)
|
61
|
+
end
|
62
|
+
@aasm[state_machine_name].instance_eval(&block) if block # new DSL
|
63
|
+
@aasm[state_machine_name]
|
64
|
+
end
|
65
|
+
end # ClassMethods
|
66
|
+
|
67
|
+
# this is the entry point for all instance-level access to AASM
|
68
|
+
def aasm(name=:default)
|
69
|
+
unless AASM::StateMachineStore.fetch(self.class, true).machine(name)
|
70
|
+
raise AASM::UnknownStateMachineError.new("There is no state machine with the name '#{name}' defined in #{self.class.name}!")
|
71
|
+
end
|
72
|
+
@aasm ||= Concurrent::Map.new
|
73
|
+
@aasm[name.to_sym] ||= AASM::InstanceBase.new(self, name.to_sym)
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize_dup(other)
|
77
|
+
@aasm = Concurrent::Map.new
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Takes args and a from state and removes the first
|
84
|
+
# element from args if it is a valid to_state for
|
85
|
+
# the event given the from_state
|
86
|
+
def process_args(event, from_state, *args)
|
87
|
+
# If the first arg doesn't respond to to_sym then
|
88
|
+
# it isn't a symbol or string so it can't be a state
|
89
|
+
# name anyway
|
90
|
+
return args unless args.first.respond_to?(:to_sym)
|
91
|
+
if event.transitions_from_state(from_state).map(&:to).flatten.include?(args.first)
|
92
|
+
return args[1..-1]
|
93
|
+
end
|
94
|
+
return args
|
95
|
+
end
|
96
|
+
|
97
|
+
def aasm_fire_event(state_machine_name, event_name, options, *args, &block)
|
98
|
+
event = self.class.aasm(state_machine_name).state_machine.events[event_name]
|
99
|
+
begin
|
100
|
+
old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
|
101
|
+
|
102
|
+
fire_default_callbacks(event, *process_args(event, aasm(state_machine_name).current_state, *args))
|
103
|
+
|
104
|
+
if may_fire_to = event.may_fire?(self, *args)
|
105
|
+
fire_exit_callbacks(old_state, *process_args(event, aasm(state_machine_name).current_state, *args))
|
106
|
+
if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
|
107
|
+
aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
|
108
|
+
else
|
109
|
+
aasm_failed(state_machine_name, event_name, old_state, event.failed_callbacks)
|
110
|
+
end
|
111
|
+
else
|
112
|
+
aasm_failed(state_machine_name, event_name, old_state, event.failed_callbacks)
|
113
|
+
end
|
114
|
+
rescue StandardError => e
|
115
|
+
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
116
|
+
event.fire_global_callbacks(:error_on_all_events, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
117
|
+
raise(e)
|
118
|
+
false
|
119
|
+
ensure
|
120
|
+
event.fire_callbacks(:ensure, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
121
|
+
event.fire_global_callbacks(:ensure_on_all_events, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
122
|
+
end
|
123
|
+
end
|
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
|
+
|
145
|
+
def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
|
146
|
+
persist = options[:persist]
|
147
|
+
|
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)
|
150
|
+
|
151
|
+
new_state.fire_callbacks(:before_enter, self, *callback_args)
|
152
|
+
|
153
|
+
new_state.fire_callbacks(:enter, self, *callback_args) # TODO: remove for AASM 4?
|
154
|
+
|
155
|
+
persist_successful = true
|
156
|
+
if persist
|
157
|
+
persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
|
158
|
+
if persist_successful
|
159
|
+
yield if block_given?
|
160
|
+
event.fire_callbacks(:before_success, self, *callback_args)
|
161
|
+
event.fire_transition_callbacks(self, *process_args(event, old_state.name, *args))
|
162
|
+
event.fire_callbacks(:success, self, *callback_args)
|
163
|
+
end
|
164
|
+
else
|
165
|
+
aasm(state_machine_name).current_state = new_state_name
|
166
|
+
yield if block_given?
|
167
|
+
end
|
168
|
+
|
169
|
+
binding_event = event.options[:binding_event]
|
170
|
+
if binding_event
|
171
|
+
__send__("#{binding_event}#{'!' if persist}")
|
172
|
+
end
|
173
|
+
|
174
|
+
if persist_successful
|
175
|
+
old_state.fire_callbacks(:after_exit, self, *callback_args)
|
176
|
+
new_state.fire_callbacks(:after_enter, self, *callback_args)
|
177
|
+
event.fire_callbacks(
|
178
|
+
:after,
|
179
|
+
self,
|
180
|
+
*process_args(event, old_state.name, *args)
|
181
|
+
)
|
182
|
+
event.fire_global_callbacks(
|
183
|
+
:after_all_events,
|
184
|
+
self,
|
185
|
+
*process_args(event, old_state.name, *args)
|
186
|
+
)
|
187
|
+
|
188
|
+
self.aasm_event_fired(event.name, old_state.name, aasm(state_machine_name).current_state) if self.respond_to?(:aasm_event_fired)
|
189
|
+
else
|
190
|
+
self.aasm_event_failed(event.name, old_state.name) if self.respond_to?(:aasm_event_failed)
|
191
|
+
end
|
192
|
+
|
193
|
+
persist_successful
|
194
|
+
end
|
195
|
+
|
196
|
+
def aasm_failed(state_machine_name, event_name, old_state, failures = [])
|
197
|
+
if self.respond_to?(:aasm_event_failed)
|
198
|
+
self.aasm_event_failed(event_name, old_state.name)
|
199
|
+
end
|
200
|
+
|
201
|
+
if AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.whiny_transitions
|
202
|
+
raise AASM::InvalidTransition.new(self, event_name, state_machine_name, failures)
|
203
|
+
else
|
204
|
+
false
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
data/lib/aasm/base.rb
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module AASM
|
4
|
+
class Base
|
5
|
+
|
6
|
+
attr_reader :klass, :state_machine
|
7
|
+
|
8
|
+
def initialize(klass, name, state_machine, options={}, &block)
|
9
|
+
@klass = klass
|
10
|
+
@name = name
|
11
|
+
# @state_machine = klass.aasm(@name).state_machine
|
12
|
+
@state_machine = state_machine
|
13
|
+
@state_machine.config.column ||= (options[:column] || default_column).to_sym
|
14
|
+
# @state_machine.config.column = options[:column].to_sym if options[:column] # master
|
15
|
+
@options = options
|
16
|
+
|
17
|
+
# let's cry if the transition is invalid
|
18
|
+
configure :whiny_transitions, true
|
19
|
+
|
20
|
+
# create named scopes for each state
|
21
|
+
configure :create_scopes, true
|
22
|
+
|
23
|
+
# don't store any new state if the model is invalid (in ActiveRecord)
|
24
|
+
configure :skip_validation_on_save, false
|
25
|
+
|
26
|
+
# raise if the model is invalid (in ActiveRecord)
|
27
|
+
configure :whiny_persistence, false
|
28
|
+
|
29
|
+
# Use transactions (in ActiveRecord)
|
30
|
+
configure :use_transactions, true
|
31
|
+
|
32
|
+
# use requires_new for nested transactions (in ActiveRecord)
|
33
|
+
configure :requires_new_transaction, true
|
34
|
+
|
35
|
+
# use pessimistic locking (in ActiveRecord)
|
36
|
+
# true for FOR UPDATE lock
|
37
|
+
# string for a specific lock type i.e. FOR UPDATE NOWAIT
|
38
|
+
configure :requires_lock, false
|
39
|
+
|
40
|
+
# set to true to forbid direct assignment of aasm_state column (in ActiveRecord)
|
41
|
+
configure :no_direct_assignment, false
|
42
|
+
|
43
|
+
# allow a AASM::Base sub-class to be used for state machine
|
44
|
+
configure :with_klass, AASM::Base
|
45
|
+
|
46
|
+
configure :enum, nil
|
47
|
+
|
48
|
+
# Set to true to namespace reader methods and constants
|
49
|
+
configure :namespace, false
|
50
|
+
|
51
|
+
# Configure a logger, with default being a Logger to STDERR
|
52
|
+
configure :logger, Logger.new(STDERR)
|
53
|
+
|
54
|
+
# make sure to raise an error if no_direct_assignment is enabled
|
55
|
+
# and attribute is directly assigned though
|
56
|
+
aasm_name = @name
|
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
|
67
|
+
end
|
68
|
+
|
69
|
+
# This method is both a getter and a setter
|
70
|
+
def attribute_name(column_name=nil)
|
71
|
+
if column_name
|
72
|
+
@state_machine.config.column = column_name.to_sym
|
73
|
+
else
|
74
|
+
@state_machine.config.column ||= :aasm_state
|
75
|
+
end
|
76
|
+
@state_machine.config.column
|
77
|
+
end
|
78
|
+
|
79
|
+
def initial_state(new_initial_state=nil)
|
80
|
+
if new_initial_state
|
81
|
+
@state_machine.initial_state = new_initial_state
|
82
|
+
else
|
83
|
+
@state_machine.initial_state
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# define a state
|
88
|
+
# args
|
89
|
+
# [0] state
|
90
|
+
# [1] options (or nil)
|
91
|
+
# or
|
92
|
+
# [0] state
|
93
|
+
# [1..] state
|
94
|
+
def state(*args)
|
95
|
+
names, options = interpret_state_args(args)
|
96
|
+
names.each do |name|
|
97
|
+
@state_machine.add_state(name, klass, options)
|
98
|
+
|
99
|
+
aasm_name = @name.to_sym
|
100
|
+
state = name.to_sym
|
101
|
+
|
102
|
+
method_name = namespace? ? "#{namespace}_#{name}" : name
|
103
|
+
safely_define_method klass, "#{method_name}?", -> do
|
104
|
+
aasm(aasm_name).current_state == state
|
105
|
+
end
|
106
|
+
|
107
|
+
const_name = namespace? ? "STATE_#{namespace.upcase}_#{name.upcase}" : "STATE_#{name.upcase}"
|
108
|
+
unless klass.const_defined?(const_name)
|
109
|
+
klass.const_set(const_name, name)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# define an event
|
115
|
+
def event(name, options={}, &block)
|
116
|
+
@state_machine.add_event(name, options, &block)
|
117
|
+
|
118
|
+
aasm_name = @name.to_sym
|
119
|
+
event = name.to_sym
|
120
|
+
|
121
|
+
# an addition over standard aasm so that, before firing an event, you can ask
|
122
|
+
# may_event? and get back a boolean that tells you whether the guard method
|
123
|
+
# on the transition will let this happen.
|
124
|
+
safely_define_method klass, "may_#{name}?", ->(*args) do
|
125
|
+
aasm(aasm_name).may_fire_event?(event, *args)
|
126
|
+
end
|
127
|
+
|
128
|
+
safely_define_method klass, "#{name}!", ->(*args, &block) do
|
129
|
+
aasm(aasm_name).current_event = :"#{name}!"
|
130
|
+
aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
safely_define_method klass, name, ->(*args, &block) do
|
134
|
+
aasm(aasm_name).current_event = event
|
135
|
+
aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block)
|
136
|
+
end
|
137
|
+
|
138
|
+
skip_instance_level_validation(event, name, aasm_name, klass)
|
139
|
+
|
140
|
+
# Create aliases for the event methods. Keep the old names to maintain backwards compatibility.
|
141
|
+
if namespace?
|
142
|
+
klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?")
|
143
|
+
klass.send(:alias_method, "#{name}_#{namespace}!", "#{name}!")
|
144
|
+
klass.send(:alias_method, "#{name}_#{namespace}", name)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
def after_all_transitions(*callbacks, &block)
|
150
|
+
@state_machine.add_global_callbacks(:after_all_transitions, *callbacks, &block)
|
151
|
+
end
|
152
|
+
|
153
|
+
def after_all_transactions(*callbacks, &block)
|
154
|
+
@state_machine.add_global_callbacks(:after_all_transactions, *callbacks, &block)
|
155
|
+
end
|
156
|
+
|
157
|
+
def before_all_transactions(*callbacks, &block)
|
158
|
+
@state_machine.add_global_callbacks(:before_all_transactions, *callbacks, &block)
|
159
|
+
end
|
160
|
+
|
161
|
+
def before_all_events(*callbacks, &block)
|
162
|
+
@state_machine.add_global_callbacks(:before_all_events, *callbacks, &block)
|
163
|
+
end
|
164
|
+
|
165
|
+
def after_all_events(*callbacks, &block)
|
166
|
+
@state_machine.add_global_callbacks(:after_all_events, *callbacks, &block)
|
167
|
+
end
|
168
|
+
|
169
|
+
def error_on_all_events(*callbacks, &block)
|
170
|
+
@state_machine.add_global_callbacks(:error_on_all_events, *callbacks, &block)
|
171
|
+
end
|
172
|
+
|
173
|
+
def ensure_on_all_events(*callbacks, &block)
|
174
|
+
@state_machine.add_global_callbacks(:ensure_on_all_events, *callbacks, &block)
|
175
|
+
end
|
176
|
+
|
177
|
+
def states
|
178
|
+
@state_machine.states
|
179
|
+
end
|
180
|
+
|
181
|
+
def events
|
182
|
+
@state_machine.events.values
|
183
|
+
end
|
184
|
+
|
185
|
+
# aasm.event(:event_name).human?
|
186
|
+
def human_event_name(event) # event_name?
|
187
|
+
AASM::Localizer.new.human_event_name(klass, event)
|
188
|
+
end
|
189
|
+
|
190
|
+
def states_for_select
|
191
|
+
states.map { |state| state.for_select }
|
192
|
+
end
|
193
|
+
|
194
|
+
def from_states_for_state(state, options={})
|
195
|
+
if options[:transition]
|
196
|
+
@state_machine.events[options[:transition]].transitions_to_state(state).flatten.map(&:from).flatten
|
197
|
+
else
|
198
|
+
|
199
|
+
events.map {|e| e.transitions_to_state(state)}.flatten.map(&:from).flatten
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def default_column
|
206
|
+
@name.to_sym == :default ? :aasm_state : @name.to_sym
|
207
|
+
end
|
208
|
+
|
209
|
+
def configure(key, default_value)
|
210
|
+
if @options.key?(key)
|
211
|
+
@state_machine.config.send("#{key}=", @options[key])
|
212
|
+
elsif @state_machine.config.send(key).nil?
|
213
|
+
@state_machine.config.send("#{key}=", default_value)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def safely_define_method(klass, method_name, method_definition)
|
218
|
+
# Warn if method exists and it did not originate from an enum
|
219
|
+
if klass.method_defined?(method_name) &&
|
220
|
+
! ( @state_machine.config.enum &&
|
221
|
+
klass.respond_to?(:defined_enums) &&
|
222
|
+
klass.defined_enums.values.any?{ |methods|
|
223
|
+
methods.keys{| enum | enum + '?' == method_name }
|
224
|
+
})
|
225
|
+
unless AASM::Configuration.hide_warnings
|
226
|
+
@state_machine.config.logger.warn "#{klass.name}: overriding method '#{method_name}'!"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
klass.send(:define_method, method_name, method_definition)
|
231
|
+
end
|
232
|
+
|
233
|
+
def namespace?
|
234
|
+
!!@state_machine.config.namespace
|
235
|
+
end
|
236
|
+
|
237
|
+
def namespace
|
238
|
+
if @state_machine.config.namespace == true
|
239
|
+
@name
|
240
|
+
else
|
241
|
+
@state_machine.config.namespace
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def interpret_state_args(args)
|
246
|
+
if args.last.is_a?(Hash) && args.size == 2
|
247
|
+
[[args.first], args.last]
|
248
|
+
elsif args.size > 0
|
249
|
+
[args, {}]
|
250
|
+
else
|
251
|
+
raise "count not parse states: #{args}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def skip_instance_level_validation(event, name, aasm_name, klass)
|
256
|
+
# Overrides the skip_validation config for an instance (If skip validation is set to false in original config) and
|
257
|
+
# restores it back to the original value after the event is fired.
|
258
|
+
safely_define_method klass, "#{name}_without_validation!", ->(*args, &block) do
|
259
|
+
original_config = AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save
|
260
|
+
begin
|
261
|
+
AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = true unless original_config
|
262
|
+
aasm(aasm_name).current_event = :"#{name}!"
|
263
|
+
aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
|
264
|
+
ensure
|
265
|
+
AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = original_config
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
end
|