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.
Files changed (194) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.travis.yml +56 -23
  5. data/Appraisals +67 -0
  6. data/CHANGELOG.md +112 -0
  7. data/CONTRIBUTING.md +24 -0
  8. data/Dockerfile +44 -0
  9. data/Gemfile +3 -21
  10. data/Gemfile.lock_old +151 -0
  11. data/LICENSE +1 -1
  12. data/README.md +540 -139
  13. data/Rakefile +6 -1
  14. data/TESTING.md +25 -0
  15. data/aasm.gemspec +5 -0
  16. data/docker-compose.yml +40 -0
  17. data/gemfiles/norails.gemfile +10 -0
  18. data/gemfiles/rails_4.2.gemfile +13 -11
  19. data/gemfiles/rails_4.2_mongoid_5.gemfile +8 -11
  20. data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
  21. data/gemfiles/rails_5.0.gemfile +11 -18
  22. data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
  23. data/gemfiles/rails_5.1.gemfile +14 -0
  24. data/gemfiles/rails_5.2.gemfile +14 -0
  25. data/lib/aasm/aasm.rb +40 -29
  26. data/lib/aasm/base.rb +61 -11
  27. data/lib/aasm/configuration.rb +10 -0
  28. data/lib/aasm/core/event.rb +45 -37
  29. data/lib/aasm/core/invoker.rb +129 -0
  30. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  31. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  32. data/lib/aasm/core/invokers/literal_invoker.rb +47 -0
  33. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  34. data/lib/aasm/core/state.rb +22 -13
  35. data/lib/aasm/core/transition.rb +17 -69
  36. data/lib/aasm/dsl_helper.rb +24 -22
  37. data/lib/aasm/errors.rb +4 -6
  38. data/lib/aasm/instance_base.rb +22 -4
  39. data/lib/aasm/localizer.rb +13 -3
  40. data/lib/aasm/minitest/allow_event.rb +13 -0
  41. data/lib/aasm/minitest/allow_transition_to.rb +13 -0
  42. data/lib/aasm/minitest/have_state.rb +13 -0
  43. data/lib/aasm/minitest/transition_from.rb +21 -0
  44. data/lib/aasm/minitest.rb +5 -0
  45. data/lib/aasm/minitest_spec.rb +15 -0
  46. data/lib/aasm/persistence/active_record_persistence.rb +49 -105
  47. data/lib/aasm/persistence/base.rb +20 -5
  48. data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
  49. data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
  50. data/lib/aasm/persistence/mongoid_persistence.rb +26 -32
  51. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  52. data/lib/aasm/persistence/orm.rb +154 -0
  53. data/lib/aasm/persistence/plain_persistence.rb +2 -1
  54. data/lib/aasm/persistence/redis_persistence.rb +16 -11
  55. data/lib/aasm/persistence/sequel_persistence.rb +36 -64
  56. data/lib/aasm/persistence.rb +3 -3
  57. data/lib/aasm/rspec/allow_event.rb +5 -1
  58. data/lib/aasm/rspec/allow_transition_to.rb +5 -1
  59. data/lib/aasm/rspec/transition_from.rb +5 -1
  60. data/lib/aasm/state_machine.rb +4 -2
  61. data/lib/aasm/state_machine_store.rb +5 -2
  62. data/lib/aasm/version.rb +1 -1
  63. data/lib/aasm.rb +5 -2
  64. data/lib/generators/aasm/orm_helpers.rb +6 -0
  65. data/lib/generators/active_record/aasm_generator.rb +3 -1
  66. data/lib/generators/active_record/templates/migration.rb +1 -1
  67. data/lib/generators/active_record/templates/migration_existing.rb +1 -1
  68. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  69. data/lib/motion-aasm.rb +3 -1
  70. data/spec/database.rb +20 -7
  71. data/spec/en.yml +0 -3
  72. data/spec/generators/active_record_generator_spec.rb +49 -40
  73. data/spec/generators/mongoid_generator_spec.rb +4 -6
  74. data/spec/generators/no_brainer_generator_spec.rb +29 -0
  75. data/spec/{en_deprecated_style.yml → localizer_test_model_deprecated_style.yml} +6 -3
  76. data/spec/localizer_test_model_new_style.yml +11 -0
  77. data/spec/models/active_record/active_record_callback.rb +93 -0
  78. data/spec/models/active_record/complex_active_record_example.rb +5 -1
  79. data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
  80. data/spec/models/{invalid_persistor.rb → active_record/invalid_persistor.rb} +0 -2
  81. data/spec/models/active_record/localizer_test_model.rb +11 -3
  82. data/spec/models/active_record/namespaced.rb +16 -0
  83. data/spec/models/active_record/person.rb +23 -0
  84. data/spec/models/{silent_persistor.rb → active_record/silent_persistor.rb} +0 -2
  85. data/spec/models/active_record/simple_new_dsl.rb +15 -0
  86. data/spec/models/active_record/timestamp_example.rb +16 -0
  87. data/spec/models/{transactor.rb → active_record/transactor.rb} +25 -2
  88. data/spec/models/{validator.rb → active_record/validator.rb} +0 -2
  89. data/spec/models/active_record/work.rb +3 -0
  90. data/spec/models/{worker.rb → active_record/worker.rb} +0 -0
  91. data/spec/models/callbacks/basic.rb +5 -2
  92. data/spec/models/callbacks/with_state_arg.rb +5 -1
  93. data/spec/models/callbacks/with_state_arg_multiple.rb +4 -1
  94. data/spec/models/default_state.rb +1 -1
  95. data/spec/models/guard_arguments_check.rb +17 -0
  96. data/spec/models/guard_with_params.rb +1 -1
  97. data/spec/models/guardian_without_from_specified.rb +18 -0
  98. data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
  99. data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
  100. data/spec/models/mongoid/timestamp_example_mongoid.rb +20 -0
  101. data/spec/models/mongoid/validator_mongoid.rb +100 -0
  102. data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
  103. data/spec/models/namespaced_multiple_example.rb +14 -0
  104. data/spec/models/nobrainer/complex_no_brainer_example.rb +36 -0
  105. data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
  106. data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
  107. data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
  108. data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
  109. data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
  110. data/spec/models/{mongo_mapper/simple_mongo_mapper.rb → nobrainer/simple_no_brainer.rb} +8 -8
  111. data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
  112. data/spec/models/parametrised_event.rb +7 -0
  113. data/spec/models/{mongo_mapper/complex_mongo_mapper_example.rb → redis/complex_redis_example.rb} +8 -5
  114. data/spec/models/redis/redis_multiple.rb +20 -0
  115. data/spec/models/redis/redis_simple.rb +20 -0
  116. data/spec/models/sequel/complex_sequel_example.rb +4 -3
  117. data/spec/models/sequel/invalid_persistor.rb +52 -0
  118. data/spec/models/sequel/sequel_multiple.rb +13 -13
  119. data/spec/models/sequel/sequel_simple.rb +13 -12
  120. data/spec/models/sequel/silent_persistor.rb +50 -0
  121. data/spec/models/sequel/transactor.rb +112 -0
  122. data/spec/models/sequel/validator.rb +93 -0
  123. data/spec/models/sequel/worker.rb +12 -0
  124. data/spec/models/simple_example.rb +8 -0
  125. data/spec/models/simple_example_with_guard_args.rb +17 -0
  126. data/spec/models/simple_multiple_example.rb +12 -0
  127. data/spec/models/sub_class.rb +34 -0
  128. data/spec/models/timestamps_example.rb +19 -0
  129. data/spec/models/timestamps_with_named_machine_example.rb +13 -0
  130. data/spec/spec_helper.rb +15 -33
  131. data/spec/spec_helpers/active_record.rb +8 -0
  132. data/spec/spec_helpers/dynamoid.rb +35 -0
  133. data/spec/spec_helpers/mongoid.rb +26 -0
  134. data/spec/spec_helpers/nobrainer.rb +15 -0
  135. data/spec/spec_helpers/redis.rb +18 -0
  136. data/spec/spec_helpers/remove_warnings.rb +1 -0
  137. data/spec/spec_helpers/sequel.rb +7 -0
  138. data/spec/unit/abstract_class_spec.rb +27 -0
  139. data/spec/unit/api_spec.rb +79 -72
  140. data/spec/unit/callback_multiple_spec.rb +7 -3
  141. data/spec/unit/callbacks_spec.rb +37 -2
  142. data/spec/unit/complex_example_spec.rb +12 -3
  143. data/spec/unit/complex_multiple_example_spec.rb +20 -4
  144. data/spec/unit/event_multiple_spec.rb +1 -1
  145. data/spec/unit/event_spec.rb +29 -4
  146. data/spec/unit/exception_spec.rb +1 -1
  147. data/spec/unit/guard_arguments_check_spec.rb +9 -0
  148. data/spec/unit/guard_spec.rb +17 -0
  149. data/spec/unit/guard_with_params_spec.rb +4 -0
  150. data/spec/unit/guard_without_from_specified_spec.rb +10 -0
  151. data/spec/unit/inspection_multiple_spec.rb +9 -5
  152. data/spec/unit/inspection_spec.rb +7 -3
  153. data/spec/unit/invoker_spec.rb +189 -0
  154. data/spec/unit/invokers/base_invoker_spec.rb +72 -0
  155. data/spec/unit/invokers/class_invoker_spec.rb +95 -0
  156. data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
  157. data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
  158. data/spec/unit/localizer_spec.rb +85 -52
  159. data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
  160. data/spec/unit/namespaced_multiple_example_spec.rb +22 -0
  161. data/spec/unit/override_warning_spec.rb +8 -0
  162. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +468 -447
  163. data/spec/unit/persistence/active_record_persistence_spec.rb +639 -486
  164. data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +4 -9
  165. data/spec/unit/persistence/dynamoid_persistence_spec.rb +4 -9
  166. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +83 -13
  167. data/spec/unit/persistence/mongoid_persistence_spec.rb +97 -13
  168. data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
  169. data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
  170. data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
  171. data/spec/unit/persistence/redis_persistence_spec.rb +8 -32
  172. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +6 -11
  173. data/spec/unit/persistence/sequel_persistence_spec.rb +278 -10
  174. data/spec/unit/rspec_matcher_spec.rb +9 -0
  175. data/spec/unit/simple_example_spec.rb +15 -0
  176. data/spec/unit/simple_multiple_example_spec.rb +28 -0
  177. data/spec/unit/state_spec.rb +23 -7
  178. data/spec/unit/subclassing_multiple_spec.rb +37 -2
  179. data/spec/unit/subclassing_spec.rb +17 -2
  180. data/spec/unit/timestamps_spec.rb +32 -0
  181. data/spec/unit/transition_spec.rb +1 -1
  182. data/test/minitest_helper.rb +57 -0
  183. data/test/unit/minitest_matcher_test.rb +80 -0
  184. metadata +213 -37
  185. data/callbacks.txt +0 -51
  186. data/gemfiles/rails_3.2_stable.gemfile +0 -15
  187. data/gemfiles/rails_4.0.gemfile +0 -16
  188. data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -16
  189. data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -17
  190. data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -163
  191. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
  192. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
  193. data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -149
  194. data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -96
