aasm 4.5.1 → 5.5.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 (186) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.md +809 -129
  4. data/lib/aasm/aasm.rb +74 -37
  5. data/lib/aasm/base.rb +188 -41
  6. data/lib/aasm/configuration.rb +27 -2
  7. data/lib/aasm/core/event.rb +75 -47
  8. data/lib/aasm/core/invoker.rb +129 -0
  9. data/lib/aasm/core/invokers/base_invoker.rb +75 -0
  10. data/lib/aasm/core/invokers/class_invoker.rb +52 -0
  11. data/lib/aasm/core/invokers/literal_invoker.rb +49 -0
  12. data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
  13. data/lib/aasm/core/state.rb +22 -13
  14. data/lib/aasm/core/transition.rb +30 -23
  15. data/lib/aasm/dsl_helper.rb +24 -22
  16. data/lib/aasm/errors.rb +8 -5
  17. data/lib/aasm/instance_base.rb +63 -15
  18. data/lib/aasm/localizer.rb +13 -3
  19. data/lib/aasm/minitest/allow_event.rb +13 -0
  20. data/lib/aasm/minitest/allow_transition_to.rb +13 -0
  21. data/lib/aasm/minitest/have_state.rb +13 -0
  22. data/lib/aasm/minitest/transition_from.rb +21 -0
  23. data/lib/aasm/minitest.rb +5 -0
  24. data/lib/aasm/minitest_spec.rb +15 -0
  25. data/lib/aasm/persistence/active_record_persistence.rb +87 -79
  26. data/lib/aasm/persistence/base.rb +30 -30
  27. data/lib/aasm/persistence/core_data_query_persistence.rb +94 -0
  28. data/lib/aasm/persistence/dynamoid_persistence.rb +92 -0
  29. data/lib/aasm/persistence/mongoid_persistence.rb +49 -35
  30. data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
  31. data/lib/aasm/persistence/orm.rb +154 -0
  32. data/lib/aasm/persistence/plain_persistence.rb +2 -1
  33. data/lib/aasm/persistence/redis_persistence.rb +112 -0
  34. data/lib/aasm/persistence/sequel_persistence.rb +37 -67
  35. data/lib/aasm/persistence.rb +20 -5
  36. data/lib/aasm/rspec/allow_event.rb +5 -1
  37. data/lib/aasm/rspec/allow_transition_to.rb +5 -1
  38. data/lib/aasm/rspec/transition_from.rb +8 -4
  39. data/lib/aasm/state_machine.rb +6 -12
  40. data/lib/aasm/state_machine_store.rb +76 -0
  41. data/lib/aasm/version.rb +1 -1
  42. data/lib/aasm.rb +8 -2
  43. data/lib/generators/aasm/aasm_generator.rb +16 -0
  44. data/lib/generators/aasm/orm_helpers.rb +41 -0
  45. data/lib/generators/active_record/aasm_generator.rb +40 -0
  46. data/lib/generators/active_record/templates/migration.rb +8 -0
  47. data/lib/generators/active_record/templates/migration_existing.rb +5 -0
  48. data/lib/generators/mongoid/aasm_generator.rb +28 -0
  49. data/lib/generators/nobrainer/aasm_generator.rb +28 -0
  50. data/lib/motion-aasm.rb +37 -0
  51. metadata +104 -259
  52. data/.document +0 -6
  53. data/.gitignore +0 -19
  54. data/.travis.yml +0 -37
  55. data/API +0 -34
  56. data/CHANGELOG.md +0 -272
  57. data/CODE_OF_CONDUCT.md +0 -13
  58. data/Gemfile +0 -15
  59. data/HOWTO +0 -12
  60. data/PLANNED_CHANGES.md +0 -11
  61. data/README_FROM_VERSION_3_TO_4.md +0 -240
  62. data/Rakefile +0 -26
  63. data/aasm.gemspec +0 -31
  64. data/callbacks.txt +0 -51
  65. data/gemfiles/rails_3.2.gemfile +0 -14
  66. data/gemfiles/rails_4.0.gemfile +0 -12
  67. data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -14
  68. data/gemfiles/rails_4.1.gemfile +0 -12
  69. data/gemfiles/rails_4.1_mongo_mapper.gemfile +0 -14
  70. data/gemfiles/rails_4.2.gemfile +0 -12
  71. data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -14
  72. data/gemfiles/rails_4.2_mongoid_5.gemfile +0 -12
  73. data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -157
  74. data/spec/database.rb +0 -63
  75. data/spec/database.yml +0 -3
  76. data/spec/en.yml +0 -9
  77. data/spec/en_deprecated_style.yml +0 -10
  78. data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +0 -25
  79. data/spec/models/active_record/complex_active_record_example.rb +0 -33
  80. data/spec/models/active_record/derivate_new_dsl.rb +0 -7
  81. data/spec/models/active_record/false_state.rb +0 -35
  82. data/spec/models/active_record/gate.rb +0 -39
  83. data/spec/models/active_record/localizer_test_model.rb +0 -34
  84. data/spec/models/active_record/no_direct_assignment.rb +0 -21
  85. data/spec/models/active_record/no_scope.rb +0 -21
  86. data/spec/models/active_record/persisted_state.rb +0 -12
  87. data/spec/models/active_record/provided_and_persisted_state.rb +0 -24
  88. data/spec/models/active_record/reader.rb +0 -7
  89. data/spec/models/active_record/readme_job.rb +0 -21
  90. data/spec/models/active_record/simple_new_dsl.rb +0 -17
  91. data/spec/models/active_record/thief.rb +0 -29
  92. data/spec/models/active_record/transient.rb +0 -6
  93. data/spec/models/active_record/with_enum.rb +0 -39
  94. data/spec/models/active_record/with_false_enum.rb +0 -31
  95. data/spec/models/active_record/with_true_enum.rb +0 -39
  96. data/spec/models/active_record/writer.rb +0 -6
  97. data/spec/models/basic_two_state_machines_example.rb +0 -25
  98. data/spec/models/callbacks/basic.rb +0 -78
  99. data/spec/models/callbacks/basic_multiple.rb +0 -75
  100. data/spec/models/callbacks/guard_within_block.rb +0 -66
  101. data/spec/models/callbacks/guard_within_block_multiple.rb +0 -66
  102. data/spec/models/callbacks/multiple_transitions_transition_guard.rb +0 -65
  103. data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +0 -65
  104. data/spec/models/callbacks/private_method.rb +0 -44
  105. data/spec/models/callbacks/private_method_multiple.rb +0 -44
  106. data/spec/models/callbacks/with_args.rb +0 -61
  107. data/spec/models/callbacks/with_args_multiple.rb +0 -61
  108. data/spec/models/callbacks/with_state_arg.rb +0 -26
  109. data/spec/models/callbacks/with_state_arg_multiple.rb +0 -26
  110. data/spec/models/complex_example.rb +0 -222
  111. data/spec/models/conversation.rb +0 -93
  112. data/spec/models/default_state.rb +0 -12
  113. data/spec/models/double_definer.rb +0 -21
  114. data/spec/models/foo.rb +0 -92
  115. data/spec/models/foo_callback_multiple.rb +0 -45
  116. data/spec/models/guardian.rb +0 -48
  117. data/spec/models/guardian_multiple.rb +0 -48
  118. data/spec/models/initial_state_proc.rb +0 -31
  119. data/spec/models/invalid_persistor.rb +0 -31
  120. data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +0 -37
  121. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
  122. data/spec/models/mongo_mapper/simple_mongo_mapper.rb +0 -23
  123. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
  124. data/spec/models/mongoid/complex_mongoid_example.rb +0 -37
  125. data/spec/models/mongoid/no_scope_mongoid.rb +0 -21
  126. data/spec/models/mongoid/simple_mongoid.rb +0 -23
  127. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +0 -25
  128. data/spec/models/no_initial_state.rb +0 -25
  129. data/spec/models/not_auto_loaded/process.rb +0 -21
  130. data/spec/models/parametrised_event.rb +0 -29
  131. data/spec/models/parametrised_event_multiple.rb +0 -29
  132. data/spec/models/process_with_new_dsl.rb +0 -31
  133. data/spec/models/provided_state.rb +0 -24
  134. data/spec/models/sequel/complex_sequel_example.rb +0 -45
  135. data/spec/models/sequel/sequel_multiple.rb +0 -25
  136. data/spec/models/sequel/sequel_simple.rb +0 -25
  137. data/spec/models/silencer.rb +0 -27
  138. data/spec/models/simple_example.rb +0 -15
  139. data/spec/models/simple_multiple_example.rb +0 -30
  140. data/spec/models/state_machine_with_failed_event.rb +0 -12
  141. data/spec/models/sub_class.rb +0 -7
  142. data/spec/models/sub_class_with_more_states.rb +0 -18
  143. data/spec/models/sub_classing.rb +0 -3
  144. data/spec/models/super_class.rb +0 -46
  145. data/spec/models/this_name_better_not_be_in_use.rb +0 -11
  146. data/spec/models/transactor.rb +0 -53
  147. data/spec/models/valid_state_name.rb +0 -23
  148. data/spec/models/validator.rb +0 -79
  149. data/spec/models/worker.rb +0 -2
  150. data/spec/spec_helper.rb +0 -25
  151. data/spec/unit/api_spec.rb +0 -77
  152. data/spec/unit/basic_two_state_machines_example_spec.rb +0 -10
  153. data/spec/unit/callback_multiple_spec.rb +0 -295
  154. data/spec/unit/callbacks_spec.rb +0 -296
  155. data/spec/unit/complex_example_spec.rb +0 -84
  156. data/spec/unit/complex_multiple_example_spec.rb +0 -99
  157. data/spec/unit/edge_cases_spec.rb +0 -16
  158. data/spec/unit/event_multiple_spec.rb +0 -73
  159. data/spec/unit/event_naming_spec.rb +0 -11
  160. data/spec/unit/event_spec.rb +0 -322
  161. data/spec/unit/guard_multiple_spec.rb +0 -60
  162. data/spec/unit/guard_spec.rb +0 -60
  163. data/spec/unit/initial_state_multiple_spec.rb +0 -15
  164. data/spec/unit/initial_state_spec.rb +0 -12
  165. data/spec/unit/inspection_multiple_spec.rb +0 -201
  166. data/spec/unit/inspection_spec.rb +0 -111
  167. data/spec/unit/localizer_spec.rb +0 -76
  168. data/spec/unit/memory_leak_spec.rb +0 -38
  169. data/spec/unit/new_dsl_spec.rb +0 -12
  170. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +0 -573
  171. data/spec/unit/persistence/active_record_persistence_spec.rb +0 -552
  172. data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -146
  173. data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -93
  174. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -127
  175. data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -79
  176. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +0 -153
  177. data/spec/unit/persistence/sequel_persistence_spec.rb +0 -100
  178. data/spec/unit/readme_spec.rb +0 -42
  179. data/spec/unit/reloading_spec.rb +0 -15
  180. data/spec/unit/rspec_matcher_spec.rb +0 -79
  181. data/spec/unit/simple_example_spec.rb +0 -42
  182. data/spec/unit/simple_multiple_example_spec.rb +0 -63
  183. data/spec/unit/state_spec.rb +0 -89
  184. data/spec/unit/subclassing_multiple_spec.rb +0 -39
  185. data/spec/unit/subclassing_spec.rb +0 -31
  186. data/spec/unit/transition_spec.rb +0 -291
