aasm 4.2.0 → 4.3.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -1
  3. data/Gemfile +2 -2
  4. data/PLANNED_CHANGES.md +24 -4
  5. data/README.md +75 -5
  6. data/lib/aasm/aasm.rb +50 -36
  7. data/lib/aasm/base.rb +36 -18
  8. data/lib/aasm/core/event.rb +6 -5
  9. data/lib/aasm/core/state.rb +3 -2
  10. data/lib/aasm/core/transition.rb +5 -4
  11. data/lib/aasm/errors.rb +7 -4
  12. data/lib/aasm/instance_base.rb +14 -13
  13. data/lib/aasm/localizer.rb +1 -1
  14. data/lib/aasm/persistence/active_record_persistence.rb +41 -66
  15. data/lib/aasm/persistence/base.rb +7 -7
  16. data/lib/aasm/persistence/mongo_mapper_persistence.rb +34 -51
  17. data/lib/aasm/persistence/mongoid_persistence.rb +15 -36
  18. data/lib/aasm/persistence/plain_persistence.rb +8 -7
  19. data/lib/aasm/persistence/sequel_persistence.rb +12 -10
  20. data/lib/aasm/state_machine.rb +11 -6
  21. data/lib/aasm/version.rb +1 -1
  22. data/spec/database.rb +27 -1
  23. data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +25 -0
  24. data/spec/models/active_record/complex_active_record_example.rb +33 -0
  25. data/spec/models/active_record/derivate_new_dsl.rb +4 -0
  26. data/spec/models/active_record/false_state.rb +18 -0
  27. data/spec/models/active_record/gate.rb +20 -0
  28. data/spec/models/active_record/no_direct_assignment.rb +11 -0
  29. data/spec/models/active_record/no_scope.rb +11 -0
  30. data/spec/models/active_record/provided_and_persisted_state.rb +3 -3
  31. data/spec/models/active_record/simple_new_dsl.rb +9 -0
  32. data/spec/models/active_record/thief.rb +15 -0
  33. data/spec/models/active_record/with_enum.rb +20 -0
  34. data/spec/models/active_record/with_false_enum.rb +16 -0
  35. data/spec/models/active_record/with_true_enum.rb +20 -0
  36. data/spec/models/basic_two_state_machines_example.rb +25 -0
  37. data/spec/models/callbacks/basic_multiple.rb +75 -0
  38. data/spec/models/callbacks/guard_within_block_multiple.rb +66 -0
  39. data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +65 -0
  40. data/spec/models/callbacks/private_method_multiple.rb +44 -0
  41. data/spec/models/callbacks/with_args_multiple.rb +61 -0
  42. data/spec/models/callbacks/{with_state_args.rb → with_state_arg.rb} +0 -0
  43. data/spec/models/callbacks/with_state_arg_multiple.rb +26 -0
  44. data/spec/models/complex_example.rb +134 -0
  45. data/spec/models/conversation.rb +47 -1
  46. data/spec/models/foo.rb +57 -0
  47. data/spec/models/foo_callback_multiple.rb +45 -0
  48. data/spec/models/guardian_multiple.rb +48 -0
  49. data/spec/models/initial_state_proc.rb +16 -0
  50. data/spec/models/invalid_persistor.rb +15 -0
  51. data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +37 -0
  52. data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +11 -0
  53. data/spec/models/mongo_mapper/simple_mongo_mapper.rb +12 -0
  54. data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +13 -0
  55. data/spec/models/mongoid/complex_mongoid_example.rb +37 -0
  56. data/spec/models/mongoid/no_scope_mongoid.rb +11 -0
  57. data/spec/models/mongoid/simple_mongoid.rb +12 -0
  58. data/spec/models/mongoid/simple_new_dsl_mongoid.rb +13 -0
  59. data/spec/models/no_initial_state.rb +13 -0
  60. data/spec/models/parametrised_event.rb +1 -1
  61. data/spec/models/parametrised_event_multiple.rb +29 -0
  62. data/spec/models/provided_state.rb +3 -3
  63. data/spec/models/sequel/complex_sequel_example.rb +45 -0
  64. data/spec/models/sequel/sequel_multiple.rb +25 -0
  65. data/spec/models/sequel/sequel_simple.rb +25 -0
  66. data/spec/models/simple_multiple_example.rb +30 -0
  67. data/spec/models/sub_class.rb +4 -0
  68. data/spec/models/sub_class_with_more_states.rb +11 -0
  69. data/spec/models/super_class.rb +28 -0
  70. data/spec/models/transactor.rb +27 -0
  71. data/spec/models/valid_state_name.rb +12 -0
  72. data/spec/models/validator.rb +39 -0
  73. data/spec/unit/basic_two_state_machines_example_spec.rb +10 -0
  74. data/spec/unit/callback_multiple_spec.rb +295 -0
  75. data/spec/unit/callbacks_spec.rb +1 -1
  76. data/spec/unit/complex_multiple_example_spec.rb +99 -0
  77. data/spec/unit/edge_cases_spec.rb +16 -0
  78. data/spec/unit/event_multiple_spec.rb +73 -0
  79. data/spec/unit/event_spec.rb +11 -6
  80. data/spec/unit/guard_multiple_spec.rb +60 -0
  81. data/spec/unit/initial_state_multiple_spec.rb +15 -0
  82. data/spec/unit/inspection_multiple_spec.rb +201 -0
  83. data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +560 -0
  84. data/spec/unit/persistence/active_record_persistence_spec.rb +17 -12
  85. data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +146 -0
  86. data/spec/unit/persistence/{mongo_mapper_persistance_spec.rb → mongo_mapper_persistence_spec.rb} +7 -49
  87. data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +127 -0
  88. data/spec/unit/persistence/mongoid_persistence_spec.rb +79 -0
  89. data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +153 -0
  90. data/spec/unit/persistence/sequel_persistence_spec.rb +7 -24
  91. data/spec/unit/reloading_spec.rb +1 -1
  92. data/spec/unit/simple_multiple_example_spec.rb +63 -0
  93. data/spec/unit/state_spec.rb +3 -1
  94. data/spec/unit/subclassing_multiple_spec.rb +39 -0
  95. data/spec/unit/transition_spec.rb +31 -22
  96. metadata +73 -9
  97. data/spec/unit/persistence/mongoid_persistance_spec.rb +0 -146
