aasm 5.0.8

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