@@ -1,5 +1,4 @@
1
- require_relative 'base'
2
-
1
+ require 'aasm/persistence/orm'
3
2
  module AASM
4
3
  module Persistence
5
4
  module ActiveRecordPersistence
@@ -30,101 +29,115 @@ module AASM
30
29
  #
31
30
  def self.included(base)
32
31
  base.send(:include, AASM::Persistence::Base)
32
+ base.send(:include, AASM::Persistence::ORM)
33
33
  base.send(:include, AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
34
+ base.extend AASM::Persistence::ActiveRecordPersistence::ClassMethods
34
35
 
35
- base.after_initialize do
36
- aasm_ensure_initial_state
37
- end
36
+ base.after_initialize :aasm_ensure_initial_state
38
37
 
39
38
  # ensure state is in the list of states
40
39
  base.validate :aasm_validate_states
41
40
  end
42
41
 
43
- module InstanceMethods
44
-
45
- # Writes <tt>state</tt> to the state column and persists 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 # => :closed
52
- #
53
- # NOTE: intended to be called from an event
54
- def aasm_write_state(state, name=:default)
55
- old_value = read_attribute(self.class.aasm(name).attribute_name)
56
- aasm_write_attribute state, name
57
-
58
- success = if aasm_skipping_validations(name)
59
- value = aasm_raw_attribute_value(state, name)
60
- self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm(name).attribute_name => value) == 1
42
+ module ClassMethods
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
61
49
  else
