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,27 @@
1
+ require 'spec_helper'
2
+ if defined?(ActiveRecord)
3
+ require 'models/active_record/person'
4
+
5
+ load_schema
6
+ describe 'Abstract subclassing' do
7
+
8
+ it 'should have the parent states' do
9
+ Person.aasm.states.each do |state|
10
+ expect(Base.aasm.states).to include(state)
11
+ end
12
+ expect(Person.aasm.states).to eq(Base.aasm.states)
13
+ end
14
+
15
+ it 'should have the same events as its parent' do
16
+ expect(Base.aasm.events).to eq(Person.aasm.events)
17
+ end
18
+
19
+ it 'should not break aasm methods when super class is abstract_class' do
20
+ person = Person.new
21
+ person.status = 'active'
22
+ person.deactivate!
23
+ expect(person.aasm.current_state).to eq(:inactive)
24
+ end
25
+
26
+ end
27
+ end
@@ -1,97 +1,104 @@
1
1
  require 'spec_helper'
2
- require 'models/default_state.rb'
3
- require 'models/provided_state.rb'
4
- require 'models/active_record/persisted_state.rb'
5
- require 'models/active_record/provided_and_persisted_state.rb'
6
2
 
7
- load_schema
3
+ if defined?(ActiveRecord)
4
+ require 'models/default_state.rb'
5
+ require 'models/provided_state.rb'
6
+ require 'models/active_record/persisted_state.rb'
7
+ require 'models/active_record/provided_and_persisted_state.rb'
8
8
 
9
- describe "reading the current state" do
10
- it "uses the AASM default" do
11
- expect(DefaultState.new.aasm.current_state).to eql :alpha
12
- end
9
+ load_schema
13
10
 
14
- it "uses the provided method" do
15
- expect(ProvidedState.new.aasm.current_state).to eql :beta
16
- end
11
+ describe "reading the current state" do
12
+ it "uses the AASM default" do
13
+ expect(DefaultState.new.aasm.current_state).to eql :alpha
14
+ end
17
15
 
18
- it "uses the persistence storage" do
19
- expect(PersistedState.new.aasm.current_state).to eql :alpha
20
- end
16
+ it "uses display option" do
17
+ expect(DefaultState.new.aasm.human_state).to eql "ALPHA"
18
+ end
21
19
 
22
- it "uses the provided method even if persisted" do
23
- expect(ProvidedAndPersistedState.new.aasm.current_state).to eql :gamma
24
- end
20
+ it "uses the provided method" do
21
+ expect(ProvidedState.new.aasm.current_state).to eql :beta
22
+ end
25
23
 
26
- context "after dup" do
27
24
  it "uses the persistence storage" do
28
- source = PersistedState.create!
29
- copy = source.dup
30
- copy.save!
25
+ expect(PersistedState.new.aasm.current_state).to eql :alpha
26
+ end
31
27
 
32
- copy.release!
28
+ it "uses the provided method even if persisted" do
29
+ expect(ProvidedAndPersistedState.new.aasm.current_state).to eql :gamma
30
+ end
31
+
32
+ context "after dup" do
33
+ it "uses the persistence storage" do
34
+ source = PersistedState.create!
35
+ copy = source.dup
36
+ copy.save!
33
37
 
34
- expect(source.aasm_state).to eql 'alpha'
35
- expect(source.aasm.current_state).to eql :alpha
38
+ copy.release!
36
39
 
37
- source2 = PersistedState.find(source.id)
38
- expect(source2.reload.aasm_state).to eql 'alpha'
39
- expect(source2.aasm.current_state).to eql :alpha
40
+ expect(source.aasm_state).to eql 'alpha'
41
+ expect(source.aasm.current_state).to eql :alpha
40
42
 
41
- expect(copy.aasm_state).to eql 'beta'
42
- expect(copy.aasm.current_state).to eql :beta
43
+ source2 = PersistedState.find(source.id)
44
+ expect(source2.reload.aasm_state).to eql 'alpha'
45
+ expect(source2.aasm.current_state).to eql :alpha
46
+
47
+ expect(copy.aasm_state).to eql 'beta'
48
+ expect(copy.aasm.current_state).to eql :beta
49
+ end
43
50
  end