@@ -1,10 +1,11 @@
1
1
  module AASM::Core
2
2
  class State
3
- attr_reader :name, :options
3
+ attr_reader :name, :state_machine, :options
4
4
 
5
- def initialize(name, klass, options={})
5
+ def initialize(name, klass, state_machine, options={})
6
6
  @name = name
7
7
  @klass = klass
8
+ @state_machine = state_machine
8
9
  update(options)
9
10
  end
10
11
 
@@ -2,12 +2,13 @@ module AASM::Core
2
2
  class Transition
3
3
  include DslHelper
4
4
 
5
- attr_reader :from, :to, :opts
5
+ attr_reader :from, :to, :event, :opts
6
6
  alias_method :options, :opts
7
7
 
8
- def initialize(opts, &block)
8
+ def initialize(event, opts, &block)
9
9
  add_options_from_dsl(opts, [:on_transition, :guard, :after], &block) if block
10
10
 
11
+ @event = event
11
12
  @from = opts[:from]
12
13
  @to = opts[:to]
13
14
  @guards = Array(opts[:guards]) + Array(opts[:guard]) + Array(opts[:if])
@@ -44,8 +45,8 @@ module AASM::Core
44
45
 
45
46
  def invoke_callbacks_compatible_with_guard(code, record, args, options={})
46
47
  if record.respond_to?(:aasm)
47
- record.aasm.from_state = @from if record.aasm.respond_to?(:from_state=)
48
- record.aasm.to_state = @to if record.aasm.respond_to?(:to_state=)
48
+ record.aasm(event.state_machine.name).from_state = @from if record.aasm(event.state_machine.name).respond_to?(:from_state=)
49
+ record.aasm(event.state_machine.name).to_state = @to if record.aasm(event.state_machine.name).respond_to?(:to_state=)
49
50
  end
