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,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if defined?(ActiceRecord)
|
4
|
+
require 'i18n'
|
5
|
+
|
6
|
+
I18n.enforce_available_locales = false
|
7
|
+
load_schema
|
8
|
+
|
9
|
+
describe AASM::Localizer, "new style" do
|
10
|
+
before(:all) do
|
11
|
+
I18n.load_path << 'spec/en.yml'
|
12
|
+
I18n.default_locale = :en
|
13
|
+
I18n.reload!
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:all) do
|
17
|
+
I18n.load_path.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
let (:foo_opened) { LocalizerTestModel.new }
|
21
|
+
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
|
22
|
+
|
23
|
+
context 'aasm.human_state' do
|
24
|
+
it 'should return translated state value' do
|
25
|
+
expect(foo_opened.aasm.human_state).to eq("It's open now!")
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should return humanized value if not localized' do
|
29
|
+
expect(foo_closed.aasm.human_state).to eq("Closed")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'aasm.human_event_name' do
|
34
|
+
it 'should return translated event name' do
|
35
|
+
expect(LocalizerTestModel.aasm.human_event_name(:close)).to eq("Let's close it!")
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return humanized event name' do
|
39
|
+
expect(LocalizerTestModel.aasm.human_event_name(:open)).to eq("Open")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe AASM::Localizer, "deprecated style" do
|
45
|
+
before(:all) do
|
46
|
+
I18n.load_path << 'spec/en_deprecated_style.yml'
|
47
|
+
I18n.default_locale = :en
|
48
|
+
I18n.reload!
|
49
|
+
end
|
50
|
+
|
51
|
+
after(:all) do
|
52
|
+
I18n.load_path.clear
|
53
|
+
end
|
54
|
+
|
55
|
+
let (:foo_opened) { LocalizerTestModel.new }
|
56
|
+
let (:foo_closed) { LocalizerTestModel.new.tap { |x| x.aasm_state = :closed } }
|
57
|
+
|
58
|
+
context 'aasm.human_state' do
|
59
|
+
it 'should return translated state value' do
|
60
|
+
expect(foo_opened.aasm.human_state).to eq("It's open now!")
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should return humanized value if not localized' do
|
64
|
+
expect(foo_closed.aasm.human_state).to eq("Closed")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'aasm.human_event_name' do
|
69
|
+
it 'should return translated event name' do
|
70
|
+
expect(LocalizerTestModel.aasm.human_event_name(:close)).to eq("Let's close it!")
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should return humanized event name' do
|
74
|
+
expect(LocalizerTestModel.aasm.human_event_name(:open)).to eq("Open")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
|
3
|
+
# describe "state machines" do
|
4
|
+
|
5
|
+
# def number_of_objects(klass)
|
6
|
+
# ObjectSpace.each_object(klass) {}
|
7
|
+
# end
|
8
|
+
|
9
|
+
# def machines
|
10
|
+
# AASM::StateMachineStore.instance_variable_get("@stores")
|
11
|
+
# end
|
12
|
+
|
13
|
+
# it "should be created without memory leak" do
|
14
|
+
# machines_count = machines.size
|
15
|
+
# state_count = number_of_objects(AASM::Core::State)
|
16
|
+
# event_count = number_of_objects(AASM::Core::Event)
|
17
|
+
# puts "event_count = #{event_count}"
|
18
|
+
# transition_count = number_of_objects(AASM::Core::Transition)
|
19
|
+
|
20
|
+
# load File.expand_path(File.dirname(__FILE__) + '/../models/not_auto_loaded/process.rb')
|
21
|
+
# machines.size.should == machines_count + 1 # + Process
|
22
|
+
# number_of_objects(Models::Process).should == 0
|
23
|
+
# number_of_objects(AASM::Core::State).should == state_count + 3 # + Process
|
24
|
+
# puts "event_count = #{number_of_objects(AASM::Core::Event)}"
|
25
|
+
# number_of_objects(AASM::Core::Event).should == event_count + 2 # + Process
|
26
|
+
# number_of_objects(AASM::Core::Transition).should == transition_count + 2 # + Process
|
27
|
+
|
28
|
+
# Models.send(:remove_const, "Process") if Models.const_defined?("Process")
|
29
|
+
# load File.expand_path(File.dirname(__FILE__) + '/../models/not_auto_loaded/process.rb')
|
30
|
+
# machines.size.should == machines_count + 1 # + Process
|
31
|
+
# number_of_objects(AASM::Core::State).should == state_count + 3 # + Process
|
32
|
+
# # ObjectSpace.each_object(AASM::Core::Event) {|o| puts o.inspect}
|
33
|
+
# puts "event_count = #{number_of_objects(AASM::Core::Event)}"
|
34
|
+
# number_of_objects(AASM::Core::Event).should == event_count + 2 # + Process
|
35
|
+
# number_of_objects(AASM::Core::Transition).should == transition_count + 2 # + Process
|
36
|
+
# end
|
37
|
+
|
38
|
+
# end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "multiple transitions that differ only by guard" do
|
4
|
+
let(:job) { MultipleTransitionsThatDifferOnlyByGuard.new }
|
5
|
+
|
6
|
+
it 'does not follow the first transition if its guard fails' do
|
7
|
+
expect{job.go}.not_to raise_error
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'executes the second transition\'s callbacks' do
|
11
|
+
job.go
|
12
|
+
expect(job.executed_second).to be_truthy
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'state machine' do
|
4
|
+
let(:namespaced) { NamespacedMultipleExample.new }
|
5
|
+
|
6
|
+
it 'starts with an initial state' do
|
7
|
+
expect(namespaced.aasm(:status).current_state).to eq(:unapproved)
|
8
|
+
expect(namespaced).to respond_to(:unapproved?)
|
9
|
+
expect(namespaced).to be_unapproved
|
10
|
+
|
11
|
+
expect(namespaced.aasm(:review_status).current_state).to eq(:unapproved)
|
12
|
+
expect(namespaced).to respond_to(:review_unapproved?)
|
13
|
+
expect(namespaced).to be_review_unapproved
|
14
|
+
|
15
|
+
expect(namespaced.aasm(:car).current_state).to eq(:unsold)
|
16
|
+
expect(namespaced).to respond_to(:car_unsold?)
|
17
|
+
expect(namespaced).to be_car_unsold
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'allows transitions to other states' do
|
21
|
+
expect(namespaced).to respond_to(:approve)
|
22
|
+
expect(namespaced).to respond_to(:approve!)
|
23
|
+
namespaced.approve!
|
24
|
+
expect(namespaced).to respond_to(:approved?)
|
25
|
+
expect(namespaced).to be_approved
|
26
|
+
|
27
|
+
expect(namespaced).to respond_to(:approve_review)
|
28
|
+
expect(namespaced).to respond_to(:approve_review!)
|
29
|
+
namespaced.approve_review!
|
30
|
+
expect(namespaced).to respond_to(:review_approved?)
|
31
|
+
expect(namespaced).to be_review_approved
|
32
|
+
|
33
|
+
expect(namespaced).to respond_to(:sell_car)
|
34
|
+
expect(namespaced).to respond_to(:sell_car!)
|
35
|
+
namespaced.sell_car!
|
36
|
+
expect(namespaced).to respond_to(:car_sold?)
|
37
|
+
expect(namespaced).to be_car_sold
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'denies transitions to other states' do
|
41
|
+
expect {namespaced.unapprove}.to raise_error(AASM::InvalidTransition)
|
42
|
+
expect {namespaced.unapprove!}.to raise_error(AASM::InvalidTransition)
|
43
|
+
namespaced.approve
|
44
|
+
expect {namespaced.approve}.to raise_error(AASM::InvalidTransition)
|
45
|
+
expect {namespaced.approve!}.to raise_error(AASM::InvalidTransition)
|
46
|
+
namespaced.unapprove
|
47
|
+
|
48
|
+
expect {namespaced.unapprove_review}.to raise_error(AASM::InvalidTransition)
|
49
|
+
expect {namespaced.unapprove_review!}.to raise_error(AASM::InvalidTransition)
|
50
|
+
namespaced.approve_review
|
51
|
+
expect {namespaced.approve_review}.to raise_error(AASM::InvalidTransition)
|
52
|
+
expect {namespaced.approve_review!}.to raise_error(AASM::InvalidTransition)
|
53
|
+
namespaced.unapprove_review
|
54
|
+
|
55
|
+
expect {namespaced.return_car}.to raise_error(AASM::InvalidTransition)
|
56
|
+
expect {namespaced.return_car!}.to raise_error(AASM::InvalidTransition)
|
57
|
+
namespaced.sell_car
|
58
|
+
expect {namespaced.sell_car}.to raise_error(AASM::InvalidTransition)
|
59
|
+
expect {namespaced.sell_car!}.to raise_error(AASM::InvalidTransition)
|
60
|
+
namespaced.return_car
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'defines constants for each state name' do
|
64
|
+
expect(NamespacedMultipleExample::STATE_UNAPPROVED).to eq(:unapproved)
|
65
|
+
expect(NamespacedMultipleExample::STATE_APPROVED).to eq(:approved)
|
66
|
+
|
67
|
+
expect(NamespacedMultipleExample::STATE_REVIEW_UNAPPROVED).to eq(:unapproved)
|
68
|
+
expect(NamespacedMultipleExample::STATE_REVIEW_APPROVED).to eq(:approved)
|
69
|
+
|
70
|
+
expect(NamespacedMultipleExample::STATE_CAR_UNSOLD).to eq(:unsold)
|
71
|
+
expect(NamespacedMultipleExample::STATE_CAR_SOLD).to eq(:sold)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "the new dsl" do
|
4
|
+
|
5
|
+
let(:process) {ProcessWithNewDsl.new}
|
6
|
+
|
7
|
+
it 'should not conflict with other event or state methods' do
|
8
|
+
expect {ProcessWithNewDsl.state}.to raise_error(RuntimeError, "wrong state method")
|
9
|
+
expect {ProcessWithNewDsl.event}.to raise_error(RuntimeError, "wrong event method")
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'warns when overrides a method' do
|
4
|
+
before do
|
5
|
+
AASM::Configuration.hide_warnings = false
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
AASM::Configuration.hide_warnings = true
|
10
|
+
end
|
11
|
+
|
12
|
+
module Clumsy
|
13
|
+
def self.included base
|
14
|
+
base.send :include, AASM
|
15
|
+
|
16
|
+
base.aasm do
|
17
|
+
state :valid
|
18
|
+
event(:save) { }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module WithEnumBase
|
24
|
+
def self.included base
|
25
|
+
base.send :include, AASM
|
26
|
+
base.instance_eval do
|
27
|
+
def defined_enums
|
28
|
+
{ 'state' => { 'valid' => 0, 'invalid' => 1 } }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
base.aasm enum: true do
|
32
|
+
state :valid
|
33
|
+
event(:save) { }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'state' do
|
39
|
+
let(:base_klass) do
|
40
|
+
Class.new do
|
41
|
+
def valid?; end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
subject { base_klass.send :include, Clumsy }
|
46
|
+
|
47
|
+
it 'should log to warn' do
|
48
|
+
expect_any_instance_of(Logger).to receive(:warn).with(": overriding method 'valid?'!")
|
49
|
+
subject
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'enum' do
|
54
|
+
let(:enum_base_klass) do
|
55
|
+
Class.new do
|
56
|
+
def valid?; end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
subject { enum_base_klass.send :include, WithEnumBase }
|
61
|
+
|
62
|
+
it 'should not log to warn' do
|
63
|
+
expect_any_instance_of(Logger).to receive(:warn).never
|
64
|
+
subject
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'event' do
|
69
|
+
context 'may?' do
|
70
|
+
let(:base_klass) do
|
71
|
+
Class.new do
|
72
|
+
def may_save?; end
|
73
|
+
def save!; end
|
74
|
+
def save; end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
subject { base_klass.send :include, Clumsy }
|
79
|
+
|
80
|
+
it 'should log to warn' do
|
81
|
+
expect_any_instance_of(Logger).to receive(:warn).exactly(3).times do |logger, message|
|
82
|
+
expect(
|
83
|
+
[
|
84
|
+
": overriding method 'may_save?'!",
|
85
|
+
": overriding method 'save!'!",
|
86
|
+
": overriding method 'save'!"
|
87
|
+
]
|
88
|
+
).to include(message)
|
89
|
+
end
|
90
|
+
subject
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,618 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if defined?(ActiveRecord)
|
4
|
+
|
5
|
+
Dir[File.dirname(__FILE__) + "/../../models/active_record/*.rb"].sort.each do |f|
|
6
|
+
require File.expand_path(f)
|
7
|
+
end
|
8
|
+
|
9
|
+
load_schema
|
10
|
+
|
11
|
+
# if you want to see the statements while running the spec enable the following line
|
12
|
+
# require 'logger'
|
13
|
+
# ActiveRecord::Base.logger = Logger.new(STDERR)
|
14
|
+
|
15
|
+
describe "instance methods" do
|
16
|
+
let(:gate) {MultipleGate.new}
|
17
|
+
|
18
|
+
it "should respond to aasm persistence methods" do
|
19
|
+
expect(gate).to respond_to(:aasm_read_state)
|
20
|
+
expect(gate).to respond_to(:aasm_write_state)
|
21
|
+
expect(gate).to respond_to(:aasm_write_state_without_persistence)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "aasm_column_looks_like_enum" do
|
25
|
+
subject { lambda{ gate.send(:aasm_column_looks_like_enum, :left) } }
|
26
|
+
|
27
|
+
let(:column_name) { "value" }
|
28
|
+
let(:columns_hash) { Hash[column_name, column] }
|
29
|
+
|
30
|
+
before :each do
|
31
|
+
allow(gate.class.aasm(:left)).to receive(:attribute_name).and_return(column_name.to_sym)
|
32
|
+
allow(gate.class).to receive(:columns_hash).and_return(columns_hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when AASM column has integer type" do
|
36
|
+
let(:column) { double(Object, type: :integer) }
|
37
|
+
|
38
|
+
it "returns true" do
|
39
|
+
expect(subject.call).to be_truthy
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when AASM column has string type" do
|
44
|
+
let(:column) { double(Object, type: :string) }
|
45
|
+
|
46
|
+
it "returns false" do
|
47
|
+
expect(subject.call).to be_falsey
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "aasm_guess_enum_method" do
|
53
|
+
subject { lambda{ gate.send(:aasm_guess_enum_method, :left) } }
|
54
|
+
|
55
|
+
before :each do
|
56
|
+
allow(gate.class.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "pluralizes AASM column name" do
|
60
|
+
expect(subject.call).to eq :values
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "aasm_enum" do
|
65
|
+
context "when AASM enum setting contains an explicit enum method name" do
|
66
|
+
let(:with_enum) { MultipleWithEnum.new }
|
67
|
+
|
68
|
+
it "returns whatever value was set in AASM config" do
|
69
|
+
expect(with_enum.send(:aasm_enum, :left)).to eq :test
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when AASM enum setting is simply set to true" do
|
74
|
+
let(:with_true_enum) { MultipleWithTrueEnum.new }
|
75
|
+
before :each do
|
76
|
+
allow(MultipleWithTrueEnum.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "infers enum method name from pluralized column name" do
|
80
|
+
expect(with_true_enum.send(:aasm_enum, :left)).to eq :values
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "when AASM enum setting is explicitly disabled" do
|
85
|
+
let(:with_false_enum) { MultipleWithFalseEnum.new }
|
86
|
+
|
87
|
+
it "returns nil" do
|
88
|
+
expect(with_false_enum.send(:aasm_enum, :left)).to be_nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when AASM enum setting is not enabled" do
|
93
|
+
before :each do
|
94
|
+
allow(MultipleGate.aasm(:left)).to receive(:attribute_name).and_return(:value)
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when AASM column looks like enum" do
|
98
|
+
before :each do
|
99
|
+
allow(gate).to receive(:aasm_column_looks_like_enum).with(:left).and_return(true)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "infers enum method name from pluralized column name" do
|
103
|
+
expect(gate.send(:aasm_enum, :left)).to eq :values
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when AASM column doesn't look like enum'" do
|
108
|
+
before :each do
|
109
|
+
allow(gate).to receive(:aasm_column_looks_like_enum)
|
110
|
+
.and_return(false)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "returns nil, as we're not using enum" do
|
114
|
+
expect(gate.send(:aasm_enum, :left)).to be_nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
if ActiveRecord::VERSION::MAJOR >= 4 && ActiveRecord::VERSION::MINOR >= 1 # won't work with Rails <= 4.1
|
120
|
+
# Enum are introduced from Rails 4.1, therefore enum syntax will not work on Rails <= 4.1
|
121
|
+
context "when AASM enum setting is not enabled and aasm column not present" do
|
122
|
+
|
123
|
+
let(:multiple_with_enum_without_column) {MultipleWithEnumWithoutColumn.new}
|
124
|
+
|
125
|
+
it "should raise NoMethodError for transitions" do
|
126
|
+
expect{multiple_with_enum_without_column.send(:view, :left)}.to raise_error(NoMethodError, /undefined method .status./)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
context "when AASM is configured to use enum" do
|
135
|
+
let(:state_sym) { :running }
|
136
|
+
let(:state_code) { 2 }
|
137
|
+
let(:enum_name) { :states }
|
138
|
+
let(:enum) { Hash[state_sym, state_code] }
|
139
|
+
|
140
|
+
before :each do
|
141
|
+
allow(gate).to receive(:aasm_enum).and_return(enum_name)
|
142
|
+
allow(gate).to receive(:aasm_write_state_attribute)
|
143
|
+
allow(gate).to receive(:write_attribute)
|
144
|
+
|
145
|
+
allow(MultipleGate).to receive(enum_name).and_return(enum)
|
146
|
+
end
|
147
|
+
|
148
|
+
describe "aasm_write_state" do
|
149
|
+
context "when AASM is configured to skip validations on save" do
|
150
|
+
before :each do
|
151
|
+
allow(gate).to receive(:aasm_skipping_validations).and_return(true)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "passes state code instead of state symbol to update_all" do
|
155
|
+
# stub_chain does not allow us to give expectations on call
|
156
|
+
# parameters in the middle of the chain, so we need to use
|
157
|
+
# intermediate object instead.
|
158
|
+
obj = double(Object, update_all: 1)
|
159
|
+
allow(MultipleGate).to receive_message_chain(:unscoped, :where)
|
160
|
+
.and_return(obj)
|
161
|
+
|
162
|
+
gate.aasm_write_state state_sym, :left
|
163
|
+
|
164
|
+
expect(obj).to have_received(:update_all)
|
165
|
+
.with(Hash[gate.class.aasm(:left).attribute_name, state_code])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when AASM is not skipping validations" do
|
170
|
+
it "delegates state update to the helper method" do
|
171
|
+
# Let's pretend that validation is passed
|
172
|
+
allow(gate).to receive(:save).and_return(true)
|
173
|
+
|
174
|
+
gate.aasm_write_state state_sym, :left
|
175
|
+
|
176
|
+
expect(gate).to have_received(:aasm_write_state_attribute).with(state_sym, :left)
|
177
|
+
expect(gate).to_not have_received :write_attribute
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe "aasm_write_state_without_persistence" do
|
183
|
+
it "delegates state update to the helper method" do
|
184
|
+
gate.aasm_write_state_without_persistence state_sym, :left
|
185
|
+
|
186
|
+
expect(gate).to have_received(:aasm_write_state_attribute).with(state_sym, :left)
|
187
|
+
expect(gate).to_not have_received :write_attribute
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "aasm_raw_attribute_value" do
|
192
|
+
it "converts state symbol to state code" do
|
193
|
+
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
194
|
+
.to eq state_code
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "when AASM is configured to use string field" do
|
200
|
+
let(:state_sym) { :running }
|
201
|
+
|
202
|
+
before :each do
|
203
|
+
allow(gate).to receive(:aasm_enum).and_return(nil)
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "aasm_raw_attribute_value" do
|
207
|
+
it "converts state symbol to string" do
|
208
|
+
expect(gate.send(:aasm_raw_attribute_value, state_sym))
|
209
|
+
.to eq state_sym.to_s
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe "aasm_write_attribute helper method" do
|
215
|
+
let(:sym) { :sym }
|
216
|
+
let(:value) { 42 }
|
217
|
+
|
218
|
+
before :each do
|
219
|
+
allow(gate).to receive(:write_attribute)
|
220
|
+
allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
|
221
|
+
|
222
|
+
gate.send(:aasm_write_state_attribute, sym, :left)
|
223
|
+
end
|
224
|
+
|
225
|
+
it "generates attribute value using a helper method" do
|
226
|
+
expect(gate).to have_received(:aasm_raw_attribute_value).with(sym, :left)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "writes attribute to the model" do
|
230
|
+
expect(gate).to have_received(:write_attribute).with(:aasm_state, value)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should return the initial state when new and the aasm field is nil" do
|
235
|
+
expect(gate.aasm(:left).current_state).to eq(:opened)
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should return the aasm column when new and the aasm field is not nil" do
|
239
|
+
gate.aasm_state = "closed"
|
240
|
+
expect(gate.aasm(:left).current_state).to eq(:closed)
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should return the aasm column when not new and the aasm.attribute_name is not nil" do
|
244
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
245
|
+
gate.aasm_state = "state"
|
246
|
+
expect(gate.aasm(:left).current_state).to eq(:state)
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should allow a nil state" do
|
250
|
+
allow(gate).to receive(:new_record?).and_return(false)
|
251
|
+
gate.aasm_state = nil
|
252
|
+
expect(gate.aasm(:left).current_state).to be_nil
|
253
|
+
end
|
254
|
+
|
255
|
+
context 'on initialization' do
|
256
|
+
it "should initialize the aasm state" do
|
257
|
+
expect(MultipleGate.new.aasm_state).to eql 'opened'
|
258
|
+
expect(MultipleGate.new.aasm(:left).current_state).to eql :opened
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should not initialize the aasm state if it has not been loaded" do
|
262
|
+
# we have to create a gate in the database, for which we only want to
|
263
|
+
# load the id, and not the state
|
264
|
+
gate = MultipleGate.create!
|
265
|
+
|
266
|
+
# then we just load the gate ids
|
267
|
+
MultipleGate.select(:id).where(id: gate.id).first
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
if ActiveRecord::VERSION::MAJOR < 4 && ActiveRecord::VERSION::MINOR < 2 # won't work with Rails >= 4.2
|
274
|
+
describe "direct state column access" do
|
275
|
+
it "accepts false states" do
|
276
|
+
f = MultipleFalseState.create!
|
277
|
+
expect(f.aasm_state).to eql false
|
278
|
+
expect {
|
279
|
+
f.aasm(:left).events.map(&:name)
|
280
|
+
}.to_not raise_error
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe 'subclasses' do
|
286
|
+
it "should have the same states as its parent class" do
|
287
|
+
expect(MultipleDerivateNewDsl.aasm(:left).states).to eq(MultipleSimpleNewDsl.aasm(:left).states)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should have the same events as its parent class" do
|
291
|
+
expect(MultipleDerivateNewDsl.aasm(:left).events).to eq(MultipleSimpleNewDsl.aasm(:left).events)
|
292
|
+
end
|
293
|
+
|
294
|
+
it "should have the same column as its parent even for the new dsl" do
|
295
|
+
expect(MultipleSimpleNewDsl.aasm(:left).attribute_name).to eq(:status)
|
296
|
+
expect(MultipleDerivateNewDsl.aasm(:left).attribute_name).to eq(:status)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
describe "named scopes with the new DSL" do
|
301
|
+
context "Does not already respond_to? the scope name" do
|
302
|
+
it "should add a scope for each state" do
|
303
|
+
expect(MultipleSimpleNewDsl).to respond_to(:unknown_scope)
|
304
|
+
expect(MultipleSimpleNewDsl).to respond_to(:another_unknown_scope)
|
305
|
+
|
306
|
+
expect(MultipleSimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
|
307
|
+
expect(MultipleSimpleNewDsl.another_unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context "Already respond_to? the scope name" do
|
312
|
+
it "should not add a scope" do
|
313
|
+
expect(MultipleSimpleNewDsl).to respond_to(:new)
|
314
|
+
expect(MultipleSimpleNewDsl.new.class).to eq(MultipleSimpleNewDsl)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
it "does not create scopes if requested" do
|
319
|
+
expect(MultipleNoScope).not_to respond_to(:pending)
|
320
|
+
end
|
321
|
+
|
322
|
+
context "result of scope" do
|
323
|
+
let!(:dsl1) { MultipleSimpleNewDsl.create!(status: :new) }
|
324
|
+
let!(:dsl2) { MultipleSimpleNewDsl.create!(status: :unknown_scope) }
|
325
|
+
|
326
|
+
after do
|
327
|
+
MultipleSimpleNewDsl.destroy_all
|
328
|
+
end
|
329
|
+
|
330
|
+
it "created scope works as where(name: :scope_name)" do
|
331
|
+
expect(MultipleSimpleNewDsl.unknown_scope).to contain_exactly(dsl2)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end # scopes
|
335
|
+
|
336
|
+
describe "direct assignment" do
|
337
|
+
it "is allowed by default" do
|
338
|
+
obj = MultipleNoScope.create
|
339
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
340
|
+
|
341
|
+
obj.aasm_state = :running
|
342
|
+
expect(obj.aasm_state.to_sym).to eql :running
|
343
|
+
end
|
344
|
+
|
345
|
+
it "is forbidden if configured" do
|
346
|
+
obj = MultipleNoDirectAssignment.create
|
347
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
348
|
+
|
349
|
+
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
350
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'can be turned off and on again' do
|
354
|
+
obj = MultipleNoDirectAssignment.create
|
355
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
356
|
+
|
357
|
+
expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
|
358
|
+
expect(obj.aasm_state.to_sym).to eql :pending
|
359
|
+
|
360
|
+
# allow it temporarily
|
361
|
+
MultipleNoDirectAssignment.aasm(:left).state_machine.config.no_direct_assignment = false
|
362
|
+
obj.aasm_state = :running
|
363
|
+
expect(obj.aasm_state.to_sym).to eql :running
|
364
|
+
|
365
|
+
# and forbid it again
|
366
|
+
MultipleNoDirectAssignment.aasm(:left).state_machine.config.no_direct_assignment = true
|
367
|
+
expect {obj.aasm_state = :pending}.to raise_error(AASM::NoDirectAssignmentError)
|
368
|
+
expect(obj.aasm_state.to_sym).to eql :running
|
369
|
+
end
|
370
|
+
end # direct assignment
|
371
|
+
|
372
|
+
describe 'initial states' do
|
373
|
+
it 'should support conditions' do
|
374
|
+
expect(MultipleThief.new(:skilled => true).aasm(:left).current_state).to eq(:rich)
|
375
|
+
expect(MultipleThief.new(:skilled => false).aasm(:left).current_state).to eq(:jailed)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
describe 'transitions with persistence' do
|
380
|
+
|
381
|
+
it "should work for valid models" do
|
382
|
+
valid_object = MultipleValidator.create(:name => 'name')
|
383
|
+
expect(valid_object).to be_sleeping
|
384
|
+
valid_object.status = :running
|
385
|
+
expect(valid_object).to be_running
|
386
|
+
end
|
387
|
+
|
388
|
+
it 'should not store states for invalid models' do
|
389
|
+
validator = MultipleValidator.create(:name => 'name')
|
390
|
+
expect(validator).to be_valid
|
391
|
+
expect(validator).to be_sleeping
|
392
|
+
|
393
|
+
validator.name = nil
|
394
|
+
expect(validator).not_to be_valid
|
395
|
+
expect { validator.run! }.to raise_error(ActiveRecord::RecordInvalid)
|
396
|
+
expect(validator).to be_sleeping
|
397
|
+
|
398
|
+
validator.reload
|
399
|
+
expect(validator).not_to be_running
|
400
|
+
expect(validator).to be_sleeping
|
401
|
+
|
402
|
+
validator.name = 'another name'
|
403
|
+
expect(validator).to be_valid
|
404
|
+
expect(validator.run!).to be_truthy
|
405
|
+
expect(validator).to be_running
|
406
|
+
|
407
|
+
validator.reload
|
408
|
+
expect(validator).to be_running
|
409
|
+
expect(validator).not_to be_sleeping
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'should not store states for invalid models silently if configured' do
|
413
|
+
validator = MultipleSilentPersistor.create(:name => 'name')
|
414
|
+
expect(validator).to be_valid
|
415
|
+
expect(validator).to be_sleeping
|
416
|
+
|
417
|
+
validator.name = nil
|
418
|
+
expect(validator).not_to be_valid
|
419
|
+
expect(validator.run!).to be_falsey
|
420
|
+
expect(validator).to be_sleeping
|
421
|
+
|
422
|
+
validator.reload
|
423
|
+
expect(validator).not_to be_running
|
424
|
+
expect(validator).to be_sleeping
|
425
|
+
|
426
|
+
validator.name = 'another name'
|
427
|
+
expect(validator).to be_valid
|
428
|
+
expect(validator.run!).to be_truthy
|
429
|
+
expect(validator).to be_running
|
430
|
+
|
431
|
+
validator.reload
|
432
|
+
expect(validator).to be_running
|
433
|
+
expect(validator).not_to be_sleeping
|
434
|
+
end
|
435
|
+
|
436
|
+
it 'should store states for invalid models if configured' do
|
437
|
+
persistor = MultipleInvalidPersistor.create(:name => 'name')
|
438
|
+
expect(persistor).to be_valid
|
439
|
+
expect(persistor).to be_sleeping
|
440
|
+
|
441
|
+
persistor.name = nil
|
442
|
+
expect(persistor).not_to be_valid
|
443
|
+
expect(persistor.run!).to be_truthy
|
444
|
+
expect(persistor).to be_running
|
445
|
+
|
446
|
+
persistor = MultipleInvalidPersistor.find(persistor.id)
|
447
|
+
persistor.valid?
|
448
|
+
expect(persistor).to be_valid
|
449
|
+
expect(persistor).to be_running
|
450
|
+
expect(persistor).not_to be_sleeping
|
451
|
+
|
452
|
+
persistor.reload
|
453
|
+
expect(persistor).to be_running
|
454
|
+
expect(persistor).not_to be_sleeping
|
455
|
+
end
|
456
|
+
|
457
|
+
describe 'transactions' do
|
458
|
+
let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
|
459
|
+
let(:transactor) { MultipleTransactor.create!(:name => 'transactor', :worker => worker) }
|
460
|
+
|
461
|
+
it 'should rollback all changes' do
|
462
|
+
expect(transactor).to be_sleeping
|
463
|
+
expect(worker.status).to eq('sleeping')
|
464
|
+
|
465
|
+
expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
|
466
|
+
expect(transactor).to be_running
|
467
|
+
expect(worker.reload.status).to eq('sleeping')
|
468
|
+
end
|
469
|
+
|
470
|
+
context "nested transactions" do
|
471
|
+
it "should rollback all changes in nested transaction" do
|
472
|
+
expect(transactor).to be_sleeping
|
473
|
+
expect(worker.status).to eq('sleeping')
|
474
|
+
|
475
|
+
Worker.transaction do
|
476
|
+
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
477
|
+
end
|
478
|
+
|
479
|
+
expect(transactor).to be_running
|
480
|
+
expect(worker.reload.status).to eq('sleeping')
|
481
|
+
end
|
482
|
+
|
483
|
+
it "should only rollback changes in the main transaction not the nested one" do
|
484
|
+
# change configuration to not require new transaction
|
485
|
+
AASM::StateMachineStore[MultipleTransactor][:left].config.requires_new_transaction = false
|
486
|
+
|
487
|
+
expect(transactor).to be_sleeping
|
488
|
+
expect(worker.status).to eq('sleeping')
|
489
|
+
|
490
|
+
Worker.transaction do
|
491
|
+
expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
|
492
|
+
end
|
493
|
+
|
494
|
+
expect(transactor).to be_running
|
495
|
+
expect(worker.reload.status).to eq('running')
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
describe "after_commit callback" do
|
500
|
+
it "should fire :after_commit if transaction was successful" do
|
501
|
+
validator = MultipleValidator.create(:name => 'name')
|
502
|
+
expect(validator).to be_sleeping
|
503
|
+
|
504
|
+
validator.run!
|
505
|
+
expect(validator).to be_running
|
506
|
+
expect(validator.name).to eq("name changed")
|
507
|
+
|
508
|
+
validator.sleep!("sleeper")
|
509
|
+
expect(validator).to be_sleeping
|
510
|
+
expect(validator.name).to eq("sleeper")
|
511
|
+
end
|
512
|
+
|
513
|
+
it "should not fire :after_commit if transaction failed" do
|
514
|
+
validator = MultipleValidator.create(:name => 'name')
|
515
|
+
expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
|
516
|
+
expect(validator.name).to eq("name")
|
517
|
+
end
|
518
|
+
|
519
|
+
it "should not fire if not saving" do
|
520
|
+
validator = MultipleValidator.create(:name => 'name')
|
521
|
+
expect(validator).to be_sleeping
|
522
|
+
validator.run
|
523
|
+
expect(validator).to be_running
|
524
|
+
expect(validator.name).to eq("name")
|
525
|
+
end
|
526
|
+
|
527
|
+
end
|
528
|
+
|
529
|
+
context "when not persisting" do
|
530
|
+
it 'should not rollback all changes' do
|
531
|
+
expect(transactor).to be_sleeping
|
532
|
+
expect(worker.status).to eq('sleeping')
|
533
|
+
|
534
|
+
# Notice here we're calling "run" and not "run!" with a bang.
|
535
|
+
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
536
|
+
expect(transactor).to be_running
|
537
|
+
expect(worker.reload.status).to eq('running')
|
538
|
+
end
|
539
|
+
|
540
|
+
it 'should not create a database transaction' do
|
541
|
+
expect(transactor.class).not_to receive(:transaction)
|
542
|
+
expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
|
543
|
+
end
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
describe "invalid states with persistence" do
|
549
|
+
it "should not store states" do
|
550
|
+
validator = MultipleValidator.create(:name => 'name')
|
551
|
+
validator.status = 'invalid_state'
|
552
|
+
expect(validator.save).to be_falsey
|
553
|
+
expect {validator.save!}.to raise_error(ActiveRecord::RecordInvalid)
|
554
|
+
|
555
|
+
validator.reload
|
556
|
+
expect(validator).to be_sleeping
|
557
|
+
end
|
558
|
+
|
559
|
+
it "should store invalid states if configured" do
|
560
|
+
persistor = MultipleInvalidPersistor.create(:name => 'name')
|
561
|
+
persistor.status = 'invalid_state'
|
562
|
+
expect(persistor.save).to be_truthy
|
563
|
+
|
564
|
+
persistor.reload
|
565
|
+
expect(persistor.status).to eq('invalid_state')
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
describe "complex example" do
|
570
|
+
it "works" do
|
571
|
+
record = ComplexActiveRecordExample.new
|
572
|
+
expect_aasm_states record, :one, :alpha
|
573
|
+
|
574
|
+
record.save!
|
575
|
+
expect_aasm_states record, :one, :alpha
|
576
|
+
record.reload
|
577
|
+
expect_aasm_states record, :one, :alpha
|
578
|
+
|
579
|
+
record.increment!
|
580
|
+
expect_aasm_states record, :two, :alpha
|
581
|
+
record.reload
|
582
|
+
expect_aasm_states record, :two, :alpha
|
583
|
+
|
584
|
+
record.level_up!
|
585
|
+
expect_aasm_states record, :two, :beta
|
586
|
+
record.reload
|
587
|
+
expect_aasm_states record, :two, :beta
|
588
|
+
|
589
|
+
record.increment!
|
590
|
+
expect { record.increment! }.to raise_error(AASM::InvalidTransition)
|
591
|
+
expect_aasm_states record, :three, :beta
|
592
|
+
record.reload
|
593
|
+
expect_aasm_states record, :three, :beta
|
594
|
+
|
595
|
+
record.level_up!
|
596
|
+
expect_aasm_states record, :three, :gamma
|
597
|
+
record.reload
|
598
|
+
expect_aasm_states record, :three, :gamma
|
599
|
+
|
600
|
+
record.level_down # without saving
|
601
|
+
expect_aasm_states record, :three, :beta
|
602
|
+
record.reload
|
603
|
+
expect_aasm_states record, :three, :gamma
|
604
|
+
|
605
|
+
record.level_down # without saving
|
606
|
+
expect_aasm_states record, :three, :beta
|
607
|
+
record.reset!
|
608
|
+
expect_aasm_states record, :one, :beta
|
609
|
+
end
|
610
|
+
|
611
|
+
def expect_aasm_states(record, left_state, right_state)
|
612
|
+
expect(record.aasm(:left).current_state).to eql left_state.to_sym
|
613
|
+
expect(record.left).to eql left_state.to_s
|
614
|
+
expect(record.aasm(:right).current_state).to eql right_state.to_sym
|
615
|
+
expect(record.right).to eql right_state.to_s
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|