44
51
  end
45
- end
46
52
 
47
- describe "writing and persisting the current state" do
48
- it "uses the AASM default" do
49
- o = DefaultState.new
50
- o.release!
51
- expect(o.persisted_store).to be_nil
52
- end
53
+ describe "writing and persisting the current state" do
54
+ it "uses the AASM default" do
55
+ o = DefaultState.new
56
+ o.release!
57
+ expect(o.persisted_store).to be_nil
58
+ end
53
59
 
54
- it "uses the provided method" do
55
- o = ProvidedState.new
56
- o.release!
57
- expect(o.persisted_store).to eql :beta
58
- end
60
+ it "uses the provided method" do
61
+ o = ProvidedState.new
62
+ o.release!
63
+ expect(o.persisted_store).to eql :beta
64
+ end
59
65
 
60
- it "uses the persistence storage" do
61
- o = PersistedState.new
62
- o.release!
63
- expect(o.persisted_store).to be_nil
64
- end
66
+ it "uses the persistence storage" do
67
+ o = PersistedState.new
68
+ o.release!
69
+ expect(o.persisted_store).to be_nil
70
+ end
65
71
 
66
- it "uses the provided method even if persisted" do
67
- o = ProvidedAndPersistedState.new
68
- o.release!
69
- expect(o.persisted_store).to eql :beta
72
+ it "uses the provided method even if persisted" do
73
+ o = ProvidedAndPersistedState.new
74
+ o.release!
75
+ expect(o.persisted_store).to eql :beta
76
+ end
70
77
  end
71
- end
72
78
 
73
- describe "writing the current state without persisting it" do
74
- it "uses the AASM default" do
75
- o = DefaultState.new
76
- o.release
77
- expect(o.transient_store).to be_nil
78
- end
79
+ describe "writing the current state without persisting it" do
80
+ it "uses the AASM default" do
81
+ o = DefaultState.new
82
+ o.release
83
+ expect(o.transient_store).to be_nil
84
+ end
79
85
 
80
- it "uses the provided method" do
81
- o = ProvidedState.new
82
- o.release
83
- expect(o.transient_store).to eql :beta
84
- end
86
+ it "uses the provided method" do
87
+ o = ProvidedState.new
88
+ o.release
89
+ expect(o.transient_store).to eql :beta
90
+ end
85
91
 
86
- it "uses the persistence storage" do
87
- o = PersistedState.new
88
- o.release
89
- expect(o.transient_store).to be_nil
90
- end
92
+ it "uses the persistence storage" do
93
+ o = PersistedState.new
94
+ o.release
95
+ expect(o.transient_store).to be_nil
96
+ end
91
97
 
92
- it "uses the provided method even if persisted" do
93
- o = ProvidedAndPersistedState.new
94
- o.release
95
- expect(o.transient_store).to eql :beta
98
+ it "uses the provided method even if persisted" do
99
+ o = ProvidedAndPersistedState.new
100
+ o.release
101
+ expect(o.transient_store).to eql :beta
102
+ end
96
103
  end
97
104
  end
@@ -49,7 +49,7 @@ describe 'callbacks for the new DSL' do
49
49
 
50
50
  expect {
51
51
  callback.left_close!
52
- }.to raise_error(AASM::InvalidTransition, "Event 'left_close' cannot transition from 'open'. Failed callback(s): [:after_transition, :event_guard].")
52
+ }.to raise_error(AASM::InvalidTransition, "Event 'left_close' cannot transition from 'open'. Failed callback(s): [:event_guard].")
53
53
 
54
54
  end
55
55
 
@@ -88,7 +88,7 @@ describe 'callbacks for the new DSL' do
88
88
 
89
89
  expect {
90
90
  callback.left_close!
91
- }.to raise_error(AASM::InvalidTransition, "Event 'left_close' cannot transition from 'open'. Failed callback(s): [:after_transition, :event_guard, :transition_guard].")
91
+ }.to raise_error(AASM::InvalidTransition, "Event 'left_close' cannot transition from 'open'. Failed callback(s): [:transition_guard].")
92
92
  end
93
93
 
