aasm 4.11.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 (194) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.travis.yml +56 -23
  5. data/Appraisals +67 -0
  6. data/CHANGELOG.md +112 -0
  7. data/CONTRIBUTING.md +24 -0
  8. data/Dockerfile +44 -0
  9. data/Gemfile +3 -21
  10. data/Gemfile.lock_old +151 -0
  11. data/LICENSE +1 -1
  12. data/README.md +540 -139
  13. data/Rakefile +6 -1
  14. data/TESTING.md +25 -0
  15. data/aasm.gemspec +5 -0
  16. data/docker-compose.yml +40 -0
  17. data/gemfiles/norails.gemfile +10 -0
  18. data/gemfiles/rails_4.2.gemfile +13 -11
  19. data/gemfiles/rails_4.2_mongoid_5.gemfile +8 -11
  20. data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
  21. data/gemfiles/rails_5.0.gemfile +11 -18
  22. data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
  23. data/gemfiles/rails_5.1.gemfile +14 -0
  24. data/gemfiles/rails_5.2.gemfile +14 -0
  25. data/lib/aasm/aasm.rb +40 -29
  26. data/lib/aasm/base.rb +61 -11
  27. data/lib/aasm/configuration.rb +10 -0
  28. data/lib/aasm/core/event.rb +45 -37
  29. data/lib/aasm/core/invoker.rb +129 -0
  30. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  31. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  32. data/lib/aasm/core/invokers/literal_invoker.rb +47 -0
  33. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  34. data/lib/aasm/core/state.rb +22 -13
  35. data/lib/aasm/core/transition.rb +17 -69
  36. data/lib/aasm/dsl_helper.rb +24 -22
  37. data/lib/aasm/errors.rb +4 -6
  38. data/lib/aasm/instance_base.rb +22 -4
  39. data/lib/aasm/localizer.rb +13 -3
  40. data/lib/aasm/minitest/allow_event.rb +13 -0
  41. data/lib/aasm/minitest/allow_transition_to.rb +13 -0
  42. data/lib/aasm/minitest/have_state.rb +13 -0
  43. data/lib/aasm/minitest/transition_from.rb +21 -0
  44. data/lib/aasm/minitest.rb +5 -0
  45. data/lib/aasm/minitest_spec.rb +15 -0
  46. data/lib/aasm/persistence/active_record_persistence.rb +49 -105
  47. data/lib/aasm/persistence/base.rb +20 -5
  48. data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
  49. data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
  50. data/lib/aasm/persistence/mongoid_persistence.rb +26 -32
  51. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  52. data/lib/aasm/persistence/orm.rb +154 -0
  53. data/lib/aasm/persistence/plain_persistence.rb +2 -1
  54. data/lib/aasm/persistence/redis_persistence.rb +16 -11
  55. data/lib/aasm/persistence/sequel_persistence.rb +36 -64
  56. data/lib/aasm/persistence.rb +3 -3
  57. data/lib/aasm/rspec/allow_event.rb +5 -1
  58. data/lib/aasm/rspec/allow_transition_to.rb +5 -1
  59. data/lib/aasm/rspec/transition_from.rb +5 -1
  60. data/lib/aasm/state_machine.rb +4 -2
  61. data/lib/aasm/state_machine_store.rb +5 -2
  62. data/lib/aasm/version.rb +1 -1
  63. data/lib/aasm.rb +5 -2
  64. data/lib/generators/aasm/orm_helpers.rb +6 -0
  65. data/lib/generators/active_record/aasm_generator.rb +3 -1
  66. data/lib/generators/active_record/templates/migration.rb +1 -1
  67. data/lib/generators/active_record/templates/migration_existing.rb +1 -1
  68. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  69. data/lib/motion-aasm.rb +3 -1
  70. data/spec/database.rb +20 -7
  71. data/spec/en.yml +0 -3
  72. data/spec/generators/active_record_generator_spec.rb +49 -40
  73. data/spec/generators/mongoid_generator_spec.rb +4 -6
  74. data/spec/generators/no_brainer_generator_spec.rb +29 -0
  75. data/spec/{en_deprecated_style.yml → localizer_test_model_deprecated_style.yml} +6 -3
  76. data/spec/localizer_test_model_new_style.yml +11 -0
  77. data/spec/models/active_record/active_record_callback.rb +93 -0
  78. data/spec/models/active_record/complex_active_record_example.rb +5 -1
  79. data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
  80. data/spec/models/{invalid_persistor.rb → active_record/invalid_persistor.rb} +0 -2
  81. data/spec/models/active_record/localizer_test_model.rb +11 -3
  82. data/spec/models/active_record/namespaced.rb +16 -0
  83. data/spec/models/active_record/person.rb +23 -0
  84. data/spec/models/{silent_persistor.rb → active_record/silent_persistor.rb} +0 -2
  85. data/spec/models/active_record/simple_new_dsl.rb +15 -0
  86. data/spec/models/active_record/timestamp_example.rb +16 -0
  87. data/spec/models/{transactor.rb → active_record/transactor.rb} +25 -2
  88. data/spec/models/{validator.rb → active_record/validator.rb} +0 -2
  89. data/spec/models/active_record/work.rb +3 -0
  90. data/spec/models/{worker.rb → active_record/worker.rb} +0 -0
  91. data/spec/models/callbacks/basic.rb +5 -2
  92. data/spec/models/callbacks/with_state_arg.rb +5 -1
  93. data/spec/models/callbacks/with_state_arg_multiple.rb +4 -1
  94. data/spec/models/default_state.rb +1 -1
  95. data/spec/models/guard_arguments_check.rb +17 -0
  96. data/spec/models/guard_with_params.rb +1 -1
  97. data/spec/models/guardian_without_from_specified.rb +18 -0
  98. data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
  99. data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
  100. data/spec/models/mongoid/timestamp_example_mongoid.rb +20 -0
  101. data/spec/models/mongoid/validator_mongoid.rb +100 -0
  102. data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
  103. data/spec/models/namespaced_multiple_example.rb +14 -0
  104. data/spec/models/nobrainer/complex_no_brainer_example.rb +36 -0
  105. data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
  106. data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
  107. data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
  108. data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
  109. data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
  110. data/spec/models/{mongo_mapper/simple_mongo_mapper.rb → nobrainer/simple_no_brainer.rb} +8 -8
  111. data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
  112. data/spec/models/parametrised_event.rb +7 -0
  113. data/spec/models/{mongo_mapper/complex_mongo_mapper_example.rb → redis/complex_redis_example.rb} +8 -5
  114. data/spec/models/redis/redis_multiple.rb +20 -0
  115. data/spec/models/redis/redis_simple.rb +20 -0
  116. data/spec/models/sequel/complex_sequel_example.rb +4 -3
  117. data/spec/models/sequel/invalid_persistor.rb +52 -0
  118. data/spec/models/sequel/sequel_multiple.rb +13 -13
  119. data/spec/models/sequel/sequel_simple.rb +13 -12
  120. data/spec/models/sequel/silent_persistor.rb +50 -0
  121. data/spec/models/sequel/transactor.rb +112 -0
  122. data/spec/models/sequel/validator.rb +93 -0
  123. data/spec/models/sequel/worker.rb +12 -0
  124. data/spec/models/simple_example.rb +8 -0
  125. data/spec/models/simple_example_with_guard_args.rb +17 -0
  126. data/spec/models/simple_multiple_example.rb +12 -0
  127. data/spec/models/sub_class.rb +34 -0
  128. data/spec/models/timestamps_example.rb +19 -0
  129. data/spec/models/timestamps_with_named_machine_example.rb +13 -0
  130. data/spec/spec_helper.rb +15 -33
  131. data/spec/spec_helpers/active_record.rb +8 -0
  132. data/spec/spec_helpers/dynamoid.rb +35 -0
  133. data/spec/spec_helpers/mongoid.rb +26 -0
  134. data/spec/spec_helpers/nobrainer.rb +15 -0
  135. data/spec/spec_helpers/redis.rb +18 -0
  136. data/spec/spec_helpers/remove_warnings.rb +1 -0
  137. data/spec/spec_helpers/sequel.rb +7 -0
  138. data/spec/unit/abstract_class_spec.rb +27 -0
  139. data/spec/unit/api_spec.rb +79 -72
  140. data/spec/unit/callback_multiple_spec.rb +7 -3
  141. data/spec/unit/callbacks_spec.rb +37 -2
  142. data/spec/unit/complex_example_spec.rb +12 -3
  143. data/spec/unit/complex_multiple_example_spec.rb +20 -4
  144. data/spec/unit/event_multiple_spec.rb +1 -1
  145. data/spec/unit/event_spec.rb +29 -4
  146. data/spec/unit/exception_spec.rb +1 -1
  147. data/spec/unit/guard_arguments_check_spec.rb +9 -0
  148. data/spec/unit/guard_spec.rb +17 -0
  149. data/spec/unit/guard_with_params_spec.rb +4 -0
  150. data/spec/unit/guard_without_from_specified_spec.rb +10 -0
  151. data/spec/unit/inspection_multiple_spec.rb +9 -5
  152. data/spec/unit/inspection_spec.rb +7 -3
  153. data/spec/unit/invoker_spec.rb +189 -0
  154. data/spec/unit/invokers/base_invoker_spec.rb +72 -0
  155. data/spec/unit/invokers/class_invoker_spec.rb +95 -0
  156. data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
  157. data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
  158. data/spec/unit/localizer_spec.rb +85 -52
  159. data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
  160. data/spec/unit/namespaced_multiple_example_spec.rb +22 -0
  161. data/spec/unit/override_warning_spec.rb +8 -0
  162. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +468 -447
  163. data/spec/unit/persistence/active_record_persistence_spec.rb +639 -486
  164. data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +4 -9
  165. data/spec/unit/persistence/dynamoid_persistence_spec.rb +4 -9
  166. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +83 -13
  167. data/spec/unit/persistence/mongoid_persistence_spec.rb +97 -13
  168. data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
  169. data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
  170. data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
  171. data/spec/unit/persistence/redis_persistence_spec.rb +8 -32
  172. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +6 -11
  173. data/spec/unit/persistence/sequel_persistence_spec.rb +278 -10
  174. data/spec/unit/rspec_matcher_spec.rb +9 -0
  175. data/spec/unit/simple_example_spec.rb +15 -0
  176. data/spec/unit/simple_multiple_example_spec.rb +28 -0
  177. data/spec/unit/state_spec.rb +23 -7
  178. data/spec/unit/subclassing_multiple_spec.rb +37 -2
  179. data/spec/unit/subclassing_spec.rb +17 -2
  180. data/spec/unit/timestamps_spec.rb +32 -0
  181. data/spec/unit/transition_spec.rb +1 -1
  182. data/test/minitest_helper.rb +57 -0
  183. data/test/unit/minitest_matcher_test.rb +80 -0
  184. metadata +213 -37
  185. data/callbacks.txt +0 -51
  186. data/gemfiles/rails_3.2_stable.gemfile +0 -15
  187. data/gemfiles/rails_4.0.gemfile +0 -16
  188. data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -16
  189. data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -17
  190. data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -163
  191. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
  192. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
  193. data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -149
  194. data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -96
