aasm 5.0.0 → 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.
- checksums.yaml +5 -5
- data/README.md +243 -118
- data/lib/aasm/aasm.rb +29 -27
- data/lib/aasm/base.rb +61 -11
- data/lib/aasm/configuration.rb +3 -0
- data/lib/aasm/core/event.rb +21 -9
- data/lib/aasm/core/invokers/class_invoker.rb +1 -1
- data/lib/aasm/core/invokers/literal_invoker.rb +3 -1
- data/lib/aasm/core/state.rb +6 -5
- data/lib/aasm/core/transition.rb +1 -1
- data/lib/aasm/dsl_helper.rb +24 -22
- data/lib/aasm/errors.rb +3 -1
- data/lib/aasm/instance_base.rb +28 -5
- data/lib/aasm/localizer.rb +13 -3
- data/lib/aasm/persistence/active_record_persistence.rb +25 -5
- data/lib/aasm/persistence/base.rb +14 -3
- data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
- data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
- data/lib/aasm/persistence/mongoid_persistence.rb +1 -1
- data/lib/aasm/persistence/no_brainer_persistence.rb +1 -1
- data/lib/aasm/persistence/orm.rb +23 -19
- data/lib/aasm/persistence/redis_persistence.rb +1 -1
- data/lib/aasm/rspec/transition_from.rb +5 -1
- data/lib/aasm/version.rb +1 -1
- data/lib/aasm.rb +0 -2
- data/lib/generators/aasm/orm_helpers.rb +7 -1
- data/lib/generators/active_record/aasm_generator.rb +3 -1
- data/lib/generators/active_record/templates/migration.rb +1 -1
- metadata +35 -385
- data/.document +0 -6
- data/.gitignore +0 -20
- data/.travis.yml +0 -54
- data/API +0 -34
- data/Appraisals +0 -66
- data/CHANGELOG.md +0 -377
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -24
- data/Dockerfile +0 -44
- data/Gemfile +0 -6
- data/Gemfile.lock_old +0 -151
- data/HOWTO +0 -12
- data/PLANNED_CHANGES.md +0 -11
- data/README_FROM_VERSION_3_TO_4.md +0 -240
- data/Rakefile +0 -31
- data/TESTING.md +0 -25
- data/aasm.gemspec +0 -35
- data/callbacks.txt +0 -51
- data/docker-compose.yml +0 -40
- data/gemfiles/rails_3.2.gemfile +0 -13
- data/gemfiles/rails_4.0.gemfile +0 -15
- data/gemfiles/rails_4.2.gemfile +0 -16
- data/gemfiles/rails_4.2_mongoid_5.gemfile +0 -11
- data/gemfiles/rails_4.2_nobrainer.gemfile +0 -9
- data/gemfiles/rails_5.0.gemfile +0 -13
- data/gemfiles/rails_5.0_nobrainer.gemfile +0 -9
- data/gemfiles/rails_5.1.gemfile +0 -13
- data/spec/database.rb +0 -44
- data/spec/database.yml +0 -3
- data/spec/en.yml +0 -12
- data/spec/en_deprecated_style.yml +0 -10
- data/spec/generators/active_record_generator_spec.rb +0 -47
- data/spec/generators/mongoid_generator_spec.rb +0 -31
- data/spec/generators/no_brainer_generator_spec.rb +0 -29
- data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +0 -25
- data/spec/models/active_record/complex_active_record_example.rb +0 -37
- data/spec/models/active_record/derivate_new_dsl.rb +0 -7
- data/spec/models/active_record/false_state.rb +0 -35
- data/spec/models/active_record/gate.rb +0 -39
- data/spec/models/active_record/invalid_persistor.rb +0 -29
- data/spec/models/active_record/localizer_test_model.rb +0 -34
- data/spec/models/active_record/no_direct_assignment.rb +0 -21
- data/spec/models/active_record/no_scope.rb +0 -21
- data/spec/models/active_record/persisted_state.rb +0 -12
- data/spec/models/active_record/provided_and_persisted_state.rb +0 -24
- data/spec/models/active_record/reader.rb +0 -7
- data/spec/models/active_record/readme_job.rb +0 -21
- data/spec/models/active_record/silent_persistor.rb +0 -29
- data/spec/models/active_record/simple_new_dsl.rb +0 -17
- data/spec/models/active_record/thief.rb +0 -29
- data/spec/models/active_record/transactor.rb +0 -124
- data/spec/models/active_record/transient.rb +0 -6
- data/spec/models/active_record/validator.rb +0 -118
- data/spec/models/active_record/with_enum.rb +0 -39
- data/spec/models/active_record/with_enum_without_column.rb +0 -38
- data/spec/models/active_record/with_false_enum.rb +0 -31
- data/spec/models/active_record/with_true_enum.rb +0 -39
- data/spec/models/active_record/worker.rb +0 -2
- data/spec/models/active_record/writer.rb +0 -6
- data/spec/models/basic_two_state_machines_example.rb +0 -25
- data/spec/models/callbacks/basic.rb +0 -98
- data/spec/models/callbacks/basic_multiple.rb +0 -75
- data/spec/models/callbacks/guard_within_block.rb +0 -67
- data/spec/models/callbacks/guard_within_block_multiple.rb +0 -66
- data/spec/models/callbacks/multiple_transitions_transition_guard.rb +0 -66
- data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +0 -65
- data/spec/models/callbacks/private_method.rb +0 -44
- data/spec/models/callbacks/private_method_multiple.rb +0 -44
- data/spec/models/callbacks/with_args.rb +0 -62
- data/spec/models/callbacks/with_args_multiple.rb +0 -61
- data/spec/models/callbacks/with_state_arg.rb +0 -30
- data/spec/models/callbacks/with_state_arg_multiple.rb +0 -26
- data/spec/models/complex_example.rb +0 -222
- data/spec/models/conversation.rb +0 -93
- data/spec/models/default_state.rb +0 -12
- data/spec/models/double_definer.rb +0 -21
- data/spec/models/dynamoid/complex_dynamoid_example.rb +0 -37
- data/spec/models/dynamoid/dynamoid_multiple.rb +0 -18
- data/spec/models/dynamoid/dynamoid_simple.rb +0 -18
- data/spec/models/foo.rb +0 -106
- data/spec/models/foo_callback_multiple.rb +0 -45
- data/spec/models/guard_arguments_check.rb +0 -17
- data/spec/models/guard_with_params.rb +0 -24
- data/spec/models/guard_with_params_multiple.rb +0 -18
- data/spec/models/guardian.rb +0 -58
- data/spec/models/guardian_multiple.rb +0 -48
- data/spec/models/guardian_without_from_specified.rb +0 -18
- data/spec/models/initial_state_proc.rb +0 -31
- data/spec/models/mongoid/complex_mongoid_example.rb +0 -37
- data/spec/models/mongoid/invalid_persistor_mongoid.rb +0 -39
- data/spec/models/mongoid/mongoid_relationships.rb +0 -26
- data/spec/models/mongoid/no_scope_mongoid.rb +0 -21
- data/spec/models/mongoid/silent_persistor_mongoid.rb +0 -39
- data/spec/models/mongoid/simple_mongoid.rb +0 -23
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +0 -25
- data/spec/models/mongoid/validator_mongoid.rb +0 -100
- data/spec/models/multi_transitioner.rb +0 -34
- data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +0 -31
- data/spec/models/namespaced_multiple_example.rb +0 -42
- data/spec/models/no_initial_state.rb +0 -25
- data/spec/models/nobrainer/complex_no_brainer_example.rb +0 -36
- data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +0 -39
- data/spec/models/nobrainer/no_scope_no_brainer.rb +0 -21
- data/spec/models/nobrainer/nobrainer_relationships.rb +0 -25
- data/spec/models/nobrainer/silent_persistor_no_brainer.rb +0 -39
- data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +0 -25
- data/spec/models/nobrainer/simple_no_brainer.rb +0 -23
- data/spec/models/nobrainer/validator_no_brainer.rb +0 -98
- data/spec/models/not_auto_loaded/process.rb +0 -21
- data/spec/models/parametrised_event.rb +0 -42
- data/spec/models/parametrised_event_multiple.rb +0 -29
- data/spec/models/process_with_new_dsl.rb +0 -31
- data/spec/models/provided_state.rb +0 -24
- data/spec/models/redis/complex_redis_example.rb +0 -40
- data/spec/models/redis/redis_multiple.rb +0 -20
- data/spec/models/redis/redis_simple.rb +0 -20
- data/spec/models/sequel/complex_sequel_example.rb +0 -46
- data/spec/models/sequel/invalid_persistor.rb +0 -52
- data/spec/models/sequel/sequel_multiple.rb +0 -25
- data/spec/models/sequel/sequel_simple.rb +0 -26
- data/spec/models/sequel/silent_persistor.rb +0 -50
- data/spec/models/sequel/transactor.rb +0 -112
- data/spec/models/sequel/validator.rb +0 -93
- data/spec/models/sequel/worker.rb +0 -12
- data/spec/models/silencer.rb +0 -27
- data/spec/models/simple_custom_example.rb +0 -53
- data/spec/models/simple_example.rb +0 -17
- data/spec/models/simple_example_with_guard_args.rb +0 -17
- data/spec/models/simple_multiple_example.rb +0 -42
- data/spec/models/state_machine_with_failed_event.rb +0 -20
- data/spec/models/states_on_one_line_example.rb +0 -8
- data/spec/models/sub_class.rb +0 -41
- data/spec/models/sub_class_with_more_states.rb +0 -18
- data/spec/models/sub_classing.rb +0 -3
- data/spec/models/super_class.rb +0 -46
- data/spec/models/this_name_better_not_be_in_use.rb +0 -11
- data/spec/models/valid_state_name.rb +0 -23
- data/spec/spec_helper.rb +0 -26
- data/spec/spec_helpers/active_record.rb +0 -8
- data/spec/spec_helpers/dynamoid.rb +0 -35
- data/spec/spec_helpers/mongoid.rb +0 -26
- data/spec/spec_helpers/nobrainer.rb +0 -15
- data/spec/spec_helpers/redis.rb +0 -18
- data/spec/spec_helpers/remove_warnings.rb +0 -1
- data/spec/spec_helpers/sequel.rb +0 -7
- data/spec/unit/api_spec.rb +0 -100
- data/spec/unit/basic_two_state_machines_example_spec.rb +0 -10
- data/spec/unit/callback_multiple_spec.rb +0 -300
- data/spec/unit/callbacks_spec.rb +0 -491
- data/spec/unit/complex_example_spec.rb +0 -94
- data/spec/unit/complex_multiple_example_spec.rb +0 -115
- data/spec/unit/edge_cases_spec.rb +0 -16
- data/spec/unit/event_multiple_spec.rb +0 -73
- data/spec/unit/event_naming_spec.rb +0 -16
- data/spec/unit/event_spec.rb +0 -381
- data/spec/unit/exception_spec.rb +0 -11
- data/spec/unit/guard_arguments_check_spec.rb +0 -9
- data/spec/unit/guard_multiple_spec.rb +0 -60
- data/spec/unit/guard_spec.rb +0 -89
- data/spec/unit/guard_with_params_multiple_spec.rb +0 -10
- data/spec/unit/guard_with_params_spec.rb +0 -14
- data/spec/unit/guard_without_from_specified_spec.rb +0 -10
- data/spec/unit/initial_state_multiple_spec.rb +0 -15
- data/spec/unit/initial_state_spec.rb +0 -12
- data/spec/unit/inspection_multiple_spec.rb +0 -201
- data/spec/unit/inspection_spec.rb +0 -149
- data/spec/unit/invoker_spec.rb +0 -189
- data/spec/unit/invokers/base_invoker_spec.rb +0 -72
- data/spec/unit/invokers/class_invoker_spec.rb +0 -95
- data/spec/unit/invokers/literal_invoker_spec.rb +0 -86
- data/spec/unit/invokers/proc_invoker_spec.rb +0 -86
- data/spec/unit/localizer_spec.rb +0 -78
- data/spec/unit/memory_leak_spec.rb +0 -38
- data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +0 -14
- data/spec/unit/namespaced_multiple_example_spec.rb +0 -75
- data/spec/unit/new_dsl_spec.rb +0 -12
- data/spec/unit/override_warning_spec.rb +0 -94
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +0 -618
- data/spec/unit/persistence/active_record_persistence_spec.rb +0 -735
- data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +0 -135
- data/spec/unit/persistence/dynamoid_persistence_spec.rb +0 -84
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -200
- data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -165
- data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +0 -198
- data/spec/unit/persistence/no_brainer_persistence_spec.rb +0 -158
- data/spec/unit/persistence/redis_persistence_multiple_spec.rb +0 -88
- data/spec/unit/persistence/redis_persistence_spec.rb +0 -53
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +0 -148
- data/spec/unit/persistence/sequel_persistence_spec.rb +0 -368
- data/spec/unit/readme_spec.rb +0 -41
- data/spec/unit/reloading_spec.rb +0 -15
- data/spec/unit/rspec_matcher_spec.rb +0 -85
- data/spec/unit/simple_custom_example_spec.rb +0 -39
- data/spec/unit/simple_example_spec.rb +0 -42
- data/spec/unit/simple_multiple_example_spec.rb +0 -91
- data/spec/unit/state_spec.rb +0 -89
- data/spec/unit/states_on_one_line_example_spec.rb +0 -16
- data/spec/unit/subclassing_multiple_spec.rb +0 -74
- data/spec/unit/subclassing_spec.rb +0 -46
- data/spec/unit/transition_spec.rb +0 -436
- data/test/minitest_helper.rb +0 -57
- data/test/unit/minitest_matcher_test.rb +0 -80
data/lib/aasm/aasm.rb
CHANGED
@@ -99,25 +99,10 @@ private
|
|
99
99
|
begin
|
100
100
|
old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
|
101
101
|
|
102
|
-
event.
|
103
|
-
:before_all_events,
|
104
|
-
self,
|
105
|
-
*process_args(event, aasm(state_machine_name).current_state, *args)
|
106
|
-
)
|
107
|
-
|
108
|
-
# new event before callback
|
109
|
-
event.fire_callbacks(
|
110
|
-
:before,
|
111
|
-
self,
|
112
|
-
*process_args(event, aasm(state_machine_name).current_state, *args)
|
113
|
-
)
|
102
|
+
fire_default_callbacks(event, *process_args(event, aasm(state_machine_name).current_state, *args))
|
114
103
|
|
115
104
|
if may_fire_to = event.may_fire?(self, *args)
|
116
|
-
old_state
|
117
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
118
|
-
old_state.fire_callbacks(:exit, self,
|
119
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
120
|
-
|
105
|
+
fire_exit_callbacks(old_state, *process_args(event, aasm(state_machine_name).current_state, *args))
|
121
106
|
if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
|
122
107
|
aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
|
123
108
|
else
|
@@ -137,25 +122,44 @@ private
|
|
137
122
|
end
|
138
123
|
end
|
139
124
|
|
125
|
+
def fire_default_callbacks(event, *processed_args)
|
126
|
+
event.fire_global_callbacks(
|
127
|
+
:before_all_events,
|
128
|
+
self,
|
129
|
+
*processed_args
|
130
|
+
)
|
131
|
+
|
132
|
+
# new event before callback
|
133
|
+
event.fire_callbacks(
|
134
|
+
:before,
|
135
|
+
self,
|
136
|
+
*processed_args
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
def fire_exit_callbacks(old_state, *processed_args)
|
141
|
+
old_state.fire_callbacks(:before_exit, self, *processed_args)
|
142
|
+
old_state.fire_callbacks(:exit, self, *processed_args)
|
143
|
+
end
|
144
|
+
|
140
145
|
def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
|
141
146
|
persist = options[:persist]
|
142
147
|
|
143
148
|
new_state = aasm(state_machine_name).state_object_for_name(new_state_name)
|
149
|
+
callback_args = process_args(event, aasm(state_machine_name).current_state, *args)
|
144
150
|
|
145
|
-
new_state.fire_callbacks(:before_enter, self,
|
146
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
151
|
+
new_state.fire_callbacks(:before_enter, self, *callback_args)
|
147
152
|
|
148
|
-
new_state.fire_callbacks(:enter, self,
|
149
|
-
*process_args(event, aasm(state_machine_name).current_state, *args)) # TODO: remove for AASM 4?
|
153
|
+
new_state.fire_callbacks(:enter, self, *callback_args) # TODO: remove for AASM 4?
|
150
154
|
|
151
155
|
persist_successful = true
|
152
156
|
if persist
|
153
157
|
persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
|
154
158
|
if persist_successful
|
155
159
|
yield if block_given?
|
156
|
-
event.fire_callbacks(:before_success, self)
|
160
|
+
event.fire_callbacks(:before_success, self, *callback_args)
|
157
161
|
event.fire_transition_callbacks(self, *process_args(event, old_state.name, *args))
|
158
|
-
event.fire_callbacks(:success, self)
|
162
|
+
event.fire_callbacks(:success, self, *callback_args)
|
159
163
|
end
|
160
164
|
else
|
161
165
|
aasm(state_machine_name).current_state = new_state_name
|
@@ -168,10 +172,8 @@ private
|
|
168
172
|
end
|
169
173
|
|
170
174
|
if persist_successful
|
171
|
-
old_state.fire_callbacks(:after_exit, self,
|
172
|
-
|
173
|
-
new_state.fire_callbacks(:after_enter, self,
|
174
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
175
|
+
old_state.fire_callbacks(:after_exit, self, *callback_args)
|
176
|
+
new_state.fire_callbacks(:after_enter, self, *callback_args)
|
175
177
|
event.fire_callbacks(
|
176
178
|
:after,
|
177
179
|
self,
|
data/lib/aasm/base.rb
CHANGED
@@ -37,6 +37,9 @@ module AASM
|
|
37
37
|
# string for a specific lock type i.e. FOR UPDATE NOWAIT
|
38
38
|
configure :requires_lock, false
|
39
39
|
|
40
|
+
# automatically set `"#{state_name}_at" = ::Time.now` on state changes
|
41
|
+
configure :timestamps, false
|
42
|
+
|
40
43
|
# set to true to forbid direct assignment of aasm_state column (in ActiveRecord)
|
41
44
|
configure :no_direct_assignment, false
|
42
45
|
|
@@ -51,18 +54,12 @@ module AASM
|
|
51
54
|
# Configure a logger, with default being a Logger to STDERR
|
52
55
|
configure :logger, Logger.new(STDERR)
|
53
56
|
|
57
|
+
# setup timestamp-setting callback if enabled
|
58
|
+
setup_timestamps(@name)
|
59
|
+
|
54
60
|
# make sure to raise an error if no_direct_assignment is enabled
|
55
61
|
# and attribute is directly assigned though
|
56
|
-
|
57
|
-
klass.send :define_method, "#{@state_machine.config.column}=", ->(state_name) do
|
58
|
-
if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
|
59
|
-
raise AASM::NoDirectAssignmentError.new(
|
60
|
-
'direct assignment of AASM column has been disabled (see AASM configuration for this class)'
|
61
|
-
)
|
62
|
-
else
|
63
|
-
super(state_name)
|
64
|
-
end
|
65
|
-
end
|
62
|
+
setup_no_direct_assignment(@name)
|
66
63
|
end
|
67
64
|
|
68
65
|
# This method is both a getter and a setter
|
@@ -134,6 +131,8 @@ module AASM
|
|
134
131
|
aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block)
|
135
132
|
end
|
136
133
|
|
134
|
+
skip_instance_level_validation(event, name, aasm_name, klass)
|
135
|
+
|
137
136
|
# Create aliases for the event methods. Keep the old names to maintain backwards compatibility.
|
138
137
|
if namespace?
|
139
138
|
klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?")
|
@@ -224,7 +223,20 @@ module AASM
|
|
224
223
|
end
|
225
224
|
end
|
226
225
|
|
227
|
-
klass.send(:define_method, method_name, method_definition)
|
226
|
+
klass.send(:define_method, method_name, method_definition).tap do |sym|
|
227
|
+
apply_ruby2_keyword(klass, sym)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def apply_ruby2_keyword(klass, sym)
|
232
|
+
if RUBY_VERSION >= '2.7.1'
|
233
|
+
if klass.instance_method(sym).parameters.find { |type, _| type.to_s.start_with?('rest') }
|
234
|
+
# If there is a place where you are receiving in *args, do ruby2_keywords.
|
235
|
+
klass.module_eval do
|
236
|
+
ruby2_keywords sym
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
228
240
|
end
|
229
241
|
|
230
242
|
def namespace?
|
@@ -249,5 +261,43 @@ module AASM
|
|
249
261
|
end
|
250
262
|
end
|
251
263
|
|
264
|
+
def skip_instance_level_validation(event, name, aasm_name, klass)
|
265
|
+
# Overrides the skip_validation config for an instance (If skip validation is set to false in original config) and
|
266
|
+
# restores it back to the original value after the event is fired.
|
267
|
+
safely_define_method klass, "#{name}_without_validation!", ->(*args, &block) do
|
268
|
+
original_config = AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save
|
269
|
+
begin
|
270
|
+
AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = true unless original_config
|
271
|
+
aasm(aasm_name).current_event = :"#{name}!"
|
272
|
+
aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
|
273
|
+
ensure
|
274
|
+
AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = original_config
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def setup_timestamps(aasm_name)
|
280
|
+
return unless @state_machine.config.timestamps
|
281
|
+
|
282
|
+
after_all_transitions do
|
283
|
+
if self.class.aasm(:"#{aasm_name}").state_machine.config.timestamps
|
284
|
+
ts_setter = "#{aasm(aasm_name).to_state}_at="
|
285
|
+
respond_to?(ts_setter) && send(ts_setter, ::Time.now)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def setup_no_direct_assignment(aasm_name)
|
291
|
+
return unless @state_machine.config.no_direct_assignment
|
292
|
+
|
293
|
+
@klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name|
|
294
|
+
if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
|
295
|
+
raise AASM::NoDirectAssignmentError.new('direct assignment of AASM column has been disabled (see AASM configuration for this class)')
|
296
|
+
else
|
297
|
+
super(state_name)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
252
302
|
end
|
253
303
|
end
|
data/lib/aasm/configuration.rb
CHANGED
@@ -24,6 +24,9 @@ module AASM
|
|
24
24
|
# for ActiveRecord: use pessimistic locking
|
25
25
|
attr_accessor :requires_lock
|
26
26
|
|
27
|
+
# automatically set `"#{state_name}_at" = ::Time.now` on state changes
|
28
|
+
attr_accessor :timestamps
|
29
|
+
|
27
30
|
# forbid direct assignment in aasm_state column (in ActiveRecord)
|
28
31
|
attr_accessor :no_direct_assignment
|
29
32
|
|
data/lib/aasm/core/event.rb
CHANGED
@@ -2,17 +2,18 @@
|
|
2
2
|
|
3
3
|
module AASM::Core
|
4
4
|
class Event
|
5
|
-
include DslHelper
|
5
|
+
include AASM::DslHelper
|
6
6
|
|
7
|
-
attr_reader :name, :state_machine, :options
|
7
|
+
attr_reader :name, :state_machine, :options, :default_display_name
|
8
8
|
|
9
9
|
def initialize(name, state_machine, options = {}, &block)
|
10
10
|
@name = name
|
11
11
|
@state_machine = state_machine
|
12
12
|
@transitions = []
|
13
|
-
@valid_transitions = {}
|
13
|
+
@valid_transitions = Hash.new { |h, k| h[k] = {} }
|
14
14
|
@guards = Array(options[:guard] || options[:guards] || options[:if])
|
15
15
|
@unless = Array(options[:unless]) #TODO: This could use a better name
|
16
|
+
@default_display_name = name.to_s.gsub(/_/, ' ').capitalize
|
16
17
|
|
17
18
|
# from aasm4
|
18
19
|
@options = options # QUESTION: .dup ?
|
@@ -78,8 +79,9 @@ module AASM::Core
|
|
78
79
|
|
79
80
|
def fire_transition_callbacks(obj, *args)
|
80
81
|
from_state = obj.aasm(state_machine.name).current_state
|
81
|
-
transition = @valid_transitions[from_state]
|
82
|
-
|
82
|
+
transition = @valid_transitions[obj.object_id][from_state]
|
83
|
+
transition.invoke_success_callbacks(obj, *args) if transition
|
84
|
+
@valid_transitions.delete(obj.object_id)
|
83
85
|
end
|
84
86
|
|
85
87
|
def ==(event)
|
@@ -109,6 +111,10 @@ module AASM::Core
|
|
109
111
|
transitions.flat_map(&:failures)
|
110
112
|
end
|
111
113
|
|
114
|
+
def to_s
|
115
|
+
name.to_s
|
116
|
+
end
|
117
|
+
|
112
118
|
private
|
113
119
|
|
114
120
|
def attach_event_guards(definitions)
|
@@ -125,18 +131,19 @@ module AASM::Core
|
|
125
131
|
|
126
132
|
def _fire(obj, options={}, to_state=::AASM::NO_VALUE, *args)
|
127
133
|
result = options[:test_only] ? false : nil
|
134
|
+
clear_failed_callbacks
|
128
135
|
transitions = @transitions.select { |t| t.from == obj.aasm(state_machine.name).current_state || t.from == nil}
|
129
136
|
return result if transitions.size == 0
|
130
137
|
|
131
138
|
if to_state == ::AASM::NO_VALUE
|
132
139
|
to_state = nil
|
133
|
-
elsif to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym)
|
134
|
-
# nop, to_state is a valid to-state
|
135
|
-
else
|
140
|
+
elsif !(to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym))
|
136
141
|
# to_state is an argument
|
137
142
|
args.unshift(to_state)
|
138
143
|
to_state = nil
|
139
144
|
end
|
145
|
+
|
146
|
+
# nop, to_state is a valid to-state
|
140
147
|
|
141
148
|
transitions.each do |transition|
|
142
149
|
next if to_state and !Array(transition.to).include?(to_state)
|
@@ -147,7 +154,7 @@ module AASM::Core
|
|
147
154
|
result = transition
|
148
155
|
else
|
149
156
|
result = to_state || Array(transition.to).first
|
150
|
-
Array(transition.to).each {|to| @valid_transitions[to] = transition }
|
157
|
+
Array(transition.to).each {|to| @valid_transitions[obj.object_id][to] = transition }
|
151
158
|
transition.execute(obj, *args)
|
152
159
|
end
|
153
160
|
|
@@ -157,6 +164,11 @@ module AASM::Core
|
|
157
164
|
result
|
158
165
|
end
|
159
166
|
|
167
|
+
def clear_failed_callbacks
|
168
|
+
# https://github.com/aasm/aasm/issues/383, https://github.com/aasm/aasm/issues/599
|
169
|
+
transitions.each { |transition| transition.failures.clear }
|
170
|
+
end
|
171
|
+
|
160
172
|
def invoke_callbacks(code, record, args)
|
161
173
|
Invoker.new(code, record, args)
|
162
174
|
.with_default_return_value(false)
|
@@ -30,7 +30,9 @@ module AASM
|
|
30
30
|
raise(*record_error) unless record.respond_to?(subject, true)
|
31
31
|
return record.__send__(subject) if subject_arity.zero?
|
32
32
|
return record.__send__(subject, *args) if subject_arity < 0
|
33
|
-
|
33
|
+
req_args = args[0..(subject_arity - 1)]
|
34
|
+
return record.__send__(subject, **req_args[0]) if req_args[0].is_a?(Hash)
|
35
|
+
record.__send__(subject, *req_args)
|
34
36
|
end
|
35
37
|
# rubocop:enable Metrics/AbcSize
|
36
38
|
|
data/lib/aasm/core/state.rb
CHANGED
@@ -2,12 +2,13 @@
|
|
2
2
|
|
3
3
|
module AASM::Core
|
4
4
|
class State
|
5
|
-
attr_reader :name, :state_machine, :options
|
5
|
+
attr_reader :name, :state_machine, :options, :default_display_name
|
6
6
|
|
7
7
|
def initialize(name, klass, state_machine, options={})
|
8
8
|
@name = name
|
9
9
|
@klass = klass
|
10
10
|
@state_machine = state_machine
|
11
|
+
@default_display_name = name.to_s.gsub(/_/, ' ').capitalize
|
11
12
|
update(options)
|
12
13
|
end
|
13
14
|
|
@@ -54,11 +55,11 @@ module AASM::Core
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def display_name
|
57
|
-
@display_name
|
58
|
+
@display_name = begin
|
58
59
|
if Module.const_defined?(:I18n)
|
59
60
|
localized_name
|
60
61
|
else
|
61
|
-
|
62
|
+
@default_display_name
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
@@ -75,8 +76,8 @@ module AASM::Core
|
|
75
76
|
private
|
76
77
|
|
77
78
|
def update(options = {})
|
78
|
-
if options.key?(:display)
|
79
|
-
@
|
79
|
+
if options.key?(:display)
|
80
|
+
@default_display_name = options.delete(:display)
|
80
81
|
end
|
81
82
|
@options = options
|
82
83
|
self
|
data/lib/aasm/core/transition.rb
CHANGED
data/lib/aasm/dsl_helper.rb
CHANGED
@@ -1,30 +1,32 @@
|
|
1
|
-
module
|
1
|
+
module AASM
|
2
|
+
module DslHelper
|
2
3
|
|
3
|
-
|
4
|
-
|
4
|
+
class Proxy
|
5
|
+
attr_accessor :options
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def initialize(options, valid_keys, source)
|
8
|
+
@valid_keys = valid_keys
|
9
|
+
@source = source
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
@options = options
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def method_missing(name, *args, &block)
|
15
|
+
if @valid_keys.include?(name)
|
16
|
+
options[name] = Array(options[name])
|
17
|
+
options[name] << block if block
|
18
|
+
options[name] += Array(args)
|
19
|
+
else
|
20
|
+
@source.send name, *args, &block
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
22
|
-
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
def add_options_from_dsl(options, valid_keys, &block)
|
26
|
+
proxy = Proxy.new(options, valid_keys, self)
|
27
|
+
proxy.instance_eval(&block)
|
28
|
+
proxy.options
|
29
|
+
end
|
29
30
|
|
30
|
-
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/aasm/errors.rb
CHANGED
@@ -3,10 +3,11 @@ module AASM
|
|
3
3
|
class UnknownStateMachineError < RuntimeError; end
|
4
4
|
|
5
5
|
class InvalidTransition < RuntimeError
|
6
|
-
attr_reader :object, :event_name, :originating_state, :failures
|
6
|
+
attr_reader :object, :event_name, :originating_state, :failures, :state_machine_name
|
7
7
|
|
8
8
|
def initialize(object, event_name, state_machine_name, failures = [])
|
9
9
|
@object, @event_name, @originating_state, @failures = object, event_name, object.aasm(state_machine_name).current_state, failures
|
10
|
+
@state_machine_name = state_machine_name
|
10
11
|
super("Event '#{event_name}' cannot transition from '#{originating_state}'.#{reasoning}")
|
11
12
|
end
|
12
13
|
|
@@ -16,5 +17,6 @@ module AASM
|
|
16
17
|
end
|
17
18
|
|
18
19
|
class UndefinedState < RuntimeError; end
|
20
|
+
class UndefinedEvent < UndefinedState; end
|
19
21
|
class NoDirectAssignmentError < RuntimeError; end
|
20
22
|
end
|
data/lib/aasm/instance_base.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module AASM
|
2
2
|
class InstanceBase
|
3
|
-
|
4
3
|
attr_accessor :from_state, :to_state, :current_event
|
5
4
|
|
6
5
|
def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
|
@@ -28,7 +27,7 @@ module AASM
|
|
28
27
|
end
|
29
28
|
|
30
29
|
def human_state
|
31
|
-
|
30
|
+
state_object_for_name(current_state).display_name
|
32
31
|
end
|
33
32
|
|
34
33
|
def states(options={}, *args)
|
@@ -78,6 +77,17 @@ module AASM
|
|
78
77
|
events
|
79
78
|
end
|
80
79
|
|
80
|
+
def permitted_transitions
|
81
|
+
events(permitted: true).flat_map do |event|
|
82
|
+
available_transitions = event.transitions_from_state(current_state)
|
83
|
+
allowed_transitions = available_transitions.select { |t| t.allowed?(@instance) }
|
84
|
+
|
85
|
+
allowed_transitions.map do |transition|
|
86
|
+
{ event: event.name, state: transition.to }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
81
91
|
def state_object_for_name(name)
|
82
92
|
obj = @instance.class.aasm(@name).states.find {|s| s.name == name}
|
83
93
|
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
|
@@ -91,7 +101,7 @@ module AASM
|
|
91
101
|
when Proc
|
92
102
|
state.call(@instance)
|
93
103
|
else
|
94
|
-
raise NotImplementedError, "Unrecognized state-type given.
|
104
|
+
raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
|
95
105
|
end
|
96
106
|
end
|
97
107
|
|
@@ -104,11 +114,15 @@ module AASM
|
|
104
114
|
end
|
105
115
|
|
106
116
|
def fire(event_name, *args, &block)
|
107
|
-
|
117
|
+
event_exists?(event_name)
|
118
|
+
|
119
|
+
@instance.send(event_name, *args, &block)
|
108
120
|
end
|
109
121
|
|
110
122
|
def fire!(event_name, *args, &block)
|
111
|
-
|
123
|
+
event_exists?(event_name, true)
|
124
|
+
bang_event_name = "#{event_name}!".to_sym
|
125
|
+
@instance.send(bang_event_name, *args, &block)
|
112
126
|
end
|
113
127
|
|
114
128
|
def set_current_state_with_persistence(state)
|
@@ -117,5 +131,14 @@ module AASM
|
|
117
131
|
save_success
|
118
132
|
end
|
119
133
|
|
134
|
+
private
|
135
|
+
|
136
|
+
def event_exists?(event_name, bang = false)
|
137
|
+
event = @instance.class.aasm(@name).state_machine.events[event_name.to_sym]
|
138
|
+
return true if event
|
139
|
+
|
140
|
+
event_error = bang ? "#{event_name}!" : event_name
|
141
|
+
raise AASM::UndefinedEvent, "Event :#{event_error} doesn't exist" if event.nil?
|
142
|
+
end
|
120
143
|
end
|
121
144
|
end
|
data/lib/aasm/localizer.rb
CHANGED
@@ -5,7 +5,7 @@ module AASM
|
|
5
5
|
list << :"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
|
6
6
|
list
|
7
7
|
end
|
8
|
-
translate_queue(checklist) || I18n.translate(checklist.shift, :default => event
|
8
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => default_display_name(event))
|
9
9
|
end
|
10
10
|
|
11
11
|
def human_state_name(klass, state)
|
@@ -14,7 +14,7 @@ module AASM
|
|
14
14
|
list << item_for(klass, state, ancestor, :old_style => true)
|
15
15
|
list
|
16
16
|
end
|
17
|
-
translate_queue(checklist) || I18n.translate(checklist.shift, :default => state
|
17
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => default_display_name(state))
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
@@ -46,8 +46,18 @@ module AASM
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def ancestors_list(klass)
|
49
|
+
has_active_record_base = defined?(::ActiveRecord::Base)
|
49
50
|
klass.ancestors.select do |ancestor|
|
50
|
-
|
51
|
+
not_active_record_base = has_active_record_base ? (ancestor != ::ActiveRecord::Base) : true
|
52
|
+
ancestor.respond_to?(:model_name) && not_active_record_base
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_display_name(object) # Can use better arguement name
|
57
|
+
if object.respond_to?(:default_display_name)
|
58
|
+
object.default_display_name
|
59
|
+
else
|
60
|
+
object.to_s.gsub(/_/, ' ').capitalize
|
51
61
|
end
|
52
62
|
end
|
53
63
|
end
|
@@ -41,14 +41,15 @@ module AASM
|
|
41
41
|
|
42
42
|
module ClassMethods
|
43
43
|
def aasm_create_scope(state_machine_name, scope_name)
|
44
|
-
conditions = {
|
45
|
-
table_name => { aasm(state_machine_name).attribute_name => scope_name.to_s }
|
46
|
-
}
|
47
44
|
if ActiveRecord::VERSION::MAJOR >= 3
|
45
|
+
conditions = { aasm(state_machine_name).attribute_name => scope_name.to_s }
|
48
46
|
class_eval do
|
49
|
-
scope scope_name, lambda { where(conditions) }
|
47
|
+
scope scope_name, lambda { where(table_name => conditions) }
|
50
48
|
end
|
51
49
|
else
|
50
|
+
conditions = {
|
51
|
+
table_name => { aasm(state_machine_name).attribute_name => scope_name.to_s }
|
52
|
+
}
|
52
53
|
class_eval do
|
53
54
|
named_scope scope_name, :conditions => conditions
|
54
55
|
end
|
@@ -60,6 +61,24 @@ module AASM
|
|
60
61
|
|
61
62
|
private
|
62
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
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
63
82
|
def aasm_raise_invalid_record
|
64
83
|
raise ActiveRecord::RecordInvalid.new(self)
|
65
84
|
end
|
@@ -140,7 +159,8 @@ module AASM
|
|
140
159
|
|
141
160
|
def aasm_column_is_blank?(state_machine_name)
|
142
161
|
attribute_name = self.class.aasm(state_machine_name).attribute_name
|
143
|
-
attribute_names.include?(attribute_name.to_s) &&
|
162
|
+
attribute_names.include?(attribute_name.to_s) &&
|
163
|
+
(send(attribute_name).respond_to?(:empty?) ? !!send(attribute_name).empty? : !send(attribute_name))
|
144
164
|
end
|
145
165
|
|
146
166
|
def aasm_validate_states
|
@@ -34,7 +34,7 @@ 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 state.
|
37
|
+
if !state || state.empty?
|
38
38
|
aasm_new_record? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : nil
|
39
39
|
else
|
40
40
|
state.to_sym
|
@@ -59,7 +59,9 @@ module AASM
|
|
59
59
|
# make sure to create a (named) scope for each state
|
60
60
|
def state_with_scope(*args)
|
61
61
|
names = state_without_scope(*args)
|
62
|
-
names.each
|
62
|
+
names.each do |name|
|
63
|
+
create_scopes(name)
|
64
|
+
end
|
63
65
|
end
|
64
66
|
alias_method :state_without_scope, :state
|
65
67
|
alias_method :state, :state_with_scope
|
@@ -71,7 +73,16 @@ module AASM
|
|
71
73
|
end
|
72
74
|
|
73
75
|
def create_scope(name)
|
74
|
-
@klass.aasm_create_scope(@name, 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)
|
75
86
|
end
|
76
87
|
end # Base
|
77
88
|
|
@@ -77,7 +77,8 @@ module AASM
|
|
77
77
|
#
|
78
78
|
def aasm_ensure_initial_state
|
79
79
|
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
80
|
-
send(
|
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)
|
81
82
|
end
|
82
83
|
end
|
83
84
|
end # InstanceMethods
|
@@ -83,7 +83,7 @@ module AASM
|
|
83
83
|
#
|
84
84
|
def aasm_ensure_initial_state
|
85
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).
|
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
87
|
end
|
88
88
|
end
|
89
89
|
end # InstanceMethods
|