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.
- checksums.yaml +5 -5
- data/LICENSE +1 -1
- data/README.md +809 -129
- data/lib/aasm/aasm.rb +74 -37
- data/lib/aasm/base.rb +188 -41
- data/lib/aasm/configuration.rb +27 -2
- data/lib/aasm/core/event.rb +75 -47
- data/lib/aasm/core/invoker.rb +129 -0
- data/lib/aasm/core/invokers/base_invoker.rb +75 -0
- data/lib/aasm/core/invokers/class_invoker.rb +52 -0
- data/lib/aasm/core/invokers/literal_invoker.rb +49 -0
- data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
- data/lib/aasm/core/state.rb +22 -13
- data/lib/aasm/core/transition.rb +30 -23
- data/lib/aasm/dsl_helper.rb +24 -22
- data/lib/aasm/errors.rb +8 -5
- data/lib/aasm/instance_base.rb +63 -15
- data/lib/aasm/localizer.rb +13 -3
- data/lib/aasm/minitest/allow_event.rb +13 -0
- data/lib/aasm/minitest/allow_transition_to.rb +13 -0
- data/lib/aasm/minitest/have_state.rb +13 -0
- data/lib/aasm/minitest/transition_from.rb +21 -0
- data/lib/aasm/minitest.rb +5 -0
- data/lib/aasm/minitest_spec.rb +15 -0
- data/lib/aasm/persistence/active_record_persistence.rb +87 -79
- data/lib/aasm/persistence/base.rb +30 -30
- data/lib/aasm/persistence/core_data_query_persistence.rb +94 -0
- data/lib/aasm/persistence/dynamoid_persistence.rb +92 -0
- data/lib/aasm/persistence/mongoid_persistence.rb +49 -35
- data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
- data/lib/aasm/persistence/orm.rb +154 -0
- data/lib/aasm/persistence/plain_persistence.rb +2 -1
- data/lib/aasm/persistence/redis_persistence.rb +112 -0
- data/lib/aasm/persistence/sequel_persistence.rb +37 -67
- data/lib/aasm/persistence.rb +20 -5
- data/lib/aasm/rspec/allow_event.rb +5 -1
- data/lib/aasm/rspec/allow_transition_to.rb +5 -1
- data/lib/aasm/rspec/transition_from.rb +8 -4
- data/lib/aasm/state_machine.rb +6 -12
- data/lib/aasm/state_machine_store.rb +76 -0
- data/lib/aasm/version.rb +1 -1
- data/lib/aasm.rb +8 -2
- data/lib/generators/aasm/aasm_generator.rb +16 -0
- data/lib/generators/aasm/orm_helpers.rb +41 -0
- data/lib/generators/active_record/aasm_generator.rb +40 -0
- data/lib/generators/active_record/templates/migration.rb +8 -0
- data/lib/generators/active_record/templates/migration_existing.rb +5 -0
- data/lib/generators/mongoid/aasm_generator.rb +28 -0
- data/lib/generators/nobrainer/aasm_generator.rb +28 -0
- data/lib/motion-aasm.rb +37 -0
- metadata +104 -259
- data/.document +0 -6
- data/.gitignore +0 -19
- data/.travis.yml +0 -37
- data/API +0 -34
- data/CHANGELOG.md +0 -272
- data/CODE_OF_CONDUCT.md +0 -13
- data/Gemfile +0 -15
- data/HOWTO +0 -12
- data/PLANNED_CHANGES.md +0 -11
- data/README_FROM_VERSION_3_TO_4.md +0 -240
- data/Rakefile +0 -26
- data/aasm.gemspec +0 -31
- data/callbacks.txt +0 -51
- data/gemfiles/rails_3.2.gemfile +0 -14
- data/gemfiles/rails_4.0.gemfile +0 -12
- data/gemfiles/rails_4.0_mongo_mapper.gemfile +0 -14
- data/gemfiles/rails_4.1.gemfile +0 -12
- data/gemfiles/rails_4.1_mongo_mapper.gemfile +0 -14
- data/gemfiles/rails_4.2.gemfile +0 -12
- data/gemfiles/rails_4.2_mongo_mapper.gemfile +0 -14
- data/gemfiles/rails_4.2_mongoid_5.gemfile +0 -12
- data/lib/aasm/persistence/mongo_mapper_persistence.rb +0 -157
- data/spec/database.rb +0 -63
- data/spec/database.yml +0 -3
- data/spec/en.yml +0 -9
- data/spec/en_deprecated_style.yml +0 -10
- 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 -33
- 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/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/simple_new_dsl.rb +0 -17
- data/spec/models/active_record/thief.rb +0 -29
- data/spec/models/active_record/transient.rb +0 -6
- data/spec/models/active_record/with_enum.rb +0 -39
- 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/writer.rb +0 -6
- data/spec/models/basic_two_state_machines_example.rb +0 -25
- data/spec/models/callbacks/basic.rb +0 -78
- data/spec/models/callbacks/basic_multiple.rb +0 -75
- data/spec/models/callbacks/guard_within_block.rb +0 -66
- data/spec/models/callbacks/guard_within_block_multiple.rb +0 -66
- data/spec/models/callbacks/multiple_transitions_transition_guard.rb +0 -65
- 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 -61
- data/spec/models/callbacks/with_args_multiple.rb +0 -61
- data/spec/models/callbacks/with_state_arg.rb +0 -26
- 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/foo.rb +0 -92
- data/spec/models/foo_callback_multiple.rb +0 -45
- data/spec/models/guardian.rb +0 -48
- data/spec/models/guardian_multiple.rb +0 -48
- data/spec/models/initial_state_proc.rb +0 -31
- data/spec/models/invalid_persistor.rb +0 -31
- data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +0 -37
- data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +0 -21
- data/spec/models/mongo_mapper/simple_mongo_mapper.rb +0 -23
- data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +0 -25
- data/spec/models/mongoid/complex_mongoid_example.rb +0 -37
- data/spec/models/mongoid/no_scope_mongoid.rb +0 -21
- data/spec/models/mongoid/simple_mongoid.rb +0 -23
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +0 -25
- data/spec/models/no_initial_state.rb +0 -25
- data/spec/models/not_auto_loaded/process.rb +0 -21
- data/spec/models/parametrised_event.rb +0 -29
- 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/sequel/complex_sequel_example.rb +0 -45
- data/spec/models/sequel/sequel_multiple.rb +0 -25
- data/spec/models/sequel/sequel_simple.rb +0 -25
- data/spec/models/silencer.rb +0 -27
- data/spec/models/simple_example.rb +0 -15
- data/spec/models/simple_multiple_example.rb +0 -30
- data/spec/models/state_machine_with_failed_event.rb +0 -12
- data/spec/models/sub_class.rb +0 -7
- 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/transactor.rb +0 -53
- data/spec/models/valid_state_name.rb +0 -23
- data/spec/models/validator.rb +0 -79
- data/spec/models/worker.rb +0 -2
- data/spec/spec_helper.rb +0 -25
- data/spec/unit/api_spec.rb +0 -77
- data/spec/unit/basic_two_state_machines_example_spec.rb +0 -10
- data/spec/unit/callback_multiple_spec.rb +0 -295
- data/spec/unit/callbacks_spec.rb +0 -296
- data/spec/unit/complex_example_spec.rb +0 -84
- data/spec/unit/complex_multiple_example_spec.rb +0 -99
- 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 -11
- data/spec/unit/event_spec.rb +0 -322
- data/spec/unit/guard_multiple_spec.rb +0 -60
- data/spec/unit/guard_spec.rb +0 -60
- 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 -111
- data/spec/unit/localizer_spec.rb +0 -76
- data/spec/unit/memory_leak_spec.rb +0 -38
- data/spec/unit/new_dsl_spec.rb +0 -12
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +0 -573
- data/spec/unit/persistence/active_record_persistence_spec.rb +0 -552
- data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +0 -146
- data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +0 -93
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -127
- data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -79
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +0 -153
- data/spec/unit/persistence/sequel_persistence_spec.rb +0 -100
- data/spec/unit/readme_spec.rb +0 -42
- data/spec/unit/reloading_spec.rb +0 -15
- data/spec/unit/rspec_matcher_spec.rb +0 -79
- data/spec/unit/simple_example_spec.rb +0 -42
- data/spec/unit/simple_multiple_example_spec.rb +0 -63
- data/spec/unit/state_spec.rb +0 -89
- data/spec/unit/subclassing_multiple_spec.rb +0 -39
- data/spec/unit/subclassing_spec.rb +0 -31
- data/spec/unit/transition_spec.rb +0 -291
data/lib/aasm/aasm.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module AASM
|
2
|
+
# this is used internally as an argument default value to represent no value
|
3
|
+
NO_VALUE = :_aasm_no_value
|
2
4
|
|
3
5
|
# provide a state machine for the including class
|
4
6
|
# make sure to load class methods as well
|
@@ -8,7 +10,7 @@ module AASM
|
|
8
10
|
|
9
11
|
# do not overwrite existing state machines, which could have been created by
|
10
12
|
# inheritance, see class method inherited
|
11
|
-
AASM::
|
13
|
+
AASM::StateMachineStore.register(base)
|
12
14
|
|
13
15
|
AASM::Persistence.load_persistence(base)
|
14
16
|
super
|
@@ -17,10 +19,8 @@ module AASM
|
|
17
19
|
module ClassMethods
|
18
20
|
# make sure inheritance (aka subclassing) works with AASM
|
19
21
|
def inherited(base)
|
20
|
-
AASM::
|
21
|
-
|
22
|
-
AASM::StateMachine[base][state_machine_name] = AASM::StateMachine[self][state_machine_name].clone
|
23
|
-
end
|
22
|
+
AASM::StateMachineStore.register(base, self)
|
23
|
+
|
24
24
|
super
|
25
25
|
end
|
26
26
|
|
@@ -36,9 +36,15 @@ module AASM
|
|
36
36
|
options = args[0] || {}
|
37
37
|
end
|
38
38
|
|
39
|
-
AASM::
|
39
|
+
AASM::StateMachineStore.fetch(self, true).register(state_machine_name, AASM::StateMachine.new(state_machine_name))
|
40
|
+
|
41
|
+
# use a default despite the DSL configuration default.
|
42
|
+
# this is because configuration hasn't been setup for the AASM class but we are accessing a DSL option already for the class.
|
43
|
+
aasm_klass = options[:with_klass] || AASM::Base
|
40
44
|
|
41
|
-
|
45
|
+
raise ArgumentError, "The class #{aasm_klass} must inherit from AASM::Base!" unless aasm_klass.ancestors.include?(AASM::Base)
|
46
|
+
|
47
|
+
@aasm ||= Concurrent::Map.new
|
42
48
|
if @aasm[state_machine_name]
|
43
49
|
# make sure to use provided options
|
44
50
|
options.each do |key, value|
|
@@ -46,10 +52,10 @@ module AASM
|
|
46
52
|
end
|
47
53
|
else
|
48
54
|
# create a new base
|
49
|
-
@aasm[state_machine_name] =
|
55
|
+
@aasm[state_machine_name] = aasm_klass.new(
|
50
56
|
self,
|
51
57
|
state_machine_name,
|
52
|
-
AASM::
|
58
|
+
AASM::StateMachineStore.fetch(self, true).machine(state_machine_name),
|
53
59
|
options
|
54
60
|
)
|
55
61
|
end
|
@@ -60,13 +66,18 @@ module AASM
|
|
60
66
|
|
61
67
|
# this is the entry point for all instance-level access to AASM
|
62
68
|
def aasm(name=:default)
|
63
|
-
unless AASM::
|
69
|
+
unless AASM::StateMachineStore.fetch(self.class, true).machine(name)
|
64
70
|
raise AASM::UnknownStateMachineError.new("There is no state machine with the name '#{name}' defined in #{self.class.name}!")
|
65
71
|
end
|
66
|
-
@aasm ||=
|
72
|
+
@aasm ||= Concurrent::Map.new
|
67
73
|
@aasm[name.to_sym] ||= AASM::InstanceBase.new(self, name.to_sym)
|
68
74
|
end
|
69
75
|
|
76
|
+
def initialize_dup(other)
|
77
|
+
@aasm = Concurrent::Map.new
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
70
81
|
private
|
71
82
|
|
72
83
|
# Takes args and a from state and removes the first
|
@@ -88,65 +99,91 @@ private
|
|
88
99
|
begin
|
89
100
|
old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
|
90
101
|
|
91
|
-
|
92
|
-
event.fire_callbacks(
|
93
|
-
:before,
|
94
|
-
self,
|
95
|
-
*process_args(event, aasm(state_machine_name).current_state, *args)
|
96
|
-
)
|
102
|
+
fire_default_callbacks(event, *process_args(event, aasm(state_machine_name).current_state, *args))
|
97
103
|
|
98
104
|
if may_fire_to = event.may_fire?(self, *args)
|
99
|
-
old_state
|
100
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
101
|
-
old_state.fire_callbacks(:exit, self,
|
102
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
103
|
-
|
105
|
+
fire_exit_callbacks(old_state, *process_args(event, aasm(state_machine_name).current_state, *args))
|
104
106
|
if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
|
105
107
|
aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
|
106
108
|
else
|
107
|
-
aasm_failed(state_machine_name, event_name, old_state)
|
109
|
+
aasm_failed(state_machine_name, event_name, old_state, event.failed_callbacks)
|
108
110
|
end
|
109
111
|
else
|
110
|
-
aasm_failed(state_machine_name, event_name, old_state)
|
112
|
+
aasm_failed(state_machine_name, event_name, old_state, event.failed_callbacks)
|
111
113
|
end
|
112
114
|
rescue StandardError => e
|
113
|
-
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
115
|
+
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
116
|
+
event.fire_global_callbacks(:error_on_all_events, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
117
|
+
raise(e)
|
118
|
+
false
|
119
|
+
ensure
|
120
|
+
event.fire_callbacks(:ensure, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
121
|
+
event.fire_global_callbacks(:ensure_on_all_events, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
114
122
|
end
|
115
123
|
end
|
116
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
|
+
|
117
145
|
def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
|
118
146
|
persist = options[:persist]
|
119
147
|
|
120
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)
|
121
150
|
|
122
|
-
new_state.fire_callbacks(:before_enter, self,
|
123
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
151
|
+
new_state.fire_callbacks(:before_enter, self, *callback_args)
|
124
152
|
|
125
|
-
new_state.fire_callbacks(:enter, self,
|
126
|
-
*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?
|
127
154
|
|
128
155
|
persist_successful = true
|
129
156
|
if persist
|
130
157
|
persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
|
131
158
|
if persist_successful
|
132
159
|
yield if block_given?
|
133
|
-
event.fire_callbacks(:
|
160
|
+
event.fire_callbacks(:before_success, self, *callback_args)
|
161
|
+
event.fire_transition_callbacks(self, *process_args(event, old_state.name, *args))
|
162
|
+
event.fire_callbacks(:success, self, *callback_args)
|
134
163
|
end
|
135
164
|
else
|
136
165
|
aasm(state_machine_name).current_state = new_state_name
|
137
166
|
yield if block_given?
|
138
167
|
end
|
139
168
|
|
169
|
+
binding_event = event.options[:binding_event]
|
170
|
+
if binding_event
|
171
|
+
__send__("#{binding_event}#{'!' if persist}")
|
172
|
+
end
|
173
|
+
|
140
174
|
if persist_successful
|
141
|
-
old_state.fire_callbacks(:after_exit, self,
|
142
|
-
|
143
|
-
new_state.fire_callbacks(:after_enter, self,
|
144
|
-
*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)
|
145
177
|
event.fire_callbacks(
|
146
178
|
:after,
|
147
179
|
self,
|
148
180
|
*process_args(event, old_state.name, *args)
|
149
181
|
)
|
182
|
+
event.fire_global_callbacks(
|
183
|
+
:after_all_events,
|
184
|
+
self,
|
185
|
+
*process_args(event, old_state.name, *args)
|
186
|
+
)
|
150
187
|
|
151
188
|
self.aasm_event_fired(event.name, old_state.name, aasm(state_machine_name).current_state) if self.respond_to?(:aasm_event_fired)
|
152
189
|
else
|
@@ -156,13 +193,13 @@ private
|
|
156
193
|
persist_successful
|
157
194
|
end
|
158
195
|
|
159
|
-
def aasm_failed(state_machine_name, event_name, old_state)
|
196
|
+
def aasm_failed(state_machine_name, event_name, old_state, failures = [])
|
160
197
|
if self.respond_to?(:aasm_event_failed)
|
161
198
|
self.aasm_event_failed(event_name, old_state.name)
|
162
199
|
end
|
163
200
|
|
164
|
-
if AASM::
|
165
|
-
raise AASM::InvalidTransition.new(self, event_name, state_machine_name)
|
201
|
+
if AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.whiny_transitions
|
202
|
+
raise AASM::InvalidTransition.new(self, event_name, state_machine_name, failures)
|
166
203
|
else
|
167
204
|
false
|
168
205
|
end
|
data/lib/aasm/base.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
module AASM
|
2
4
|
class Base
|
3
5
|
|
4
|
-
attr_reader :state_machine
|
6
|
+
attr_reader :klass, :state_machine
|
5
7
|
|
6
8
|
def initialize(klass, name, state_machine, options={}, &block)
|
7
9
|
@klass = klass
|
8
10
|
@name = name
|
9
|
-
# @state_machine =
|
11
|
+
# @state_machine = klass.aasm(@name).state_machine
|
10
12
|
@state_machine = state_machine
|
11
13
|
@state_machine.config.column ||= (options[:column] || default_column).to_sym
|
12
14
|
# @state_machine.config.column = options[:column].to_sym if options[:column] # master
|
@@ -21,27 +23,43 @@ module AASM
|
|
21
23
|
# don't store any new state if the model is invalid (in ActiveRecord)
|
22
24
|
configure :skip_validation_on_save, false
|
23
25
|
|
26
|
+
# raise if the model is invalid (in ActiveRecord)
|
27
|
+
configure :whiny_persistence, false
|
28
|
+
|
29
|
+
# Use transactions (in ActiveRecord)
|
30
|
+
configure :use_transactions, true
|
31
|
+
|
24
32
|
# use requires_new for nested transactions (in ActiveRecord)
|
25
33
|
configure :requires_new_transaction, true
|
26
34
|
|
35
|
+
# use pessimistic locking (in ActiveRecord)
|
36
|
+
# true for FOR UPDATE lock
|
37
|
+
# string for a specific lock type i.e. FOR UPDATE NOWAIT
|
38
|
+
configure :requires_lock, false
|
39
|
+
|
40
|
+
# automatically set `"#{state_name}_at" = ::Time.now` on state changes
|
41
|
+
configure :timestamps, false
|
42
|
+
|
27
43
|
# set to true to forbid direct assignment of aasm_state column (in ActiveRecord)
|
28
44
|
configure :no_direct_assignment, false
|
29
45
|
|
46
|
+
# allow a AASM::Base sub-class to be used for state machine
|
47
|
+
configure :with_klass, AASM::Base
|
48
|
+
|
30
49
|
configure :enum, nil
|
31
50
|
|
51
|
+
# Set to true to namespace reader methods and constants
|
52
|
+
configure :namespace, false
|
53
|
+
|
54
|
+
# Configure a logger, with default being a Logger to STDERR
|
55
|
+
configure :logger, Logger.new(STDERR)
|
56
|
+
|
57
|
+
# setup timestamp-setting callback if enabled
|
58
|
+
setup_timestamps(@name)
|
59
|
+
|
32
60
|
# make sure to raise an error if no_direct_assignment is enabled
|
33
61
|
# and attribute is directly assigned though
|
34
|
-
@
|
35
|
-
def #{@state_machine.config.column}=(state_name)
|
36
|
-
if self.class.aasm(:#{@name}).state_machine.config.no_direct_assignment
|
37
|
-
raise AASM::NoDirectAssignmentError.new(
|
38
|
-
'direct assignment of AASM column has been disabled (see AASM configuration for this class)'
|
39
|
-
)
|
40
|
-
else
|
41
|
-
super
|
42
|
-
end
|
43
|
-
end
|
44
|
-
)
|
62
|
+
setup_no_direct_assignment(@name)
|
45
63
|
end
|
46
64
|
|
47
65
|
# This method is both a getter and a setter
|
@@ -63,21 +81,29 @@ module AASM
|
|
63
81
|
end
|
64
82
|
|
65
83
|
# define a state
|
66
|
-
|
67
|
-
|
84
|
+
# args
|
85
|
+
# [0] state
|
86
|
+
# [1] options (or nil)
|
87
|
+
# or
|
88
|
+
# [0] state
|
89
|
+
# [1..] state
|
90
|
+
def state(*args)
|
91
|
+
names, options = interpret_state_args(args)
|
92
|
+
names.each do |name|
|
93
|
+
@state_machine.add_state(name, klass, options)
|
68
94
|
|
69
|
-
|
70
|
-
|
71
|
-
end
|
95
|
+
aasm_name = @name.to_sym
|
96
|
+
state = name.to_sym
|
72
97
|
|
73
|
-
|
74
|
-
|
75
|
-
aasm(
|
98
|
+
method_name = namespace? ? "#{namespace}_#{name}" : name
|
99
|
+
safely_define_method klass, "#{method_name}?", -> do
|
100
|
+
aasm(aasm_name).current_state == state
|
76
101
|
end
|
77
|
-
EORUBY
|
78
102
|
|
79
|
-
|
80
|
-
|
103
|
+
const_name = namespace? ? "STATE_#{namespace.upcase}_#{name.upcase}" : "STATE_#{name.upcase}"
|
104
|
+
unless klass.const_defined?(const_name)
|
105
|
+
klass.const_set(const_name, name)
|
106
|
+
end
|
81
107
|
end
|
82
108
|
end
|
83
109
|
|
@@ -85,34 +111,65 @@ module AASM
|
|
85
111
|
def event(name, options={}, &block)
|
86
112
|
@state_machine.add_event(name, options, &block)
|
87
113
|
|
88
|
-
|
89
|
-
|
90
|
-
end
|
114
|
+
aasm_name = @name.to_sym
|
115
|
+
event = name.to_sym
|
91
116
|
|
92
117
|
# an addition over standard aasm so that, before firing an event, you can ask
|
93
118
|
# may_event? and get back a boolean that tells you whether the guard method
|
94
119
|
# on the transition will let this happen.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
120
|
+
safely_define_method klass, "may_#{name}?", ->(*args) do
|
121
|
+
aasm(aasm_name).may_fire_event?(event, *args)
|
122
|
+
end
|
99
123
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
124
|
+
safely_define_method klass, "#{name}!", ->(*args, &block) do
|
125
|
+
aasm(aasm_name).current_event = :"#{name}!"
|
126
|
+
aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
|
127
|
+
end
|
128
|
+
|
129
|
+
safely_define_method klass, name, ->(*args, &block) do
|
130
|
+
aasm(aasm_name).current_event = event
|
131
|
+
aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block)
|
132
|
+
end
|
133
|
+
|
134
|
+
skip_instance_level_validation(event, name, aasm_name, klass)
|
135
|
+
|
136
|
+
# Create aliases for the event methods. Keep the old names to maintain backwards compatibility.
|
137
|
+
if namespace?
|
138
|
+
klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?")
|
139
|
+
klass.send(:alias_method, "#{name}_#{namespace}!", "#{name}!")
|
140
|
+
klass.send(:alias_method, "#{name}_#{namespace}", name)
|
141
|
+
end
|
104
142
|
|
105
|
-
def #{name}(*args, &block)
|
106
|
-
aasm(:#{@name}).current_event = :#{name}
|
107
|
-
aasm_fire_event(:#{@name}, :#{name}, {:persist => false}, *args, &block)
|
108
|
-
end
|
109
|
-
EORUBY
|
110
143
|
end
|
111
144
|
|
112
145
|
def after_all_transitions(*callbacks, &block)
|
113
146
|
@state_machine.add_global_callbacks(:after_all_transitions, *callbacks, &block)
|
114
147
|
end
|
115
148
|
|
149
|
+
def after_all_transactions(*callbacks, &block)
|
150
|
+
@state_machine.add_global_callbacks(:after_all_transactions, *callbacks, &block)
|
151
|
+
end
|
152
|
+
|
153
|
+
def before_all_transactions(*callbacks, &block)
|
154
|
+
@state_machine.add_global_callbacks(:before_all_transactions, *callbacks, &block)
|
155
|
+
end
|
156
|
+
|
157
|
+
def before_all_events(*callbacks, &block)
|
158
|
+
@state_machine.add_global_callbacks(:before_all_events, *callbacks, &block)
|
159
|
+
end
|
160
|
+
|
161
|
+
def after_all_events(*callbacks, &block)
|
162
|
+
@state_machine.add_global_callbacks(:after_all_events, *callbacks, &block)
|
163
|
+
end
|
164
|
+
|
165
|
+
def error_on_all_events(*callbacks, &block)
|
166
|
+
@state_machine.add_global_callbacks(:error_on_all_events, *callbacks, &block)
|
167
|
+
end
|
168
|
+
|
169
|
+
def ensure_on_all_events(*callbacks, &block)
|
170
|
+
@state_machine.add_global_callbacks(:ensure_on_all_events, *callbacks, &block)
|
171
|
+
end
|
172
|
+
|
116
173
|
def states
|
117
174
|
@state_machine.states
|
118
175
|
end
|
@@ -123,7 +180,7 @@ module AASM
|
|
123
180
|
|
124
181
|
# aasm.event(:event_name).human?
|
125
182
|
def human_event_name(event) # event_name?
|
126
|
-
AASM::Localizer.new.human_event_name(
|
183
|
+
AASM::Localizer.new.human_event_name(klass, event)
|
127
184
|
end
|
128
185
|
|
129
186
|
def states_for_select
|
@@ -134,6 +191,7 @@ module AASM
|
|
134
191
|
if options[:transition]
|
135
192
|
@state_machine.events[options[:transition]].transitions_to_state(state).flatten.map(&:from).flatten
|
136
193
|
else
|
194
|
+
|
137
195
|
events.map {|e| e.transitions_to_state(state)}.flatten.map(&:from).flatten
|
138
196
|
end
|
139
197
|
end
|
@@ -152,5 +210,94 @@ module AASM
|
|
152
210
|
end
|
153
211
|
end
|
154
212
|
|
213
|
+
def safely_define_method(klass, method_name, method_definition)
|
214
|
+
# Warn if method exists and it did not originate from an enum
|
215
|
+
if klass.method_defined?(method_name) &&
|
216
|
+
! ( @state_machine.config.enum &&
|
217
|
+
klass.respond_to?(:defined_enums) &&
|
218
|
+
klass.defined_enums.values.any?{ |methods|
|
219
|
+
methods.keys{| enum | enum + '?' == method_name }
|
220
|
+
})
|
221
|
+
unless AASM::Configuration.hide_warnings
|
222
|
+
@state_machine.config.logger.warn "#{klass.name}: overriding method '#{method_name}'!"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
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
|
240
|
+
end
|
241
|
+
|
242
|
+
def namespace?
|
243
|
+
!!@state_machine.config.namespace
|
244
|
+
end
|
245
|
+
|
246
|
+
def namespace
|
247
|
+
if @state_machine.config.namespace == true
|
248
|
+
@name
|
249
|
+
else
|
250
|
+
@state_machine.config.namespace
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def interpret_state_args(args)
|
255
|
+
if args.last.is_a?(Hash) && args.size == 2
|
256
|
+
[[args.first], args.last]
|
257
|
+
elsif args.size > 0
|
258
|
+
[args, {}]
|
259
|
+
else
|
260
|
+
raise "count not parse states: #{args}"
|
261
|
+
end
|
262
|
+
end
|
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
|
+
|
155
302
|
end
|
156
303
|
end
|
data/lib/aasm/configuration.rb
CHANGED
@@ -9,15 +9,40 @@ module AASM
|
|
9
9
|
# for all persistence layers: create named scopes for each state
|
10
10
|
attr_accessor :create_scopes
|
11
11
|
|
12
|
-
# for ActiveRecord:
|
12
|
+
# for ActiveRecord: when the model is invalid, true -> raise, false -> return false
|
13
|
+
attr_accessor :whiny_persistence
|
14
|
+
|
15
|
+
# for ActiveRecord: store the new state even if the model is invalid and return true
|
13
16
|
attr_accessor :skip_validation_on_save
|
14
17
|
|
18
|
+
# for ActiveRecord: use transactions
|
19
|
+
attr_accessor :use_transactions
|
20
|
+
|
15
21
|
# for ActiveRecord: use requires_new for nested transactions?
|
16
22
|
attr_accessor :requires_new_transaction
|
17
23
|
|
24
|
+
# for ActiveRecord: use pessimistic locking
|
25
|
+
attr_accessor :requires_lock
|
26
|
+
|
27
|
+
# automatically set `"#{state_name}_at" = ::Time.now` on state changes
|
28
|
+
attr_accessor :timestamps
|
29
|
+
|
18
30
|
# forbid direct assignment in aasm_state column (in ActiveRecord)
|
19
31
|
attr_accessor :no_direct_assignment
|
20
32
|
|
33
|
+
# allow a AASM::Base sub-class to be used for state machine
|
34
|
+
attr_accessor :with_klass
|
35
|
+
|
21
36
|
attr_accessor :enum
|
37
|
+
|
38
|
+
# namespace reader methods and constants
|
39
|
+
attr_accessor :namespace
|
40
|
+
|
41
|
+
# Configure a logger, with default being a Logger to STDERR
|
42
|
+
attr_accessor :logger
|
43
|
+
|
44
|
+
class << self
|
45
|
+
attr_accessor :hide_warnings
|
46
|
+
end
|
22
47
|
end
|
23
|
-
end
|
48
|
+
end
|