@@ -0,0 +1,154 @@
1
+ module AASM
2
+ module Persistence
3
+ # This module adds transactional support for any database that supports it.
4
+ # This includes rollback capability and rollback/commit callbacks.
5
+ module ORM
6
+
7
+ # Writes <tt>state</tt> to the state column and persists it to the database
8
+ #
9
+ # foo = Foo.find(1)
10
+ # foo.aasm.current_state # => :opened
11
+ # foo.close!
12
+ # foo.aasm.current_state # => :closed
13
+ # Foo.find(1).aasm.current_state # => :closed
14
+ #
15
+ # NOTE: intended to be called from an event
16
+ def aasm_write_state(state, name=:default)
17
+ attribute_name = self.class.aasm(name).attribute_name
18
+ old_value = aasm_read_attribute(attribute_name)
19
+ aasm_write_state_attribute state, name
20
+
21
+ success = if aasm_skipping_validations(name)
22
+ aasm_update_column(attribute_name, aasm_raw_attribute_value(state, name))
23
+ else
24
+ aasm_save
25
+ end
26
+
27
+ unless success
28
+ aasm_rollback(name, old_value)
29
+ aasm_raise_invalid_record if aasm_whiny_persistence(name)
30
+ end
31
+
32
+ success
33
+ end
34
+
35
+ # Writes <tt>state</tt> to the state field, but does not persist it to the database
36
+ #
37
+ # foo = Foo.find(1)
38
+ # foo.aasm.current_state # => :opened
39
+ # foo.close
40
+ # foo.aasm.current_state # => :closed
41
+ # Foo.find(1).aasm.current_state # => :opened
42
+ # foo.save
43
+ # foo.aasm.current_state # => :closed
44
+ # Foo.find(1).aasm.current_state # => :closed
45
+ #
46
+ # NOTE: intended to be called from an event
47
+ def aasm_write_state_without_persistence(state, name=:default)
48
+ aasm_write_state_attribute(state, name)
49
+ end
50
+
51
+ private
52
+
53
+ # Save the record and return true if it succeeded/false otherwise.
54
+ def aasm_save
55
+ raise("Define #aasm_save_without_error in the AASM Persistence class.")
56
+ end
57
+
58
+ def aasm_raise_invalid_record
59
+ raise("Define #aasm_raise_invalid_record in the AASM Persistence class.")
60
+ end
61
+
62
+ # Update only the column without running validations.
63
+ def aasm_update_column(attribute_name, value)
64
+ raise("Define #aasm_update_column in the AASM Persistence class.")
65
+ end
66
+
67
+ def aasm_read_attribute(name)
68
+ raise("Define #aasm_read_attribute the AASM Persistence class.")
69
+ end
70
+
71
+ def aasm_write_attribute(name, value)
72
+ raise("Define #aasm_write_attribute in the AASM Persistence class.")
73
+ end
74
+
75
+ # Returns true or false if transaction completed successfully.
76
+ def aasm_transaction(requires_new, requires_lock)
77
+ raise("Define #aasm_transaction the AASM Persistence class.")
78
+ end
79
+
80
+ def aasm_supports_transactions?
81
+ true
82
+ end
83
+
84
+ def aasm_execute_after_commit
85
+ yield
86
+ end
87
+
88
+ def aasm_write_state_attribute(state, name=:default)
89
+ aasm_write_attribute(self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name))
90
+ end
91
+
92
+ def aasm_raw_attribute_value(state, _name=:default)
93
+ state.to_s
94
+ end
95
+
96
+ def aasm_rollback(name, old_value)
97
+ aasm_write_attribute(self.class.aasm(name).attribute_name, old_value)
98
+ false
99
+ end
100
+
101
+ def aasm_whiny_persistence(state_machine_name)
102
+ AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.whiny_persistence
103
+ end
104
+
105
+ def aasm_skipping_validations(state_machine_name)
106
+ AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.skip_validation_on_save
107
+ end
108
+
109
+ def use_transactions?(state_machine_name)
110
+ AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.use_transactions
111
+ end
112
+
113
+ def requires_new?(state_machine_name)
114
+ AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.requires_new_transaction
115
+ end
116
+
117
+ def requires_lock?(state_machine_name)
118
+ AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.requires_lock
119
+ end
120
+
121
+ # Returns true if event was fired successfully and transaction completed.
122
+ def aasm_fire_event(state_machine_name, name, options, *args, &block)
123
+ return super unless aasm_supports_transactions? && options[:persist]
124
+
125
+ event = self.class.aasm(state_machine_name).state_machine.events[name]
126
+ event.fire_callbacks(:before_transaction, self, *args)
127
+ event.fire_global_callbacks(:before_all_transactions, self, *args)
128
+
129
+ begin
130
+ success = if options[:persist] && use_transactions?(state_machine_name)
131
+ aasm_transaction(requires_new?(state_machine_name), requires_lock?(state_machine_name)) do
132
+ super
133
+ end
134
+ else
135
+ super
136
+ end
137
+
138
+ if success && !(event.options.keys & [:after_commit, :after_all_commits]).empty?
139
+ aasm_execute_after_commit do
140
+ event.fire_callbacks(:after_commit, self, *args)
141
+ event.fire_global_callbacks(:after_all_commits, self, *args)
142
+ end
143
+ end
144
+
145
+ success
146
+ ensure
147
+ event.fire_callbacks(:after_transaction, self, *args)
148
+ event.fire_global_callbacks(:after_all_transactions, self, *args)
149
+ end
150
+ end
151
+
152
+ end # Transactional
153
+ end # Persistence
154
+ end # AASM
@@ -5,7 +5,8 @@ module AASM
5
5
  # may be overwritten by persistence mixins