62
- self.save
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
63
56
  end
64
- unless success
65
- write_attribute(self.class.aasm(name).attribute_name, old_value)
66
- return false
57
+ end
58
+ end
59
+
60
+ module InstanceMethods
61
+
62
+ private
63
+
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
67
79
  end
80
+ end
68
81
 
69
- true
82
+ def aasm_raise_invalid_record
83
+ raise ActiveRecord::RecordInvalid.new(self)
70
84
  end
71
85
 
72
- # Writes <tt>state</tt> to the state column, but does not persist it to the database
73
- #
74
- # foo = Foo.find(1)
75
- # foo.aasm.current_state # => :opened
76
- # foo.close
77
- # foo.aasm.current_state # => :closed
78
- # Foo.find(1).aasm.current_state # => :opened
79
- # foo.save
80
- # foo.aasm.current_state # => :closed
81
- # Foo.find(1).aasm.current_state # => :closed
82
- #
83
- # NOTE: intended to be called from an event
84
- def aasm_write_state_without_persistence(state, name=:default)
85
- aasm_write_attribute(state, name)
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)
100
+ end
101
+
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
106
+ end
86
107
  end
87
108
 
88
- private
89
109
  def aasm_enum(name=:default)
90
- case AASM::StateMachine[self.class][name].config.enum
91
- when false then nil
92
- when true then aasm_guess_enum_method(name)
93
- when nil then aasm_guess_enum_method(name) if aasm_column_looks_like_enum(name)
94
- else AASM::StateMachine[self.class][name].config.enum
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
95
115
  end
