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
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+
3
+ if defined?(Redis)
4
+ describe 'redis' do
5
+
6
+ Dir[File.dirname(__FILE__) + "/../../models/redis/*.rb"].sort.each do |f|
7
+ require File.expand_path(f)
8
+ end
9
+
10
+ before(:all) do
11
+ @model = RedisMultiple
12
+ end
13
+
14
+ describe "instance methods" do
15
+ let(:model) {@model.new}
16
+
17
+ it "should respond to aasm persistence methods" do
18
+ expect(model).to respond_to(:aasm_read_state)
19
+ expect(model).to respond_to(:aasm_write_state)
20
+ expect(model).to respond_to(:aasm_write_state_without_persistence)
21
+ end
22
+
23
+ it "should return the initial state when new and the aasm field is nil" do
24
+ expect(model.aasm(:left).current_state).to eq(:alpha)
25
+ end
26
+
27
+ it "should save the initial state" do
28
+ expect(model.status).to eq("alpha")
29
+ end
30
+
31
+ it "should return the aasm column the aasm field is not nil" do
32
+ model.status = "beta"
33
+ expect(model.aasm(:left).current_state).to eq(:beta)
34
+ end
35
+
36
+ it "should allow a nil state" do
37
+ model.status = nil
38
+ expect(model.aasm(:left).current_state).to be_nil
39
+ end
40
+ end
41
+
42
+ describe 'subclasses' do
43
+ it "should have the same states as its parent class" do
44
+ expect(Class.new(@model).aasm(:left).states).to eq(@model.aasm(:left).states)
45
+ end
46
+
47
+ it "should have the same events as its parent class" do
48
+ expect(Class.new(@model).aasm(:left).events).to eq(@model.aasm(:left).events)
49
+ end
50
+
51
+ it "should have the same column as its parent even for the new dsl" do
52
+ expect(@model.aasm(:left).attribute_name).to eq(:status)
53
+ expect(Class.new(@model).aasm(:left).attribute_name).to eq(:status)
54
+ end
55
+ end
56
+
57
+ describe "complex example" do
58
+ it "works" do
59
+ record = RedisComplexExample.new
60
+
61
+ expect(record.aasm(:left).current_state).to eql :one
62
+ expect(record.aasm(:right).current_state).to eql :alpha
63
+
64
+ expect_aasm_states record, :one, :alpha
65
+
66
+ record.increment!
67
+ expect_aasm_states record, :two, :alpha
68
+
69
+ record.level_up!
70
+ expect_aasm_states record, :two, :beta
71
+
72
+ record.increment!
73
+ expect { record.increment! }.to raise_error(AASM::InvalidTransition)
74
+ expect_aasm_states record, :three, :beta
75
+
76
+ record.level_up!
77
+ expect_aasm_states record, :three, :gamma
78
+ end
79
+
80
+ def expect_aasm_states(record, left_state, right_state)
81
+ expect(record.aasm(:left).current_state).to eql left_state.to_sym
82
+ expect(record.left.value.to_s).to eql left_state.to_s
83
+ expect(record.aasm(:right).current_state).to eql right_state.to_sym
84
+ expect(record.right.value.to_s).to eql right_state.to_s
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,35 +1,14 @@
1
+ require 'spec_helper'
1
2
 
2
- describe 'redis' do
3
- begin
4
- require 'redis-objects'
5
- require 'logger'
6
- require 'spec_helper'
3
+ if defined?(Redis::Objects)
4
+ describe 'redis' do
7
5
 
8
- before(:all) do
9
- Redis.current = Redis.new(host: '127.0.0.1', port: 6379)
10
-
11
- @model = Class.new do
12
- attr_accessor :default
13
-
14
- include Redis::Objects
15
- include AASM
16
-
17
- value :status
18
-
19
- def id
20
- 1
21
- end
6
+ Dir[File.dirname(__FILE__) + "/../../models/redis/*.rb"].sort.each do |f|
7
+ require File.expand_path(f)
8
+ end
22
9
 
23
- aasm column: :status
24
- aasm do
25
- state :alpha, initial: true
26
- state :beta
27
- state :gamma
28
- event :release do
29
- transitions from: [:alpha, :beta, :gamma], to: :beta
30
- end
31
- end
32
- end
10
+ before(:all) do
11
+ @model = RedisSimple
33
12
  end
34
13
 
35
14
  describe "instance methods" do
@@ -70,8 +49,5 @@ describe 'redis' do
70
49
  expect(Class.new(@model).aasm.attribute_name).to eq(:status)
