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