94
94
  it "does not run transition_guard twice for multiple permitted transitions" do
@@ -154,6 +154,8 @@ describe 'callbacks for the new DSL' do
154
154
  expect(cb).to receive(:before_method).with(:arg1).once.ordered
155
155
  expect(cb).to receive(:transition_method).never
156
156
  expect(cb).to receive(:transition_method2).with(:arg1).once.ordered
157
+ expect(cb).to receive(:before_success_method).with(:arg1).once.ordered
158
+ expect(cb).to receive(:success_method).with(:arg1).once.ordered
157
159
  expect(cb).to receive(:after_method).with(:arg1).once.ordered
158
160
  cb.close!(:out_to_lunch, :arg1)
159
161
 
@@ -161,6 +163,8 @@ describe 'callbacks for the new DSL' do
161
163
  some_object = double('some object')
162
164
  expect(cb).to receive(:before_method).with(some_object).once.ordered
163
165
  expect(cb).to receive(:transition_method2).with(some_object).once.ordered
166
+ expect(cb).to receive(:before_success_method).with(some_object).once.ordered
167
+ expect(cb).to receive(:success_method).with(some_object).once.ordered
164
168
  expect(cb).to receive(:after_method).with(some_object).once.ordered
165
169
  cb.close!(:out_to_lunch, some_object)
166
170
  end
@@ -287,7 +291,7 @@ describe 'event callbacks' do
287
291
  expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
288
292
  expect{
289
293
  @foo.null
290
- }.to raise_error(AASM::InvalidTransition, "Event 'null' cannot transition from 'open'. Failed callback(s): [:always_false, :always_false].")
294
+ }.to raise_error(AASM::InvalidTransition, "Event 'null' cannot transition from 'open'. Failed callback(s): [:always_false].")
291
295
  end
292
296
 
293
297
  it 'should not call it if persist fails for bang fire' do
@@ -3,14 +3,14 @@ Dir[File.dirname(__FILE__) + "/../models/callbacks/*.rb"].sort.each { |f| requir
3
3
 
4
4
  shared_examples 'an implemented callback that accepts error' do
5
5
  context 'with callback defined' do
6
- it "should run error_callback if an exception is raised" do
6
+ it "should run error_callback if an exception is raised and always return false" do
7
7
  aasm_model.class.send(:define_method, callback_name) do |e|
8
8
  @data = [e]
9
9
  end
10
10
 
11
11
  allow(aasm_model).to receive(:before_enter).and_raise(e = StandardError.new)
12
12
 
13
- aasm_model.safe_close!
13
+ expect(aasm_model.safe_close!).to be false
14
14
  expect(aasm_model.data).to eql [e]
15
15
  end
16
16
 
@@ -118,6 +118,7 @@ describe 'callbacks for the new DSL' do
118
118
  expect(callback).to receive(:before_enter_closed).once.ordered
119
119
  expect(callback).to receive(:enter_closed).once.ordered
120
120
  expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
121
+ expect(callback).to receive(:event_before_success).once.ordered
121
122
  expect(callback).to receive(:success_transition).once.ordered.and_return(true) # these should be after the state changes
122
123
  expect(callback).to receive(:after_exit_open).once.ordered
123
124
  expect(callback).to receive(:after_enter_closed).once.ordered
@@ -143,6 +144,7 @@ describe 'callbacks for the new DSL' do
143
144
  expect(callback).to_not receive(:before_enter_closed)
144
145
  expect(callback).to_not receive(:enter_closed)
145
146
  expect(callback).to_not receive(:aasm_write_state)
147
+ expect(callback).to_not receive(:event_before_success)
146
148
  expect(callback).to_not receive(:success_transition)
147
149
  expect(callback).to_not receive(:after_exit_open)
148
150
  expect(callback).to_not receive(:after_enter_closed)
@@ -186,6 +188,7 @@ describe 'callbacks for the new DSL' do
186
188
  expect(callback).to_not receive(:before_enter_closed)
187
189
  expect(callback).to_not receive(:enter_closed)
188
190
  expect(callback).to_not receive(:aasm_write_state)
191
+ expect(callback).to_not receive(:event_before_success)
189
192
  expect(callback).to_not receive(:success_transition)
190
193
  expect(callback).to_not receive(:after_exit_open)
191
194
  expect(callback).to_not receive(:after_enter_closed)
@@ -200,6 +203,32 @@ describe 'callbacks for the new DSL' do
200
203
  }.to raise_error(AASM::InvalidTransition)