50
51
 
51
52
  case code
@@ -1,13 +1,16 @@
1
1
  module AASM
2
2
 
3
+ class UnknownStateMachineError < RuntimeError; end
4
+
3
5
  class InvalidTransition < RuntimeError
4
- attr_reader :object, :event_name
5
- def initialize(object, event_name)
6
- @object, @event_name = object, event_name
6
+ attr_reader :object, :event_name, :state_machine_name
7
+
8
+ def initialize(object, event_name, state_machine_name)
9
+ @object, @event_name, @state_machine_name = object, event_name, state_machine_name
7
10
  end
8
11
 
9
12
  def message
10
- "Event '#{event_name}' cannot transition from '#{object.aasm.current_state}'"
13
+ "Event '#{event_name}' cannot transition from '#{object.aasm(state_machine_name).current_state}'"
11
14
  end
12
15
  end
13
16
 
@@ -3,21 +3,22 @@ module AASM
3
3
 
4
4
  attr_accessor :from_state, :to_state, :current_event
5
5
 
6
- def initialize(instance)
6
+ def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
7
7
  @instance = instance
8
+ @name = name
8
9
  end
9
10
 
10
11
  def current_state
11
- @instance.aasm_read_state
12
+ @instance.aasm_read_state(@name)
12
13
  end
13
14
 
14
15
  def current_state=(state)
15
- @instance.aasm_write_state_without_persistence(state)
16
- @current_state = state
16
+ @instance.aasm_write_state_without_persistence(state, @name)
17
+ # @current_state = state
17
18
  end
18
19
 
19
20
  def enter_initial_state
20
- state_name = determine_state_name(@instance.class.aasm.initial_state)
21
+ state_name = determine_state_name(@instance.class.aasm(@name).initial_state)
21
22
  state_object = state_object_for_name(state_name)
22
23
 
23
24
  state_object.fire_callbacks(:before_enter, @instance)
@@ -29,24 +30,24 @@ module AASM
29
30
  end
30
31
 
31
32
  def human_state
32
- AASM::Localizer.new.human_state_name(@instance.class, current_state)
33
+ AASM::Localizer.new.human_state_name(@instance.class, state_object_for_name(current_state))
33
34
  end
34
35
 
35
36
  def states(options={})
36
37
  if options[:permitted]
37
38
  # ugliness level 1000
38
39
  permitted_event_names = events(:permitted => true).map(&:name)
39
- transitions = @instance.class.aasm.state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
40
+ transitions = @instance.class.aasm(@name).state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
40
41
  tos = transitions.map {|t| t[0] ? t[0].to : nil}.flatten.compact.map(&:to_sym).uniq
41
- @instance.class.aasm.states.select {|s| tos.include?(s.name.to_sym)}
42
+ @instance.class.aasm(@name).states.select {|s| tos.include?(s.name.to_sym)}
42
43
  else
43
- @instance.class.aasm.states
44
+ @instance.class.aasm(@name).states
44
45
  end
45
46
  end
46
47
 
47
48
  def events(options={})
48
49
  state = options[:state] || current_state
49
- events = @instance.class.aasm.events.select {|e| e.transitions_from_state?(state) }
50
+ events = @instance.class.aasm(@name).events.select {|e| e.transitions_from_state?(state) }
50
51
 
51
52
  if options[:permitted]
52
53
  # filters the results of events_for_current_state so that only those that
@@ -58,7 +59,7 @@ module AASM
58
59
  end
59
60
 
60
61
  def state_object_for_name(name)
61
- obj = @instance.class.aasm.states.find {|s| s == name}
62
+ obj = @instance.class.aasm(@name).states.find {|s| s == name}
62
63
  raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
63
64
  obj
64
65
  end
@@ -75,7 +76,7 @@ module AASM
75
76
  end