6
6
  def aasm_read_state(name=:default)
7
7
  # all the following lines behave like @current_state ||= aasm(name).enter_initial_state
8
- current = aasm(name).instance_variable_get("@current_state_#{name}")
8
+ current = aasm(name).instance_variable_defined?("@current_state_#{name}") &&
9
+ aasm(name).instance_variable_get("@current_state_#{name}")
9
10
  return current if current
10
11
  aasm(name).instance_variable_set("@current_state_#{name}", aasm(name).enter_initial_state)
11
12
  end
@@ -8,13 +8,12 @@ module AASM
8
8
  end
9
9
 
10
10
  module InstanceMethods
11
- # Add the inital value to intiializer
11
+ # Initialize with default values
12
12
  #
13
- # redis-objects removed the key from redis when set to nil
13
+ # Redis::Objects removes the key from Redis when set to `nil`
14
14
  def initialize(*args)
15
15
  super
16
- state = send(self.class.aasm.attribute_name)
17
- state.value = aasm.determine_state_name(self.class.aasm.initial_state)
16
+ aasm_ensure_initial_state
18
17
  end
19
18
  # Returns the value of the aasm.attribute_name - called from <tt>aasm.current_state</tt>
20
19
  #
@@ -68,8 +67,10 @@ module AASM
68
67
  # foo.aasm_state # => nil
