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,45 @@
1
+ module AASM
2
+ class Configuration
3
+ # for all persistence layers: which database column to use?
4
+ attr_accessor :column
5
+
6
+ # let's cry if the transition is invalid
7
+ attr_accessor :whiny_transitions
8
+
9
+ # for all persistence layers: create named scopes for each state
10
+ attr_accessor :create_scopes
11
+
12
+ # for ActiveRecord: when the model is invalid, true -> raise, false -> return false
13
+ attr_accessor :whiny_persistence
14
+
15
+ # for ActiveRecord: store the new state even if the model is invalid and return true
16
+ attr_accessor :skip_validation_on_save
17
+
18
+ # for ActiveRecord: use transactions
19
+ attr_accessor :use_transactions
20
+
21
+ # for ActiveRecord: use requires_new for nested transactions?
22
+ attr_accessor :requires_new_transaction
23
+
24
+ # for ActiveRecord: use pessimistic locking
25
+ attr_accessor :requires_lock
26
+
27
+ # forbid direct assignment in aasm_state column (in ActiveRecord)
28
+ attr_accessor :no_direct_assignment
29
+
30
+ # allow a AASM::Base sub-class to be used for state machine
31
+ attr_accessor :with_klass
32
+
33
+ attr_accessor :enum
34
+
35
+ # namespace reader methods and constants
36
+ attr_accessor :namespace
37
+
38
+ # Configure a logger, with default being a Logger to STDERR
39
+ attr_accessor :logger
40
+
41
+ class << self
42
+ attr_accessor :hide_warnings
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM::Core
4
+ class Event
5
+ include DslHelper
6
+
7
+ attr_reader :name, :state_machine, :options
8
+
9
+ def initialize(name, state_machine, options = {}, &block)
10
+ @name = name
11
+ @state_machine = state_machine
12
+ @transitions = []
13
+ @valid_transitions = {}
14
+ @guards = Array(options[:guard] || options[:guards] || options[:if])
15
+ @unless = Array(options[:unless]) #TODO: This could use a better name
16
+
17
+ # from aasm4
18
+ @options = options # QUESTION: .dup ?
19
+ add_options_from_dsl(@options, [
20
+ :after,
21
+ :after_commit,
22
+ :after_transaction,
23
+ :before,
24
+ :before_transaction,
25
+ :ensure,
26
+ :error,
27
+ :before_success,
28
+ :success,
29
+ ], &block) if block
30
+ end
31
+
32
+ # called internally by Ruby 1.9 after clone()
33
+ def initialize_copy(orig)
34
+ super
35
+ @transitions = @transitions.collect { |transition| transition.clone }
36
+ @guards = @guards.dup
37
+ @unless = @unless.dup
38
+ @options = {}
39
+ orig.options.each_pair { |name, setting| @options[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
40
+ end
41
+
42
+ # a neutered version of fire - it doesn't actually fire the event, it just
43
+ # executes the transition guards to determine if a transition is even
44
+ # an option given current conditions.
45
+ def may_fire?(obj, to_state=::AASM::NO_VALUE, *args)
46
+ _fire(obj, {:test_only => true}, to_state, *args) # true indicates test firing
47
+ end
48
+
49
+ def fire(obj, options={}, to_state=::AASM::NO_VALUE, *args)
50
+ _fire(obj, options, to_state, *args) # false indicates this is not a test (fire!)
51
+ end
52
+
53
+ def transitions_from_state?(state)
54
+ transitions_from_state(state).any?
55
+ end
56
+
57
+ def transitions_from_state(state)
58
+ @transitions.select { |t| t.from.nil? or t.from == state }
59
+ end
60
+
61
+ def transitions_to_state?(state)
62
+ transitions_to_state(state).any?
63
+ end
64
+
65
+ def transitions_to_state(state)
66
+ @transitions.select { |t| t.to == state }
67
+ end
68
+
69
+ def fire_global_callbacks(callback_name, record, *args)
70
+ invoke_callbacks(state_machine.global_callbacks[callback_name], record, args)
71
+ end
72
+
73
+ def fire_callbacks(callback_name, record, *args)
74
+ # strip out the first element in args if it's a valid to_state
75
+ # #given where we're coming from, this condition implies args not empty
76
+ invoke_callbacks(@options[callback_name], record, args)
77
+ end
78
+
79
+ def fire_transition_callbacks(obj, *args)
80
+ from_state = obj.aasm(state_machine.name).current_state
81
+ transition = @valid_transitions[from_state]
82
+ @valid_transitions[from_state].invoke_success_callbacks(obj, *args) if transition
83
+ end
84
+
85
+ def ==(event)
86
+ if event.is_a? Symbol
87
+ name == event
88
+ else
89
+ name == event.name
90
+ end
91
+ end
92
+
93
+ ## DSL interface
94
+ def transitions(definitions=nil, &block)
95
+ if definitions # define new transitions
96
+ # Create a separate transition for each from-state to the given state
97
+ Array(definitions[:from]).each do |s|
98
+ @transitions << AASM::Core::Transition.new(self, attach_event_guards(definitions.merge(:from => s.to_sym)), &block)
99
+ end
100
+ # Create a transition if :to is specified without :from (transitions from ANY state)
101
+ if !definitions[:from] && definitions[:to]
102
+ @transitions << AASM::Core::Transition.new(self, attach_event_guards(definitions), &block)
103
+ end
104
+ end
105
+ @transitions
106
+ end
107
+
108
+ def failed_callbacks
109
+ transitions.flat_map(&:failures)
110
+ end
111
+
112
+ private
113
+
114
+ def attach_event_guards(definitions)
115
+ unless @guards.empty?
116
+ given_guards = Array(definitions.delete(:guard) || definitions.delete(:guards) || definitions.delete(:if))
117
+ definitions[:guards] = @guards + given_guards # from aasm4
118
+ end
119
+ unless @unless.empty?
120
+ given_unless = Array(definitions.delete(:unless))
121
+ definitions[:unless] = given_unless + @unless
122
+ end
123
+ definitions
124
+ end
125
+
126
+ def _fire(obj, options={}, to_state=::AASM::NO_VALUE, *args)
127
+ result = options[:test_only] ? false : nil
128
+ clear_failed_callbacks
129
+ transitions = @transitions.select { |t| t.from == obj.aasm(state_machine.name).current_state || t.from == nil}
130
+ return result if transitions.size == 0
131
+
132
+ if to_state == ::AASM::NO_VALUE
133
+ to_state = nil
134
+ elsif !(to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym))
135
+ # to_state is an argument
136
+ args.unshift(to_state)
137
+ to_state = nil
138
+ end
139
+
140
+ # nop, to_state is a valid to-state
141
+
142
+ transitions.each do |transition|
143
+ next if to_state and !Array(transition.to).include?(to_state)
144
+ if (options.key?(:may_fire) && transition.eql?(options[:may_fire])) ||
145
+ (!options.key?(:may_fire) && transition.allowed?(obj, *args))
146
+
147
+ if options[:test_only]
148
+ result = transition
149
+ else
150
+ result = to_state || Array(transition.to).first
151
+ Array(transition.to).each {|to| @valid_transitions[to] = transition }
152
+ transition.execute(obj, *args)
153
+ end
154
+
155
+ break
156
+ end
157
+ end
158
+ result
159
+ end
160
+
161
+ def clear_failed_callbacks
162
+ # https://github.com/aasm/aasm/issues/383, https://github.com/aasm/aasm/issues/599
163
+ transitions.each { |transition| transition.failures.clear }
164
+ end
165
+
166
+ def invoke_callbacks(code, record, args)
167
+ Invoker.new(code, record, args)
168
+ .with_default_return_value(false)
169
+ .invoke
170
+ end
171
+ end
172
+ end # AASM
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ ##
6
+ # main invoker class which encapsulates the logic
7
+ # for invoking literal-based, proc-based, class-based
8
+ # and array-based callbacks for different entities.
9
+ class Invoker
10
+ DEFAULT_RETURN_VALUE = true
11
+
12
+ ##
13
+ # Initialize a new invoker instance.
14
+ # NOTE that invoker must be used per-subject/record
15
+ # (one instance per subject/record)
16
+ #
17
+ # ==Options:
18
+ #
19
+ # +subject+ - invoking subject, may be Proc,
20
+ # Class, String, Symbol or Array
21
+ # +record+ - invoking record
22
+ # +args+ - arguments which will be passed to the callback
23
+
24
+ def initialize(subject, record, args)
25
+ @subject = subject
26
+ @record = record
27
+ @args = args
28
+ @options = {}
29
+ @failures = []
30
+ @default_return_value = DEFAULT_RETURN_VALUE
31
+ end
32
+
33
+ ##
34
+ # Pass additional options to concrete invoker
35
+ #
36
+ # ==Options:
37
+ #
38
+ # +options+ - hash of options which will be passed to
39
+ # concrete invokers
40
+ #
41
+ # ==Example:
42
+ #
43
+ # with_options(guard: proc {...})
44
+
45
+ def with_options(options)
46
+ @options = options
47
+ self
48
+ end
49
+
50
+ ##
51
+ # Collect failures to a specified buffer
52
+ #
53
+ # ==Options:
54
+ #
55
+ # +failures+ - failures buffer to collect failures
56
+
57
+ def with_failures(failures)
58
+ @failures = failures
59
+ self
60
+ end
61
+
62
+ ##
63
+ # Change default return value of #invoke method
64
+ # if none of invokers processed the request.
65
+ #
66
+ # The default return value is #DEFAULT_RETURN_VALUE
67
+ #
68
+ # ==Options:
69
+ #
70
+ # +value+ - default return value for #invoke method
71
+
72
+ def with_default_return_value(value)
73
+ @default_return_value = value
74
+ self
75
+ end
76
+
77
+ ##
78
+ # Find concrete invoker for specified subject and invoker it,
79
+ # or return default value set by #DEFAULT_RETURN_VALUE or
80
+ # overridden by #with_default_return_value
81
+
82
+ # rubocop:disable Metrics/AbcSize
83
+ def invoke
84
+ return invoke_array if subject.is_a?(Array)
85
+ return literal_invoker.invoke if literal_invoker.may_invoke?
86
+ return proc_invoker.invoke if proc_invoker.may_invoke?
87
+ return class_invoker.invoke if class_invoker.may_invoke?
88
+ default_return_value
89
+ end
90
+ # rubocop:enable Metrics/AbcSize
91
+
92
+ private
93
+
94
+ attr_reader :subject, :record, :args, :options, :failures,
95
+ :default_return_value
96
+
97
+ def invoke_array
98
+ return subject.all? { |item| sub_invoke(item) } if options[:guard]
99
+ return subject.all? { |item| !sub_invoke(item) } if options[:unless]
100
+ subject.map { |item| sub_invoke(item) }
101
+ end
102
+
103
+ def sub_invoke(new_subject)
104
+ self.class.new(new_subject, record, args)
105
+ .with_failures(failures)
106
+ .with_options(options)
107
+ .invoke
108
+ end
109
+
110
+ def proc_invoker
111
+ @proc_invoker ||= Invokers::ProcInvoker
112
+ .new(subject, record, args)
113
+ .with_failures(failures)
114
+ end
115
+
116
+ def class_invoker
117
+ @class_invoker ||= Invokers::ClassInvoker
118
+ .new(subject, record, args)
119
+ .with_failures(failures)
120
+ end
121
+
122
+ def literal_invoker
123
+ @literal_invoker ||= Invokers::LiteralInvoker
124
+ .new(subject, record, args)
125
+ .with_failures(failures)
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ module Invokers
6
+ ##
7
+ # Base concrete invoker class which contain basic
8
+ # invoking and logging definitions
9
+ class BaseInvoker
10
+ attr_reader :failures, :subject, :record, :args, :result
11
+
12
+ ##
13
+ # Initialize a new concrete invoker instance.
14
+ # NOTE that concrete invoker must be used per-subject/record
15
+ # (one instance per subject/record)
16
+ #
17
+ # ==Options:
18
+ #
19
+ # +subject+ - invoking subject comparable with this invoker
20
+ # +record+ - invoking record
21
+ # +args+ - arguments which will be passed to the callback
22
+
23
+ def initialize(subject, record, args)
24
+ @subject = subject
25
+ @record = record
26
+ @args = args
27
+ @result = false
28
+ @failures = []
29
+ end
30
+
31
+ ##
32
+ # Collect failures to a specified buffer
33
+ #
34
+ # ==Options:
35
+ #
36
+ # +failures+ - failures buffer to collect failures
37
+
38
+ def with_failures(failures_buffer)
39
+ @failures = failures_buffer
40
+ self
41
+ end
42
+
43
+ ##
44
+ # Execute concrete invoker, log the error and return result
45
+
46
+ def invoke
47
+ return unless may_invoke?
48
+ log_failure unless invoke_subject
49
+ result
50
+ end
51
+
52
+ ##
53
+ # Check if concrete invoker may be invoked for a specified subject
54
+
55
+ def may_invoke?
56
+ raise NoMethodError, '"#may_invoke?" is not implemented'
57
+ end
58
+
59
+ ##
60
+ # Log failed invoking
61
+
62
+ def log_failure
63
+ raise NoMethodError, '"#log_failure" is not implemented'
64
+ end
65
+
66
+ ##
67
+ # Execute concrete invoker
68
+
69
+ def invoke_subject
70
+ raise NoMethodError, '"#invoke_subject" is not implemented'
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ module Invokers
6
+ ##
7
+ # Class invoker which allows to use classes which respond to #call
8
+ # to be used as state/event/transition callbacks.
9
+ class ClassInvoker < BaseInvoker
10
+ def may_invoke?
11
+ subject.is_a?(Class) && subject.instance_methods.include?(:call)
12
+ end
13
+
14
+ def log_failure
15
+ return log_source_location if Method.method_defined?(:source_location)
16
+ log_method_info
17
+ end
18
+
19
+ def invoke_subject
20
+ @result = retrieve_instance.call
21
+ end
22
+
23
+ private
24
+
25
+ def log_source_location
26
+ failures << instance.method(:call).source_location.join('#')
27
+ end
28
+
29
+ def log_method_info
30
+ failures << instance.method(:call)
31
+ end
32
+
33
+ def instance
34
+ @instance ||= retrieve_instance
35
+ end
36
+
37
+ # rubocop:disable Metrics/AbcSize
38
+ def retrieve_instance
39
+ return subject.new if subject_arity.zero?
40
+ return subject.new(record) if subject_arity == 1
41
+ return subject.new(record, *args) if subject_arity < 0
42
+ subject.new(record, *args[0..(subject_arity - 2)])
43
+ end
44
+ # rubocop:enable Metrics/AbcSize
45
+
46
+ def subject_arity
47
+ @arity ||= subject.instance_method(:initialize).arity
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end