76
77
 
77
78
  def may_fire_event?(name, *args)
78
- if event = @instance.class.aasm.state_machine.events[name]
79
+ if event = @instance.class.aasm(@name).state_machine.events[name]
79
80
  !!event.may_fire?(@instance, *args)
80
81
  else
81
82
  false # unknown event
@@ -83,7 +84,7 @@ module AASM
83
84
  end
84
85
 
85
86
  def set_current_state_with_persistence(state)
86
- save_success = @instance.aasm_write_state(state)
87
+ save_success = @instance.aasm_write_state(state, @name)
87
88
  self.current_state = state if save_success
88
89
  save_success
89
90
  end
@@ -21,7 +21,7 @@ module AASM
21
21
 
22
22
  def item_for(klass, state, ancestor, options={})
23
23
  separator = options[:old_style] ? '.' : '/'
24
- :"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm.attribute_name}#{separator}#{state}"
24
+ :"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm(state.state_machine.name).attribute_name}#{separator}#{state}"
25
25
  end
26
26
 
27
27
  def translate_queue(checklist)
@@ -30,7 +30,6 @@ module AASM
30
30
  #
31
31
  def self.included(base)
32
32
  base.send(:include, AASM::Persistence::Base)
33
- base.extend AASM::Persistence::ActiveRecordPersistence::ClassMethods
34
33
  base.send(:include, AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
35
34
 
36
35
  base.after_initialize do
@@ -41,34 +40,6 @@ module AASM
41
40
  base.validate :aasm_validate_states
42
41
  end
43
42
 
44
- module ClassMethods
45
-
46
- def find_in_state(number, state, *args)
47
- with_state_scope state do
48
- find(number, *args)
49
- end
50
- end
51
-
52
- def count_in_state(state, *args)
53
- with_state_scope state do
54
- count(*args)
55
- end
56
- end
57
-
58
- def calculate_in_state(state, *args)
59
- with_state_scope state do
60
- calculate(*args)
61
- end
62
- end
63
-
64
- protected
65
- def with_state_scope(state)
66
- with_scope :find => {:conditions => ["#{table_name}.#{aasm_column} = ?", state.to_s]} do
67
- yield if block_given?
68
- end
69
- end
70
- end
71
-
72
43
  module InstanceMethods
73
44
 
74
45
  # Writes <tt>state</tt> to the state column and persists it to the database
@@ -80,18 +51,18 @@ module AASM
80
51
  # Foo.find(1).aasm.current_state # => :closed
81
52
  #
82
53
  # NOTE: intended to be called from an event
83
- def aasm_write_state(state)
84
- old_value = read_attribute(self.class.aasm.attribute_name)
85
- aasm_write_attribute state
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
86
57
 
87
- success = if aasm_skipping_validations
88
- value = aasm_raw_attribute_value state
89
- self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm.attribute_name => value) == 1
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
90
61
  else
91
62
  self.save
92
63
  end
93
64
  unless success
94
- write_attribute(self.class.aasm.attribute_name, old_value)
65
+ write_attribute(self.class.aasm(name).attribute_name, old_value)
95
66
  return false
96
67
  end
97
68
 
@@ -110,39 +81,39 @@ module AASM
110
81
  # Foo.find(1).aasm.current_state # => :closed
111
82
  #
112
83
  # NOTE: intended to be called from an event
113
- def aasm_write_state_without_persistence(state)
114
- aasm_write_attribute state
84
+ def aasm_write_state_without_persistence(state, name=:default)
85
+ aasm_write_attribute(state, name)
115
86
  end
116
87
 
117
88
  private
118
- def aasm_enum
119
- case AASM::StateMachine[self.class].config.enum
89
+ def aasm_enum(name=:default)
90
+ case AASM::StateMachine[self.class][name].config.enum
120
91
  when false then nil
121
- when true then aasm_guess_enum_method
122
- when nil then aasm_guess_enum_method if aasm_column_looks_like_enum
123
- else AASM::StateMachine[self.class].config.enum
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
124
95
  end