@@ -1,699 +1,852 @@
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
2
 
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)
3
+ if defined?(ActiveRecord)
12
4
 
13
- describe "instance methods" do
14
- let(:gate) {Gate.new}
15
-
16
- it "should respond to aasm persistence methods" do
17
- expect(gate).to respond_to(:aasm_read_state)
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
- describe "aasm_column_looks_like_enum" do
23
- subject { lambda{ gate.send(:aasm_column_looks_like_enum) } }
9
+ load_schema
24
10
 
25
- let(:column_name) { "value" }
26
- let(:columns_hash) { Hash[column_name, column] }
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)
27
14
 
28
- before :each do
29
- allow(gate.class.aasm).to receive(:attribute_name).and_return(column_name.to_sym)
30
- allow(gate.class).to receive(:columns_hash).and_return(columns_hash)
31
- end
15
+ describe "instance methods" do
16
+ let(:gate) {Gate.new}
32
17
 
33
- context "when AASM column has integer type" do
34
- let(:column) { double(Object, type: :integer) }
35
-
36
- it "returns true" do
37
- expect(subject.call).to be_truthy
38
- end
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
- context "when AASM column has string type" do
42
- let(:column) { double(Object, type: :string) }
24
+ describe "aasm_column_looks_like_enum" do
25
+ subject { lambda{ gate.send(:aasm_column_looks_like_enum) } }
43
26
 
44
- it "returns false" do
45
- expect(subject.call).to be_falsey
46
- end
47
- end
48
- end
27
+ let(:column_name) { "value" }
28
+ let(:columns_hash) { Hash[column_name, column] }
49
29
 
50
- describe "aasm_guess_enum_method" do
51
- subject { lambda{ gate.send(:aasm_guess_enum_method) } }
30
+ before :each do
31
+ allow(gate.class.aasm).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
- before :each do
54
- allow(gate.class.aasm).to receive(:attribute_name).and_return(:value)
55
- end
35
+ context "when AASM column has integer type" do
36
+ let(:column) { double(Object, type: :integer) }
56
37
 
57
- it "pluralizes AASM column name" do
58
- expect(subject.call).to eq :values
59
- end
60
- end
38
+ it "returns true" do
39
+ expect(subject.call).to be_truthy
40
+ end
41
+ end
61
42
 
62
- describe "aasm_enum" do
63
- context "when AASM enum setting contains an explicit enum method name" do
64
- let(:with_enum) { WithEnum.new }
43
+ context "when AASM column has string type" do
44
+ let(:column) { double(Object, type: :string) }
65
45
 
66
- it "returns whatever value was set in AASM config" do
67
- expect(with_enum.send(:aasm_enum)).to eq :test
46
+ it "returns false" do
47
+ expect(subject.call).to be_falsey
48
+ end
68
49
  end
69
50
  end
70
51
 
71
- context "when AASM enum setting is simply set to true" do
72
- let(:with_true_enum) { WithTrueEnum.new }
52
+ describe "aasm_guess_enum_method" do
53
+ subject { lambda{ gate.send(:aasm_guess_enum_method) } }
54
+
73
55
  before :each do
74
- allow(WithTrueEnum.aasm).to receive(:attribute_name).and_return(:value)
56
+ allow(gate.class.aasm).to receive(:attribute_name).and_return(:value)
75
57
  end
76
58
 
77
- it "infers enum method name from pluralized column name" do
78
- expect(with_true_enum.send(:aasm_enum)).to eq :values
59
+ it "pluralizes AASM column name" do
60
+ expect(subject.call).to eq :values
79
61
  end
80
62
  end
81
63
 
82
- context "when AASM enum setting is explicitly disabled" do
83
- let(:with_false_enum) { WithFalseEnum.new }
84
-
85
- it "returns nil" do
86
- expect(with_false_enum.send(:aasm_enum)).to be_nil
87
- end
88
- end
64
+ describe "aasm_enum" do
65
+ context "when AASM enum setting contains an explicit enum method name" do
66
+ let(:with_enum) { WithEnum.new }
89
67
 
90
- context "when AASM enum setting is not enabled" do
91
- before :each do
92
- allow(Gate.aasm).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)).to eq :test
70
+ end
93
71
  end
94
72
 
95
- context "when AASM column looks like enum" do
73
+ context "when AASM enum setting is simply set to true" do
74
+ let(:with_true_enum) { WithTrueEnum.new }
96
75
  before :each do
