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,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", "4.2.5"
7
+ gem "nokogiri", "1.6.8.1", platforms: [:ruby_19]
8
+ gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
9
+ gem "mongoid", "~> 4.0"
10
+ gem "sequel"
11
+ gem "dynamoid", "~> 1", platforms: :ruby
12
+ gem "aws-sdk", "~> 2", platforms: :ruby
13
+ gem "redis-objects"
14
+ gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
15
+
16
+ gemspec path: "../"
@@ -0,0 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", "4.2.5"
7
+ gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
8
+ gem "mongoid", "~> 5.0"
9
+ gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
10
+
11
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", "4.2.5"
7
+ gem "nobrainer", "~> 0.33.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", "5.0.0"
7
+ gem "mongoid", "~> 6.0"
8
+ gem "sequel"
9
+ gem "dynamoid", "~> 1.3", platforms: :ruby
10
+ gem "aws-sdk", "~> 2", platforms: :ruby
11
+ gem "redis-objects"
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", "5.0.0"
7
+ gem "nobrainer", "~> 0.33.0"
8
+
9
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", "5.1"
7
+ gem "mongoid", "~>6.0"
8
+ gem "sequel"
9
+ gem "dynamoid", "~> 1.3", platforms: :ruby
10
+ gem "aws-sdk", "~>2", platforms: :ruby
11
+ gem "redis-objects"
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sqlite3", "~> 1.3.5", platforms: :ruby
6
+ gem "rails", "5.2"
7
+ gem "mongoid", "~>6.0"
8
+ gem "sequel"
9
+ gem "dynamoid", "~>2.2", platforms: :ruby
10
+ gem "aws-sdk", "~>2", platforms: :ruby
11
+ gem "redis-objects"
12
+
13
+ gemspec path: "../"
@@ -0,0 +1,23 @@
1
+ require 'ostruct'
2
+
3
+ require 'aasm/version'
4
+ require 'aasm/errors'
5
+ require 'aasm/configuration'
6
+ require 'aasm/base'
7
+ require 'aasm/dsl_helper'
8
+ require 'aasm/instance_base'
9
+ require 'aasm/core/transition'
10
+ require 'aasm/core/event'
11
+ require 'aasm/core/state'
12
+ require 'aasm/core/invoker'
13
+ require 'aasm/core/invokers/base_invoker'
14
+ require 'aasm/core/invokers/class_invoker'
15
+ require 'aasm/core/invokers/literal_invoker'
16
+ require 'aasm/core/invokers/proc_invoker'
17
+ require 'aasm/localizer'
18
+ require 'aasm/state_machine_store'
19
+ require 'aasm/state_machine'
20
+ require 'aasm/persistence'
21
+ require 'aasm/persistence/base'
22
+ require 'aasm/persistence/plain_persistence'
23
+ require 'aasm/aasm'
@@ -0,0 +1,208 @@
1
+ module AASM
2
+ # this is used internally as an argument default value to represent no value
3
+ NO_VALUE = :_aasm_no_value
4
+
5
+ # provide a state machine for the including class
6
+ # make sure to load class methods as well
7
+ # initialize persistence for the state machine
8
+ def self.included(base) #:nodoc:
9
+ base.extend AASM::ClassMethods
10
+
11
+ # do not overwrite existing state machines, which could have been created by
12
+ # inheritance, see class method inherited
13
+ AASM::StateMachineStore.register(base)
14
+
15
+ AASM::Persistence.load_persistence(base)
16
+ super
17
+ end
18
+
19
+ module ClassMethods
20
+ # make sure inheritance (aka subclassing) works with AASM
21
+ def inherited(base)
22
+ AASM::StateMachineStore.register(base, self)
23
+
24
+ super
25
+ end
26
+
27
+ # this is the entry point for all state and event definitions
28
+ def aasm(*args, &block)
29
+ if args[0].is_a?(Symbol) || args[0].is_a?(String)
30
+ # using custom name
31
+ state_machine_name = args[0].to_sym
32
+ options = args[1] || {}
33
+ else
34
+ # using the default state_machine_name
35
+ state_machine_name = :default
36
+ options = args[0] || {}
37
+ end
38
+
39
+ AASM::StateMachineStore.fetch(self, true).register(state_machine_name, AASM::StateMachine.new(state_machine_name))
40
+
41
+ # use a default despite the DSL configuration default.
42
+ # this is because configuration hasn't been setup for the AASM class but we are accessing a DSL option already for the class.
43
+ aasm_klass = options[:with_klass] || AASM::Base
44
+
45
+ raise ArgumentError, "The class #{aasm_klass} must inherit from AASM::Base!" unless aasm_klass.ancestors.include?(AASM::Base)
46
+
47
+ @aasm ||= Concurrent::Map.new
48
+ if @aasm[state_machine_name]
49
+ # make sure to use provided options
50
+ options.each do |key, value|
51
+ @aasm[state_machine_name].state_machine.config.send("#{key}=", value)
52
+ end
53
+ else
54
+ # create a new base
55
+ @aasm[state_machine_name] = aasm_klass.new(
56
+ self,
57
+ state_machine_name,
58
+ AASM::StateMachineStore.fetch(self, true).machine(state_machine_name),
59
+ options
60
+ )
61
+ end
62
+ @aasm[state_machine_name].instance_eval(&block) if block # new DSL
63
+ @aasm[state_machine_name]
64
+ end
65
+ end # ClassMethods
66
+
67
+ # this is the entry point for all instance-level access to AASM
68
+ def aasm(name=:default)
69
+ unless AASM::StateMachineStore.fetch(self.class, true).machine(name)
70
+ raise AASM::UnknownStateMachineError.new("There is no state machine with the name '#{name}' defined in #{self.class.name}!")
71
+ end
72
+ @aasm ||= Concurrent::Map.new
73
+ @aasm[name.to_sym] ||= AASM::InstanceBase.new(self, name.to_sym)
74
+ end
75
+
76
+ def initialize_dup(other)
77
+ @aasm = Concurrent::Map.new
78
+ super
79
+ end
80
+
81
+ private
82
+
83
+ # Takes args and a from state and removes the first
84
+ # element from args if it is a valid to_state for
85
+ # the event given the from_state
86
+ def process_args(event, from_state, *args)
87
+ # If the first arg doesn't respond to to_sym then
88
+ # it isn't a symbol or string so it can't be a state
89
+ # name anyway
90
+ return args unless args.first.respond_to?(:to_sym)
91
+ if event.transitions_from_state(from_state).map(&:to).flatten.include?(args.first)
92
+ return args[1..-1]
93
+ end
94
+ return args
95
+ end
96
+
97
+ def aasm_fire_event(state_machine_name, event_name, options, *args, &block)
98
+ event = self.class.aasm(state_machine_name).state_machine.events[event_name]
99
+ begin
100
+ old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
101
+
102
+ fire_default_callbacks(event, *process_args(event, aasm(state_machine_name).current_state, *args))
103
+
104
+ if may_fire_to = event.may_fire?(self, *args)
105
+ fire_exit_callbacks(old_state, *process_args(event, aasm(state_machine_name).current_state, *args))
106
+ if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
107
+ aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
108
+ else
109
+ aasm_failed(state_machine_name, event_name, old_state, event.failed_callbacks)
110
+ end
111
+ else
112
+ aasm_failed(state_machine_name, event_name, old_state, event.failed_callbacks)
113
+ end
114
+ rescue StandardError => e
115
+ event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
116
+ event.fire_global_callbacks(:error_on_all_events, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
117
+ raise(e)
118
+ false
119
+ ensure
120
+ event.fire_callbacks(:ensure, self, *process_args(event, aasm(state_machine_name).current_state, *args))
121
+ event.fire_global_callbacks(:ensure_on_all_events, self, *process_args(event, aasm(state_machine_name).current_state, *args))
122
+ end
123
+ end
124
+
125
+ def fire_default_callbacks(event, *processed_args)
126
+ event.fire_global_callbacks(
127
+ :before_all_events,
128
+ self,
129
+ *processed_args
130
+ )
131
+
132
+ # new event before callback
133
+ event.fire_callbacks(
134
+ :before,
135
+ self,
136
+ *processed_args
137
+ )
138
+ end
139
+
140
+ def fire_exit_callbacks(old_state, *processed_args)
141
+ old_state.fire_callbacks(:before_exit, self, *processed_args)
142
+ old_state.fire_callbacks(:exit, self, *processed_args)
143
+ end
144
+
145
+ def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
146
+ persist = options[:persist]
147
+
148
+ new_state = aasm(state_machine_name).state_object_for_name(new_state_name)
149
+ callback_args = process_args(event, aasm(state_machine_name).current_state, *args)
150
+
151
+ new_state.fire_callbacks(:before_enter, self, *callback_args)
152
+
153
+ new_state.fire_callbacks(:enter, self, *callback_args) # TODO: remove for AASM 4?
154
+
155
+ persist_successful = true
156
+ if persist
157
+ persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
158
+ if persist_successful
159
+ yield if block_given?
160
+ event.fire_callbacks(:before_success, self, *callback_args)
161
+ event.fire_transition_callbacks(self, *process_args(event, old_state.name, *args))
162
+ event.fire_callbacks(:success, self, *callback_args)
163
+ end
164
+ else
165
+ aasm(state_machine_name).current_state = new_state_name
166
+ yield if block_given?
167
+ end
168
+
169
+ binding_event = event.options[:binding_event]
170
+ if binding_event
171
+ __send__("#{binding_event}#{'!' if persist}")
172
+ end
173
+
174
+ if persist_successful
175
+ old_state.fire_callbacks(:after_exit, self, *callback_args)
176
+ new_state.fire_callbacks(:after_enter, self, *callback_args)
177
+ event.fire_callbacks(
178
+ :after,
179
+ self,
180
+ *process_args(event, old_state.name, *args)
181
+ )
182
+ event.fire_global_callbacks(
183
+ :after_all_events,
184
+ self,
185
+ *process_args(event, old_state.name, *args)
186
+ )
187
+
188
+ self.aasm_event_fired(event.name, old_state.name, aasm(state_machine_name).current_state) if self.respond_to?(:aasm_event_fired)
189
+ else
190
+ self.aasm_event_failed(event.name, old_state.name) if self.respond_to?(:aasm_event_failed)
191
+ end
192
+
193
+ persist_successful
194
+ end
195
+
196
+ def aasm_failed(state_machine_name, event_name, old_state, failures = [])
197
+ if self.respond_to?(:aasm_event_failed)
198
+ self.aasm_event_failed(event_name, old_state.name)
199
+ end
200
+
201
+ if AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.whiny_transitions
202
+ raise AASM::InvalidTransition.new(self, event_name, state_machine_name, failures)
203
+ else
204
+ false
205
+ end
206
+ end
207
+
208
+ end
@@ -0,0 +1,271 @@
1
+ require 'logger'
2
+
3
+ module AASM
4
+ class Base
5
+
6
+ attr_reader :klass, :state_machine
7
+
8
+ def initialize(klass, name, state_machine, options={}, &block)
9
+ @klass = klass
10
+ @name = name
11
+ # @state_machine = klass.aasm(@name).state_machine
12
+ @state_machine = state_machine
13
+ @state_machine.config.column ||= (options[:column] || default_column).to_sym
14
+ # @state_machine.config.column = options[:column].to_sym if options[:column] # master
15
+ @options = options
16
+
17
+ # let's cry if the transition is invalid
18
+ configure :whiny_transitions, true
19
+
20
+ # create named scopes for each state
21
+ configure :create_scopes, true
22
+
23
+ # don't store any new state if the model is invalid (in ActiveRecord)
24
+ configure :skip_validation_on_save, false
25
+
26
+ # raise if the model is invalid (in ActiveRecord)
27
+ configure :whiny_persistence, false
28
+
29
+ # Use transactions (in ActiveRecord)
30
+ configure :use_transactions, true
31
+
32
+ # use requires_new for nested transactions (in ActiveRecord)
33
+ configure :requires_new_transaction, true
34
+
35
+ # use pessimistic locking (in ActiveRecord)
36
+ # true for FOR UPDATE lock
37
+ # string for a specific lock type i.e. FOR UPDATE NOWAIT
38
+ configure :requires_lock, false
39
+
40
+ # set to true to forbid direct assignment of aasm_state column (in ActiveRecord)
41
+ configure :no_direct_assignment, false
42
+
43
+ # allow a AASM::Base sub-class to be used for state machine
44
+ configure :with_klass, AASM::Base
45
+
46
+ configure :enum, nil
47
+
48
+ # Set to true to namespace reader methods and constants
49
+ configure :namespace, false
50
+
51
+ # Configure a logger, with default being a Logger to STDERR
52
+ configure :logger, Logger.new(STDERR)
53
+
54
+ # make sure to raise an error if no_direct_assignment is enabled
55
+ # and attribute is directly assigned though
56
+ aasm_name = @name
57
+
58
+ if @state_machine.config.no_direct_assignment
59
+ @klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name|
60
+ if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
61
+ raise AASM::NoDirectAssignmentError.new('direct assignment of AASM column has been disabled (see AASM configuration for this class)')
62
+ else
63
+ super(state_name)
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ # This method is both a getter and a setter
70
+ def attribute_name(column_name=nil)
71
+ if column_name
72
+ @state_machine.config.column = column_name.to_sym
73
+ else
74
+ @state_machine.config.column ||= :aasm_state
75
+ end
76
+ @state_machine.config.column
77
+ end
78
+
79
+ def initial_state(new_initial_state=nil)
80
+ if new_initial_state
81
+ @state_machine.initial_state = new_initial_state
82
+ else
83
+ @state_machine.initial_state
84
+ end
85
+ end
86
+
87
+ # define a state
88
+ # args
89
+ # [0] state
90
+ # [1] options (or nil)
91
+ # or
92
+ # [0] state
93
+ # [1..] state
94
+ def state(*args)
95
+ names, options = interpret_state_args(args)
96
+ names.each do |name|
97
+ @state_machine.add_state(name, klass, options)
98
+
99
+ aasm_name = @name.to_sym
100
+ state = name.to_sym
101
+
102
+ method_name = namespace? ? "#{namespace}_#{name}" : name
103
+ safely_define_method klass, "#{method_name}?", -> do
104
+ aasm(aasm_name).current_state == state
105
+ end
106
+
107
+ const_name = namespace? ? "STATE_#{namespace.upcase}_#{name.upcase}" : "STATE_#{name.upcase}"
108
+ unless klass.const_defined?(const_name)
109
+ klass.const_set(const_name, name)
110
+ end
111
+ end
112
+ end
113
+
114
+ # define an event
115
+ def event(name, options={}, &block)
116
+ @state_machine.add_event(name, options, &block)
117
+
118
+ aasm_name = @name.to_sym
119
+ event = name.to_sym
120
+
121
+ # an addition over standard aasm so that, before firing an event, you can ask
122
+ # may_event? and get back a boolean that tells you whether the guard method
123
+ # on the transition will let this happen.
124
+ safely_define_method klass, "may_#{name}?", ->(*args) do
125
+ aasm(aasm_name).may_fire_event?(event, *args)
126
+ end
127
+
128
+ safely_define_method klass, "#{name}!", ->(*args, &block) do
129
+ aasm(aasm_name).current_event = :"#{name}!"
130
+ aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
131
+ end
132
+
133
+ safely_define_method klass, name, ->(*args, &block) do
134
+ aasm(aasm_name).current_event = event
135
+ aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block)
136
+ end
137
+
138
+ skip_instance_level_validation(event, name, aasm_name, klass)
139
+
140
+ # Create aliases for the event methods. Keep the old names to maintain backwards compatibility.
141
+ if namespace?
142
+ klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?")
143
+ klass.send(:alias_method, "#{name}_#{namespace}!", "#{name}!")
144
+ klass.send(:alias_method, "#{name}_#{namespace}", name)
145
+ end
146
+
147
+ end
148
+
149
+ def after_all_transitions(*callbacks, &block)
150
+ @state_machine.add_global_callbacks(:after_all_transitions, *callbacks, &block)
151
+ end
152
+
153
+ def after_all_transactions(*callbacks, &block)
154
+ @state_machine.add_global_callbacks(:after_all_transactions, *callbacks, &block)
155
+ end
156
+
157
+ def before_all_transactions(*callbacks, &block)
158
+ @state_machine.add_global_callbacks(:before_all_transactions, *callbacks, &block)
159
+ end
160
+
161
+ def before_all_events(*callbacks, &block)
162
+ @state_machine.add_global_callbacks(:before_all_events, *callbacks, &block)
163
+ end
164
+
165
+ def after_all_events(*callbacks, &block)
166
+ @state_machine.add_global_callbacks(:after_all_events, *callbacks, &block)
167
+ end
168
+
169
+ def error_on_all_events(*callbacks, &block)
170
+ @state_machine.add_global_callbacks(:error_on_all_events, *callbacks, &block)
171
+ end
172
+
173
+ def ensure_on_all_events(*callbacks, &block)
174
+ @state_machine.add_global_callbacks(:ensure_on_all_events, *callbacks, &block)
175
+ end
176
+
177
+ def states
178
+ @state_machine.states
179
+ end
180
+
181
+ def events
182
+ @state_machine.events.values
183
+ end
184
+
185
+ # aasm.event(:event_name).human?
186
+ def human_event_name(event) # event_name?
187
+ AASM::Localizer.new.human_event_name(klass, event)
188
+ end
189
+
190
+ def states_for_select
191
+ states.map { |state| state.for_select }
192
+ end
193
+
194
+ def from_states_for_state(state, options={})
195
+ if options[:transition]
196
+ @state_machine.events[options[:transition]].transitions_to_state(state).flatten.map(&:from).flatten
197
+ else
198
+
199
+ events.map {|e| e.transitions_to_state(state)}.flatten.map(&:from).flatten
200
+ end
201
+ end
202
+
203
+ private
204
+
205
+ def default_column
206
+ @name.to_sym == :default ? :aasm_state : @name.to_sym
207
+ end
208
+
209
+ def configure(key, default_value)
210
+ if @options.key?(key)
211
+ @state_machine.config.send("#{key}=", @options[key])
212
+ elsif @state_machine.config.send(key).nil?
213
+ @state_machine.config.send("#{key}=", default_value)
214
+ end
215
+ end
216
+
217
+ def safely_define_method(klass, method_name, method_definition)
218
+ # Warn if method exists and it did not originate from an enum
219
+ if klass.method_defined?(method_name) &&
220
+ ! ( @state_machine.config.enum &&
221
+ klass.respond_to?(:defined_enums) &&
222
+ klass.defined_enums.values.any?{ |methods|
223
+ methods.keys{| enum | enum + '?' == method_name }
224
+ })
225
+ unless AASM::Configuration.hide_warnings
226
+ @state_machine.config.logger.warn "#{klass.name}: overriding method '#{method_name}'!"
227
+ end
228
+ end
229
+
230
+ klass.send(:define_method, method_name, method_definition)
231
+ end
232
+
233
+ def namespace?
234
+ !!@state_machine.config.namespace
235
+ end
236
+
237
+ def namespace
238
+ if @state_machine.config.namespace == true
239
+ @name
240
+ else
241
+ @state_machine.config.namespace
242
+ end
243
+ end
244
+
245
+ def interpret_state_args(args)
246
+ if args.last.is_a?(Hash) && args.size == 2
247
+ [[args.first], args.last]
248
+ elsif args.size > 0
249
+ [args, {}]
250
+ else
251
+ raise "count not parse states: #{args}"
252
+ end
253
+ end
254
+
255
+ def skip_instance_level_validation(event, name, aasm_name, klass)
256
+ # Overrides the skip_validation config for an instance (If skip validation is set to false in original config) and
257
+ # restores it back to the original value after the event is fired.
258
+ safely_define_method klass, "#{name}_without_validation!", ->(*args, &block) do
259
+ original_config = AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save
260
+ begin
261
+ AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = true unless original_config
262
+ aasm(aasm_name).current_event = :"#{name}!"
263
+ aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
264
+ ensure
265
+ AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = original_config
266
+ end
267
+ end
268
+ end
269
+
270
+ end
271
+ end