69
68
  #
70
69
  def aasm_ensure_initial_state
71
- aasm.enter_initial_state if
72
- send(self.class.aasm.attribute_name).to_s.strip.empty?
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
73
74
  end
74
75
 
75
76
  # Writes <tt>state</tt> to the state column and persists it to the database
@@ -81,12 +82,16 @@ module AASM
81
82
  # Foo[1].aasm.current_state # => :closed
82
83
  #
83
84
  # NOTE: intended to be called from an event
84
- def aasm_write_state(state)
85
- aasm_column = self.class.aasm.attribute_name
86
- self.send("#{aasm_column}=", state)
85
+ def aasm_write_state(state, name=:default)
86
+ aasm_column = self.class.aasm(name).attribute_name
87
+ send("#{aasm_column}").value = state
87
88
  end
88
89
 
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.
90
95
  #
91
96
  # foo = Foo[1]
92
97
  # foo.aasm.current_state # => :opened
@@ -98,8 +103,8 @@ module AASM
98
103
  # Foo[1].aasm.current_state # => :closed
99
104
  #
100
105
  # NOTE: intended to be called from an event
101
- def aasm_write_state_without_persistence(state)
102
- send("#{self.class.aasm.attribute_name}=", state)
106
+ def aasm_write_state_without_persistence(state, name=:default)
107
+ aasm_write_state(state, name)
103
108
  end