96
116
  end
97
117
 
98
118
  def aasm_column_looks_like_enum(name=:default)
99
- self.class.columns_hash[self.class.aasm(name).attribute_name.to_s].type == :integer
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
100
123
  end
101
124
 
102
125
  def aasm_guess_enum_method(name=:default)
103
126
  self.class.aasm(name).attribute_name.to_s.pluralize.to_sym
104
127
  end
105
128
 
106
- def aasm_skipping_validations(state_machine_name)
107
- AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
108
- end
109
-
110
- def aasm_write_attribute(state, name=:default)
111
- write_attribute(self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name))
112
- end
113
-
114
129
  def aasm_raw_attribute_value(state, name=:default)
115
130
  if aasm_enum(name)
116
131
  self.class.send(aasm_enum(name))[state]
117
132
  else
118
- state.to_s
133
+ super
119
134
  end
120
135
  end
121
136
 
122
137
  # Ensures that if the aasm_state column is nil and the record is new
123
- # that the initial state gets populated before validation on create
138
+ # then the initial state gets populated after initialization
124
139
  #
125
140
  # foo = Foo.new
126
- # foo.aasm_state # => nil
127
- # foo.valid?
128
141
  # foo.aasm_state # => "open" (where :open is the initial state)
129
142
  #
130
143
  #
@@ -135,39 +148,34 @@ module AASM
135
148
  # foo.aasm_state # => nil
136
149
  #
137
150
  def aasm_ensure_initial_state
138
- AASM::StateMachine[self.class].keys.each do |state_machine_name|
151
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
139
152
  # checking via respond_to? does not work in Rails <= 3
140
153
  # if respond_to?(self.class.aasm(state_machine_name).attribute_name) && send(self.class.aasm(state_machine_name).attribute_name).blank? # Rails 4
141
- if attribute_names.include?(self.class.aasm(state_machine_name).attribute_name.to_s) && send(self.class.aasm(state_machine_name).attribute_name).blank?
154
+ if aasm_column_is_blank?(state_machine_name)
142
155
  aasm(state_machine_name).enter_initial_state
143
156
  end
144
157
  end
145
158
  end
146
159
 