125
96
  end
126
97
 
127
- def aasm_column_looks_like_enum
128
- self.class.columns_hash[self.class.aasm.attribute_name.to_s].type == :integer
98
+ def aasm_column_looks_like_enum(name=:default)
99
+ self.class.columns_hash[self.class.aasm(name).attribute_name.to_s].type == :integer
129
100
  end
130
101
 
131
- def aasm_guess_enum_method
132
- self.class.aasm.attribute_name.to_s.pluralize.to_sym
102
+ def aasm_guess_enum_method(name=:default)
103
+ self.class.aasm(name).attribute_name.to_s.pluralize.to_sym
133
104
  end
134
105
 
135
- def aasm_skipping_validations
136
- AASM::StateMachine[self.class].config.skip_validation_on_save
106
+ def aasm_skipping_validations(state_machine_name)
107
+ AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
137
108
  end
138
109
 
139
- def aasm_write_attribute(state)
140
- write_attribute self.class.aasm.attribute_name, aasm_raw_attribute_value(state)
110
+ def aasm_write_attribute(state, name=:default)
111
+ write_attribute(self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name))
141
112
  end
142
113
 
143
- def aasm_raw_attribute_value(state)
144
- if aasm_enum
145
- self.class.send(aasm_enum)[state]
114
+ def aasm_raw_attribute_value(state, name=:default)
115
+ if aasm_enum(name)
116
+ self.class.send(aasm_enum(name))[state]
146
117
  else
147
118
  state.to_s
148
119
  end
@@ -164,32 +135,36 @@ module AASM
164
135
  # foo.aasm_state # => nil
165
136
  #
166
137
  def aasm_ensure_initial_state
167
- # checking via respond_to? does not work in Rails <= 3
168
- # if respond_to?(self.class.aasm.attribute_name) && send(self.class.aasm.attribute_name).blank? # Rails 4
169
- if attribute_names.include?(self.class.aasm.attribute_name.to_s) && send(self.class.aasm.attribute_name).blank?
170
- aasm.enter_initial_state
138
+ AASM::StateMachine[self.class].keys.each do |state_machine_name|
139
+ # checking via respond_to? does not work in Rails <= 3
140
+ # 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?
142
+ aasm(state_machine_name).enter_initial_state
143
+ end
171
144
  end
172
145
  end
173
146
 
174
- def aasm_fire_event(name, options, *args, &block)
175
- success = options[:persist] ? self.class.transaction(:requires_new => requires_new?) { super } : super
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
176
149
 
177
150
  if success && options[:persist]
178
- event = self.class.aasm.state_machine.events[name]
151
+ event = self.class.aasm(state_machine_name).state_machine.events[name]
179
152
  event.fire_callbacks(:after_commit, self, *args)
180
153
  end
181
154
 
182
155
  success
183
156
  end
184
157
 
185
- def requires_new?
186
- AASM::StateMachine[self.class].config.requires_new_transaction
158
+ def requires_new?(state_machine_name)
159
+ AASM::StateMachine[self.class][state_machine_name].config.requires_new_transaction
187
160
  end
188
161
 
189
162
  def aasm_validate_states
190
- unless aasm_skipping_validations
191
- if aasm.current_state && !aasm.states.include?(aasm.current_state)
192
- self.errors.add(AASM::StateMachine[self.class].config.column , "is invalid")
163
+ AASM::StateMachine[self.class].keys.each do |state_machine_name|
164
+ 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")
167
+ end
193
168
  end
194
169
  end
195
170
  end
@@ -32,10 +32,10 @@ module AASM
32
32
  # NOTE: intended to be called from an event
33
33
  #
34
34
  # This allows for nil aasm states - be sure to add validation to your model
35
- def aasm_read_state
36
- state = send(self.class.aasm.attribute_name)
35
+ def aasm_read_state(name=:default)
36
+ state = send(self.class.aasm(name).attribute_name)
37
37
  if new_record?