201
204
  end
202
205
 
206
+ it "does not propagate failures to next attempt of same transition" do
207
+ callback = Callbacks::Basic.new(:log => false, :fail_transition_guard => true)
208
+
209
+ expect {
210
+ callback.close!
211
+ }.to raise_error(AASM::InvalidTransition, "Event 'close' cannot transition from 'open'. Failed callback(s): [:transition_guard].")
212
+
213
+ expect {
214
+ callback.close!
215
+ }.to raise_error(AASM::InvalidTransition, "Event 'close' cannot transition from 'open'. Failed callback(s): [:transition_guard].")
216
+ end
217
+
218
+ it "does not propagate failures to next attempt of same event when no transition is applicable" do
219
+ callback = Callbacks::Basic.new(:log => false, :fail_transition_guard => true)
220
+
221
+ expect {
222
+ callback.close!
223
+ }.to raise_error(AASM::InvalidTransition, "Event 'close' cannot transition from 'open'. Failed callback(s): [:transition_guard].")
224
+
225
+ callback.aasm.current_state = :closed
226
+
227
+ expect {
228
+ callback.close!
229
+ }.to raise_error(AASM::InvalidTransition, "Event 'close' cannot transition from 'closed'.")
230
+ end
231
+
203
232
  it "does not run transition_guard twice for multiple permitted transitions" do
204
233
  show_debug_log = false
205
234
  callback = Callbacks::MultipleTransitionsTransitionGuard.new(:log => show_debug_log, :fail_transition_guard => true)
@@ -217,6 +246,7 @@ describe 'callbacks for the new DSL' do
217
246
  expect(callback).to receive(:after).once.ordered
218
247
 
219
248
  expect(callback).to_not receive(:transitioning)
249
+ expect(callback).to_not receive(:event_before_success)
220
250
  expect(callback).to_not receive(:success_transition)
221
251
  expect(callback).to_not receive(:before_enter_closed)
222
252
  expect(callback).to_not receive(:enter_closed)
@@ -240,6 +270,7 @@ describe 'callbacks for the new DSL' do
240
270
  expect(callback).to_not receive(:before_enter_closed)
241
271
  expect(callback).to_not receive(:enter_closed)
242
272
  expect(callback).to_not receive(:aasm_write_state)
273
+ expect(callback).to_not receive(:event_before_success)
243
274
  expect(callback).to_not receive(:success_transition)
244
275
  expect(callback).to_not receive(:after_exit_open)
245
276
  expect(callback).to_not receive(:after_enter_closed)
@@ -284,7 +315,9 @@ describe 'callbacks for the new DSL' do
284
315
  expect(cb).to receive(:before_method).with(:arg1).once.ordered
285
316
  expect(cb).to receive(:transition_method).with(:arg1).once.ordered
286
317
  expect(cb).to receive(:transition_method).never
318
+ expect(cb).to receive(:before_success_method).with(:arg1).once.ordered
287
319
  expect(cb).to receive(:success_method).with(:arg1).once.ordered
320
+ expect(cb).to receive(:success_method3).with(:arg1).once.ordered
288
321
  expect(cb).to receive(:success_method).never
289
322
  expect(cb).to receive(:after_method).with(:arg1).once.ordered
290
323
  cb.close!(:arg1)
@@ -294,7 +327,9 @@ describe 'callbacks for the new DSL' do
294
327
  expect(cb).to receive(:before_method).with(some_object).once.ordered
295
328
  expect(cb).to receive(:transition_method).with(some_object).once.ordered
296
329
  expect(cb).to receive(:transition_method).never
330
+ expect(cb).to receive(:before_success_method).with(some_object).once.ordered
297
331
  expect(cb).to receive(:success_method).with(some_object).once.ordered
332
+ expect(cb).to receive(:success_method3).with(some_object).once.ordered
298
333
  expect(cb).to receive(:success_method).never