71
50
  end
72
51
  end
73
-
74
- rescue LoadError
75
- puts "Not running Redis specs because sequel gem is not installed!!!"
76
52
  end
77
53
  end
@@ -1,15 +1,14 @@
1
- describe 'sequel' do
2
- begin
3
- require 'sequel'
4
- require 'logger'
5
- require 'spec_helper'
1
+ require 'spec_helper'
2
+
3
+ if defined?(Sequel)
4
+ describe 'sequel' do
6
5
 
7
6
  Dir[File.dirname(__FILE__) + "/../../models/sequel/*.rb"].sort.each do |f|
8
7
  require File.expand_path(f)
9
8
  end
10
9
 
11
10
  before(:all) do
12
- @model = SequelMultiple
11
+ @model = Sequel::Multiple
13
12
  end
14
13
 
15
14
  describe "instance methods" do
@@ -94,7 +93,7 @@ describe 'sequel' do
94
93
 
95
94
  describe "complex example" do
96
95
  it "works" do
97
- record = ComplexSequelExample.new
96
+ record = Sequel::ComplexExample.new
98
97
  expect(record.aasm(:left).current_state).to eql :one
99
98
  expect(record.left).to be_nil
100
99
  expect(record.aasm(:right).current_state).to eql :alpha
@@ -145,9 +144,5 @@ describe 'sequel' do
145
144
  end
146
145
  end
147
146
 
148
- rescue LoadError
149
- puts "------------------------------------------------------------------------"
150
- puts "Not running Sequel multiple-specs because sequel gem is not installed!!!"
151
- puts "------------------------------------------------------------------------"
152
147
  end
153
148
  end
@@ -1,15 +1,13 @@
1
- describe 'sequel' do
2
- begin
3
- require 'sequel'
4
- require 'logger'
5
- require 'spec_helper'
1
+ require 'spec_helper'
2
+ if defined?(Sequel)
3
+ describe 'sequel' do
6
4
 
7
5
  Dir[File.dirname(__FILE__) + "/../../models/sequel/*.rb"].sort.each do |f|
8
6
  require File.expand_path(f)
9
7
  end
10
8
 
11
9
  before(:all) do
12
- @model = SequelSimple
10
+ @model = Sequel::Simple
13
11
  end
14
12
 
15
13
  describe "instance methods" do
@@ -92,9 +90,279 @@ describe 'sequel' do
92
90
  end
93
91
  end
94
92
 