104
109
  end
105
110
  end
@@ -1,8 +1,10 @@
1
+ require 'aasm/persistence/orm'
1
2
  module AASM
2
3
  module Persistence
3
4
  module SequelPersistence
4
5
  def self.included(base)
5
6
  base.send(:include, AASM::Persistence::Base)
7
+ base.send(:include, AASM::Persistence::ORM)
6
8
  base.send(:include, AASM::Persistence::SequelPersistence::InstanceMethods)
7
9
  end
8
10
 
@@ -13,46 +15,45 @@ module AASM
13
15
  end
14
16
 
15
17
  def before_create
16
- aasm_ensure_initial_state
17
18
  super
18
19
  end
19
20
 
20
- # Returns the value of the aasm.attribute_name - called from <tt>aasm.current_state</tt>
21
- #
22
- # If it's a new record, and the aasm state column is blank it returns the initial state
23
- #
24
- # class Foo < Sequel::Model
25
- # include AASM
26
- # aasm :column => :status do
27
- # state :opened
28
- # state :closed
29
- # end
30
- # end
31
- #
32
- # foo = Foo.new
33
- # foo.current_state # => :opened
34
- # foo.close
35
- # foo.current_state # => :closed
36
- #
37
- # foo = Foo[1]
38
- # foo.current_state # => :opened
39
- # foo.aasm_state = nil
40
- # foo.current_state # => nil
41
- #
42
- # NOTE: intended to be called from an event
43
- #
44
- # This allows for nil aasm states - be sure to add validation to your model
45
- def aasm_read_state(name=:default)
46
- state = send(self.class.aasm(name).attribute_name)
47
- if new? && state.to_s.strip.empty?
48
- aasm(name).determine_state_name(self.class.aasm(name).initial_state)
49
- elsif state.nil?
50
- nil
51
- else
52
- state.to_sym
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
53
50
  end