299
334
  expect(cb).to receive(:after_method).with(some_object).once.ordered
300
335
  cb.close!(some_object)
@@ -30,14 +30,14 @@ describe 'when being unsuspended' do
30
30
  it 'should be able to be unsuspended into active if polite' do
31
31
  auth.suspend!
32
32
  expect(auth.may_wait?(:waiting, :please)).to be true
33
- auth.wait!(nil, :please)
33
+ auth.wait!(:please)
34
34
  end
35
35
 
36
36
  it 'should not be able to be unsuspended into active if not polite' do
37
37
  auth.suspend!
38
38
  expect(auth.may_wait?(:waiting)).not_to be true
39
39
  expect(auth.may_wait?(:waiting, :rude)).not_to be true
40
- expect {auth.wait!(nil, :rude)}.to raise_error(AASM::InvalidTransition)
40
+ expect {auth.wait!(:rude)}.to raise_error(AASM::InvalidTransition)
41
41
  expect {auth.wait!}.to raise_error(AASM::InvalidTransition)
42
42
  end
43
43
 
@@ -77,8 +77,17 @@ describe 'when being unsuspended' do
77
77
  expect(auth.aasm.may_fire_event?(:activate)).to be true
78
78
  end
79
79
 
80
+ it "should be able to fire event by name" do
81
+ expect(auth.aasm.fire(:activate)).to be true
82
+ expect(auth.aasm.current_state).to eq(:active)
83
+ end
84
+
85
+ it "should be able to fire! event by name" do
86
+ expect(auth.aasm.fire!(:activate)).to be true
87
+ expect(auth.aasm.current_state).to eq(:active)
88
+ end
89
+
80
90
  it "should not be able to fire unknown events" do
81
91
  expect(auth.aasm.may_fire_event?(:unknown)).to be false
82
92
  end
83
-
84
93
  end
@@ -39,18 +39,18 @@ describe 'when being unsuspended' do
39
39
  it 'should be able to wait into waiting if polite' do
40
40
  auth.left_suspend!
41
41
  expect(auth.may_left_wait?(:waiting, :please)).to be true
42
- auth.left_wait!(nil, :please)
42
+ auth.left_wait!(:please)
43
43
 
44
44
  auth.right_suspend!
45
45
  expect(auth.may_right_wait?(:waiting)).to be false
46
- auth.right_wait!(nil, :please)
46
+ auth.right_wait!(:please)
47
47
  end
48
48
 
49
49
  it 'should not be able to be unsuspended into active if not polite' do
50
50
  auth.left_suspend!
51
51
  expect(auth.may_left_wait?(:waiting)).not_to be true
52
52
  expect(auth.may_left_wait?(:waiting, :rude)).not_to be true
53
- expect {auth.left_wait!(nil, :rude)}.to raise_error(AASM::InvalidTransition)
53
+ expect {auth.left_wait!(:rude)}.to raise_error(AASM::InvalidTransition)
54
54
  expect {auth.left_wait!}.to raise_error(AASM::InvalidTransition)
55
55
  end
56
56
 
@@ -91,9 +91,25 @@ describe 'when being unsuspended' do
91
91
  expect(auth.aasm(:right).may_fire_event?(:right_activate)).to be true
92
92
  end
93
93
 
94
- it "should not be able to fire unknown events" do
94
+ it 'should not be able to fire unknown events' do
95
95
  expect(auth.aasm(:left).may_fire_event?(:unknown)).to be false
96
96
  expect(auth.aasm(:right).may_fire_event?(:unknown)).to be false
97
97
  end
98
98
 
99
+ it 'should be able to fire event by name' do
100
+ expect(auth.aasm(:left).fire(:left_activate)).to be true
101
+ expect(auth.aasm(:left).current_state).to eq(:active)
102
+
103
+ expect(auth.aasm(:right).fire(:right_activate)).to be true
104
+ expect(auth.aasm(:right).current_state).to eq(:active)
105
+ end
106
+
107
+ it 'should be able to fire! event by name' do
108
+ expect(auth.aasm(:left).fire!(:left_activate)).to be true
109
+ expect(auth.aasm(:left).current_state).to eq(:active)
110
+
111
+ expect(auth.aasm(:right).fire!(:right_activate)).to be true
112
+ expect(auth.aasm(:right).current_state).to eq(:active)
113
+ end
114
+
99
115
  end