95
- rescue LoadError
96
- puts "------------------------------------------------------------------------"
97
- puts "Not running Sequel specs because sequel gem is not installed!!!"
98
- puts "------------------------------------------------------------------------"
93
+ describe 'transitions with persistence' do
94
+
95
+ it "should work for valid models" do
96
+ valid_object = Sequel::Validator.create(:name => 'name')
97
+ expect(valid_object).to be_sleeping
98
+ valid_object.status = :running
99
+ expect(valid_object).to be_running
100
+ end
101
+
102
+ it 'should not store states for invalid models' do
103
+ validator = Sequel::Validator.create(:name => 'name')
104
+ expect(validator).to be_valid
105
+ expect(validator).to be_sleeping
106
+
107
+ validator.name = nil
108
+ expect(validator).not_to be_valid
109
+ expect { validator.run! }.to raise_error(Sequel::ValidationFailed)
110
+ expect(validator).to be_sleeping
111
+
112
+ validator.reload
113
+ expect(validator).not_to be_running
114
+ expect(validator).to be_sleeping
115
+
116
+ validator.name = 'another name'
117
+ expect(validator).to be_valid
118
+ expect(validator.run!).to be_truthy
119
+ expect(validator).to be_running
120
+
121
+ validator.reload
122
+ expect(validator).to be_running
123
+ expect(validator).not_to be_sleeping
124
+ end
125
+
126
+ it 'should not store states for invalid models silently if configured' do
127
+ validator = Sequel::SilentPersistor.create(:name => 'name')
128
+ expect(validator).to be_valid
129
+ expect(validator).to be_sleeping
130
+
131
+ validator.name = nil
132
+ expect(validator).not_to be_valid
133
+ expect(validator.run!).to be_falsey
134
+ expect(validator).to be_sleeping
135
+
136
+ validator.reload
137
+ expect(validator).not_to be_running
138
+ expect(validator).to be_sleeping
139
+
140
+ validator.name = 'another name'
141
+ expect(validator).to be_valid
142
+ expect(validator.run!).to be_truthy
143
+ expect(validator).to be_running
144
+
145
+ validator.reload
146
+ expect(validator).to be_running
147
+ expect(validator).not_to be_sleeping
148
+ end
149
+
150
+ it 'should store states for invalid models if configured' do
151
+ persistor = Sequel::InvalidPersistor.create(:name => 'name')
152
+ expect(persistor).to be_valid
153
+ expect(persistor).to be_sleeping
154
+
155
+ persistor.name = nil
156
+ expect(persistor).not_to be_valid
157
+ expect(persistor.run!).to be_truthy
158
+ expect(persistor).to be_running
159
+
160
+ persistor = Sequel::InvalidPersistor[persistor.id]
161
+ persistor.valid?
162
+ expect(persistor).to be_valid
163
+ expect(persistor).to be_running
164
+ expect(persistor).not_to be_sleeping
165
+
166
+ persistor.reload
167
+ expect(persistor).to be_running
168
+ expect(persistor).not_to be_sleeping
169
+ end
170
+
171
+ describe 'pessimistic locking' do
172
+ let(:worker) { Sequel::Worker.create(:name => 'worker', :status => 'sleeping') }
173
+
174
+ subject { transactor.run! }
175
+
176
+ context 'no lock' do
177
+ let(:transactor) { Sequel::NoLockTransactor.create(:name => 'no_lock_transactor', :worker => worker) }
178
+
179
+ it 'should not invoke lock!' do
180
+ expect(transactor).to_not receive(:lock!)
181
+ subject
182
+ end
183
+ end
184
+
185
+ context 'a default lock' do
186
+ let(:transactor) { Sequel::LockTransactor.create(:name => 'lock_transactor', :worker => worker) }
187
+
188
+ it 'should invoke lock!' do
189
+ expect(transactor).to receive(:lock!).and_call_original
190
+ subject
191
+ end
192
+ end
193
+
194
+ context 'a FOR UPDATE NOWAIT lock' do
195
+ let(:transactor) { Sequel::LockNoWaitTransactor.create(:name => 'lock_no_wait_transactor', :worker => worker) }
196
+
197
+ it 'should invoke lock! with FOR UPDATE NOWAIT' do
198
+ # TODO: With and_call_original, get an error with syntax, should look into it.
199
+ expect(transactor).to receive(:lock!).with('FOR UPDATE NOWAIT')# .and_call_original
200
+ subject
201
+ end
202
+ end
203
+ end
204
+
205
+ describe 'transactions' do
206
+ let(:worker) { Sequel::Worker.create(:name => 'worker', :status => 'sleeping') }
207
+ let(:transactor) { Sequel::Transactor.create(:name => 'transactor', :worker => worker) }
208
+
209
+ it 'should rollback all changes' do
210
+ expect(transactor).to be_sleeping
211
+ expect(worker.status).to eq('sleeping')
212
+
213
+ expect {transactor.run!}.to raise_error(StandardError, 'failed on purpose')
214
+ expect(transactor).to be_running
215
+ expect(worker.reload.status).to eq('sleeping')
216
+ end
217
+
218
+ context "nested transactions" do
219
+ it "should rollback all changes in nested transaction" do
220
+ expect(transactor).to be_sleeping
221
+ expect(worker.status).to eq('sleeping')
222
+
223
+ Sequel::Worker.db.transaction do
224
+ expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
225
+ end
226
+
227
+ expect(transactor).to be_running
228
+ expect(worker.reload.status).to eq('sleeping')
229
+ end
230
+
231
+ it "should only rollback changes in the main transaction not the nested one" do
232
+ # change configuration to not require new transaction
233
+ AASM::StateMachineStore[Sequel::Transactor][:default].config.requires_new_transaction = false
234
+
235
+ expect(transactor).to be_sleeping
236
+ expect(worker.status).to eq('sleeping')
237
+ Sequel::Worker.db.transaction do
238
+ expect { transactor.run! }.to raise_error(StandardError, 'failed on purpose')
239
+ end
240
+ expect(transactor).to be_running
241
+ expect(worker.reload.status).to eq('running')
242
+ end
243
+ end
244
+
245
+ describe "after_commit callback" do
246
+ it "should fire :after_commit if transaction was successful" do
247
+ validator = Sequel::Validator.create(:name => 'name')
248
+ expect(validator).to be_sleeping
249
+
250
+ validator.run!
251
+ expect(validator).to be_running
252
+ expect(validator.name).to eq("name changed")
253
+
254
+ validator.sleep!("sleeper")
255
+ expect(validator).to be_sleeping
256
+ expect(validator.name).to eq("sleeper")
257
+ end
258
+
259
+ it "should not fire :after_commit if transaction failed" do
260
+ validator = Sequel::Validator.create(:name => 'name')
261
+ expect { validator.fail! }.to raise_error(StandardError, 'failed on purpose')
262
+ expect(validator.name).to eq("name")
263
+ end
264
+
265
+ it "should not fire :after_commit if validation failed when saving object" do
266
+ validator = Sequel::Validator.create(:name => 'name')
267
+ validator.invalid = true
268
+ expect { validator.run! }.to raise_error(Sequel::ValidationFailed, 'validator invalid')
269
+ expect(validator).to be_sleeping
270
+ expect(validator.name).to eq("name")
271
+ end
272
+
273
+ it "should not fire if not saving" do
274
+ validator = Sequel::Validator.create(:name => 'name')
275
+ expect(validator).to be_sleeping
276
+ validator.run
277
+ expect(validator).to be_running
278
+ expect(validator.name).to eq("name")
279
+ end
280
+ end
281
+
282
+ describe 'before and after transaction callbacks' do
283
+ [:after, :before].each do |event_type|
284
+ describe "#{event_type}_transaction callback" do
285
+ it "should fire :#{event_type}_transaction if transaction was successful" do
286
+ validator = Sequel::Validator.create(:name => 'name')
287
+ expect(validator).to be_sleeping
288
+
289
+ expect { validator.run! }.to change { validator.send("#{event_type}_transaction_performed_on_run") }.from(nil).to(true)
290
+ expect(validator).to be_running
291
+ end
292
+
293
+ it "should fire :#{event_type}_transaction if transaction failed" do
294
+ validator = Sequel::Validator.create(:name => 'name')
295
+ expect do
296
+ begin
297
+ validator.fail!
298
+ rescue => ignored
299
+ end
300
+ end.to change { validator.send("#{event_type}_transaction_performed_on_fail") }.from(nil).to(true)
301
+ expect(validator).to_not be_running
302
+ end
303
+
304
+ it "should not fire :#{event_type}_transaction if not saving" do
305
+ validator = Sequel::Validator.create(:name => 'name')
306
+ expect(validator).to be_sleeping
307
+ expect { validator.run }.to_not change { validator.send("#{event_type}_transaction_performed_on_run") }
308
+ expect(validator).to be_running
309
+ expect(validator.name).to eq("name")
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ describe 'before and after all transactions callbacks' do
316
+ [:after, :before].each do |event_type|
317
+ describe "#{event_type}_all_transactions callback" do
318
+ it "should fire :#{event_type}_all_transactions if transaction was successful" do
319
+ validator = Sequel::Validator.create(:name => 'name')
320
+ expect(validator).to be_sleeping
321
+
322
+ expect { validator.run! }.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
323
+ expect(validator).to be_running
324
+ end
325
+
326
+ it "should fire :#{event_type}_all_transactions if transaction failed" do
327
+ validator = Sequel::Validator.create(:name => 'name')
328
+ expect do
329
+ begin
330
+ validator.fail!
331
+ rescue => ignored
332
+ end
333
+ end.to change { validator.send("#{event_type}_all_transactions_performed") }.from(nil).to(true)
334
+ expect(validator).to_not be_running
335
+ end
336
+
337
+ it "should not fire :#{event_type}_all_transactions if not saving" do
338
+ validator = Sequel::Validator.create(:name => 'name')
339
+ expect(validator).to be_sleeping
340
+ expect { validator.run }.to_not change { validator.send("#{event_type}_all_transactions_performed") }
341
+ expect(validator).to be_running
342
+ expect(validator.name).to eq("name")
343
+ end
344
+ end
345
+ end
346
+ end
347
+
348
+ context "when not persisting" do
349
+ it 'should not rollback all changes' do
350
+ expect(transactor).to be_sleeping
351
+ expect(worker.status).to eq('sleeping')
352
+
353
+ # Notice here we're calling "run" and not "run!" with a bang.
354
+ expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
355
+ expect(transactor).to be_running
356
+ expect(worker.reload.status).to eq('running')
357
+ end
358
+
359
+ it 'should not create a database transaction' do
360
+ expect(transactor.class).not_to receive(:transaction)
361
+ expect {transactor.run}.to raise_error(StandardError, 'failed on purpose')
362
+ end
363
+ end
364
+ end
365
+ end
366
+
99
367
  end