54
51
  end
55
52
 
53
+ def aasm_update_column(attribute_name, value)
54
+ this.update(attribute_name => value)
55
+ end
56
+
56
57
  # Ensures that if the aasm_state column is nil and the record is new
57
58
  # that the initial state gets populated before validation on create
58
59
  #
@@ -72,39 +73,10 @@ module AASM
72
73
  AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
73
74
  aasm(state_machine_name).enter_initial_state if
74
75
  (new? || values.key?(self.class.aasm(state_machine_name).attribute_name)) &&
75
- send(self.class.aasm(state_machine_name).attribute_name).to_s.strip.empty?
76
+ send(self.class.aasm(state_machine_name).attribute_name).to_s.strip.empty?
76
77
  end
77
78
  end
78
79
 
79
- # Writes <tt>state</tt> to the state column and persists it to the database
80
- #
81
- # foo = Foo[1]
82
- # foo.aasm.current_state # => :opened
83
- # foo.close!
84
- # foo.aasm.current_state # => :closed
85
- # Foo[1].aasm.current_state # => :closed
86
- #
87
- # NOTE: intended to be called from an event
88
- def aasm_write_state state, name=:default
89
- aasm_column = self.class.aasm(name).attribute_name
90
- update_only({aasm_column => state.to_s}, aasm_column)
91
- end
92
-
93
- # Writes <tt>state</tt> to the state column, but does not persist it to the database
94
- #
95
- # foo = Foo[1]
96
- # foo.aasm.current_state # => :opened
97
- # foo.close
98
- # foo.aasm.current_state # => :closed
99
- # Foo[1].aasm.current_state # => :opened
100
- # foo.save
101
- # foo.aasm.current_state # => :closed
102
- # Foo[1].aasm.current_state # => :closed
103
- #
104
- # NOTE: intended to be called from an event
105
- def aasm_write_state_without_persistence state, name=:default
106
- send("#{self.class.aasm(name).attribute_name}=", state.to_s)
107
- end
108
80
  end
109
81
  end
110
82
  end
@@ -12,9 +12,9 @@ module AASM
12
12
  elsif hierarchy.include?("Mongoid::Document")
13
13
  require_persistence :mongoid
14
14
  include_persistence base, :mongoid
15
- elsif hierarchy.include?("MongoMapper::Document")
16
- require_persistence :mongo_mapper
17
- include_persistence base, :mongo_mapper
15
+ elsif hierarchy.include?("NoBrainer::Document")
16
+ require_persistence :no_brainer
17
+ include_persistence base, :no_brainer
18
18
  elsif hierarchy.include?("Sequel::Model")
19
19
  require_persistence :sequel
20
20
  include_persistence base, :sequel
@@ -1,13 +1,17 @@
1
1
  RSpec::Matchers.define :allow_event do |event|
2
2
  match do |obj|
3
3
  @state_machine_name ||= :default
4
- obj.aasm(@state_machine_name).may_fire_event?(event)
4
+ obj.aasm(@state_machine_name).may_fire_event?(event, *@args)
5
5
  end
6
6
 
7
7
  chain :on do |state_machine_name|
8
8
  @state_machine_name = state_machine_name
9
9
  end
10
10
 
11
+ chain :with do |*args|
12
+ @args = args
13
+ end
14
+
11
15
  description do
12
16
  "allow event #{expected} (on :#{@state_machine_name})"
13
17
  end
@@ -1,13 +1,17 @@
1
1
  RSpec::Matchers.define :allow_transition_to do |state|
2
2
  match do |obj|
3
3
  @state_machine_name ||= :default
4
- obj.aasm(@state_machine_name).states(:permitted => true).include?(state)
4
+ obj.aasm(@state_machine_name).states({:permitted => true}, *@args).include?(state)
5
5
  end
6
6
 
7
7
  chain :on do |state_machine_name|
8
8
  @state_machine_name = state_machine_name
9
9
  end
10
10
 
11
+ chain :with do |*args|
12
+ @args = args
13
+ end
14
+
11
15
  description do
12
16
  "allow transition to #{expected} (on :#{@state_machine_name})"
13
17
  end
@@ -2,7 +2,11 @@ RSpec::Matchers.define :transition_from do |from_state|
2
2
  match do |obj|
3
3
  @state_machine_name ||= :default