97
- allow(gate).to receive(:aasm_column_looks_like_enum).and_return(true)
76
+ allow(WithTrueEnum.aasm).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(gate.send(:aasm_enum)).to eq :values
80
+ expect(with_true_enum.send(:aasm_enum)).to eq :values
81
+ end
82
+ end
83
+
84
+ context "when AASM enum setting is explicitly disabled" do
85
+ let(:with_false_enum) { WithFalseEnum.new }
86
+
87
+ it "returns nil" do
88
+ expect(with_false_enum.send(:aasm_enum)).to be_nil
102
89
  end
103
90
  end
104
91
 
105
- context "when AASM column doesn't look like enum'" do
92
+ context "when AASM enum setting is not enabled" do
106
93
  before :each do
107
- allow(gate).to receive(:aasm_column_looks_like_enum)
108
- .and_return(false)
94
+ allow(Gate.aasm).to receive(:attribute_name).and_return(:value)
109
95
  end
110
96
 
111
- it "returns nil, as we're not using enum" do
112
- expect(gate.send(:aasm_enum)).to be_nil
97
+ context "when AASM column looks like enum" do
98
+ before :each do
99
+ allow(gate).to receive(:aasm_column_looks_like_enum).and_return(true)
100
+ end
101
+
102
+ it "infers enum method name from pluralized column name" do
103
+ expect(gate.send(:aasm_enum)).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)).to be_nil
115
+ end
113
116
  end
114
117
  end
115
- end
116
118
 
117
- if ActiveRecord::VERSION::MAJOR >= 4 && ActiveRecord::VERSION::MINOR >= 1 # won't work with Rails <= 4.1
118
- # Enum are introduced from Rails 4.1, therefore enum syntax will not work on Rails <= 4.1
119
- context "when AASM enum setting is not enabled and aasm column not present" do
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
- let(:with_enum_without_column) {WithEnumWithoutColumn.new}
123
+ let(:with_enum_without_column) {WithEnumWithoutColumn.new}
122
124
 
123
- it "should raise NoMethodError for transitions" do
124
- expect{with_enum_without_column.send(:view)}.to raise_error(NoMethodError, "undefined method 'status' for WithEnumWithoutColumn")
125
+ it "should raise NoMethodError for transitions" do
126
+ expect{with_enum_without_column.send(:view)}.to raise_error(NoMethodError, /undefined method .status./)
127
+ end
125
128
  end
129
+
126
130
  end
127
131
 
128
132
  end
129
133
 
130
- end
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] }
131
139
 
132
- context "when AASM is configured to use enum" do
133
- let(:state_sym) { :running }
134
- let(:state_code) { 2 }
135
- let(:enum_name) { :states }
136
- let(:enum) { Hash[state_sym, state_code] }
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)
137
144
 
138
- before :each do
139
- allow(gate).to receive(:aasm_enum).and_return(enum_name)
140
- allow(gate).to receive(:aasm_write_attribute)
141
- allow(gate).to receive(:write_attribute)
145
+ allow(Gate).to receive(enum_name).and_return(enum)
146
+ end
142
147
 
143
- allow(Gate).to receive(enum_name).and_return(enum)
144
- end
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
145
153
 
146
- describe "aasm_write_state" do
147
- context "when AASM is configured to skip validations on save" do
148
- before :each do
149
- allow(gate).to receive(:aasm_skipping_validations).and_return(true)
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(Gate).to receive_message_chain(:unscoped, :where).and_return(obj)
160
+
161
+ gate.aasm_write_state state_sym
162
+
163
+ expect(obj).to have_received(:update_all)
164
+ .with(Hash[gate.class.aasm.attribute_name, state_code])
165
+ end
166
+
167
+ it "searches model outside of default_scope when update_all" do
168
+ # stub_chain does not allow us to give expectations on call
169
+ # parameters in the middle of the chain, so we need to use
170
+ # intermediate object instead.
171
+ unscoped = double(Object, update_all: 1)
172
+ scoped = double(Object, update_all: 1)
173
+
174
+ allow(Gate).to receive(:unscoped).and_return(unscoped)
175
+ allow(Gate).to receive(:where).and_return(scoped)
176
+ allow(unscoped).to receive(:where).and_return(unscoped)
177
+
178
+ gate.aasm_write_state state_sym
179
+
180
+ expect(unscoped).to have_received(:update_all)
181
+ .with(Hash[gate.class.aasm.attribute_name, state_code])
182
+ expect(scoped).to_not have_received(:update_all)
183
+ .with(Hash[gate.class.aasm.attribute_name, state_code])
184
+ end
150
185
  end
151
186
 
152
- it "passes state code instead of state symbol to update_all" do
153
- # stub_chain does not allow us to give expectations on call
154
- # parameters in the middle of the chain, so we need to use
155
- # intermediate object instead.
156
- obj = double(Object, update_all: 1)
157
- allow(Gate).to receive(:where).and_return(obj)
187
+ context "when AASM is not skipping validations" do
188
+ it "delegates state update to the helper method" do
189
+ # Let's pretend that validation is passed
190
+ allow(gate).to receive(:save).and_return(true)
158
191
 
159
- gate.aasm_write_state state_sym
192
+ gate.aasm_write_state state_sym
160
193
 
161
- expect(obj).to have_received(:update_all)
162
- .with(Hash[gate.class.aasm.attribute_name, state_code])
194
+ expect(gate).to have_received(:aasm_write_state_attribute).with(state_sym, :default)
195
+ expect(gate).to_not have_received :write_attribute
196
+ end
163
197
  end
164
198
  end
165
199
 
166
- context "when AASM is not skipping validations" do
200
+ describe "aasm_write_state_without_persistence" do
167
201
  it "delegates state update to the helper method" do
168
- # Let's pretend that validation is passed
169
- allow(gate).to receive(:save).and_return(true)
170
-
171
- gate.aasm_write_state state_sym
202
+ gate.aasm_write_state_without_persistence state_sym
172
203
 
173
- expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :default)
204
+ expect(gate).to have_received(:aasm_write_state_attribute).with(state_sym, :default)
174
205
  expect(gate).to_not have_received :write_attribute
175
206
  end
176
207
  end
208
+
209
+ describe "aasm_raw_attribute_value" do
210
+ it "converts state symbol to state code" do
211
+ expect(gate.send(:aasm_raw_attribute_value, state_sym))
212
+ .to eq state_code
213
+ end
214
+ end
177
215
  end
178
216
 
179
- describe "aasm_write_state_without_persistence" do
180
- it "delegates state update to the helper method" do
181
- gate.aasm_write_state_without_persistence state_sym
217
+ context "when AASM is configured to use string field" do
218
+ let(:state_sym) { :running }
182
219
 
183
- expect(gate).to have_received(:aasm_write_attribute).with(state_sym, :default)
184
- expect(gate).to_not have_received :write_attribute
220
+ before :each do
221
+ allow(gate).to receive(:aasm_enum).and_return(nil)
185
222
  end
186
- end
187
223
 
188
- describe "aasm_raw_attribute_value" do
189
- it "converts state symbol to state code" do
190
- expect(gate.send(:aasm_raw_attribute_value, state_sym))
191
- .to eq state_code
224
+ describe "aasm_raw_attribute_value" do
225
+ it "converts state symbol to string" do
226
+ expect(gate.send(:aasm_raw_attribute_value, state_sym))
227
+ .to eq state_sym.to_s
228
+ end
192
229
  end
193
230
  end
194
- end
195
231
 
196
- context "when AASM is configured to use string field" do
197
- let(:state_sym) { :running }
232
+ describe "aasm_write_attribute helper method" do
233
+ let(:sym) { :sym }
234
+ let(:value) { 42 }
198
235
 
