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,26 @@
1
+ module AASM
2
+ module Persistence
3
+ module PlainPersistence
4
+
5
+ # may be overwritten by persistence mixins
6
+ def aasm_read_state(name=:default)
7
+ # all the following lines behave like @current_state ||= aasm(name).enter_initial_state
8
+ current = aasm(name).instance_variable_defined?("@current_state_#{name}") &&
9
+ aasm(name).instance_variable_get("@current_state_#{name}")
10
+ return current if current
11
+ aasm(name).instance_variable_set("@current_state_#{name}", aasm(name).enter_initial_state)
12
+ end
13
+
14
+ # may be overwritten by persistence mixins
15
+ def aasm_write_state(new_state, name=:default)
16
+ true
17
+ end
18
+
19
+ # may be overwritten by persistence mixins
20
+ def aasm_write_state_without_persistence(new_state, name=:default)
21
+ aasm(name).instance_variable_set("@current_state_#{name}", new_state)
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,112 @@
1
+ module AASM
2
+ module Persistence
3
+ module RedisPersistence
4
+
5
+ def self.included(base)
6
+ base.send(:include, AASM::Persistence::Base)
7
+ base.send(:include, AASM::Persistence::RedisPersistence::InstanceMethods)
8
+ end
9
+
10
+ module InstanceMethods
11
+ # Initialize with default values
12
+ #
13
+ # Redis::Objects removes the key from Redis when set to `nil`
14
+ def initialize(*args)
15
+ super
16
+ aasm_ensure_initial_state
17
+ end
18
+ # Returns the value of the aasm.attribute_name - called from <tt>aasm.current_state</tt>
19
+ #
20
+ # If it's a new record, and the aasm state column is blank it returns the initial state
21
+ #
22
+ # class Foo
23
+ # include Redis::Objects
24
+ # include AASM
25
+ # aasm :column => :status do
26
+ # state :opened
27
+ # state :closed
28
+ # end
29
+ # end
30
+ #
31
+ # foo = Foo.new
32
+ # foo.current_state # => :opened
33
+ # foo.close
34
+ # foo.current_state # => :closed
35
+ #
36
+ # foo = Foo[1]
37
+ # foo.current_state # => :opened
38
+ # foo.aasm_state = nil
39
+ # foo.current_state # => nil
40
+ #
41
+ # NOTE: intended to be called from an event
42
+ #
43
+ # This allows for nil aasm states - be sure to add validation to your model
44
+ def aasm_read_state(name=:default)
45
+ state = send(self.class.aasm(name).attribute_name)
46
+
47
+ if state.value.nil?
48
+ nil
49
+ else
50
+ state.value.to_sym
51
+ end
52
+ end
53
+
54
+ # Ensures that if the aasm_state column is nil and the record is new
55
+ # that the initial state gets populated before validation on create
56
+ #
57
+ # foo = Foo.new
58
+ # foo.aasm_state # => nil
59
+ # foo.valid?
60
+ # foo.aasm_state # => "open" (where :open is the initial state)
61
+ #
62
+ #
63
+ # foo = Foo.find(:first)
64
+ # foo.aasm_state # => 1
65
+ # foo.aasm_state = nil
66
+ # foo.valid?
67
+ # foo.aasm_state # => nil
68
+ #
69
+ def aasm_ensure_initial_state
70
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |name|
71
+ aasm_column = self.class.aasm(name).attribute_name
72
+ aasm(name).enter_initial_state if !send(aasm_column).value || send(aasm_column).value.empty?
73
+ end
74
+ end
75
+
76
+ # Writes <tt>state</tt> to the state column and persists it to the database
77
+ #
78
+ # foo = Foo[1]
79
+ # foo.aasm.current_state # => :opened
80
+ # foo.close!
81
+ # foo.aasm.current_state # => :closed
82
+ # Foo[1].aasm.current_state # => :closed
83
+ #
84
+ # NOTE: intended to be called from an event
85
+ def aasm_write_state(state, name=:default)
86
+ aasm_column = self.class.aasm(name).attribute_name
87
+ send("#{aasm_column}").value = state
88
+ end
89
+
90
+ # Writes <tt>state</tt> to the state column, but does not persist it to the database
91
+ # (but actually it still does)
92
+ #
93
+ # With Redis::Objects it's not possible to skip persisting - it's not an ORM,
94
+ # it does not operate like an AR model and does not know how to postpone changes.
95
+ #
96
+ # foo = Foo[1]
97
+ # foo.aasm.current_state # => :opened
98
+ # foo.close
99
+ # foo.aasm.current_state # => :closed
100
+ # Foo[1].aasm.current_state # => :opened
101
+ # foo.save
102
+ # foo.aasm.current_state # => :closed
103
+ # Foo[1].aasm.current_state # => :closed
104
+ #
105
+ # NOTE: intended to be called from an event
106
+ def aasm_write_state_without_persistence(state, name=:default)
107
+ aasm_write_state(state, name)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,83 @@
1
+ require 'aasm/persistence/orm'
2
+ module AASM
3
+ module Persistence
4
+ module SequelPersistence
5
+ def self.included(base)
6
+ base.send(:include, AASM::Persistence::Base)
7
+ base.send(:include, AASM::Persistence::ORM)
8
+ base.send(:include, AASM::Persistence::SequelPersistence::InstanceMethods)
9
+ end
10
+
11
+ module InstanceMethods
12
+ def before_validation
13
+ aasm_ensure_initial_state
14
+ super
15
+ end
16
+
17
+ def before_create
18
+ super
19
+ end
20
+
21
+ def aasm_raise_invalid_record
22
+ raise Sequel::ValidationFailed.new(self)
23
+ end
24
+
25
+ def aasm_new_record?
26
+ new?
27
+ end
28
+
29
+ # Returns nil if fails silently
30
+ # http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-save
31
+ def aasm_save
32
+ !save(raise_on_failure: false).nil?
33
+ end
34
+
35
+ def aasm_read_attribute(name)
36
+ send(name)
37
+ end
38
+
39
+ def aasm_write_attribute(name, value)
40
+ send("#{name}=", value)
41
+ end
42
+
43
+ def aasm_transaction(requires_new, requires_lock)
44
+ self.class.db.transaction(savepoint: requires_new) do
45
+ if requires_lock
46
+ # http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-lock-21
47
+ requires_lock.is_a?(String) ? lock!(requires_lock) : lock!
48
+ end
49
+ yield
50
+ end
51
+ end
52
+
53
+ def aasm_update_column(attribute_name, value)
54
+ this.update(attribute_name => value)
55
+ end
56
+
57
+ # Ensures that if the aasm_state column is nil and the record is new
58
+ # that the initial state gets populated before validation on create
59
+ #
60
+ # foo = Foo.new
61
+ # foo.aasm_state # => nil
62
+ # foo.valid?
63
+ # foo.aasm_state # => "open" (where :open is the initial state)
64
+ #
65
+ #
66
+ # foo = Foo.find(:first)
67
+ # foo.aasm_state # => 1
68
+ # foo.aasm_state = nil
69
+ # foo.valid?
70
+ # foo.aasm_state # => nil
71
+ #
72
+ def aasm_ensure_initial_state
73
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
74
+ aasm(state_machine_name).enter_initial_state if
75
+ (new? || values.key?(self.class.aasm(state_machine_name).attribute_name)) &&
76
+ send(self.class.aasm(state_machine_name).attribute_name).to_s.strip.empty?
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,5 @@
1
+ # relative-require all rspec files
2
+ Dir[File.dirname(__FILE__) + '/rspec/*.rb'].each do |file|
3
+ require 'aasm/rspec/' + File.basename(file, File.extname(file))
4
+ end
5
+
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :allow_event do |event|
2
+ match do |obj|
3
+ @state_machine_name ||= :default
4
+ obj.aasm(@state_machine_name).may_fire_event?(event, *@args)
5
+ end
6
+
7
+ chain :on do |state_machine_name|
8
+ @state_machine_name = state_machine_name
9
+ end
10
+
11
+ chain :with do |*args|
12
+ @args = args
13
+ end
14
+
15
+ description do
16
+ "allow event #{expected} (on :#{@state_machine_name})"
17
+ end
18
+
19
+ failure_message do |obj|
20
+ "expected that the event :#{expected} would be allowed (on :#{@state_machine_name})"
21
+ end
22
+
23
+ failure_message_when_negated do |obj|
24
+ "expected that the event :#{expected} would not be allowed (on :#{@state_machine_name})"
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ RSpec::Matchers.define :allow_transition_to do |state|
2
+ match do |obj|
3
+ @state_machine_name ||= :default
4
+ obj.aasm(@state_machine_name).states({:permitted => true}, *@args).include?(state)
5
+ end
6
+
7
+ chain :on do |state_machine_name|
8
+ @state_machine_name = state_machine_name
9
+ end
10
+
11
+ chain :with do |*args|
12
+ @args = args
13
+ end
14
+
15
+ description do
16
+ "allow transition to #{expected} (on :#{@state_machine_name})"
17
+ end
18
+
19
+ failure_message do |obj|
20
+ "expected that the state :#{expected} would be reachable (on :#{@state_machine_name})"
21
+ end
22
+
23
+ failure_message_when_negated do |obj|
24
+ "expected that the state :#{expected} would not be reachable (on :#{@state_machine_name})"
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ RSpec::Matchers.define :have_state do |state|
2
+ match do |obj|
3
+ @state_machine_name ||= :default
4
+ obj.aasm(@state_machine_name).current_state == state.to_sym
5
+ end
6
+
7
+ chain :on do |state_machine_name|
8
+ @state_machine_name = state_machine_name
9
+ end
10
+
11
+ description do
12
+ "have state #{expected} (on :#{@state_machine_name})"
13
+ end
14
+
15
+ failure_message do |obj|
16
+ "expected that :#{obj.aasm(@state_machine_name).current_state} would be :#{expected} (on :#{@state_machine_name})"
17
+ end
18
+
19
+ failure_message_when_negated do |obj|
20
+ "expected that :#{obj.aasm(@state_machine_name).current_state} would not be :#{expected} (on :#{@state_machine_name})"
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ RSpec::Matchers.define :transition_from do |from_state|
2
+ match do |obj|
3
+ @state_machine_name ||= :default
4
+ obj.aasm(@state_machine_name).current_state = from_state.to_sym
5
+ begin
6
+ obj.send(@event, *@args) && obj.aasm(@state_machine_name).current_state == @to_state.to_sym
7
+ rescue AASM::InvalidTransition
8
+ false
9
+ end
10
+ end
11
+
12
+ chain :on do |state_machine_name|
13
+ @state_machine_name = state_machine_name
14
+ end
15
+
16
+ chain :to do |state|
17
+ @to_state = state
18
+ end
19
+
20
+ chain :on_event do |event, *args|
21
+ @event = event
22
+ @args = args
23
+ end
24
+
25
+ description do
26
+ "transition state to :#{@to_state} from :#{expected} on event :#{@event}, with params: #{@args} (on :#{@state_machine_name})"
27
+ end
28
+
29
+ failure_message do |obj|
30
+ "expected that :#{obj.aasm(@state_machine_name).current_state} would be :#{@to_state} (on :#{@state_machine_name})"
31
+ end
32
+
33
+ failure_message_when_negated do |obj|
34
+ "expected that :#{obj.aasm(@state_machine_name).current_state} would not be :#{@to_state} (on :#{@state_machine_name})"
35
+ end
36
+ end
@@ -0,0 +1,53 @@
1
+ module AASM
2
+ class StateMachine
3
+ # the following four methods provide the storage of all state machines
4
+
5
+ attr_accessor :states, :events, :initial_state, :config, :name, :global_callbacks
6
+
7
+ def initialize(name)
8
+ @initial_state = nil
9
+ @states = []
10
+ @events = {}
11
+ @global_callbacks = {}
12
+ @config = AASM::Configuration.new
13
+ @name = name
14
+ end
15
+
16
+ # called internally by Ruby 1.9 after clone()
17
+ def initialize_copy(orig)
18
+ super
19
+ @states = orig.states.collect { |state| state.clone }
20
+ @events = {}
21
+ orig.events.each_pair { |name, event| @events[name] = event.clone }
22
+ @global_callbacks = @global_callbacks.dup
23
+ end
24
+
25
+ def add_state(state_name, klass, options)
26
+ set_initial_state(state_name, options)
27
+
28
+ # allow reloading, extending or redefining a state
29
+ @states.delete(state_name) if @states.include?(state_name)
30
+
31
+ @states << AASM::Core::State.new(state_name, klass, self, options)
32
+ end
33
+
34
+ def add_event(name, options, &block)
35
+ @events[name] = AASM::Core::Event.new(name, self, options, &block)
36
+ end
37
+
38
+ def add_global_callbacks(name, *callbacks, &block)
39
+ @global_callbacks[name] ||= []
40
+ callbacks.each do |callback|
41
+ @global_callbacks[name] << callback unless @global_callbacks[name].include? callback
42
+ end
43
+ @global_callbacks[name] << block if block
44
+ end
45
+
46
+ private
47
+
48
+ def set_initial_state(name, options)
49
+ @initial_state = name if options[:initial] || !initial_state
50
+ end
51
+
52
+ end # StateMachine
53
+ end # AASM
@@ -0,0 +1,76 @@
1
+ require 'concurrent'
2
+ module AASM
3
+ class StateMachineStore
4
+ @stores = Concurrent::Map.new
5
+
6
+ class << self
7
+ def stores
8
+ @stores
9
+ end
10
+
11
+ # do not overwrite existing state machines, which could have been created by
12
+ # inheritance, see AASM::ClassMethods method inherited
13
+ def register(klass, overwrite = false, state_machine = nil)
14
+ raise "Cannot register #{klass}" unless klass.is_a?(Class)
15
+
16
+ case name = template = overwrite
17
+ when FalseClass then stores[klass.to_s] ||= new
18
+ when TrueClass then stores[klass.to_s] = new
19
+ when Class then stores[klass.to_s] = stores[template.to_s].clone
20
+ when Symbol then stores[klass.to_s].register(name, state_machine)
21
+ when String then stores[klass.to_s].register(name, state_machine)
22
+ else raise "Don't know what to do with #{overwrite}"
23
+ end
24
+ end
25
+ alias_method :[]=, :register
26
+
27
+ def fetch(klass, fallback = nil)
28
+ stores[klass.to_s] || fallback && begin
29
+ match = klass.ancestors.find do |ancestor|
30
+ ancestor.include? AASM and stores[ancestor.to_s]
31
+ end
32
+
33
+ stores[match.to_s]
34
+ end
35
+ end
36
+ alias_method :[], :fetch
37
+
38
+ def unregister(klass)
39
+ stores.delete(klass.to_s)
40
+ end
41
+ end
42
+
43
+ def initialize
44
+ @machines = Concurrent::Map.new
45
+ end
46
+
47
+ def clone
48
+ StateMachineStore.new.tap do |store|
49
+ @machines.each_pair do |name, machine|
50
+ store.register(name, machine.clone)
51
+ end
52
+ end
53
+ end
54
+
55
+ def machine(name)
56
+ @machines[name.to_s]
57
+ end
58
+ alias_method :[], :machine
59
+
60
+ def machine_names
61
+ @machines.keys
62
+ end
63
+ alias_method :keys, :machine_names
64
+
65
+ def register(name, machine, force = false)
66
+ raise "Cannot use #{name.inspect} for machine name" unless name.is_a?(Symbol) or name.is_a?(String)
67
+ raise "Cannot use #{machine.inspect} as a machine" unless machine.is_a?(AASM::StateMachine)
68
+
69
+ if force
70
+ @machines[name.to_s] = machine
71
+ else
72
+ @machines[name.to_s] ||= machine
73
+ end
74
+ end
75
+ end
76
+ end