4
4
  obj.aasm(@state_machine_name).current_state = from_state.to_sym
5
- obj.send(@event, *@args) && obj.aasm(@state_machine_name).current_state == @to_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
6
10
  end
7
11
 
8
12
  chain :on do |state_machine_name|
@@ -16,8 +16,10 @@ module AASM
16
16
  # called internally by Ruby 1.9 after clone()
17
17
  def initialize_copy(orig)
18
18
  super
19
- @states = @states.dup
20
- @events = @events.dup
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
21
23
  end
22
24
 
23
25
  def add_state(state_name, klass, options)
@@ -1,8 +1,11 @@
1
+ require 'concurrent'
1
2
  module AASM
2
3
  class StateMachineStore
4
+ @stores = Concurrent::Map.new
5
+
3
6
  class << self
4
7
  def stores
5
- @stores ||= {}
8
+ @stores
6
9
  end
7
10
 
8
11
  # do not overwrite existing state machines, which could have been created by
@@ -38,7 +41,7 @@ module AASM
38
41
  end
39
42
 
40
43
  def initialize
41
- @machines = {}
44
+ @machines = Concurrent::Map.new
42
45
  end
43
46
 
44
47
  def clone
data/lib/aasm/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module AASM
2
- VERSION = "4.11.1"
2
+ VERSION = "5.2.0"
3
3
  end
data/lib/aasm.rb CHANGED
@@ -1,5 +1,3 @@
1
- require 'ostruct'
2
-
3
1
  require 'aasm/version'
4
2
  require 'aasm/errors'
5
3
  require 'aasm/configuration'
@@ -9,6 +7,11 @@ require 'aasm/instance_base'
9
7
  require 'aasm/core/transition'
10
8
  require 'aasm/core/event'
11
9
  require 'aasm/core/state'
10
+ require 'aasm/core/invoker'
11
+ require 'aasm/core/invokers/base_invoker'
12
+ require 'aasm/core/invokers/class_invoker'
13
+ require 'aasm/core/invokers/literal_invoker'
14
+ require 'aasm/core/invokers/proc_invoker'
12
15
  require 'aasm/localizer'
13
16
  require 'aasm/state_machine_store'
14
17
  require 'aasm/state_machine'
@@ -22,6 +22,12 @@ RUBY
22
22
 
23
23
  private
24
24
 
25
+ def column_exists?
26
+ table_name.singularize.humanize.constantize.column_names.include?(column_name.to_s)
27
+ rescue NameError
28
+ false
29
+ end
30
+
25
31
  def model_exists?
26
32
  File.exists?(File.join(destination_root, model_path))
27
33
  end
@@ -11,7 +11,9 @@ module ActiveRecord
11
11
  source_root File.expand_path("../templates", __FILE__)
12
12
 
13
13
  def copy_aasm_migration
14
- if model_exists?
14
+ if column_exists?
15
+ puts "Both model and column exists"
16
+ elsif model_exists?
15
17
  migration_template "migration_existing.rb", "db/migrate/add_#{column_name}_to_#{table_name}.rb"
16
18
  else
17
19
  migration_template "migration.rb", "db/migrate/aasm_create_#{table_name}.rb"
@@ -1,4 +1,4 @@
1
- class AASMCreate<%= table_name.camelize %> < ActiveRecord::Migration
1
+ class AASMCreate<%= table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
2
2
  def change
3
3
  create_table(:<%= table_name %>) do |t|
4
4
  t.string :<%= column_name %>
@@ -1,4 +1,4 @@
1
- class Add<%= column_name.camelize %>To<%= table_name.camelize %> < ActiveRecord::Migration
1
+ class Add<%= column_name.camelize %>To<%= table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecord::VERSION::STRING.to_f %>]
2
2
  def change
3
3
  add_column :<%= table_name %>, :<%= column_name %>, :string
4
4
  end