199
- before :each do
200
- allow(gate).to receive(:aasm_enum).and_return(nil)
201
- end
236
+ before :each do
237
+ allow(gate).to receive(:write_attribute)
238
+ allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
202
239
 
203
- describe "aasm_raw_attribute_value" do
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
240
+ gate.send(:aasm_write_state_attribute, sym)
207
241
  end
208
- end
209
- end
210
242
 
211
- describe "aasm_write_attribute helper method" do
212
- let(:sym) { :sym }
213
- let(:value) { 42 }
243
+ it "generates attribute value using a helper method" do
244
+ expect(gate).to have_received(:aasm_raw_attribute_value).with(sym, :default)
245
+ end
214
246
 
215
- before :each do
216
- allow(gate).to receive(:write_attribute)
217
- allow(gate).to receive(:aasm_raw_attribute_value).and_return(value)
247
+ it "writes attribute to the model" do
248
+ expect(gate).to have_received(:write_attribute).with(:aasm_state, value)
249
+ end
250
+ end
218
251
 
219
- gate.send(:aasm_write_attribute, sym)
252
+ it "should return the initial state when new and the aasm field is nil" do
253
+ expect(gate.aasm.current_state).to eq(:opened)
220
254
  end
221
255
 
222
- it "generates attribute value using a helper method" do
223
- expect(gate).to have_received(:aasm_raw_attribute_value).with(sym, :default)
256
+ it "should return the aasm column when new and the aasm field is not nil" do
257
+ gate.aasm_state = "closed"
258
+ expect(gate.aasm.current_state).to eq(:closed)
224
259
  end
225
260
 
226
- it "writes attribute to the model" do
227
- expect(gate).to have_received(:write_attribute).with(:aasm_state, value)
261
+ it "should return the aasm column when not new and the aasm.attribute_name is not nil" do
262
+ allow(gate).to receive(:new_record?).and_return(false)
263
+ gate.aasm_state = "state"
264
+ expect(gate.aasm.current_state).to eq(:state)
228
265
  end
229
- end
230
266
 
231
- it "should return the initial state when new and the aasm field is nil" do
232
- expect(gate.aasm.current_state).to eq(:opened)
233
- end
267
+ it "should allow a nil state" do
268
+ allow(gate).to receive(:new_record?).and_return(false)
269
+ gate.aasm_state = nil
270
+ expect(gate.aasm.current_state).to be_nil
271
+ end
234
272
 
235
- it "should return the aasm column when new and the aasm field is not nil" do
236
- gate.aasm_state = "closed"
237
- expect(gate.aasm.current_state).to eq(:closed)
238
- end
273
+ context 'on initialization' do
274
+ it "should initialize the aasm state" do
275
+ expect(Gate.new.aasm_state).to eql 'opened'
276
+ expect(Gate.new.aasm.current_state).to eql :opened
277
+ end
239
278
 
240
- it "should return the aasm column when not new and the aasm.attribute_name is not nil" do
241
- allow(gate).to receive(:new_record?).and_return(false)
242
- gate.aasm_state = "state"
243
- expect(gate.aasm.current_state).to eq(:state)
244
- end
279
+ it "should not initialize the aasm state if it has not been loaded" do
280
+ # we have to create a gate in the database, for which we only want to
281
+ # load the id, and not the state
282
+ gate = Gate.create!
245
283
 
246
- it "should allow a nil state" do
247
- allow(gate).to receive(:new_record?).and_return(false)
248
- gate.aasm_state = nil
249
- expect(gate.aasm.current_state).to be_nil
250
- end
251
-
252
- context 'on initialization' do
253
- it "should initialize the aasm state" do
254
- expect(Gate.new.aasm_state).to eql 'opened'
255
- expect(Gate.new.aasm.current_state).to eql :opened
284
+ # then we just load the gate ids
285
+ Gate.select(:id).where(id: gate.id).first
286
+ end
256
287
  end
257
288
 
258
- it "should not initialize the aasm state if it has not been loaded" do
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 = Gate.create!
289
+ end
262
290
 
263
- # then we just load the gate ids
264
- Gate.select(:id).where(id: gate.id).first
291
+ if ActiveRecord::VERSION::MAJOR < 4 && ActiveRecord::VERSION::MINOR < 2 # won't work with Rails >= 4.2
292
+ describe "direct state column access" do
293
+ it "accepts false states" do
294
+ f = FalseState.create!
295
+ expect(f.aasm_state).to eql false
296
+ expect {
297
+ f.aasm.events.map(&:name)
298
+ }.to_not raise_error
265
299
  end
266
300
  end
301
+ end
267
302
 
268
- end
303
+ describe 'subclasses' do
304
+ it "should have the same states as its parent class" do
305
+ expect(DerivateNewDsl.aasm.states).to eq(SimpleNewDsl.aasm.states)
306
+ end
269
307
 
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 = FalseState.create!
274
- expect(f.aasm_state).to eql false
275
- expect {
276
- f.aasm.events.map(&:name)
277
- }.to_not raise_error
278
- end
279
- end
280
- end
308
+ it "should have the same events as its parent class" do
309
+ expect(DerivateNewDsl.aasm.events).to eq(SimpleNewDsl.aasm.events)
310
+ end
281
311
 
282
- describe 'subclasses' do
283
- it "should have the same states as its parent class" do
284
- expect(DerivateNewDsl.aasm.states).to eq(SimpleNewDsl.aasm.states)
312
+ it "should have the same column as its parent even for the new dsl" do
313
+ expect(SimpleNewDsl.aasm.attribute_name).to eq(:status)
314
+ expect(DerivateNewDsl.aasm.attribute_name).to eq(:status)
315
+ end
285
316
  end
286
317
 
287
- it "should have the same events as its parent class" do
288
- expect(DerivateNewDsl.aasm.events).to eq(SimpleNewDsl.aasm.events)
289
- end
318
+ describe "named scopes with the new DSL" do
319
+ context "Does not already respond_to? the scope name" do
320
+ it "should add a scope for each state" do
321
+ expect(SimpleNewDsl).to respond_to(:unknown_scope)
322
+ expect(SimpleNewDsl).to respond_to(:another_unknown_scope)
290
323
 