100
368
  end
@@ -8,14 +8,17 @@ describe 'state machine' do
8
8
  it "works for simple state machines" do
9
9
  expect(simple).to transition_from(:initialised).to(:filled_out).on_event(:fill_out)
10
10
  expect(simple).to_not transition_from(:initialised).to(:authorised).on_event(:fill_out)
11
+ expect(simple).to_not transition_from(:authorised).to(:filled_out).on_event(:fill_out)
11
12
  end
12
13
 
13
14
  it "works for multiple state machines" do
14
15
  expect(multiple).to transition_from(:standing).to(:walking).on_event(:walk).on(:move)
15
16
  expect(multiple).to_not transition_from(:standing).to(:running).on_event(:walk).on(:move)
17
+ expect(multiple).to_not transition_from(:running).to(:walking).on_event(:walk).on(:move)
16
18
 
17
19
  expect(multiple).to transition_from(:sleeping).to(:processing).on_event(:start).on(:work)
18
20
  expect(multiple).to_not transition_from(:sleeping).to(:sleeping).on_event(:start).on(:work)
21
+ expect(multiple).to_not transition_from(:processing).to(:sleeping).on_event(:start).on(:work)
19
22
  end
20
23
  end
21
24
 
@@ -63,6 +66,12 @@ describe 'state machine' do
63
66
  expect(simple).to allow_event :authorise