@@ -37,7 +37,7 @@ describe 'parametrised events' do
37
37
  end
38
38
 
39
39
  it 'should transition to default state when :after transition invoked' do
40
- pe.dress!(nil, 'purple', 'dressy')
40
+ pe.dress!('purple', 'dressy')
41
41
  expect(pe.aasm(:left).current_state).to eq(:working)
42
42
  end
43
43
 
@@ -106,7 +106,7 @@ describe 'firing an event' do
106
106
  obj = double('object', :aasm => double('aasm', :current_state => :open))
107
107
  expect(obj).to receive(:guard_fn).with('arg1', 'arg2').and_return(true)
108
108
 
109
- expect(event.fire(obj, {}, nil, 'arg1', 'arg2')).to eq(:closed)
109
+ expect(event.fire(obj, {}, 'arg1', 'arg2')).to eq(:closed)
110
110
  end
111
111
 
112
112
  context 'when given a gaurd proc' do
@@ -118,7 +118,7 @@ describe 'firing an event' do
118
118
  line_number = __LINE__ - 2
119
119
  obj = double('object', :aasm => double('aasm', :current_state => :student))
120
120
 
121
- event.fire(obj, {}, nil)
121
+ event.fire(obj, {})
122
122
  expect(event.failed_callbacks).to eq ["#{__FILE__}##{line_number}"]
123
123
  end
124
124
  end
@@ -133,7 +133,7 @@ describe 'firing an event' do
133
133
  obj = double('object', :aasm => double('aasm', :current_state => :student))
134
134
  allow(obj).to receive(:paid_tuition?).and_return(false)
135
135
 
136
- event.fire(obj, {}, nil)
136
+ event.fire(obj, {})
137
137
  expect(event.failed_callbacks).to eq [:paid_tuition?]
138
138
  end
139
139
  end
@@ -294,6 +294,19 @@ describe 'current event' do
294
294
  pe.wakeup!
295
295
  expect(pe.aasm.current_event).to eql :wakeup!
296
296
  end
297
+
298
+ describe "when calling events with fire/fire!" do
299
+ it "fire should populate aasm.current_event and transition (sleeping to showering)" do
300
+ pe.aasm.fire(:wakeup)
301
+ expect(pe.aasm.current_event).to eq :wakeup
302
+ expect(pe.aasm.current_state).to eq :showering
303
+ end
304
+ it "fire! should populate aasm.current_event and transition (sleeping to showering)" do
305
+ pe.aasm.fire!(:wakeup)
306
+ expect(pe.aasm.current_event).to eq :wakeup!
307
+ expect(pe.aasm.current_state).to eq :showering
308
+ end
309
+ end
297
310
  end
298
311
 
299
312
  describe 'parametrised events' do
@@ -315,7 +328,7 @@ describe 'parametrised events' do
315
328
  end
316
329
 
317
330
  it 'should transition to default state when :after transition invoked' do
318
- pe.dress!(nil, 'purple', 'dressy')
331
+ pe.dress!('purple', 'dressy')
319
332
  expect(pe.aasm.current_state).to eq(:working)
320
333
  end
321
334
 
@@ -325,6 +338,12 @@ describe 'parametrised events' do
325
338
  pe.dress!(:working, 'blue', 'jeans')
326
339
  end
327
340
 
341
+ it 'should call :after transition method if arg is nil' do
342
+ dryer = nil
343
+ expect(pe).to receive(:wet_hair).with(dryer)
344
+ pe.shower!(dryer)
345
+ end
346
+
328
347
  it 'should call :after transition proc' do
329
348
  pe.wakeup!(:showering)
330
349
  expect(pe).to receive(:wear_clothes).with('purple', 'slacks')
@@ -344,6 +363,12 @@ describe 'parametrised events' do
344
363
  pe.dress!(:working, 'foundation', 'SPF')
345
364
  end
346
365
 
