aasm 2.1.1 → 5.2.0

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 (275) 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 +82 -0
  7. data/API +34 -0
  8. data/Appraisals +67 -0
  9. data/CHANGELOG.md +453 -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/{MIT-LICENSE → LICENSE} +1 -1
  17. data/PLANNED_CHANGES.md +11 -0
  18. data/README.md +1524 -0
  19. data/README_FROM_VERSION_3_TO_4.md +240 -0
  20. data/Rakefile +20 -84
  21. data/TESTING.md +25 -0
  22. data/aasm.gemspec +37 -0
  23. data/docker-compose.yml +40 -0
  24. data/gemfiles/norails.gemfile +10 -0
  25. data/gemfiles/rails_4.2.gemfile +17 -0
  26. data/gemfiles/rails_4.2_mongoid_5.gemfile +12 -0
  27. data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
  28. data/gemfiles/rails_5.0.gemfile +14 -0
  29. data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
  30. data/gemfiles/rails_5.1.gemfile +14 -0
  31. data/gemfiles/rails_5.2.gemfile +14 -0
  32. data/lib/aasm/aasm.rb +160 -137
  33. data/lib/aasm/base.rb +290 -0
  34. data/lib/aasm/configuration.rb +48 -0
  35. data/lib/aasm/core/event.rb +177 -0
  36. data/lib/aasm/core/invoker.rb +129 -0
  37. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  38. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  39. data/lib/aasm/core/invokers/literal_invoker.rb +47 -0
  40. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  41. data/lib/aasm/core/state.rb +91 -0
  42. data/lib/aasm/core/transition.rb +83 -0
  43. data/lib/aasm/dsl_helper.rb +32 -0
  44. data/lib/aasm/errors.rb +21 -0
  45. data/lib/aasm/instance_base.rb +133 -0
  46. data/lib/aasm/localizer.rb +64 -0
  47. data/lib/aasm/minitest/allow_event.rb +13 -0
  48. data/lib/aasm/minitest/allow_transition_to.rb +13 -0
  49. data/lib/aasm/minitest/have_state.rb +13 -0
  50. data/lib/aasm/minitest/transition_from.rb +21 -0
  51. data/lib/aasm/minitest.rb +5 -0
  52. data/lib/aasm/minitest_spec.rb +15 -0
  53. data/lib/aasm/persistence/active_record_persistence.rb +108 -173
  54. data/lib/aasm/persistence/base.rb +89 -0
  55. data/lib/aasm/persistence/core_data_query_persistence.rb +94 -0
  56. data/lib/aasm/persistence/dynamoid_persistence.rb +92 -0
  57. data/lib/aasm/persistence/mongoid_persistence.rb +126 -0
  58. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  59. data/lib/aasm/persistence/orm.rb +154 -0
  60. data/lib/aasm/persistence/plain_persistence.rb +26 -0
  61. data/lib/aasm/persistence/redis_persistence.rb +112 -0
  62. data/lib/aasm/persistence/sequel_persistence.rb +83 -0
  63. data/lib/aasm/persistence.rb +48 -10
  64. data/lib/aasm/rspec/allow_event.rb +26 -0
  65. data/lib/aasm/rspec/allow_transition_to.rb +26 -0
  66. data/lib/aasm/rspec/have_state.rb +22 -0
  67. data/lib/aasm/rspec/transition_from.rb +36 -0
  68. data/lib/aasm/rspec.rb +5 -0
  69. data/lib/aasm/state_machine.rb +40 -22
  70. data/lib/aasm/state_machine_store.rb +76 -0
  71. data/lib/aasm/version.rb +3 -0
  72. data/lib/aasm.rb +21 -1
  73. data/lib/generators/aasm/aasm_generator.rb +16 -0
  74. data/lib/generators/aasm/orm_helpers.rb +41 -0
  75. data/lib/generators/active_record/aasm_generator.rb +40 -0
  76. data/lib/generators/active_record/templates/migration.rb +8 -0
  77. data/lib/generators/active_record/templates/migration_existing.rb +5 -0
  78. data/lib/generators/mongoid/aasm_generator.rb +28 -0
  79. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  80. data/lib/motion-aasm.rb +37 -0
  81. data/spec/database.rb +57 -0
  82. data/spec/database.yml +3 -0
  83. data/spec/en.yml +9 -0
  84. data/spec/generators/active_record_generator_spec.rb +53 -0
  85. data/spec/generators/mongoid_generator_spec.rb +31 -0
  86. data/spec/generators/no_brainer_generator_spec.rb +29 -0
  87. data/spec/localizer_test_model_deprecated_style.yml +13 -0
  88. data/spec/localizer_test_model_new_style.yml +11 -0
  89. data/spec/models/active_record/active_record_callback.rb +93 -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 +42 -0
  98. data/spec/models/active_record/namespaced.rb +16 -0
  99. data/spec/models/active_record/no_direct_assignment.rb +21 -0
  100. data/spec/models/active_record/no_scope.rb +21 -0
  101. data/spec/models/active_record/persisted_state.rb +12 -0
  102. data/spec/models/active_record/person.rb +23 -0
  103. data/spec/models/active_record/provided_and_persisted_state.rb +24 -0
  104. data/spec/models/active_record/reader.rb +7 -0
  105. data/spec/models/active_record/readme_job.rb +21 -0
  106. data/spec/models/active_record/silent_persistor.rb +29 -0
  107. data/spec/models/active_record/simple_new_dsl.rb +32 -0
  108. data/spec/models/active_record/thief.rb +29 -0
  109. data/spec/models/active_record/timestamp_example.rb +16 -0
  110. data/spec/models/active_record/transactor.rb +124 -0
  111. data/spec/models/active_record/transient.rb +6 -0
  112. data/spec/models/active_record/validator.rb +118 -0
  113. data/spec/models/active_record/with_enum.rb +39 -0
  114. data/spec/models/active_record/with_enum_without_column.rb +38 -0
  115. data/spec/models/active_record/with_false_enum.rb +31 -0
  116. data/spec/models/active_record/with_true_enum.rb +39 -0
  117. data/spec/models/active_record/work.rb +3 -0
  118. data/spec/models/active_record/worker.rb +2 -0
  119. data/spec/models/active_record/writer.rb +6 -0
  120. data/spec/models/basic_two_state_machines_example.rb +25 -0
  121. data/spec/models/callbacks/basic.rb +98 -0
  122. data/spec/models/callbacks/basic_multiple.rb +75 -0
  123. data/spec/models/callbacks/guard_within_block.rb +67 -0
  124. data/spec/models/callbacks/guard_within_block_multiple.rb +66 -0
  125. data/spec/models/callbacks/multiple_transitions_transition_guard.rb +66 -0
  126. data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +65 -0
  127. data/spec/models/callbacks/private_method.rb +44 -0
  128. data/spec/models/callbacks/private_method_multiple.rb +44 -0
  129. data/spec/models/callbacks/with_args.rb +62 -0
  130. data/spec/models/callbacks/with_args_multiple.rb +61 -0
  131. data/spec/models/callbacks/with_state_arg.rb +34 -0
  132. data/spec/models/callbacks/with_state_arg_multiple.rb +29 -0
  133. data/spec/models/complex_example.rb +222 -0
  134. data/spec/models/conversation.rb +93 -0
  135. data/spec/models/default_state.rb +12 -0
  136. data/spec/models/double_definer.rb +21 -0
  137. data/spec/models/dynamoid/complex_dynamoid_example.rb +37 -0
  138. data/spec/models/dynamoid/dynamoid_multiple.rb +18 -0
  139. data/spec/models/dynamoid/dynamoid_simple.rb +18 -0
  140. data/spec/models/foo.rb +106 -0
  141. data/spec/models/foo_callback_multiple.rb +45 -0
  142. data/spec/models/guard_arguments_check.rb +17 -0
  143. data/spec/models/guard_with_params.rb +24 -0
  144. data/spec/models/guard_with_params_multiple.rb +18 -0
  145. data/spec/models/guardian.rb +58 -0
  146. data/spec/models/guardian_multiple.rb +48 -0
  147. data/spec/models/guardian_without_from_specified.rb +18 -0
  148. data/spec/models/initial_state_proc.rb +31 -0
  149. data/spec/models/mongoid/complex_mongoid_example.rb +37 -0
  150. data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
  151. data/spec/models/mongoid/mongoid_relationships.rb +26 -0
  152. data/spec/models/mongoid/no_scope_mongoid.rb +21 -0
  153. data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
  154. data/spec/models/mongoid/simple_mongoid.rb +23 -0
  155. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +25 -0
  156. data/spec/models/mongoid/timestamp_example_mongoid.rb +20 -0
  157. data/spec/models/mongoid/validator_mongoid.rb +100 -0
  158. data/spec/models/multi_transitioner.rb +34 -0
  159. data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
  160. data/spec/models/namespaced_multiple_example.rb +42 -0
  161. data/spec/models/no_initial_state.rb +25 -0
  162. data/spec/models/nobrainer/complex_no_brainer_example.rb +36 -0
  163. data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
  164. data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
  165. data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
  166. data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
  167. data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
  168. data/spec/models/nobrainer/simple_no_brainer.rb +23 -0
  169. data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
  170. data/spec/models/not_auto_loaded/process.rb +21 -0
  171. data/spec/models/parametrised_event.rb +42 -0
  172. data/spec/models/parametrised_event_multiple.rb +29 -0
  173. data/spec/models/process_with_new_dsl.rb +31 -0
  174. data/spec/models/provided_state.rb +24 -0
  175. data/spec/models/redis/complex_redis_example.rb +40 -0
  176. data/spec/models/redis/redis_multiple.rb +20 -0
  177. data/spec/models/redis/redis_simple.rb +20 -0
  178. data/spec/models/sequel/complex_sequel_example.rb +46 -0
  179. data/spec/models/sequel/invalid_persistor.rb +52 -0
  180. data/spec/models/sequel/sequel_multiple.rb +25 -0
  181. data/spec/models/sequel/sequel_simple.rb +26 -0
  182. data/spec/models/sequel/silent_persistor.rb +50 -0
  183. data/spec/models/sequel/transactor.rb +112 -0
  184. data/spec/models/sequel/validator.rb +93 -0
  185. data/spec/models/sequel/worker.rb +12 -0
  186. data/spec/models/silencer.rb +27 -0
  187. data/spec/models/simple_custom_example.rb +53 -0
  188. data/spec/models/simple_example.rb +23 -0
  189. data/spec/models/simple_example_with_guard_args.rb +17 -0
  190. data/spec/models/simple_multiple_example.rb +42 -0
  191. data/spec/models/state_machine_with_failed_event.rb +20 -0
  192. data/spec/models/states_on_one_line_example.rb +8 -0
  193. data/spec/models/sub_class.rb +41 -0
  194. data/spec/models/sub_class_with_more_states.rb +18 -0
  195. data/spec/models/sub_classing.rb +3 -0
  196. data/spec/models/super_class.rb +46 -0
  197. data/spec/models/this_name_better_not_be_in_use.rb +11 -0
  198. data/spec/models/timestamps_example.rb +19 -0
  199. data/spec/models/timestamps_with_named_machine_example.rb +13 -0
  200. data/spec/models/valid_state_name.rb +23 -0
  201. data/spec/spec_helper.rb +41 -0
  202. data/spec/spec_helpers/active_record.rb +8 -0
  203. data/spec/spec_helpers/dynamoid.rb +35 -0
  204. data/spec/spec_helpers/mongoid.rb +26 -0
  205. data/spec/spec_helpers/nobrainer.rb +15 -0
  206. data/spec/spec_helpers/redis.rb +18 -0
  207. data/spec/spec_helpers/remove_warnings.rb +1 -0
  208. data/spec/spec_helpers/sequel.rb +7 -0
  209. data/spec/unit/abstract_class_spec.rb +27 -0
  210. data/spec/unit/api_spec.rb +104 -0
  211. data/spec/unit/basic_two_state_machines_example_spec.rb +10 -0
  212. data/spec/unit/callback_multiple_spec.rb +304 -0
  213. data/spec/unit/callbacks_spec.rb +521 -0
  214. data/spec/unit/complex_example_spec.rb +93 -0
  215. data/spec/unit/complex_multiple_example_spec.rb +115 -0
  216. data/spec/unit/edge_cases_spec.rb +16 -0
  217. data/spec/unit/event_multiple_spec.rb +73 -0
  218. data/spec/unit/event_naming_spec.rb +16 -0
  219. data/spec/unit/event_spec.rb +394 -0
  220. data/spec/unit/exception_spec.rb +11 -0
  221. data/spec/unit/guard_arguments_check_spec.rb +9 -0
  222. data/spec/unit/guard_multiple_spec.rb +60 -0
  223. data/spec/unit/guard_spec.rb +89 -0
  224. data/spec/unit/guard_with_params_multiple_spec.rb +10 -0
  225. data/spec/unit/guard_with_params_spec.rb +14 -0
  226. data/spec/unit/guard_without_from_specified_spec.rb +10 -0
  227. data/spec/unit/initial_state_multiple_spec.rb +15 -0
  228. data/spec/unit/initial_state_spec.rb +12 -0
  229. data/spec/unit/inspection_multiple_spec.rb +205 -0
  230. data/spec/unit/inspection_spec.rb +153 -0
  231. data/spec/unit/invoker_spec.rb +189 -0
  232. data/spec/unit/invokers/base_invoker_spec.rb +72 -0
  233. data/spec/unit/invokers/class_invoker_spec.rb +95 -0
  234. data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
  235. data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
  236. data/spec/unit/localizer_spec.rb +109 -0
  237. data/spec/unit/memory_leak_spec.rb +38 -0
  238. data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
  239. data/spec/unit/namespaced_multiple_example_spec.rb +75 -0
  240. data/spec/unit/new_dsl_spec.rb +12 -0
  241. data/spec/unit/override_warning_spec.rb +94 -0
  242. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +635 -0
  243. data/spec/unit/persistence/active_record_persistence_spec.rb +852 -0
  244. data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +135 -0
  245. data/spec/unit/persistence/dynamoid_persistence_spec.rb +84 -0
  246. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +200 -0
  247. data/spec/unit/persistence/mongoid_persistence_spec.rb +177 -0
  248. data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
  249. data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
  250. data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
  251. data/spec/unit/persistence/redis_persistence_spec.rb +53 -0
  252. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +148 -0
  253. data/spec/unit/persistence/sequel_persistence_spec.rb +368 -0
  254. data/spec/unit/readme_spec.rb +41 -0
  255. data/spec/unit/reloading_spec.rb +15 -0
  256. data/spec/unit/rspec_matcher_spec.rb +88 -0
  257. data/spec/unit/simple_custom_example_spec.rb +39 -0
  258. data/spec/unit/simple_example_spec.rb +57 -0
  259. data/spec/unit/simple_multiple_example_spec.rb +91 -0
  260. data/spec/unit/state_spec.rb +105 -0
  261. data/spec/unit/states_on_one_line_example_spec.rb +16 -0
  262. data/spec/unit/subclassing_multiple_spec.rb +74 -0
  263. data/spec/unit/subclassing_spec.rb +46 -0
  264. data/spec/unit/timestamps_spec.rb +32 -0
  265. data/spec/unit/transition_spec.rb +436 -0
  266. data/test/minitest_helper.rb +57 -0
  267. data/test/unit/minitest_matcher_test.rb +80 -0
  268. metadata +607 -60
  269. data/CHANGELOG +0 -33
  270. data/README.rdoc +0 -122
  271. data/TODO +0 -9
  272. data/doc/jamis.rb +0 -591
  273. data/lib/aasm/event.rb +0 -76
  274. data/lib/aasm/state.rb +0 -35
  275. data/lib/aasm/state_transition.rb +0 -36