38
- state.blank? ? aasm.determine_state_name(self.class.aasm.initial_state) : state.to_sym
38
+ state.blank? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : state.to_sym
39
39
  else
40
40
  state.blank? ? nil : state.to_sym
41
41
  end
@@ -55,10 +55,10 @@ module AASM
55
55
  # make sure to create a (named) scope for each state
56
56
  def state_with_scope(name, *args)
57
57
  state_without_scope(name, *args)
58
- if AASM::StateMachine[@klass].config.create_scopes && !@klass.respond_to?(name)
58
+ if @state_machine.config.create_scopes && !@klass.respond_to?(name)
59
59
  if @klass.ancestors.map {|klass| klass.to_s}.include?("ActiveRecord::Base")
60
60
 
61
- conditions = {"#{@klass.table_name}.#{@klass.aasm.attribute_name}" => name.to_s}
61
+ conditions = {"#{@klass.table_name}.#{@klass.aasm(@name).attribute_name}" => name.to_s}
62
62
  if ActiveRecord::VERSION::MAJOR >= 3
63
63
  @klass.class_eval do
64
64
  scope name, lambda { where(conditions) }
@@ -69,10 +69,10 @@ module AASM
69
69
  end
70
70
  end
71
71
  elsif @klass.ancestors.map {|klass| klass.to_s}.include?("Mongoid::Document")
72
- scope_options = lambda { @klass.send(:where, {@klass.aasm.attribute_name.to_sym => name.to_s}) }
72
+ scope_options = lambda { @klass.send(:where, {@klass.aasm(@name).attribute_name.to_sym => name.to_s}) }
73
73
  @klass.send(:scope, name, scope_options)
74
74
  elsif @klass.ancestors.map {|klass| klass.to_s}.include?("MongoMapper::Document")
75
- conditions = { @klass.aasm.attribute_name.to_sym => name.to_s }
75
+ conditions = { @klass.aasm(@name).attribute_name.to_sym => name.to_s }
76
76
  @klass.scope(name, lambda { @klass.where(conditions) })
77
77
  end
78
78
  end
@@ -32,7 +32,6 @@ module AASM
32
32
  #
33
33
  def self.included(base)
34
34
  base.send(:include, AASM::Persistence::Base)
35
- base.extend AASM::Persistence::MongoMapperPersistence::ClassMethods
36
35
  base.send(:include, AASM::Persistence::MongoMapperPersistence::InstanceMethods)
37
36
 
38
37
  base.before_create :aasm_ensure_initial_state
@@ -41,26 +40,6 @@ module AASM
41
40
  base.validate :aasm_validate_states
42
41
  end
43
42
 
44
- module ClassMethods
45
-
46
- def find_in_state(number, state, *args)
47
- with_state_scope(state).find!(number, *args)
48
- end
49
-
50
- def count_in_state(state, *args)
51
- with_state_scope(state).count(*args)
52
- end
53
-
54
- def calculate_in_state(state, *args)
55
- with_state_scope(state).calculate(*args)
56
- end
57
-
58
- protected
59
- def with_state_scope(state)
60
- where(aasm.attribute_name.to_sym => state.to_s)
61
- end
62
- end
63
-
64
43
  module InstanceMethods
65
44
 
66
45
  # Writes <tt>state</tt> to the state column and persists it to the database
@@ -72,18 +51,18 @@ module AASM
72
51
  # Foo.find(1).aasm.current_state # => :closed
73
52
  #
74
53
  # NOTE: intended to be called from an event
75
- def aasm_write_state(state)
76
- old_value = read_attribute(self.class.aasm.attribute_name)
77
- aasm_write_attribute state
54
+ def aasm_write_state(state, name=:default)
55
+ old_value = read_attribute(self.class.aasm(name).attribute_name)
56
+ write_attribute(self.class.aasm(name).attribute_name, state)
78
57
 
79
- success = if aasm_skipping_validations
80
- value = aasm_raw_attribute_value state
81
- self.class.where(self.class.primary_key => self.id).update_all(self.class.aasm.attribute_name => value) == 1
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
82
61
  else