291
- it "should have the same column as its parent even for the new dsl" do
292
- expect(SimpleNewDsl.aasm.attribute_name).to eq(:status)
293
- expect(DerivateNewDsl.aasm.attribute_name).to eq(:status)
294
- end
295
- end
324
+ expect(SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
325
+ expect(SimpleNewDsl.another_unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
326
+ end
327
+ end
328
+
329
+ context "Already respond_to? the scope name" do
330
+ it "should not add a scope" do
331
+ expect(SimpleNewDsl).to respond_to(:new)
332
+ expect(SimpleNewDsl.new.class).to eq(SimpleNewDsl)
333
+ end
334
+ end
296
335
 
297
- describe "named scopes with the new DSL" do
298
- context "Does not already respond_to? the scope name" do
299
- it "should add a scope for each state" do
300
- expect(SimpleNewDsl).to respond_to(:unknown_scope)
301
- expect(SimpleNewDsl).to respond_to(:another_unknown_scope)
336
+ # Scopes on abstract classes didn't work until Rails 5.
337
+ #
338
+ # Reference:
339
+ # https://github.com/rails/rails/issues/10658
340
+ if ActiveRecord::VERSION::MAJOR >= 5
341
+ context "For a descendant of an abstract model" do
342
+ it "should add the scope without the table_name" do
343
+ expect(ImplementedAbstractClassDsl).to respond_to(:unknown_scope)
344
+ expect(ImplementedAbstractClassDsl).to respond_to(:another_unknown_scope)
302
345
 
303
- expect(SimpleNewDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
304
- expect(SimpleNewDsl.another_unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
346
+ expect(ImplementedAbstractClassDsl.unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
347
+ expect(ImplementedAbstractClassDsl.another_unknown_scope.is_a?(ActiveRecord::Relation)).to be_truthy
348
+ end
349
+ end
305
350
  end
306
- end
307
351
 
308
- context "Already respond_to? the scope name" do
309
- it "should not add a scope" do
310
- expect(SimpleNewDsl).to respond_to(:new)
311
- expect(SimpleNewDsl.new.class).to eq(SimpleNewDsl)
352
+ it "does not create scopes if requested" do
353
+ expect(NoScope).not_to respond_to(:pending)
312
354
  end
313
- end
314
355
 
315
- it "does not create scopes if requested" do
316
- expect(NoScope).not_to respond_to(:pending)
317
- end
356
+ context "result of scope" do
357
+ let!(:dsl1) { SimpleNewDsl.create!(status: :new) }
358
+ let!(:dsl2) { SimpleNewDsl.create!(status: :unknown_scope) }
318
359
 
319
- context "result of scope" do
320
- let!(:dsl1) { SimpleNewDsl.create!(status: :new) }
321
- let!(:dsl2) { SimpleNewDsl.create!(status: :unknown_scope) }
360
+ after do
361
+ SimpleNewDsl.destroy_all
362
+ end
322
363
 
323
- after do
324
- SimpleNewDsl.destroy_all
364
+ it "created scope works as where(name: :scope_name)" do
365
+ expect(SimpleNewDsl.unknown_scope).to contain_exactly(dsl2)
366
+ end
325
367
  end
368
+ end # scopes
326
369
 
327
- it "created scope works as where(name: :scope_name)" do
328
- expect(SimpleNewDsl.unknown_scope).to contain_exactly(dsl2)
370
+ describe "direct assignment" do
371
+ it "is allowed by default" do
372
+ obj = NoScope.create
373
+ expect(obj.aasm_state.to_sym).to eql :pending
374
+
375
+ obj.aasm_state = :running
376
+ expect(obj.aasm_state.to_sym).to eql :running
329
377
  end
330
- end
331
- end # scopes
332
378
 
333
- describe "direct assignment" do
334
- it "is allowed by default" do
335
- obj = NoScope.create
336
- expect(obj.aasm_state.to_sym).to eql :pending
379
+ it "is forbidden if configured" do
380
+ obj = NoDirectAssignment.create
381
+ expect(obj.aasm_state.to_sym).to eql :pending
337
382
 
338
- obj.aasm_state = :running
339
- expect(obj.aasm_state.to_sym).to eql :running
340
- end
383
+ expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
384
+ expect(obj.aasm_state.to_sym).to eql :pending
385
+ end
341
386
 
342
- it "is forbidden if configured" do
343
- obj = NoDirectAssignment.create
344
- expect(obj.aasm_state.to_sym).to eql :pending
387
+ it 'can be turned off and on again' do
388
+ obj = NoDirectAssignment.create
389
+ expect(obj.aasm_state.to_sym).to eql :pending
345
390
 
346
- expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
347
- expect(obj.aasm_state.to_sym).to eql :pending
348
- end
391
+ expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
392
+ expect(obj.aasm_state.to_sym).to eql :pending
349
393
 
350
- it 'can be turned off and on again' do
351
- obj = NoDirectAssignment.create
352
- expect(obj.aasm_state.to_sym).to eql :pending
394
+ # allow it temporarily
395
+ NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = false
396
+ obj.aasm_state = :running
397
+ expect(obj.aasm_state.to_sym).to eql :running
353
398
 
354
- expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
355
- expect(obj.aasm_state.to_sym).to eql :pending
399
+ # and forbid it again
400
+ NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = true
401
+ expect {obj.aasm_state = :pending}.to raise_error(AASM::NoDirectAssignmentError)
402
+ expect(obj.aasm_state.to_sym).to eql :running
403
+ end
404
+ end # direct assignment
356
405
 
357
- # allow it temporarily
358
- NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = false
359
- obj.aasm_state = :pending
360
- expect(obj.aasm_state.to_sym).to eql :pending
406
+ describe 'initial states' do
361
407
 
362
- # and forbid it again
363
- NoDirectAssignment.aasm.state_machine.config.no_direct_assignment = true
364
- expect {obj.aasm_state = :running}.to raise_error(AASM::NoDirectAssignmentError)
365
- expect(obj.aasm_state.to_sym).to eql :pending
408
+ it 'should support conditions' do
409
+ expect(Thief.new(:skilled => true).aasm.current_state).to eq(:rich)
410
+ expect(Thief.new(:skilled => false).aasm.current_state).to eq(:jailed)
411
+ end
366
412
  end
367
- end # direct assignment
368
413
 
369
- describe 'initial states' do
414
+ describe 'transitions with persistence' do
370
415
 
371
- it 'should support conditions' do
372
- expect(Thief.new(:skilled => true).aasm.current_state).to eq(:rich)
373
- expect(Thief.new(:skilled => false).aasm.current_state).to eq(:jailed)
374
- end
375
- end
416
+ it "should work for valid models" do
417
+ valid_object = Validator.create(:name => 'name')
418
+ expect(valid_object).to be_sleeping
419
+ valid_object.status = :running
420
+ expect(valid_object).to be_running
421
+ end
376
422
 
377
- describe 'transitions with persistence' do
423
+ it 'should not store states for invalid models' do
424
+ validator = Validator.create(:name => 'name')
425
+ expect(validator).to be_valid
426
+ expect(validator).to be_sleeping
378
427
 
379
- it "should work for valid models" do
380
- valid_object = Validator.create(:name => 'name')
381
- expect(valid_object).to be_sleeping
382
- valid_object.status = :running
383
- expect(valid_object).to be_running
384
- end
428
+ validator.name = nil
429
+ expect(validator).not_to be_valid
430
+ expect { validator.run! }.to raise_error(ActiveRecord::RecordInvalid)
431
+ expect(validator).to be_sleeping
385
432
 
386
- it 'should not store states for invalid models' do
387
- validator = Validator.create(:name => 'name')
388
- expect(validator).to be_valid
389
- expect(validator).to be_sleeping
433
+ validator.reload
434
+ expect(validator).not_to be_running
435
+ expect(validator).to be_sleeping
390
436
 
391
- validator.name = nil
392
- expect(validator).not_to be_valid
393
- expect { validator.run! }.to raise_error(ActiveRecord::RecordInvalid)
394
- expect(validator).to be_sleeping
437
+ validator.name = 'another name'
438
+ expect(validator).to be_valid
439
+ expect(validator.run!).to be_truthy
440
+ expect(validator).to be_running
395
441
 
396
- validator.reload
397
- expect(validator).not_to be_running
398
- expect(validator).to be_sleeping
442
+ validator.reload
443
+ expect(validator).to be_running
444
+ expect(validator).not_to be_sleeping
445
+ end
399
446
 
400
- validator.name = 'another name'
401
- expect(validator).to be_valid
402
- expect(validator.run!).to be_truthy
403
- expect(validator).to be_running
447
+ it 'should not store states for invalid models silently if configured' do
448
+ validator = SilentPersistor.create(:name => 'name')
449
+ expect(validator).to be_valid
450
+ expect(validator).to be_sleeping
404
451
 
405
- validator.reload
406
- expect(validator).to be_running
407
- expect(validator).not_to be_sleeping
408
- end
452
+ validator.name = nil
453
+ expect(validator).not_to be_valid
454
+ expect(validator.run!).to be_falsey
455
+ expect(validator).to be_sleeping
456
+
457
+ validator.reload
458
+ expect(validator).not_to be_running
459
+ expect(validator).to be_sleeping
409
460
 
410
- it 'should not store states for invalid models silently if configured' do
411
- validator = SilentPersistor.create(:name => 'name')
412
- expect(validator).to be_valid
413
- expect(validator).to be_sleeping
461
+ validator.name = 'another name'
462
+ expect(validator).to be_valid
463
+ expect(validator.run!).to be_truthy
464
+ expect(validator).to be_running
414
465
 
415
- validator.name = nil
416
- expect(validator).not_to be_valid
417
- expect(validator.run!).to be_falsey
418
- expect(validator).to be_sleeping
466
+ validator.reload
467
+ expect(validator).to be_running
468
+ expect(validator).not_to be_sleeping
469
+ end
419
470
 
420
- validator.reload
421
- expect(validator).not_to be_running
422
- expect(validator).to be_sleeping
471
+ it 'should store states for invalid models if configured' do
472
+ persistor = InvalidPersistor.create(:name => 'name')
473
+ expect(persistor).to be_valid
474
+ expect(persistor).to be_sleeping
423
475
 
424
- validator.name = 'another name'
425
- expect(validator).to be_valid
426
- expect(validator.run!).to be_truthy
427
- expect(validator).to be_running
476
+ persistor.name = nil
477
+ expect(persistor).not_to be_valid
478
+ expect(persistor.run!).to be_truthy
479
+ expect(persistor).to be_running
428
480
 
429
- validator.reload
430
- expect(validator).to be_running
431
- expect(validator).not_to be_sleeping
432
- end
481
+ persistor = InvalidPersistor.find(persistor.id)
482
+ persistor.valid?
483
+ expect(persistor).to be_valid
484
+ expect(persistor).to be_running
485
+ expect(persistor).not_to be_sleeping
433
486
 
434
- it 'should store states for invalid models if configured' do
435
- persistor = InvalidPersistor.create(:name => 'name')
436
- expect(persistor).to be_valid
437
- expect(persistor).to be_sleeping
438
-
439
- persistor.name = nil
440
- expect(persistor).not_to be_valid
441
- expect(persistor.run!).to be_truthy
442
- expect(persistor).to be_running
443
-
444
- persistor = InvalidPersistor.find(persistor.id)
445
- persistor.valid?
446
- expect(persistor).to be_valid
447
- expect(persistor).to be_running
448
- expect(persistor).not_to be_sleeping
449
-
450
- persistor.reload
451
- expect(persistor).to be_running
452
- expect(persistor).not_to be_sleeping
453
- end
487
+ persistor.reload
488
+ expect(persistor).to be_running
489
+ expect(persistor).not_to be_sleeping
490
+ end
454
491
 
455
- describe 'pessimistic locking' do
456
- let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
492
+ describe 'pessimistic locking' do
493
+ let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
457
494
 
458
- subject { transactor.run! }
495
+ subject { transactor.run! }
459
496
 
460
- context 'no lock' do
461
- let(:transactor) { NoLockTransactor.create!(:name => 'no_lock_transactor', :worker => worker) }
497
+ context 'no lock' do
498
+ let(:transactor) { NoLockTransactor.create!(:name => 'no_lock_transactor', :worker => worker) }
462
499
 
463
- it 'should not invoke lock!' do
464
- expect(transactor).to_not receive(:lock!)
465
- subject
500
+ it 'should not invoke lock!' do
501
+ expect(transactor).to_not receive(:lock!)
502
+ subject
503
+ end
466
504
  end
467
- end
468
505
 
469
- context 'a default lock' do
470
- let(:transactor) { LockTransactor.create!(:name => 'lock_transactor', :worker => worker) }
506
+ context 'a default lock' do
507
+ let(:transactor) { LockTransactor.create!(:name => 'lock_transactor', :worker => worker) }
471
508
 
472
- it 'should invoke lock! with true' do
473
- expect(transactor).to receive(:lock!).with(true).and_call_original
474
- subject
509
+ it 'should invoke lock! with true' do
510
+ expect(transactor).to receive(:lock!).with(true).and_call_original
511
+ subject
512
+ end
475
513
  end
476
- end
477
514
 
478
- context 'a FOR UPDATE NOWAIT lock' do
479
- let(:transactor) { LockNoWaitTransactor.create!(:name => 'lock_no_wait_transactor', :worker => worker) }
515
+ context 'a FOR UPDATE NOWAIT lock' do
516
+ let(:transactor) { LockNoWaitTransactor.create!(:name => 'lock_no_wait_transactor', :worker => worker) }
480
517
 
481
- it 'should invoke lock! with FOR UPDATE NOWAIT' do
482
- expect(transactor).to receive(:lock!).with('FOR UPDATE NOWAIT').and_call_original
483
- subject
518
+ it 'should invoke lock! with FOR UPDATE NOWAIT' do
519
+ expect(transactor).to receive(:lock!).with('FOR UPDATE NOWAIT').and_call_original
520
+ subject
521
+ end
484
522
  end
485
523
  end
486
- end
487
524
 
488
- describe 'transactions' do
489
- let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
490
- let(:transactor) { Transactor.create!(:name => 'transactor', :worker => worker) }
525
+ describe 'without transactions' do
526
+ let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
527
+ let(:no_transactor) { NoTransactor.create!(:name => 'transactor', :worker => worker) }
491
528
 
492
- it 'should rollback all changes' do
493
- expect(transactor).to be_sleeping
494
- expect(worker.status).to eq('sleeping')
529
+ it 'should not rollback all changes' do
530
+ expect(no_transactor).to be_sleeping
531
+ expect(worker.status).to eq('sleeping')
495
532
 
496
- expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
497
- expect(transactor).to be_running
498
- expect(worker.reload.status).to eq('sleeping')
533
+ expect {no_transactor.run!}.to raise_error(StandardError, 'failed on purpose')
534
+ expect(no_transactor).to be_running
535
+ expect(worker.reload.status).to eq('running')
536
+ end
499
537
  end
500
538
 
501
- context "nested transactions" do
502
- it "should rollback all changes in nested transaction" do
539
+ describe 'transactions' do
540
+ let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }
541
+ let(:transactor) { Transactor.create!(:name => 'transactor', :worker => worker) }
542
+
543
+ it 'should rollback all changes' do
503
544
  expect(transactor).to be_sleeping
504
545
  expect(worker.status).to eq('sleeping')
505
546
 
506
- Worker.transaction do
507
- expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
508
- end
509
-
547
+ expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
510
548
  expect(transactor).to be_running
511
549
  expect(worker.reload.status).to eq('sleeping')
512
550
  end
513
551
 
514
- it "should only rollback changes in the main transaction not the nested one" do
515
- # change configuration to not require new transaction
516
- AASM::StateMachineStore[Transactor][:default].config.requires_new_transaction = false
552
+ context "nested transactions" do
553
+ it "should rollback all changes in nested transaction" do
554
+ expect(transactor).to be_sleeping
555
+ expect(worker.status).to eq('sleeping')
517
556
 
518
- expect(transactor).to be_sleeping
519
- expect(worker.status).to eq('sleeping')
557
+ Worker.transaction do
558
+ expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
559
+ end
520
560
 
521
- Worker.transaction do
522
- expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
561
+ expect(transactor).to be_running
562
+ expect(worker.reload.status).to eq('sleeping')
523
563
  end
524
564
 
525
- expect(transactor).to be_running
526
- expect(worker.reload.status).to eq('running')
527
- end
528
- end
565
+ it "should only rollback changes in the main transaction not the nested one" do
566
+ # change configuration to not require new transaction
567
+ AASM::StateMachineStore[Transactor][:default].config.requires_new_transaction = false
529
568
 
530
- describe "after_commit callback" do
531
- it "should fire :after_commit if transaction was successful" do
532
- validator = Validator.create(:name => 'name')
533
- expect(validator).to be_sleeping
569
+ expect(transactor).to be_sleeping
570
+ expect(worker.status).to eq('sleeping')
534
571
 
535
- validator.run!
536
- expect(validator).to be_running
537
- expect(validator.name).to eq("name changed")
572
+ Worker.transaction do
573
+ expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
574
+ end
538
575
 
539
- validator.sleep!("sleeper")
540
- expect(validator).to be_sleeping
541
- expect(validator.name).to eq("sleeper")
576
+ expect(transactor).to be_running
577
+ expect(worker.reload.status).to eq('running')
578
+ end
542
579
  end
543
580
 
544
- it "should not fire :after_commit if transaction failed" do
545
- validator = Validator.create(:name => 'name')
546
- expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
547
- expect(validator.name).to eq("name")
548
- end
581
+ describe "after_commit callback" do
582
+ it "should fire :after_commit if transaction was successful" do
583
+ validator = Validator.create(:name => 'name')
584
+ expect(validator).to be_sleeping
549
585
 
550
- it "should not fire :after_commit if validation failed when saving object" do
551
- validator = Validator.create(:name => 'name')
552
- validator.invalid = true
553
- expect { validator.run! }.to raise_error(ActiveRecord::RecordInvalid, 'Invalid record')
554
- expect(validator).to be_sleeping
555
- expect(validator.name).to eq("name")
556
- end
586
+ validator.run!
587
+ expect(validator).to be_running
588
+ expect(validator.name).to eq("name changed")
557
589
 
558
- it "should not fire if not saving" do
559
- validator = Validator.create(:name => 'name')
560
- expect(validator).to be_sleeping
561
- validator.run
562
- expect(validator).to be_running
563
- expect(validator.name).to eq("name")
564
- end
565
- end
590
+ validator.sleep!("sleeper")
591
+ expect(validator).to be_sleeping
592
+ expect(validator.name).to eq("sleeper")
593
+ end
566
594
 
567
- describe 'before and after transaction callbacks' do
568
- [:after, :before].each do |event_type|
569
- describe "#{event_type}_transaction callback" do
570
- it "should fire :#{event_type}_transaction if transaction was successful" do
595
+ it "should not fire :after_commit if transaction failed" do
596
+ validator = Validator.create(:name => 'name')
597
+ expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
598
+ expect(validator.name).to eq("name")
599
+ end
600
+
601
+ it "should not fire :after_commit if validation failed when saving object" do
602
+ validator = Validator.create(:name => 'name')
603
+ validator.invalid = true
604
+ expect { validator.run! }.to raise_error(ActiveRecord::RecordInvalid, 'Invalid record')
605
+ expect(validator).to be_sleeping
606
+ expect(validator.name).to eq("name")
607
+ end
608
+
609
+ it "should not fire if not saving" do
610
+ validator = Validator.create(:name => 'name')
611
+ expect(validator).to be_sleeping
612
+ validator.run
613
+ expect(validator).to be_running
614
+ expect(validator.name).to eq("name")
615
+ end
616
+
617
+ context "nested transaction" do
618
+ it "should fire :after_commit if root transaction was successful" do
571
619
  validator = Validator.create(:name => 'name')
572
620
  expect(validator).to be_sleeping
573
621
 
574
- expect { validator.run! }.to change { validator.send("#{event_type}_transaction_performed_on_run") }.from(nil).to(true)
575
- expect(validator).to be_running
576
- end
622
+ validator.transaction do
623
+ validator.run!
624
+ expect(validator.name).to eq("name")
625
+ expect(validator).to be_running
626
+ end
577
627
 
578
- it "should fire :#{event_type}_transaction if transaction failed" do
579
- validator = Validator.create(:name => 'name')
580
- expect do
581
- begin
582
- validator.fail!
583
- rescue => ignored
584
- end
585
- end.to change { validator.send("#{event_type}_transaction_performed_on_fail") }.from(nil).to(true)
586
- expect(validator).to_not be_running
628
+ expect(validator.name).to eq("name changed")
629
+ expect(validator.reload).to be_running
587
630
  end
588
631
 
589
- it "should not fire :#{event_type}_transaction if not saving" do
632
+ it "should not fire :after_commit if root transaction failed" do
590
633
  validator = Validator.create(:name => 'name')
591
634
  expect(validator).to be_sleeping
592
- expect { validator.run }.to_not change { validator.send("#{event_type}_transaction_performed_on_run") }
593
- expect(validator).to be_running
635
+
636
+ validator.transaction do
637
+ validator.run!
638
+ expect(validator.name).to eq("name")
639
+ expect(validator).to be_running
640
+
641
+ raise ActiveRecord::Rollback, "failed on purpose"
642
+ end
643
+
594
644
  expect(validator.name).to eq("name")
645
+ expect(validator.reload).to be_sleeping
595
646
  end
596
647
  end
597
648
  end
598
- end
599
-
600
- describe 'before and after all transactions callbacks' do
601
- [:after, :before].each do |event_type|
602
- describe "#{event_type}_all_transactions callback" do
603
- it "should fire :#{event_type}_all_transactions if transaction was successful" do
604
- validator = Validator.create(:name => 'name')
605
- expect(validator).to be_sleeping
606
649
 
607
- expect { validator.run! }.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
608
- expect(validator).to be_running
650
+ describe 'callbacks for the new DSL' do
651
+
652
+ it "be called in order" do
653
+ show_debug_log = false
654
+
655
+ callback = ActiveRecordCallback.create
656
+ callback.aasm.current_state
657
+
658
+ unless show_debug_log
659
+ expect(callback).to receive(:before_all_events).once.ordered
660
+ expect(callback).to receive(:before_event).once.ordered
661
+ expect(callback).to receive(:event_guard).once.ordered.and_return(true)
662
+ expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
663
+ expect(callback).to receive(:before_exit_open).once.ordered # these should be before the state changes
664
+ expect(callback).to receive(:exit_open).once.ordered
665
+ # expect(callback).to receive(:event_guard).once.ordered.and_return(true)
666
+ # expect(callback).to receive(:transition_guard).once.ordered.and_return(true)
667
+ expect(callback).to receive(:after_all_transitions).once.ordered
668
+ expect(callback).to receive(:after_transition).once.ordered
669
+ expect(callback).to receive(:before_enter_closed).once.ordered
670
+ expect(callback).to receive(:enter_closed).once.ordered
671
+ expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
672
+ expect(callback).to receive(:after_exit_open).once.ordered # these should be after the state changes
673
+ expect(callback).to receive(:after_enter_closed).once.ordered
674
+ expect(callback).to receive(:after_event).once.ordered
675
+ expect(callback).to receive(:after_all_events).once.ordered
676
+ expect(callback).to receive(:ensure_event).once.ordered
677
+ expect(callback).to receive(:ensure_on_all_events).once.ordered
678
+ expect(callback).to receive(:event_after_commit).once.ordered
609
679
  end
610
680
 
611
- it "should fire :#{event_type}_all_transactions if transaction failed" do
612
- validator = Validator.create(:name => 'name')
613
- expect do
614
- begin
615
- validator.fail!
616
- rescue => ignored
617
- end
618
- end.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
619
- expect(validator).to_not be_running
681
+ callback.close!
682
+ end
683
+ end
684
+
685
+ describe 'before and after transaction callbacks' do
686
+ [:after, :before].each do |event_type|
687
+ describe "#{event_type}_transaction callback" do
688
+ it "should fire :#{event_type}_transaction if transaction was successful" do
689
+ validator = Validator.create(:name => 'name')
690
+ expect(validator).to be_sleeping
691
+
692
+ expect { validator.run! }.to change { validator.send("#{event_type}_transaction_performed_on_run") }.from(nil).to(true)
693
+ expect(validator).to be_running
694
+ end
695
+
696
+ it "should fire :#{event_type}_transaction if transaction failed" do
697
+ validator = Validator.create(:name => 'name')
698
+ expect do
699
+ begin
700
+ validator.fail!
701
+ rescue => ignored
702
+ end
703
+ end.to change { validator.send("#{event_type}_transaction_performed_on_fail") }.from(nil).to(true)
704
+ expect(validator).to_not be_running
705
+ end
706
+
707
+ it "should not fire :#{event_type}_transaction if not saving" do
708
+ validator = Validator.create(:name => 'name')
709
+ expect(validator).to be_sleeping
710
+ expect { validator.run }.to_not change { validator.send("#{event_type}_transaction_performed_on_run") }
711
+ expect(validator).to be_running
712
+ expect(validator.name).to eq("name")
713
+ end
620
714
  end
715
+ end
716
+ end
621
717
 
622
- it "should not fire :#{event_type}_all_transactions if not saving" do
623
- validator = Validator.create(:name => 'name')
624
- expect(validator).to be_sleeping
625
- expect { validator.run }.to_not change { validator.send("#{event_type}_all_transactions_performed") }
626
- expect(validator).to be_running
627
- expect(validator.name).to eq("name")
718
+ describe 'before and after all transactions callbacks' do
719
+ [:after, :before].each do |event_type|
720
+ describe "#{event_type}_all_transactions callback" do
721
+ it "should fire :#{event_type}_all_transactions if transaction was successful" do
722
+ validator = Validator.create(:name => 'name')
723
+ expect(validator).to be_sleeping
724
+
725
+ expect { validator.run! }.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
726
+ expect(validator).to be_running
727
+ end
728
+
729
+ it "should fire :#{event_type}_all_transactions if transaction failed" do
730
+ validator = Validator.create(:name => 'name')
731
+ expect do
732
+ begin
733
+ validator.fail!
734
+ rescue => ignored
735
+ end
736
+ end.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
737
+ expect(validator).to_not be_running
738
+ end
739
+
740
+ it "should not fire :#{event_type}_all_transactions if not saving" do
741
+ validator = Validator.create(:name => 'name')
742
+ expect(validator).to be_sleeping
743
+ expect { validator.run }.to_not change { validator.send("#{event_type}_all_transactions_performed") }
744
+ expect(validator).to be_running
745
+ expect(validator.name).to eq("name")
746
+ end
628
747
  end
629
748
  end
630
749
  end
631
- end
632
750
 
633
- context "when not persisting" do
634
- it 'should not rollback all changes' do
635
- expect(transactor).to be_sleeping
636
- expect(worker.status).to eq('sleeping')
751
+ context "when not persisting" do
752
+ it 'should not rollback all changes' do
753
+ expect(transactor).to be_sleeping
754
+ expect(worker.status).to eq('sleeping')
637
755
 
638
- # Notice here we're calling "run" and not "run!" with a bang.
639
- expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
640
- expect(transactor).to be_running
641
- expect(worker.reload.status).to eq('running')
642
- end
756
+ # Notice here we're calling "run" and not "run!" with a bang.
757
+ expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
758
+ expect(transactor).to be_running
759
+ expect(worker.reload.status).to eq('running')
760
+ end
643
761
 
644
- it 'should not create a database transaction' do
645
- expect(transactor.class).not_to receive(:transaction)
646
- expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
762
+ it 'should not create a database transaction' do
763
+ expect(transactor.class).not_to receive(:transaction)
764
+ expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
765
+ end
647
766
  end
648
767
  end
649
768
  end
650
- end
651
769
 
652
- describe "invalid states with persistence" do
770
+ describe "invalid states with persistence" do
653
771
 
654
- it "should not store states" do
655
- validator = Validator.create(:name => 'name')
656
- validator.status = 'invalid_state'
657
- expect(validator.save).to be_falsey
658
- expect {validator.save!}.to raise_error(ActiveRecord::RecordInvalid)
772
+ it "should not store states" do
773
+ validator = Validator.create(:name => 'name')
774
+ validator.status = 'invalid_state'
775
+ expect(validator.save).to be_falsey
776
+ expect {validator.save!}.to raise_error(ActiveRecord::RecordInvalid)
777
+
778
+ validator.reload
779
+ expect(validator).to be_sleeping
780
+ end
781
+
782
+ it "should store invalid states if configured" do
783
+ persistor = InvalidPersistor.create(:name => 'name')
784
+ persistor.status = 'invalid_state'
785
+ expect(persistor.save).to be_truthy
786
+
787
+ persistor.reload
788
+ expect(persistor.status).to eq('invalid_state')
789
+ end
659
790
 
660
- validator.reload
661
- expect(validator).to be_sleeping
662
791
  end
663
792
 
664
- it "should store invalid states if configured" do
665
- persistor = InvalidPersistor.create(:name => 'name')
666
- persistor.status = 'invalid_state'
667
- expect(persistor.save).to be_truthy
793
+ describe 'basic example with two state machines' do
794
+ let(:example) { BasicActiveRecordTwoStateMachinesExample.new }
668
795
 
669
- persistor.reload
670
- expect(persistor.status).to eq('invalid_state')
796
+ it 'should initialise properly' do
797
+ expect(example.aasm(:search).current_state).to eql :initialised
798
+ expect(example.aasm(:sync).current_state).to eql :unsynced
799
+ end
671
800
  end
672
801
 
673
- end
802
+ describe 'testing the README examples' do
803
+ it 'Usage' do
804
+ job = ReadmeJob.new
805
+
806
+ expect(job.sleeping?).to eql true
807
+ expect(job.may_run?).to eql true
808
+
809
+ job.run
674
810
 
675
- describe 'basic example with two state machines' do
676
- let(:example) { BasicActiveRecordTwoStateMachinesExample.new }
811
+ expect(job.running?).to eql true
812
+ expect(job.sleeping?).to eql false
813
+ expect(job.may_run?).to eql false
677
814
 
678
- it 'should initialise properly' do
679
- expect(example.aasm(:search).current_state).to eql :initialised
680
- expect(example.aasm(:sync).current_state).to eql :unsynced
815
+ expect { job.run }.to raise_error(AASM::InvalidTransition)
816
+ end
681
817
  end
682
- end
683
818
 
684
- describe 'testing the README examples' do
685
- it 'Usage' do
686
- job = ReadmeJob.new
819
+ describe 'testing the instance_level skip validation with _without_validation method' do
820
+ let(:example) do
821
+ obj = InstanceLevelSkipValidationExample.new(state: 'new')
822
+ obj.save(validate: false)
823
+ obj
824
+ end
825
+
826
+ it 'should be able to change the state with invalid record' do
827
+ expect(example.valid?).to be_falsey
828
+ expect(example.complete!).to be_falsey
829
+ expect(example.complete_without_validation!).to be_truthy
830
+ expect(example.state).to eq('complete')
831
+ end
687
832
 
688
- expect(job.sleeping?).to eql true
689
- expect(job.may_run?).to eql true
833
+ it 'shouldn\'t affect the behaviour of existing method after calling _without_validation! method' do
834
+ expect(example.set_draft!).to be_falsey
835
+ expect(example.set_draft_without_validation!).to be_truthy
836
+ expect(example.state).to eq('draft')
837
+ expect(example.complete!).to be_falsey
838
+ end
839
+ end
690
840
 
691
- job.run
841
+ describe 'testing the timestamps option' do
842
+ let(:example) { TimestampExample.create! }
692
843
 
693
- expect(job.running?).to eql true
694
- expect(job.sleeping?).to eql false
695
- expect(job.may_run?).to eql false
844
+ it 'should update existing timestamp columns' do
845
+ expect { example.open! }.to change { example.reload.opened_at }.from(nil).to(instance_of(::Time))
846
+ end
696
847
 
697
- expect { job.run }.to raise_error(AASM::InvalidTransition)
848
+ it 'should not fail if there is no corresponding timestamp column' do
849
+ expect { example.close! }.to change { example.reload.aasm_state }
850
+ end
698
851
  end
699
852
  end