64
67
  end
65
68
 
69
+ it "works with custom arguments" do
70
+ example = SimpleExampleWithGuardArgs.new
71
+ expect(example).to allow_event(:fill_out_with_args).with(true)
72
+ expect(example).to_not allow_event(:fill_out_with_args).with(false)
73
+ end
74
+
66
75
  it "works for multiple state machines" do
67
76
  expect(multiple).to allow_event(:walk).on(:move)
68
77
  expect(multiple).to_not allow_event(:hold).on(:move)
@@ -23,6 +23,21 @@ describe 'state machine' do
23
23
  expect(simple).to be_authorised
24
24
  end
25
25
 
26
+ it 'shows the permitted transitions' do
27
+ expect(simple.aasm.permitted_transitions).to eq(
28
+ [
29
+ { event: :fill_out, state: :filled_out },
30
+ { event: :deny, state: :denied }
31
+ ]
32
+ )
33
+
34
+ simple.fill_out!
35
+ expect(simple.aasm.permitted_transitions).to eq([{ event: :authorise, state: :authorised }])
36
+
37
+ simple.authorise
38
+ expect(simple.aasm.permitted_transitions).to eq([])
39
+ end
40
+
26
41
  it 'denies transitions to other states' do
27
42
  expect {simple.authorise}.to raise_error(AASM::InvalidTransition)
28
43
  expect {simple.authorise!}.to raise_error(AASM::InvalidTransition)
@@ -60,4 +60,32 @@ describe 'state machine' do
60
60
  expect(SimpleMultipleExample::STATE_PROCESSING).to eq(:processing)
61
61
  expect(SimpleMultipleExample::STATE_RUNNING).to eq(:running)
62
62
  end
63
+
64
+ context 'triggers binding_events in bindind_state_machine' do
65
+ it 'does persist' do
66
+ expect(simple).to be_sleeping
67
+ expect(simple).to be_answered
68
+ expect(simple).to receive(:start!).and_call_original
69
+ simple.ask!
70
+ expect(simple).to be_asked
71
+ expect(simple).to be_processing
72
+ expect(simple).to receive(:stop!).and_call_original
73
+ simple.answer!
74
+ expect(simple).to be_sleeping
75
+ expect(simple).to be_answered
76
+ end
77
+
78
+ it 'does not persist' do
79
+ expect(simple).to be_sleeping
80
+ expect(simple).to be_answered
81
+ expect(simple).to receive(:start).and_call_original
82
+ simple.ask
83
+ expect(simple).to be_asked
84
+ expect(simple).to be_processing
85
+ expect(simple).to receive(:stop).and_call_original
86
+ simple.answer
87
+ expect(simple).to be_sleeping
88
+ expect(simple).to be_answered
89
+ end
90
+ end
63
91
  end
@@ -17,12 +17,28 @@ describe AASM::Core::State do
17
17
  expect(state.name).to eq(:astate)
18
18
  end
19
19
 