@@ -0,0 +1,28 @@
1
+ require 'rails/generators/named_base'
2
+ require 'generators/aasm/orm_helpers'
3
+
4
+ module NoBrainer
5
+ module Generators
6
+ class AASMGenerator < Rails::Generators::NamedBase
7
+ include AASM::Generators::OrmHelpers
8
+ namespace 'nobrainer:aasm'
9
+ argument :column_name, type: :string, default: 'aasm_state'
10
+
11
+ def generate_model
12
+ invoke 'nobrainer:model', [name] unless model_exists?
13
+ end
14
+
15
+ def inject_aasm_content
16
+ inject_into_file model_path, model_contents, after: "include NoBrainer::Document::Timestamps\n" if model_exists?
17
+ end
18
+
19
+ def inject_field_types
20
+ inject_into_file model_path, migration_data, after: "include NoBrainer::Document::Timestamps\n" if model_exists?
21
+ end
22
+
23
+ def migration_data
24
+ " field :#{column_name}"
25
+ end
26
+ end
27
+ end
28
+ end
data/lib/motion-aasm.rb CHANGED
@@ -11,10 +11,12 @@ file_dependencies = {
11
11
 
12
12
  exclude_files = [
13
13
  'aasm/rspec.*',
14
+ 'aasm/minitest.*',
15
+ 'aasm/minitest_spec.*',
14
16
  'aasm/persistence/active_record_persistence.rb',
15
17
  'aasm/persistence/dynamoid_persistence.rb',
16
- 'aasm/persistence/mongo_mapper_persistence.rb',
17
18
  'aasm/persistence/mongoid_persistence.rb',
19
+ 'aasm/persistence/no_brainer_persistence.rb',
18
20
  'aasm/persistence/sequel_persistence.rb',
19
21
  'aasm/persistence/redis_persistence.rb'
20
22
  ]
data/spec/database.rb CHANGED
@@ -5,11 +5,10 @@ ActiveRecord::Migration.suppress_messages do
5
5
  end
6
6
  end
7
7
 
8
- ActiveRecord::Migration.create_table "simple_new_dsls", :force => true do |t|
9
- t.string "status"
10
- end
11
- ActiveRecord::Migration.create_table "multiple_simple_new_dsls", :force => true do |t|
12
- t.string "status"
8
+ %w(simple_new_dsls multiple_simple_new_dsls implemented_abstract_class_dsls users multiple_namespaceds).each do |table_name|
9
+ ActiveRecord::Migration.create_table table_name, :force => true do |t|
10
+ t.string "status"
11
+ end
13
12
  end
14
13
 
15
14
  ActiveRecord::Migration.create_table "complex_active_record_examples", :force => true do |t|
@@ -17,14 +16,18 @@ ActiveRecord::Migration.suppress_messages do
17
16
  t.string "right"
18
17
  end
19
18
 
20
- %w(validators multiple_validators workers invalid_persistors multiple_invalid_persistors silent_persistors multiple_silent_persistors).each do |table_name|
19
+ ActiveRecord::Migration.create_table "works", :force => true do |t|
20
+ t.string "status"
21
+ end
22
+
23
+ %w(validators multiple_validators workers invalid_persistors multiple_invalid_persistors silent_persistors multiple_silent_persistors active_record_callbacks).each do |table_name|
21
24
  ActiveRecord::Migration.create_table table_name, :force => true do |t|
22
25
  t.string "name"
23
26
  t.string "status"
24
27
  end
25
28
  end
26
29
 
27
- %w(transactors no_lock_transactors lock_transactors lock_no_wait_transactors multiple_transactors).each do |table_name|
30
+ %w(transactors no_lock_transactors lock_transactors lock_no_wait_transactors no_transactors multiple_transactors).each do |table_name|
28
31
  ActiveRecord::Migration.create_table table_name, :force => true do |t|
29
32
  t.string "name"
30
33
  t.string "status"
@@ -41,4 +44,14 @@ ActiveRecord::Migration.suppress_messages do
41
44
  t.string "search"
42
45
  t.string "sync"
43
46
  end
47
+
48
+ ActiveRecord::Migration.create_table "instance_level_skip_validation_examples", :force => true do |t|
49
+ t.string "state"
50
+ t.string "some_string"
51
+ end
52
+
53
+ ActiveRecord::Migration.create_table "timestamp_examples", :force => true do |t|
54
+ t.string "aasm_state"
55
+ t.datetime "opened_at"
56
+ end
44
57
  end
data/spec/en.yml CHANGED
@@ -4,9 +4,6 @@ en:
4
4
  localizer_test_model:
5
5
  close: "Let's close it!"
6
6
 
7
- attributes:
8
- localizer_test_model:
9
- aasm_state/opened: "It's open now!"
10
7
  errors:
11
8
  messages:
12
9
  record_invalid: "Invalid record"