147
- def aasm_fire_event(state_machine_name, name, options, *args, &block)
148
- success = options[:persist] ? self.class.transaction(:requires_new => requires_new?(state_machine_name)) { super } : super
149
-
150
- if success && options[:persist]
151
- event = self.class.aasm(state_machine_name).state_machine.events[name]
152
- event.fire_callbacks(:after_commit, self, *args)
153
- end
154
-
155
- success
156
- end
157
-
158
- def requires_new?(state_machine_name)
159
- AASM::StateMachine[self.class][state_machine_name].config.requires_new_transaction
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))
160
164
  end
161
165
 
162
166
  def aasm_validate_states
163
- AASM::StateMachine[self.class].keys.each do |state_machine_name|
167
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
164
168
  unless aasm_skipping_validations(state_machine_name)
165
- if aasm(state_machine_name).current_state && !aasm(state_machine_name).states.include?(aasm(state_machine_name).current_state)
166
- self.errors.add(AASM::StateMachine[self.class][state_machine_name].config.column , "is invalid")
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")
167
171
  end
168
172
  end
169
173
  end
170
174
  end
175
+
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)
178
+ end
171
179
  end # InstanceMethods
172
180
 
173
181
  end
@@ -34,13 +34,17 @@ module AASM
34
34
  # This allows for nil aasm states - be sure to add validation to your model
35
35
  def aasm_read_state(name=:default)
36
36
  state = send(self.class.aasm(name).attribute_name)
37
- if new_record?
38
- state.blank? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : state.to_sym
37
+ if !state || state.empty?
38
+ aasm_new_record? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : nil
39
39
  else
40
- state.blank? ? nil : state.to_sym
40
+ state.to_sym
41
41
  end
42
42
  end
43
43
 
44
+ def aasm_new_record?
45
+ new_record?
46
+ end
47
+
44
48
  module ClassMethods
45
49
  def aasm_column(attribute_name=nil)
46
50
  warn "[DEPRECATION] aasm_column is deprecated. Use aasm.attribute_name instead"
@@ -53,37 +57,33 @@ module AASM
53
57
 
54
58
  class Base
55
59
  # make sure to create a (named) scope for each state
56
- def state_with_scope(name, *args)
57
- state_without_scope(name, *args)
58
- if @state_machine.config.create_scopes && !@klass.respond_to?(name)
59
-
60
- if @klass.ancestors.map {|klass| klass.to_s}.include?("ActiveRecord::Base")
61
- conditions = {"#{@klass.table_name}.#{@klass.aasm(@name).attribute_name}" => name.to_s}
62
- if ActiveRecord::VERSION::MAJOR >= 3
63
- @klass.class_eval do
64
- scope name, lambda { where(conditions) }
65
- end
66
- else
67
- @klass.class_eval do
68
- named_scope name, :conditions => conditions
69
- end
70
- end
71
- elsif @klass.ancestors.map {|klass| klass.to_s}.include?("Mongoid::Document")
72
- klass = @klass
73
- state_machine_name = @name
74
- scope_options = lambda {
75
- klass.send(:where, {klass.aasm(state_machine_name).attribute_name.to_sym => name.to_s})
76
- }
77
- @klass.send(:scope, name, scope_options)
78
- elsif @klass.ancestors.map {|klass| klass.to_s}.include?("MongoMapper::Document")
79
- conditions = { @klass.aasm(@name).attribute_name.to_sym => name.to_s }
80
- @klass.scope(name, lambda { @klass.where(conditions) })
81
- end
82
-
60
+ def state_with_scope(*args)
61
+ names = state_without_scope(*args)
62
+ names.each do |name|
63
+ create_scopes(name)
83
64
  end
84
65
  end
85
66
  alias_method :state_without_scope, :state
86
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
87
  end # Base
88
88
 
89
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
@@ -1,5 +1,4 @@
1
- require_relative 'base'
2
-
1
+ require 'aasm/persistence/orm'
3
2
  module AASM
4
3
  module Persistence
5
4
  module MongoidPersistence
@@ -32,52 +31,58 @@ module AASM
32
31
  #
33
32
  def self.included(base)
34
33
  base.send(:include, AASM::Persistence::Base)