@@ -1,3 +1,4 @@
1
+ require 'aasm/persistence/orm'
1
2
  module AASM
2
3
  module Persistence
3
4
  module ActiveRecordPersistence
@@ -6,14 +7,9 @@ module AASM
6
7
  # * extends the model with ClassMethods
7
8
  # * includes InstanceMethods
8
9
  #
9
- # Unless the corresponding methods are already defined, it includes
10
- # * ReadState
11
- # * WriteState
12
- # * WriteStateWithoutPersistence
13
- #
14
10
  # Adds
15
11
  #
16
- # before_validation_on_create :aasm_ensure_initial_state
12
+ # after_initialize :aasm_ensure_initial_state
17
13
  #
18
14
  # As a result, it doesn't matter when you define your methods - the following 2 are equivalent
19
15
  #
@@ -32,119 +28,116 @@ module AASM
32
28
  # end
33
29
  #
34
30
  def self.included(base)
35
- base.extend AASM::Persistence::ActiveRecordPersistence::ClassMethods
31
+ base.send(:include, AASM::Persistence::Base)
32
+ base.send(:include, AASM::Persistence::ORM)
36
33
  base.send(:include, AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
37
- base.send(:include, AASM::Persistence::ActiveRecordPersistence::ReadState) unless base.method_defined?(:aasm_read_state)
38
- base.send(:include, AASM::Persistence::ActiveRecordPersistence::WriteState) unless base.method_defined?(:aasm_write_state)
39
- base.send(:include, AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence) unless base.method_defined?(:aasm_write_state_without_persistence)
40
-
41
- if base.respond_to?(:named_scope)
42
- base.extend(AASM::Persistence::ActiveRecordPersistence::NamedScopeMethods)
43
-
44
- base.class_eval do
45
- class << self
46
- unless method_defined?(:aasm_state_without_named_scope)
47
- alias_method :aasm_state_without_named_scope, :aasm_state
48
- alias_method :aasm_state, :aasm_state_with_named_scope
49
- end
50
- end
51
- end
52
- end
34
+ base.extend AASM::Persistence::ActiveRecordPersistence::ClassMethods
53
35
 
54
- base.before_validation_on_create :aasm_ensure_initial_state
36
+ base.after_initialize :aasm_ensure_initial_state
37
+
38
+ # ensure state is in the list of states
39
+ base.validate :aasm_validate_states
55
40
  end
56
41
 
57
42
  module ClassMethods
58
- # Maps to the aasm_column in the database. Deafults to "aasm_state". You can write:
59
- #
60
- # create_table :foos do |t|
61
- # t.string :name
62
- # t.string :aasm_state
63
- # end
64
- #
65
- # class Foo < ActiveRecord::Base
66
- # include AASM
67
- # end
68
- #
69
- # OR:
70
- #
71
- # create_table :foos do |t|
72
- # t.string :name
73
- # t.string :status
74
- # end
75
- #
76
- # class Foo < ActiveRecord::Base
77
- # include AASM
78
- # aasm_column :status
79
- # end
80
- #
81
- # This method is both a getter and a setter
82
- def aasm_column(column_name=nil)
83
- if column_name
84
- AASM::StateMachine[self].config.column = column_name.to_sym
85
- # @aasm_column = column_name.to_sym
43
+ def aasm_create_scope(state_machine_name, scope_name)
44
+ if ActiveRecord::VERSION::MAJOR >= 3
45
+ conditions = { aasm(state_machine_name).attribute_name => scope_name.to_s }
46
+ class_eval do
47
+ scope scope_name, lambda { where(table_name => conditions) }
48
+ end
86
49
  else
87
- AASM::StateMachine[self].config.column ||= :aasm_state
88
- # @aasm_column ||= :aasm_state
50
+ conditions = {
51
+ table_name => { aasm(state_machine_name).attribute_name => scope_name.to_s }
52
+ }
53
+ class_eval do
54
+ named_scope scope_name, :conditions => conditions
55
+ end
89
56
  end
90
- # @aasm_column
91
- AASM::StateMachine[self].config.column
92
57
  end
58
+ end
59
+
60
+ module InstanceMethods
61
+
62
+ private
93
63
 
94
- def find_in_state(number, state, *args)
95
- with_state_scope state do
96
- find(number, *args)
64
+ def aasm_execute_after_commit
65
+ begin
66
+ require 'after_commit_everywhere'
67
+ raise LoadError unless Gem::Version.new(::AfterCommitEverywhere::VERSION) >= Gem::Version.new('0.1.5')
68
+
69
+ self.extend ::AfterCommitEverywhere
70
+ after_commit do
71
+ yield
72
+ end
73
+ rescue LoadError
74
+ warn <<-MSG
75
+ [DEPRECATION] :after_commit AASM callback is not safe in terms of race conditions and redundant calls.
76
+ Please add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile in order to fix that.
77
+ MSG
78
+ yield
97
79
  end
98
80
  end
99
81
 
100
- def count_in_state(state, *args)
101
- with_state_scope state do
102
- count(*args)
103
- end
82
+ def aasm_raise_invalid_record
83
+ raise ActiveRecord::RecordInvalid.new(self)
84
+ end
85
+
86
+ def aasm_save
87
+ self.save
88
+ end
89
+
90
+ def aasm_update_column(attribute_name, value)
91
+ self.class.unscoped.where(self.class.primary_key => self.id).update_all(attribute_name => value) == 1
92
+ end
93
+
94
+ def aasm_read_attribute(name)
95
+ read_attribute(name)
96
+ end
97
+
98
+ def aasm_write_attribute(name, value)
99
+ write_attribute(name, value)
104
100
  end
105
101
 
106
- def calculate_in_state(state, *args)
107
- with_state_scope state do
108
- calculate(*args)
102
+ def aasm_transaction(requires_new, requires_lock)
103
+ self.class.transaction(:requires_new => requires_new) do
104
+ lock!(requires_lock) if requires_lock
105
+ yield
109
106
  end
110
107
  end
111
108
 
112
- protected
113
- def with_state_scope(state)
114
- with_scope :find => {:conditions => ["#{table_name}.#{aasm_column} = ?", state.to_s]} do
115
- yield if block_given?
109
+ def aasm_enum(name=:default)
110
+ case AASM::StateMachineStore.fetch(self.class, true).machine(name).config.enum
111
+ when false then nil
112
+ when true then aasm_guess_enum_method(name)
113
+ when nil then aasm_guess_enum_method(name) if aasm_column_looks_like_enum(name)
114
+ else AASM::StateMachineStore.fetch(self.class, true).machine(name).config.enum
116
115
  end
117
116
  end
118
- end
119
117
 
120
- module InstanceMethods
118
+ def aasm_column_looks_like_enum(name=:default)
119
+ column_name = self.class.aasm(name).attribute_name.to_s
120
+ column = self.class.columns_hash[column_name]
121
+ raise NoMethodError.new("undefined method '#{column_name}' for #{self.class}") if column.nil?
122
+ column.type == :integer
123
+ end
121
124
 
122
- # Returns the current aasm_state of the object. Respects reload and
123
- # any changes made to the aasm_state field directly
124
- #
125
- # Internally just calls <tt>aasm_read_state</tt>
126
- #
127
- # foo = Foo.find(1)
128
- # foo.aasm_current_state # => :pending
129
- # foo.aasm_state = "opened"
130
- # foo.aasm_current_state # => :opened
131
- # foo.close # => calls aasm_write_state_without_persistence
132
- # foo.aasm_current_state # => :closed
133
- # foo.reload
134
- # foo.aasm_current_state # => :pending
135
- #
136
- def aasm_current_state
137
- @current_state = aasm_read_state
125
+ def aasm_guess_enum_method(name=:default)
126
+ self.class.aasm(name).attribute_name.to_s.pluralize.to_sym
138
127
  end
139
128
 
140
- private
129
+ def aasm_raw_attribute_value(state, name=:default)
130
+ if aasm_enum(name)
131
+ self.class.send(aasm_enum(name))[state]
132
+ else
133
+ super
134
+ end
135
+ end
141
136
 
142
137
  # Ensures that if the aasm_state column is nil and the record is new
143
- # that the initial state gets populated before validation on create
138
+ # then the initial state gets populated after initialization
144
139
  #
145
140
  # foo = Foo.new
146
- # foo.aasm_state # => nil
147
- # foo.valid?
148
141
  # foo.aasm_state # => "open" (where :open is the initial state)
149
142
  #
150
143
  #
@@ -155,94 +148,36 @@ module AASM
155
148
  # foo.aasm_state # => nil
156
149
  #
157
150
  def aasm_ensure_initial_state
158
- send("#{self.class.aasm_column}=", self.aasm_current_state.to_s)
151
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
152
+ # checking via respond_to? does not work in Rails <= 3
153
+ # if respond_to?(self.class.aasm(state_machine_name).attribute_name) && send(self.class.aasm(state_machine_name).attribute_name).blank? # Rails 4
154
+ if aasm_column_is_blank?(state_machine_name)
155
+ aasm(state_machine_name).enter_initial_state
156
+ end
157
+ end
159
158
  end
160
159
 
161
- end
162
-
163
- module WriteStateWithoutPersistence
164
- # Writes <tt>state</tt> to the state column, but does not persist it to the database
165
- #
166
- # foo = Foo.find(1)
167
- # foo.aasm_current_state # => :opened
168
- # foo.close
169
- # foo.aasm_current_state # => :closed
170
- # Foo.find(1).aasm_current_state # => :opened
171
- # foo.save
172
- # foo.aasm_current_state # => :closed
173
- # Foo.find(1).aasm_current_state # => :closed
174
- #
175
- # NOTE: intended to be called from an event
176
- def aasm_write_state_without_persistence(state)
177
- write_attribute(self.class.aasm_column, state.to_s)
160
+ def aasm_column_is_blank?(state_machine_name)
161
+ attribute_name = self.class.aasm(state_machine_name).attribute_name
162
+ attribute_names.include?(attribute_name.to_s) &&
163
+ (send(attribute_name).respond_to?(:empty?) ? !!send(attribute_name).empty? : !send(attribute_name))
178
164
  end
179
- end
180
165
 
181
- module WriteState
182
- # Writes <tt>state</tt> to the state column and persists it to the database
183
- # using update_attribute (which bypasses validation)
184
- #
185
- # foo = Foo.find(1)
186
- # foo.aasm_current_state # => :opened
187
- # foo.close!
188
- # foo.aasm_current_state # => :closed
189
- # Foo.find(1).aasm_current_state # => :closed
190
- #
191
- # NOTE: intended to be called from an event
192
- def aasm_write_state(state)
193
- old_value = read_attribute(self.class.aasm_column)
194
- write_attribute(self.class.aasm_column, state.to_s)
195
-
196
- unless self.save(false)
197
- write_attribute(self.class.aasm_column, old_value)
198
- return false
166
+ def aasm_validate_states
167
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
168
+ unless aasm_skipping_validations(state_machine_name)
169
+ if aasm_invalid_state?(state_machine_name)
170
+ self.errors.add(AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.column , "is invalid")
171
+ end
172
+ end
199
173
  end
200
-
201
- true
202
174
  end
203
- end
204
175
 
205
- module ReadState
206
-
207
- # Returns the value of the aasm_column - called from <tt>aasm_current_state</tt>
208
- #
209
- # If it's a new record, and the aasm state column is blank it returns the initial state:
210
- #
211
- # class Foo < ActiveRecord::Base
212
- # include AASM
213
- # aasm_column :status
214
- # aasm_state :opened
215
- # aasm_state :closed
216
- # end
217
- #
218
- # foo = Foo.new
219
- # foo.current_state # => :opened
220
- # foo.close
221
- # foo.current_state # => :closed
222
- #
223
- # foo = Foo.find(1)
224
- # foo.current_state # => :opened
225
- # foo.aasm_state = nil
226
- # foo.current_state # => nil
227
- #
228
- # NOTE: intended to be called from an event
229
- #
230
- # This allows for nil aasm states - be sure to add validation to your model
231
- def aasm_read_state
232
- if new_record?
233
- send(self.class.aasm_column).blank? ? aasm_determine_state_name(self.class.aasm_initial_state) : send(self.class.aasm_column).to_sym
234
- else
235
- send(self.class.aasm_column).nil? ? nil : send(self.class.aasm_column).to_sym
236
- end
176
+ def aasm_invalid_state?(state_machine_name)
177
+ aasm(state_machine_name).current_state && !aasm(state_machine_name).states.include?(aasm(state_machine_name).current_state)
237
178
  end
238
- end
179
+ end # InstanceMethods
239
180
 
240
- module NamedScopeMethods
241
- def aasm_state_with_named_scope name, options = {}
242
- aasm_state_without_named_scope name, options
243
- self.named_scope name, :conditions => { "#{table_name}.#{self.aasm_column}" => name.to_s} unless self.respond_to?(name)
244
- end
245
- end
246
181
  end
247
- end
248
- end
182
+ end # Persistence
183
+ end # AASM
@@ -0,0 +1,89 @@
1
+ module AASM
2
+ module Persistence
3
+ module Base
4
+
5
+ def self.included(base) #:nodoc:
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ # Returns the value of the aasm.attribute_name - called from <tt>aasm.current_state</tt>
10
+ #
11
+ # If it's a new record, and the aasm state column is blank it returns the initial state
12
+ # (example provided here for ActiveRecord, but it's true for Mongoid as well):
13
+ #
14
+ # class Foo < ActiveRecord::Base
15
+ # include AASM
16
+ # aasm :column => :status do
17
+ # state :opened
18
+ # state :closed
19
+ # end
20
+ # end
21
+ #
22
+ # foo = Foo.new
23
+ # foo.current_state # => :opened
24
+ # foo.close
25
+ # foo.current_state # => :closed
26
+ #
27
+ # foo = Foo.find(1)
28
+ # foo.current_state # => :opened
29
+ # foo.aasm_state = nil
30
+ # foo.current_state # => nil
31
+ #
32
+ # NOTE: intended to be called from an event
33
+ #
34
+ # This allows for nil aasm states - be sure to add validation to your model
35
+ def aasm_read_state(name=:default)
36
+ state = send(self.class.aasm(name).attribute_name)
37
+ if !state || state.empty?
38
+ aasm_new_record? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : nil
39
+ else
40
+ state.to_sym
41
+ end
42
+ end
43
+
44
+ def aasm_new_record?
45
+ new_record?
46
+ end
47
+
48
+ module ClassMethods
49
+ def aasm_column(attribute_name=nil)
50
+ warn "[DEPRECATION] aasm_column is deprecated. Use aasm.attribute_name instead"
51
+ aasm.attribute_name(attribute_name)
52
+ end
53
+ end # ClassMethods
54
+
55
+ end # Base
56
+ end # Persistence
57
+
58
+ class Base
59
+ # make sure to create a (named) scope for each state
60
+ def state_with_scope(*args)
61
+ names = state_without_scope(*args)
62
+ names.each do |name|
63
+ create_scopes(name)
64
+ end
65
+ end
66
+ alias_method :state_without_scope, :state
67
+ alias_method :state, :state_with_scope
68
+
69
+ private
70
+
71
+ def create_scope?(name)
72
+ @state_machine.config.create_scopes && !@klass.respond_to?(name) && @klass.respond_to?(:aasm_create_scope)
73
+ end
74
+
75
+ def create_scope(name)
76
+ @klass.aasm_create_scope(@name, name) if create_scope?(name)
77
+ end
78
+
79
+ def create_scopes(name)
80
+ if namespace?
81
+ # Create default scopes even when namespace? for backward compatibility
82
+ namepaced_name = "#{namespace}_#{name}"
83
+ create_scope(namepaced_name)
84
+ end
85
+ create_scope(name)
86
+ end
87
+ end # Base
88
+
89
+ end # AASM
@@ -0,0 +1,94 @@
1
+ module AASM
2
+ module Persistence
3
+ module CoreDataQueryPersistence
4
+ # This method:
5
+ #
6
+ # * extends the model with ClassMethods
7
+ # * includes InstanceMethods
8
+ #
9
+ # Adds
10
+ #
11
+ # after_initialize :aasm_ensure_initial_state
12
+ #
13
+
14
+ def self.included(base)
15
+ base.send(:include, AASM::Persistence::Base)
16
+ base.send(:include, AASM::Persistence::CoreDataQueryPersistence::InstanceMethods)
17
+ base.extend AASM::Persistence::CoreDataQueryPersistence::ClassMethods
18
+
19
+ base.after_initialize :aasm_ensure_initial_state
20
+ end
21
+
22
+ module ClassMethods
23
+ def aasm_create_scope(state_machine_name, scope_name)
24
+ scope(scope_name.to_sym, lambda { where(aasm(state_machine_name).attribute_name.to_sym).eq(scope_name.to_s) })
25
+ end
26
+ end
27
+
28
+ module InstanceMethods
29
+
30
+ # Writes <tt>state</tt> to the state column and persists it to the database
31
+ # using update_attribute (which bypasses validation)
32
+ #
33
+ # foo = Foo.find(1)
34
+ # foo.aasm.current_state # => :opened
35
+ # foo.close!
36
+ # foo.aasm.current_state # => :closed
37
+ # Foo.find(1).aasm.current_state # => :closed
38
+ #
39
+ # NOTE: intended to be called from an event
40
+ def aasm_write_state(state, name=:default)
41
+ raise "Cowardly refusing to save the current CoreDataQuery context"
42
+ aasm_write_state_without_persistence(state, name)
43
+ end
44
+
45
+ # Writes <tt>state</tt> to the state column, but does not persist it to the database
46
+ #
47
+ # foo = Foo.find(1)
48
+ # foo.aasm.current_state # => :opened
49
+ # foo.close
50
+ # foo.aasm.current_state # => :closed
51
+ # Foo.find(1).aasm.current_state # => :opened
52
+ # foo.save
53
+ # foo.aasm.current_state # => :closed
54
+ # Foo.find(1).aasm.current_state # => :closed
55
+ #
56
+ # NOTE: intended to be called from an event
57
+ def aasm_write_state_without_persistence(state, name=:default)
58
+ write_attribute(self.class.aasm(name).attribute_name, state.to_s)
59
+ end
60
+
61
+ private
62
+
63
+ # Ensures that if the aasm_state column is nil and the record is new
64
+ # that the initial state gets populated before validation on create
65
+ #
66
+ # foo = Foo.new
67
+ # foo.aasm_state # => nil
68
+ # foo.valid?
69
+ # foo.aasm_state # => "open" (where :open is the initial state)
70
+ #
71
+ #
72
+ # foo = Foo.find(:first)
73
+ # foo.aasm_state # => 1
74
+ # foo.aasm_state = nil
75
+ # foo.valid?
76
+ # foo.aasm_state # => nil
77
+ #
78
+ def aasm_ensure_initial_state
79
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
80
+ next if !send(self.class.aasm(state_machine_name).attribute_name) || send(self.class.aasm(state_machine_name).attribute_name).empty?
81
+ send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s)
82
+ end
83
+ end
84
+ end # InstanceMethods
85
+
86
+ # module NamedScopeMethods
87
+ # def aasm_state_with_named_scope name, options = {}
88
+ # aasm_state_without_named_scope name, options
89
+ # self.named_scope name, :conditions => { "#{table_name}.#{self.aasm.attribute_name}" => name.to_s} unless self.respond_to?(name)
90
+ # end
91
+ # end
92
+ end
93
+ end # Persistence
94
+ end # AASM
@@ -0,0 +1,92 @@
1
+ module AASM
2
+ module Persistence
3
+ module DynamoidPersistence
4
+ def self.included(base)
5
+ base.send(:include, AASM::Persistence::Base)
6
+ base.send(:include, AASM::Persistence::DynamoidPersistence::InstanceMethods)
7
+
8
+ base.after_initialize :aasm_ensure_initial_state
9
+
10
+ # Because Dynamoid only use define_method to add attribute assignment method in Class.
11
+ #
12
+ # In AASM::Base.initialize, it redefines and calls super in this method without superclass method.
13
+ # We override method_missing to solve this problem.
14
+ #
15
+ base.class_eval %Q(
16
+ def method_missing(method_name, *arguments, &block)
17
+ if (AASM::StateMachineStore.fetch(self.class, true).machine_names.map { |state_machine_name| self.class.aasm(state_machine_name).attribute_name.to_s + "=" }).include? method_name.to_s
18
+ attribute_name = method_name.to_s.gsub("=", '')
19
+ write_attribute(attribute_name.to_sym, *arguments)
20
+ else
21
+ super
22
+ end
23
+ end
24
+ )
25
+ end
26
+
27
+ module InstanceMethods
28
+
29
+ # Writes <tt>state</tt> to the state column and persists it to the database
30
+ # using update_attribute (which bypasses validation)
31
+ #
32
+ # foo = Foo.find(1)
33
+ # foo.aasm.current_state # => :opened
34
+ # foo.close!
35
+ # foo.aasm.current_state # => :closed
36
+ # Foo.find(1).aasm.current_state # => :closed
37
+ #
38
+ # NOTE: intended to be called from an event
39
+ def aasm_write_state(state, name=:default)
40
+ old_value = read_attribute(self.class.aasm(name).attribute_name)
41
+ write_attribute(self.class.aasm(name).attribute_name, state.to_s)
42
+
43
+ unless self.save(:validate => false)
44
+ write_attribute(self.class.aasm(name).attribute_name, old_value)
45
+ return false
46
+ end
47
+
48
+ true
49
+ end
50
+
51
+ # Writes <tt>state</tt> to the state column, but does not persist it to the database
52
+ #
53
+ # foo = Foo.find(1)
54
+ # foo.aasm.current_state # => :opened
55
+ # foo.close
56
+ # foo.aasm.current_state # => :closed
57
+ # Foo.find(1).aasm.current_state # => :opened
58
+ # foo.save
59
+ # foo.aasm.current_state # => :closed
60
+ # Foo.find(1).aasm.current_state # => :closed
61
+ #
62
+ # NOTE: intended to be called from an event
63
+ def aasm_write_state_without_persistence(state, name=:default)
64
+ write_attribute(self.class.aasm(name).attribute_name, state.to_s)
65
+ end
66
+
67
+ private
68
+
69
+ # Ensures that if the aasm_state column is nil and the record is new
70
+ # that the initial state gets populated before validation on create
71
+ #
72
+ # foo = Foo.new
73
+ # foo.aasm_state # => nil
74
+ # foo.valid?
75
+ # foo.aasm_state # => "open" (where :open is the initial state)
76
+ #
77
+ #
78
+ # foo = Foo.find(:first)
79
+ # foo.aasm_state # => 1
80
+ # foo.aasm_state = nil
81
+ # foo.valid?
82
+ # foo.aasm_state # => nil
83
+ #
84
+ def aasm_ensure_initial_state
85
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
86
+ aasm(state_machine_name).enter_initial_state if !send(self.class.aasm(state_machine_name).attribute_name) || send(self.class.aasm(state_machine_name).attribute_name).empty?
87
+ end
88
+ end
89
+ end # InstanceMethods
90
+ end
91
+ end # Persistence
92
+ end # AASM