83
62
  self.save
84
63
  end
85
64
  unless success
86
- write_attribute(self.class.aasm.attribute_name, old_value)
65
+ write_attribute(self.class.aasm(name).attribute_name, old_value)
87
66
  return false
88
67
  end
89
68
 
@@ -102,39 +81,39 @@ module AASM
102
81
  # Foo.find(1).aasm.current_state # => :closed
103
82
  #
104
83
  # NOTE: intended to be called from an event
105
- def aasm_write_state_without_persistence(state)
106
- aasm_write_attribute state
84
+ def aasm_write_state_without_persistence(state, name=:default)
85
+ aasm_write_attribute(state, name)
107
86
  end
108
87
 
109
88
  private
110
- def aasm_enum
111
- case AASM::StateMachine[self.class].config.enum
89
+ def aasm_enum(name=:default)
90
+ case AASM::StateMachine[self.class][name].config.enum
112
91
  when false then nil
113
- when true then aasm_guess_enum_method
114
- when nil then aasm_guess_enum_method if aasm_column_looks_like_enum
115
- else AASM::StateMachine[self.class].config.enum
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
116
95
  end
117
96
  end
118
97
 
119
- def aasm_column_looks_like_enum
120
- self.class.keys[self.class.aasm.attribute_name.to_s].type == Integer
98
+ def aasm_column_looks_like_enum(name)
99
+ self.class.keys[self.class.aasm(name).attribute_name.to_s].type == Integer
121
100
  end
122
101
 
123
- def aasm_guess_enum_method
124
- self.class.aasm.attribute_name.to_s.pluralize.to_sym
102
+ def aasm_guess_enum_method(name)
103
+ self.class.aasm(name).attribute_name.to_s.pluralize.to_sym
125
104
  end
126
105
 
127
- def aasm_skipping_validations
128
- AASM::StateMachine[self.class].config.skip_validation_on_save
106
+ def aasm_skipping_validations(state_machine_name)
107
+ AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
129
108
  end
130
109
 
131
- def aasm_write_attribute(state)
132
- write_attribute self.class.aasm.attribute_name, aasm_raw_attribute_value(state)
110
+ def aasm_write_attribute(state, name=:default)
111
+ write_attribute self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name)
133
112
  end
134
113
 
135
- def aasm_raw_attribute_value(state)
136
- if aasm_enum
137
- self.class.send(aasm_enum)[state]
114
+ def aasm_raw_attribute_value(state, name=:default)
115
+ if aasm_enum(name)
116
+ self.class.send(aasm_enum(name))[state]
138
117
  else
139
118
  state.to_s
140
119
  end
@@ -156,14 +135,18 @@ module AASM
156
135
  # foo.aasm_state # => nil
157
136
  #
158
137
  def aasm_ensure_initial_state
159
- return send("#{self.class.aasm.attribute_name}=", aasm.enter_initial_state.to_s) if send(self.class.aasm.attribute_name).blank?
138
+ AASM::StateMachine[self.class].keys.each do |state_machine_name|
139
+ 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?
140
+ end
160
141
  end
161
142
 
162
143
  def aasm_validate_states
163
- send("#{self.class.aasm.attribute_name}=", aasm.enter_initial_state.to_s) if send(self.class.aasm.attribute_name).blank?
164
- unless AASM::StateMachine[self.class].config.skip_validation_on_save
165
- if aasm.current_state && !aasm.states.include?(aasm.current_state)
166
- self.errors.add(AASM::StateMachine[self.class].config.column , "is invalid")
144
+ AASM::StateMachine[self.class].keys.each do |state_machine_name|
145
+ 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?
146
+ unless AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
147
+ if aasm(state_machine_name).current_state && !aasm(state_machine_name).states.include?(aasm(state_machine_name).current_state)
148
+ self.errors.add(AASM::StateMachine[self.class][state_machine_name].config.column , "is invalid")
149
+ end
167
150
  end
168
151
  end
169
152
  end