20
- it 'should set the display_name from name' do
21
- expect(new_state.display_name).to eq('Astate')
22
- end
20
+ describe '#display_name' do
21
+ subject(:display_name) { new_state(options).display_name }
22
+
23
+ context 'without options' do
24
+ let(:options) { {} }
25
+
26
+ context 'without I18n' do
27
+ before { allow(Module).to receive(:const_defined?).with(:I18n).and_return(nil) }
28
+
29
+ it 'should set the display_name from name' do
30
+ expect(display_name).to eq('Astate')
31
+ end
32
+ end
33
+ end
23
34
 
24
- it 'should set the display_name from options' do
25
- expect(new_state(:display => "A State").display_name).to eq('A State')
35
+ context 'with :display option' do
36
+ let(:options) { { display: "A State" } }
37
+
38
+ it 'should set the display_name from options' do
39
+ expect(display_name).to eq('A State')
40
+ end
41
+ end
26
42
  end
27
43
 
28
44
  it 'should set the options and expose them as options' do
@@ -64,7 +80,7 @@ describe AASM::Core::State do
64
80
  expect(record).to receive(:c)
65
81
  expect(record).to receive(:foobar)
66
82
 
67
- state.fire_callbacks(:entering, record)
83
+ state.fire_callbacks(:entering, record, record)
68
84
  end
69
85
 
70
86
  it "should stop calling actions if one of them raises :halt_aasm_chain" do
@@ -84,6 +100,6 @@ describe AASM::Core::State do
84
100
  record = double('record')
85
101
  expect(record).to receive(:foobar)
86
102
 
87
- state.fire_callbacks(:entering, record)
103
+ state.fire_callbacks(:entering, record, record)
88
104
  end
89
105
  end
@@ -19,7 +19,7 @@ describe 'subclassing with multiple state machines' do
19
19
  expect(SuperClassMultiple.aasm(:right).states).not_to include(:archived)
20
20
  end
21
21
 
22
- it "should have the same events as its parent" do
22
+ it 'should have the same events as its parent' do
23
23
  expect(SubClassMultiple.aasm(:left).events).to eq(SuperClassMultiple.aasm(:left).events)
24
24
  expect(SubClassMultiple.aasm(:right).events).to eq(SuperClassMultiple.aasm(:right).events)
25
25
  end
@@ -35,5 +35,40 @@ describe 'subclassing with multiple state machines' do
35
35
  expect(son.aasm(:left).current_state).to eq(:ended)
36
36
  end
37
37
 
38
- end
38
+ it 'should allow the child to modify its left state machine' do
39
+ son = SubClassMultiple.new
40
+ expect(son.left_called_after).to eq(nil)
41
+ expect(son.right_called_after).to eq(nil)
42
+ son.foo
43
+ expect(son.left_called_after).to eq(true)
44
+ expect(son.right_called_after).to eq(nil)
45
+ global_callbacks = SubClassMultiple.aasm(:left).state_machine.global_callbacks
46
+ expect(global_callbacks).to_not be_empty
47
+ expect(global_callbacks[:after_all_transitions]).to eq :left_after_all_event
48
+ end
49
+
50
+ it 'should allow the child to modify its right state machine' do
51
+ son = SubClassMultiple.new
52
+ expect(son.right_called_after).to eq(nil)
53
+ expect(son.left_called_after).to eq(nil)
54
+ son.close
55
+ expect(son.right_called_after).to eq(true)
56
+ expect(son.left_called_after).to eq(nil)
57
+ global_callbacks = SubClassMultiple.aasm(:right).state_machine.global_callbacks
58
+ expect(global_callbacks).to_not be_empty
59
+ expect(global_callbacks[:after_all_transitions]).to eq :right_after_all_event
60
+ end
39
61
 
62
+ it 'should not modify the parent left state machine' do
63
+ super_class_event = SuperClassMultiple.aasm(:left).events.select { |event| event.name == :foo }.first
64
+ expect(super_class_event.options).to be_empty
65
+ expect(SuperClassMultiple.aasm(:left).state_machine.global_callbacks).to be_empty
66
+ end
67
+
68
+ it 'should not modify the parent right state machine' do
69
+ super_class_event = SuperClassMultiple.aasm(:right).events.select { |event| event.name == :close }.first
70
+ expect(super_class_event.options).to be_empty
71
+ expect(SuperClassMultiple.aasm(:right).state_machine.global_callbacks).to be_empty
72
+ end
73
+
74
+ end