34
+ base.send(:include, AASM::Persistence::ORM)
35
35
  base.send(:include, AASM::Persistence::MongoidPersistence::InstanceMethods)
36
+ base.extend AASM::Persistence::MongoidPersistence::ClassMethods
36
37
 
37
38
  base.after_initialize :aasm_ensure_initial_state
38
39
  end
39
40
 
41
+ module ClassMethods
42
+ def aasm_create_scope(state_machine_name, scope_name)
43
+ scope_options = lambda {
44
+ send(
45
+ :where,
46
+ { aasm(state_machine_name).attribute_name.to_sym => scope_name.to_s }
47
+ )
48
+ }
49
+ send(:scope, scope_name, scope_options)
50
+ end
51
+ end
52
+
40
53
  module InstanceMethods
41
54
 
42
- # Writes <tt>state</tt> to the state column and persists it to the database
43
- # using update_attribute (which bypasses validation)
44
- #
45
- # foo = Foo.find(1)
46
- # foo.aasm.current_state # => :opened
47
- # foo.close!
48
- # foo.aasm.current_state # => :closed
49
- # Foo.find(1).aasm.current_state # => :closed
50
- #
51
- # NOTE: intended to be called from an event
52
- def aasm_write_state(state, name=:default)
53
- old_value = read_attribute(self.class.aasm(name).attribute_name)
54
- write_attribute(self.class.aasm(name).attribute_name, state.to_s)
55
+ private
56
+
57
+ def aasm_save
58
+ self.save
59
+ end
55
60
 
56
- unless self.save(:validate => false)
57
- write_attribute(self.class.aasm(name).attribute_name, old_value)
58
- return false
61
+ def aasm_raise_invalid_record
62
+ raise Mongoid::Errors::Validations.new(self)
63
+ end
64
+
65
+ def aasm_supports_transactions?
66
+ false
67
+ end
68
+
69
+ def aasm_update_column(attribute_name, value)
70
+ if Mongoid::VERSION.to_f >= 4
71
+ set(Hash[attribute_name, value])
72
+ else
73
+ set(attribute_name, value)
59
74
  end
60
75
 
61
76
  true
62
77
  end
63
78
 
64
- # Writes <tt>state</tt> to the state column, but does not persist it to the database
65
- #
66
- # foo = Foo.find(1)
67
- # foo.aasm.current_state # => :opened
68
- # foo.close
69
- # foo.aasm.current_state # => :closed
70
- # Foo.find(1).aasm.current_state # => :opened
71
- # foo.save
72
- # foo.aasm.current_state # => :closed
73
- # Foo.find(1).aasm.current_state # => :closed
74
- #
75
- # NOTE: intended to be called from an event
76
- def aasm_write_state_without_persistence(state, name=:default)
77
- write_attribute(self.class.aasm(name).attribute_name, state.to_s)
79
+ def aasm_read_attribute(name)
80
+ read_attribute(name)
78
81
  end
79
82
 
80
- private
83
+ def aasm_write_attribute(name, value)
84
+ write_attribute(name, value)
85
+ end
81
86
 
82
87
  # Ensures that if the aasm_state column is nil and the record is new
83
88
  # that the initial state gets populated before validation on create
@@ -95,8 +100,17 @@ module AASM
95
100
  # foo.aasm_state # => nil
96
101
  #
97
102
  def aasm_ensure_initial_state
98
- AASM::StateMachine[self.class].keys.each do |state_machine_name|
99
- send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
103
+ AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
104
+ attribute_name = self.class.aasm(state_machine_name).attribute_name.to_s
105
+ # Do not load initial state when object attributes are not loaded,
106
+ # mongoid has_many relationship does not load child object attributes when
107
+ # only ids are loaded, for example parent.child_ids will not load child object attributes.
108
+ # This feature is introduced in mongoid > 4.
109
+ if attribute_names.include?(attribute_name) && !attributes[attribute_name] || attributes[attribute_name].empty?
110
+ # attribute_missing? is defined in mongoid > 4
111
+ return if Mongoid::VERSION.to_f >= 4 && attribute_missing?(attribute_name)
112
+ send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s)
113
+ end
100
114
  end
101
115
  end
102
116
  end # InstanceMethods