aasm 4.11.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
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"