366
+ it 'should call :success transition method if arg is nil' do
367
+ shirt_color = nil
368
+ expect(pe).to receive(:wear_clothes).with(shirt_color)
369
+ pe.shower!(shirt_color)
370
+ end
371
+
347
372
  it 'should call :success transition proc' do
348
373
  pe.wakeup!(:showering)
349
374
  expect(pe).to receive(:wear_makeup).with('purple', 'slacks')
@@ -5,7 +5,7 @@ describe AASM::InvalidTransition do
5
5
  process = ProcessWithNewDsl.new
6
6
  expect { process.stop! }.to raise_error do |err|
7
7
  process.start
8
- expect(err.message).to eql("Event 'stop' cannot transition from 'sleeping'. ")
8
+ expect(err.message).to eql("Event 'stop' cannot transition from 'sleeping'.")
9
9
  end
10
10
  end
11
11
  end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe "nil as first argument" do
4
+ let(:guard) { GuardArgumentsCheck.new }
5
+
6
+ it 'does not raise errors' do
7
+ expect { guard.mark_as_reviewed(nil, 'second arg') }.not_to raise_error
8
+ end
9
+ end
@@ -70,3 +70,20 @@ describe "event guards" do
70
70
  end
71
71
 
72
72
  end
73
+
74
+ if defined?(ActiveRecord)
75
+
76
+ Dir[File.dirname(__FILE__) + "/../models/active_record/*.rb"].sort.each do |f|
77
+ require File.expand_path(f)
78
+ end
79
+
80
+ load_schema
81
+
82
+ describe "ActiveRecord per-transition guards" do
83
+ let(:example) { ComplexActiveRecordExample.new }
84
+
85
+ it "should be able to increment" do
86
+ expect(example.may_increment?).to be true
87
+ end
88
+ end
89
+ end
@@ -7,4 +7,8 @@ describe "guards with params" do
7
7
  it "list permitted states" do
8
8
  expect(guard.aasm.states({:permitted => true}, user).map(&:name)).to eql [:reviewed]
9
9
  end
10
+
11
+ it "list no states if user is blank" do
12
+ expect(guard.aasm.states({:permitted => true}, nil).map(&:name)).to eql []
13
+ end
10
14
  end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe "transitions without from specified" do
4
+ let(:guardian) { GuardianWithoutFromSpecified.new }
5
+
6
+ it "allows the transitions if guard succeeds" do
7
+ expect { guardian.use_guards_where_the_first_fails! }.to_not raise_error
8
+ expect(guardian).to be_gamma
9
+ end
10
+ end
@@ -166,11 +166,15 @@ describe "special cases" do
166
166
  end
167
167
 
168
168
  describe 'aasm.states_for_select' do
169
- it "should return a select friendly array of states" do
170
- expect(FooMultiple.aasm(:left)).to respond_to(:states_for_select)
171
- expect(FooMultiple.aasm(:left).states_for_select).to eq(
172
- [['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']]
173
- )
169
+ context 'without I18n' do
170
+ before { allow(Module).to receive(:const_defined?).with(:I18n).and_return(nil) }
171
+
172
+ it "should return a select friendly array of states" do
173
+ expect(FooMultiple.aasm(:left)).to respond_to(:states_for_select)
174
+ expect(FooMultiple.aasm(:left).states_for_select).to eq(
175
+ [['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']]
176
+ )
177
+ end
174
178
  end
175
179
  end
176
180
 
@@ -102,9 +102,13 @@ describe "special cases" do
102
102
  end
103
103
 
104
104
  describe 'aasm.states_for_select' do
105
- it "should return a select friendly array of states" do
106
- expect(Foo.aasm).to respond_to(:states_for_select)
107
- expect(Foo.aasm.states_for_select).to eq([['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']])
105
+ context 'without I18n' do
106
+ before { allow(Module).to receive(:const_defined?).with(:I18n).and_return(nil) }
107
+
108
+ it "should return a select friendly array of states" do
109
+ expect(Foo.aasm).to respond_to(:states_for_select)
110
+ expect(Foo.aasm.states_for_select).to eq([['Open', 'open'], ['Closed', 'closed'], ['Final', 'final']])
111
+ end
108
112
  end
109
113
  end
110
114