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,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ module Invokers
6
+ ##
7
+ # Literal invoker which allows to use strings or symbols to call
8
+ # record methods as state/event/transition callbacks.
9
+ class LiteralInvoker < BaseInvoker
10
+ def may_invoke?
11
+ subject.is_a?(String) || subject.is_a?(Symbol)
12
+ end
13
+
14
+ def log_failure
15
+ failures << subject
16
+ end
17
+
18
+ def invoke_subject
19
+ @result = exec_subject
20
+ end
21
+
22
+ private
23
+
24
+ def subject_arity
25
+ @arity ||= record.__send__(:method, subject.to_sym).arity
26
+ end
27
+
28
+ # rubocop:disable Metrics/AbcSize
29
+ def exec_subject
30
+ raise(*record_error) unless record.respond_to?(subject, true)
31
+ return record.__send__(subject) if subject_arity.zero?
32
+ return record.__send__(subject, *args) if subject_arity < 0
33
+ record.__send__(subject, *args[0..(subject_arity - 1)])
34
+ end
35
+ # rubocop:enable Metrics/AbcSize
36
+
37
+ def record_error
38
+ [
39
+ NoMethodError,
40
+ 'NoMethodError: undefined method ' \
41
+ "`#{subject}' for #{record.inspect}:#{record.class}"
42
+ ]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM
4
+ module Core
5
+ module Invokers
6
+ ##
7
+ # Proc invoker which allows to use Procs as
8
+ # state/event/transition callbacks.
9
+ class ProcInvoker < BaseInvoker
10
+ def may_invoke?
11
+ subject.is_a?(Proc)
12
+ end
13
+
14
+ def log_failure
15
+ return log_source_location if Method.method_defined?(:source_location)
16
+ log_proc_info
17
+ end
18
+
19
+ def invoke_subject
20
+ @result = if support_parameters?
21
+ exec_proc(parameters_to_arity)
22
+ else
23
+ exec_proc(subject.arity)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def support_parameters?
30
+ subject.respond_to?(:parameters)
31
+ end
32
+
33
+ # rubocop:disable Metrics/AbcSize
34
+ def exec_proc(parameters_size)
35
+ return record.instance_exec(&subject) if parameters_size.zero?
36
+ return record.instance_exec(*args, &subject) if parameters_size < 0
37
+ record.instance_exec(*args[0..(parameters_size - 1)], &subject)
38
+ end
39
+ # rubocop:enable Metrics/AbcSize
40
+
41
+ def log_source_location
42
+ failures << subject.source_location.join('#')
43
+ end
44
+
45
+ def log_proc_info
46
+ failures << subject
47
+ end
48
+
49
+ def parameters_to_arity
50
+ subject.parameters.inject(0) do |memo, parameter|
51
+ memo += 1
52
+ memo *= -1 if parameter[0] == :rest && memo > 0
53
+ memo
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM::Core
4
+ class State
5
+ attr_reader :name, :state_machine, :options
6
+
7
+ def initialize(name, klass, state_machine, options={})
8
+ @name = name
9
+ @klass = klass
10
+ @state_machine = state_machine
11
+ update(options)
12
+ end
13
+
14
+ # called internally by Ruby 1.9 after clone()
15
+ def initialize_copy(orig)
16
+ super
17
+ @options = {}
18
+ orig.options.each_pair do |name, setting|
19
+ @options[name] = if setting.is_a?(Hash) || setting.is_a?(Array)
20
+ setting.dup
21
+ else
22
+ setting
23
+ end
24
+ end
25
+ end
26
+
27
+ def ==(state)
28
+ if state.is_a? Symbol
29
+ name == state
30
+ else
31
+ name == state.name
32
+ end
33
+ end
34
+
35
+ def <=>(state)
36
+ if state.is_a? Symbol
37
+ name <=> state
38
+ else
39
+ name <=> state.name
40
+ end
41
+ end
42
+
43
+ def to_s
44
+ name.to_s
45
+ end
46
+
47
+ def fire_callbacks(action, record, *args)
48
+ action = @options[action]
49
+ catch :halt_aasm_chain do
50
+ action.is_a?(Array) ?
51
+ action.each {|a| _fire_callbacks(a, record, args)} :
52
+ _fire_callbacks(action, record, args)
53
+ end
54
+ end
55
+
56
+ def display_name
57
+ @display_name ||= begin
58
+ if Module.const_defined?(:I18n)
59
+ localized_name
60
+ else
61
+ name.to_s.gsub(/_/, ' ').capitalize
62
+ end
63
+ end
64
+ end
65
+
66
+ def localized_name
67
+ AASM::Localizer.new.human_state_name(@klass, self)
68
+ end
69
+ alias human_name localized_name
70
+
71
+ def for_select
72
+ [display_name, name.to_s]
73
+ end
74
+
75
+ private
76
+
77
+ def update(options = {})
78
+ if options.key?(:display) then
79
+ @display_name = options.delete(:display)
80
+ end
81
+ @options = options
82
+ self
83
+ end
84
+
85
+ def _fire_callbacks(action, record, args)
86
+ Invoker.new(action, record, args).invoke
87
+ end
88
+
89
+ end
90
+ end # AASM
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AASM::Core
4
+ class Transition
5
+ include DslHelper
6
+
7
+ attr_reader :from, :to, :event, :opts, :failures
8
+ alias_method :options, :opts
9
+
10
+ def initialize(event, opts, &block)
11
+ add_options_from_dsl(opts, [:on_transition, :guard, :after, :success], &block) if block
12
+
13
+ @event = event
14
+ @from = opts[:from]
15
+ @to = opts[:to]
16
+ @guards = Array(opts[:guards]) + Array(opts[:guard]) + Array(opts[:if])
17
+ @unless = Array(opts[:unless]) #TODO: This could use a better name
18
+ @failures = []
19
+
20
+ if opts[:on_transition]
21
+ warn '[DEPRECATION] :on_transition is deprecated, use :after instead'
22
+ opts[:after] = Array(opts[:after]) + Array(opts[:on_transition])
23
+ end
24
+ @after = Array(opts[:after])
25
+ @after = @after[0] if @after.size == 1
26
+
27
+ @success = Array(opts[:success])
28
+ @success = @success[0] if @success.size == 1
29
+
30
+ @opts = opts
31
+ end
32
+
33
+ # called internally by Ruby 1.9 after clone()
34
+ def initialize_copy(orig)
35
+ super
36
+ @guards = @guards.dup
37
+ @unless = @unless.dup
38
+ @opts = {}
39
+ orig.opts.each_pair { |name, setting| @opts[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
40
+ end
41
+
42
+ def allowed?(obj, *args)
43
+ invoke_callbacks_compatible_with_guard(@guards, obj, args, :guard => true) &&
44
+ invoke_callbacks_compatible_with_guard(@unless, obj, args, :unless => true)
45
+ end
46
+
47
+ def execute(obj, *args)
48
+ invoke_callbacks_compatible_with_guard(event.state_machine.global_callbacks[:after_all_transitions], obj, args)
49
+ invoke_callbacks_compatible_with_guard(@after, obj, args)
50
+ end
51
+
52
+ def ==(obj)
53
+ @from == obj.from && @to == obj.to
54
+ end
55
+
56
+ def from?(value)
57
+ @from == value
58
+ end
59
+
60
+ def invoke_success_callbacks(obj, *args)
61
+ _fire_callbacks(@success, obj, args)
62
+ end
63
+
64
+ private
65
+
66
+ def invoke_callbacks_compatible_with_guard(code, record, args, options={})
67
+ if record.respond_to?(:aasm)
68
+ record.aasm(event.state_machine.name).from_state = @from if record.aasm(event.state_machine.name).respond_to?(:from_state=)
69
+ record.aasm(event.state_machine.name).to_state = @to if record.aasm(event.state_machine.name).respond_to?(:to_state=)
70
+ end
71
+
72
+ Invoker.new(code, record, args)
73
+ .with_options(options)
74
+ .with_failures(failures)
75
+ .invoke
76
+ end
77
+
78
+ def _fire_callbacks(code, record, args)
79
+ Invoker.new(code, record, args).invoke
80
+ end
81
+
82
+ end
83
+ end # AASM
@@ -0,0 +1,30 @@
1
+ module DslHelper
2
+
3
+ class Proxy
4
+ attr_accessor :options
5
+
6
+ def initialize(options, valid_keys, source)
7
+ @valid_keys = valid_keys
8
+ @source = source
9
+
10
+ @options = options
11
+ end
12
+
13
+ def method_missing(name, *args, &block)
14
+ if @valid_keys.include?(name)
15
+ options[name] = Array(options[name])
16
+ options[name] << block if block
17
+ options[name] += Array(args)
18
+ else
19
+ @source.send name, *args, &block
20
+ end
21
+ end
22
+ end
23
+
24
+ def add_options_from_dsl(options, valid_keys, &block)
25
+ proxy = Proxy.new(options, valid_keys, self)
26
+ proxy.instance_eval(&block)
27
+ proxy.options
28
+ end
29
+
30
+ end
@@ -0,0 +1,21 @@
1
+ module AASM
2
+
3
+ class UnknownStateMachineError < RuntimeError; end
4
+
5
+ class InvalidTransition < RuntimeError
6
+ attr_reader :object, :event_name, :originating_state, :failures, :state_machine_name
7
+
8
+ def initialize(object, event_name, state_machine_name, failures = [])
9
+ @object, @event_name, @originating_state, @failures = object, event_name, object.aasm(state_machine_name).current_state, failures
10
+ @state_machine_name = state_machine_name
11
+ super("Event '#{event_name}' cannot transition from '#{originating_state}'.#{reasoning}")
12
+ end
13
+
14
+ def reasoning
15
+ " Failed callback(s): #{failures}." unless failures.empty?
16
+ end
17
+ end
18
+
19
+ class UndefinedState < RuntimeError; end
20
+ class NoDirectAssignmentError < RuntimeError; end
21
+ end
@@ -0,0 +1,133 @@
1
+ module AASM
2
+ class InstanceBase
3
+
4
+ attr_accessor :from_state, :to_state, :current_event
5
+
6
+ def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
7
+ @instance = instance
8
+ @name = name
9
+ end
10
+
11
+ def current_state
12
+ @instance.aasm_read_state(@name)
13
+ end
14
+
15
+ def current_state=(state)
16
+ @instance.aasm_write_state_without_persistence(state, @name)
17
+ end
18
+
19
+ def enter_initial_state
20
+ state_name = determine_state_name(@instance.class.aasm(@name).initial_state)
21
+ state_object = state_object_for_name(state_name)
22
+
23
+ state_object.fire_callbacks(:before_enter, @instance)
24
+ self.current_state = state_name
25
+ state_object.fire_callbacks(:after_enter, @instance)
26
+
27
+ state_name
28
+ end
29
+
30
+ def human_state
31
+ AASM::Localizer.new.human_state_name(@instance.class, state_object_for_name(current_state))
32
+ end
33
+
34
+ def states(options={}, *args)
35
+ if options.has_key?(:permitted)
36
+ selected_events = events({:permitted => options[:permitted]}, *args)
37
+ # An array of arrays. Each inner array represents the transitions that
38
+ # transition from the current state for an event
39
+ event_transitions = selected_events.map {|e| e.transitions_from_state(current_state) }
40
+
41
+ # An array of :to transition states
42
+ to_state_names = event_transitions.map do |transitions|
43
+ return nil if transitions.empty?
44
+
45
+ # Return the :to state of the first transition that is allowed (or not) or nil
46
+ if options[:permitted]
47
+ transition = transitions.find { |t| t.allowed?(@instance, *args) }
48
+ else
49
+ transition = transitions.find { |t| !t.allowed?(@instance, *args) }
50
+ end
51
+ transition ? transition.to : nil
52
+ end.flatten.compact.uniq
53
+
54
+ # Select states that are in to_state_names
55
+ @instance.class.aasm(@name).states.select {|s| to_state_names.include?(s.name)}
56
+ else
57
+ @instance.class.aasm(@name).states
58
+ end
59
+ end
60
+
61
+ def events(options={}, *args)
62
+ state = options[:state] || current_state
63
+ events = @instance.class.aasm(@name).events.select {|e| e.transitions_from_state?(state) }
64
+
65
+ options[:reject] = Array(options[:reject])
66
+ events.reject! { |e| options[:reject].include?(e.name) }
67
+
68
+ if options.has_key?(:permitted)
69
+ # filters the results of events_for_current_state so that only those that
70
+ # are really currently possible (given transition guards) are shown.
71
+ if options[:permitted]
72
+ events.select! { |e| @instance.send("may_#{e.name}?", *args) }
73
+ else
74
+ events.select! { |e| !@instance.send("may_#{e.name}?", *args) }
75
+ end
76
+ end
77
+
78
+ events
79
+ end
80
+
81
+ def permitted_transitions
82
+ events(permitted: true).flat_map do |event|
83
+ available_transitions = event.transitions_from_state(current_state)
84
+ allowed_transitions = available_transitions.select { |t| t.allowed?(@instance) }
85
+
86
+ allowed_transitions.map do |transition|
87
+ { event: event.name, state: transition.to }
88
+ end
89
+ end
90
+ end
91
+
92
+ def state_object_for_name(name)
93
+ obj = @instance.class.aasm(@name).states.find {|s| s.name == name}
94
+ raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
95
+ obj
96
+ end
97
+
98
+ def determine_state_name(state)
99
+ case state
100
+ when Symbol, String
101
+ state
102
+ when Proc
103
+ state.call(@instance)
104
+ else
105
+ raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
106
+ end
107
+ end
108
+
109
+ def may_fire_event?(name, *args)
110
+ if event = @instance.class.aasm(@name).state_machine.events[name]
111
+ !!event.may_fire?(@instance, *args)
112
+ else
113
+ false # unknown event
114
+ end
115
+ end
116
+
117
+ def fire(event_name, *args, &block)
118
+ @instance.send(event_name, *args, &block)
119
+ end
120
+
121
+ def fire!(event_name, *args, &block)
122
+ event_name = event_name.to_s.+("!").to_sym
123
+ @instance.send(event_name, *args, &block)
124
+ end
125
+
126
+ def set_current_state_with_persistence(state)
127
+ save_success = @instance.aasm_write_state(state, @name)
128
+ self.current_state = state if save_success
129
+ save_success
130
+ end
131
+
132
+ end
133
+ end