aasm 5.0.5 → 5.1.1
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/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.travis.yml +26 -23
- data/Appraisals +6 -10
- data/CHANGELOG.md +30 -0
- data/Dockerfile +1 -6
- data/Gemfile +1 -1
- data/README.md +63 -12
- data/aasm.gemspec +2 -0
- data/gemfiles/norails.gemfile +1 -1
- data/gemfiles/rails_4.2.gemfile +1 -0
- data/gemfiles/rails_4.2_mongoid_5.gemfile +1 -0
- data/gemfiles/rails_5.0.gemfile +1 -0
- data/gemfiles/rails_5.1.gemfile +1 -0
- data/gemfiles/rails_5.2.gemfile +1 -0
- data/lib/aasm.rb +0 -2
- data/lib/aasm/aasm.rb +29 -27
- data/lib/aasm/base.rb +19 -0
- data/lib/aasm/core/event.rb +3 -3
- data/lib/aasm/instance_base.rb +16 -4
- data/lib/aasm/persistence/active_record_persistence.rb +18 -0
- data/lib/aasm/persistence/orm.rb +23 -19
- data/lib/aasm/rspec/transition_from.rb +5 -1
- data/lib/aasm/version.rb +1 -1
- data/spec/database.rb +6 -1
- data/spec/en.yml +0 -3
- data/spec/{en_deprecated_style.yml → localizer_test_model_deprecated_style.yml} +0 -4
- data/spec/localizer_test_model_new_style.yml +5 -0
- data/spec/models/active_record/active_record_callback.rb +93 -0
- data/spec/models/active_record/instance_level_skip_validation_example.rb +19 -0
- data/spec/models/active_record/localizer_test_model.rb +3 -3
- data/spec/models/callbacks/with_state_arg.rb +5 -1
- data/spec/models/callbacks/with_state_arg_multiple.rb +4 -1
- data/spec/models/default_state.rb +1 -1
- data/spec/models/simple_example.rb +6 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/unit/api_spec.rb +4 -0
- data/spec/unit/callback_multiple_spec.rb +4 -0
- data/spec/unit/callbacks_spec.rb +4 -0
- data/spec/unit/complex_example_spec.rb +0 -1
- data/spec/unit/event_spec.rb +13 -0
- data/spec/unit/inspection_multiple_spec.rb +9 -5
- data/spec/unit/inspection_spec.rb +7 -3
- data/spec/unit/localizer_spec.rb +9 -10
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +4 -4
- data/spec/unit/persistence/active_record_persistence_spec.rb +93 -4
- data/spec/unit/rspec_matcher_spec.rb +3 -0
- data/spec/unit/simple_example_spec.rb +15 -0
- data/spec/unit/state_spec.rb +21 -5
- metadata +50 -11
- data/callbacks.txt +0 -51
- data/gemfiles/rails_3.2.gemfile +0 -14
data/aasm.gemspec
CHANGED
@@ -23,6 +23,8 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_development_dependency 'rspec', ">= 3"
|
24
24
|
s.add_development_dependency 'generator_spec'
|
25
25
|
s.add_development_dependency 'appraisal'
|
26
|
+
s.add_development_dependency "simplecov"
|
27
|
+
s.add_development_dependency "codecov", ">= 0.1.17", '< 0.1.20'
|
26
28
|
|
27
29
|
# debugging
|
28
30
|
# s.add_development_dependency 'debugger'
|
data/gemfiles/norails.gemfile
CHANGED
data/gemfiles/rails_4.2.gemfile
CHANGED
data/gemfiles/rails_5.0.gemfile
CHANGED
data/gemfiles/rails_5.1.gemfile
CHANGED
data/gemfiles/rails_5.2.gemfile
CHANGED
data/lib/aasm.rb
CHANGED
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
@@ -59,6 +59,8 @@ module AASM
|
|
59
59
|
@klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name|
|
60
60
|
if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
|
61
61
|
raise AASM::NoDirectAssignmentError.new('direct assignment of AASM column has been disabled (see AASM configuration for this class)')
|
62
|
+
else
|
63
|
+
super(state_name)
|
62
64
|
end
|
63
65
|
end
|
64
66
|
end
|
@@ -133,6 +135,8 @@ module AASM
|
|
133
135
|
aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block)
|
134
136
|
end
|
135
137
|
|
138
|
+
skip_instance_level_validation(event, name, aasm_name, klass)
|
139
|
+
|
136
140
|
# Create aliases for the event methods. Keep the old names to maintain backwards compatibility.
|
137
141
|
if namespace?
|
138
142
|
klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?")
|
@@ -248,5 +252,20 @@ module AASM
|
|
248
252
|
end
|
249
253
|
end
|
250
254
|
|
255
|
+
def skip_instance_level_validation(event, name, aasm_name, klass)
|
256
|
+
# Overrides the skip_validation config for an instance (If skip validation is set to false in original config) and
|
257
|
+
# restores it back to the original value after the event is fired.
|
258
|
+
safely_define_method klass, "#{name}_without_validation!", ->(*args, &block) do
|
259
|
+
original_config = AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save
|
260
|
+
begin
|
261
|
+
AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = true unless original_config
|
262
|
+
aasm(aasm_name).current_event = :"#{name}!"
|
263
|
+
aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block)
|
264
|
+
ensure
|
265
|
+
AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = original_config
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
251
270
|
end
|
252
271
|
end
|
data/lib/aasm/core/event.rb
CHANGED
@@ -131,13 +131,13 @@ module AASM::Core
|
|
131
131
|
|
132
132
|
if to_state == ::AASM::NO_VALUE
|
133
133
|
to_state = nil
|
134
|
-
elsif to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym)
|
135
|
-
# nop, to_state is a valid to-state
|
136
|
-
else
|
134
|
+
elsif !(to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym))
|
137
135
|
# to_state is an argument
|
138
136
|
args.unshift(to_state)
|
139
137
|
to_state = nil
|
140
138
|
end
|
139
|
+
|
140
|
+
# nop, to_state is a valid to-state
|
141
141
|
|
142
142
|
transitions.each do |transition|
|
143
143
|
next if to_state and !Array(transition.to).include?(to_state)
|
data/lib/aasm/instance_base.rb
CHANGED
@@ -28,7 +28,7 @@ module AASM
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def human_state
|
31
|
-
|
31
|
+
state_object_for_name(current_state).display_name
|
32
32
|
end
|
33
33
|
|
34
34
|
def states(options={}, *args)
|
@@ -78,6 +78,17 @@ module AASM
|
|
78
78
|
events
|
79
79
|
end
|
80
80
|
|
81
|
+
def permitted_transitions
|
82
|
+
events(permitted: true).flat_map do |event|
|
83
|
+
available_transitions = event.transitions_from_state(current_state)
|
84
|
+
allowed_transitions = available_transitions.select { |t| t.allowed?(@instance) }
|
85
|
+
|
86
|
+
allowed_transitions.map do |transition|
|
87
|
+
{ event: event.name, state: transition.to }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
81
92
|
def state_object_for_name(name)
|
82
93
|
obj = @instance.class.aasm(@name).states.find {|s| s.name == name}
|
83
94
|
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
|
@@ -91,7 +102,7 @@ module AASM
|
|
91
102
|
when Proc
|
92
103
|
state.call(@instance)
|
93
104
|
else
|
94
|
-
raise NotImplementedError, "Unrecognized state-type given.
|
105
|
+
raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
|
95
106
|
end
|
96
107
|
end
|
97
108
|
|
@@ -104,11 +115,12 @@ module AASM
|
|
104
115
|
end
|
105
116
|
|
106
117
|
def fire(event_name, *args, &block)
|
107
|
-
@instance.send(
|
118
|
+
@instance.send(event_name, *args, &block)
|
108
119
|
end
|
109
120
|
|
110
121
|
def fire!(event_name, *args, &block)
|
111
|
-
|
122
|
+
event_name = event_name.to_s.+("!").to_sym
|
123
|
+
@instance.send(event_name, *args, &block)
|
112
124
|
end
|
113
125
|
|
114
126
|
def set_current_state_with_persistence(state)
|
@@ -61,6 +61,24 @@ module AASM
|
|
61
61
|
|
62
62
|
private
|
63
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', '~> 0.1', '>= 0.1.5'` to your Gemfile in order to fix that.
|
77
|
+
MSG
|
78
|
+
yield
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
64
82
|
def aasm_raise_invalid_record
|
65
83
|
raise ActiveRecord::RecordInvalid.new(self)
|
66
84
|
end
|
data/lib/aasm/persistence/orm.rb
CHANGED
@@ -81,6 +81,10 @@ module AASM
|
|
81
81
|
true
|
82
82
|
end
|
83
83
|
|
84
|
+
def aasm_execute_after_commit
|
85
|
+
yield
|
86
|
+
end
|
87
|
+
|
84
88
|
def aasm_write_state_attribute(state, name=:default)
|
85
89
|
aasm_write_attribute(self.class.aasm(name).attribute_name, aasm_raw_attribute_value(state, name))
|
86
90
|
end
|
@@ -116,32 +120,32 @@ module AASM
|
|
116
120
|
|
117
121
|
# Returns true if event was fired successfully and transaction completed.
|
118
122
|
def aasm_fire_event(state_machine_name, name, options, *args, &block)
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
129
|
-
else
|
123
|
+
return super unless aasm_supports_transactions? && options[:persist]
|
124
|
+
|
125
|
+
event = self.class.aasm(state_machine_name).state_machine.events[name]
|
126
|
+
event.fire_callbacks(:before_transaction, self, *args)
|
127
|
+
event.fire_global_callbacks(:before_all_transactions, self, *args)
|
128
|
+
|
129
|
+
begin
|
130
|
+
success = if options[:persist] && use_transactions?(state_machine_name)
|
131
|
+
aasm_transaction(requires_new?(state_machine_name), requires_lock?(state_machine_name)) do
|
130
132
|
super
|
131
133
|
end
|
134
|
+
else
|
135
|
+
super
|
136
|
+
end
|
132
137
|
|
133
|
-
|
138
|
+
if success && !(event.options.keys & [:after_commit, :after_all_commits]).empty?
|
139
|
+
aasm_execute_after_commit do
|
134
140
|
event.fire_callbacks(:after_commit, self, *args)
|
135
141
|
event.fire_global_callbacks(:after_all_commits, self, *args)
|
136
142
|
end
|
137
|
-
|
138
|
-
success
|
139
|
-
ensure
|
140
|
-
event.fire_callbacks(:after_transaction, self, *args)
|
141
|
-
event.fire_global_callbacks(:after_all_transactions, self, *args)
|
142
143
|
end
|
143
|
-
|
144
|
-
|
144
|
+
|
145
|
+
success
|
146
|
+
ensure
|
147
|
+
event.fire_callbacks(:after_transaction, self, *args)
|
148
|
+
event.fire_global_callbacks(:after_all_transactions, self, *args)
|
145
149
|
end
|
146
150
|
end
|
147
151
|
|
@@ -2,7 +2,11 @@ RSpec::Matchers.define :transition_from do |from_state|
|
|
2
2
|
match do |obj|
|
3
3
|
@state_machine_name ||= :default
|
4
4
|
obj.aasm(@state_machine_name).current_state = from_state.to_sym
|
5
|
-
|
5
|
+
begin
|
6
|
+
obj.send(@event, *@args) && obj.aasm(@state_machine_name).current_state == @to_state.to_sym
|
7
|
+
rescue AASM::InvalidTransition
|
8
|
+
false
|
9
|
+
end
|
6
10
|
end
|
7
11
|
|
8
12
|
chain :on do |state_machine_name|
|
data/lib/aasm/version.rb
CHANGED
data/spec/database.rb
CHANGED
@@ -27,7 +27,7 @@ ActiveRecord::Migration.suppress_messages do
|
|
27
27
|
t.string "status"
|
28
28
|
end
|
29
29
|
|
30
|
-
%w(validators multiple_validators workers invalid_persistors multiple_invalid_persistors silent_persistors multiple_silent_persistors).each do |table_name|
|
30
|
+
%w(validators multiple_validators workers invalid_persistors multiple_invalid_persistors silent_persistors multiple_silent_persistors active_record_callbacks).each do |table_name|
|
31
31
|
ActiveRecord::Migration.create_table table_name, :force => true do |t|
|
32
32
|
t.string "name"
|
33
33
|
t.string "status"
|
@@ -51,4 +51,9 @@ ActiveRecord::Migration.suppress_messages do
|
|
51
51
|
t.string "search"
|
52
52
|
t.string "sync"
|
53
53
|
end
|
54
|
+
|
55
|
+
ActiveRecord::Migration.create_table "instance_level_skip_validation_examples", :force => true do |t|
|
56
|
+
t.string "state"
|
57
|
+
t.string "some_string"
|
58
|
+
end
|
54
59
|
end
|
data/spec/en.yml
CHANGED
@@ -0,0 +1,93 @@
|
|
1
|
+
class ActiveRecordCallback < ActiveRecord::Base
|
2
|
+
include AASM
|
3
|
+
|
4
|
+
def reset_data
|
5
|
+
@data = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def data
|
9
|
+
@data.join(' ')
|
10
|
+
end
|
11
|
+
|
12
|
+
aasm column: :status do
|
13
|
+
before_all_events :before_all_events
|
14
|
+
after_all_events :after_all_events
|
15
|
+
ensure_on_all_events :ensure_on_all_events
|
16
|
+
after_all_transitions :after_all_transitions
|
17
|
+
|
18
|
+
state :open, :initial => true,
|
19
|
+
:before_enter => :before_enter_open,
|
20
|
+
:enter => :enter_open,
|
21
|
+
:after_enter => :after_enter_open,
|
22
|
+
:before_exit => :before_exit_open,
|
23
|
+
:exit => :exit_open,
|
24
|
+
:after_exit => :after_exit_open
|
25
|
+
|
26
|
+
state :closed,
|
27
|
+
:before_enter => :before_enter_closed,
|
28
|
+
:enter => :enter_closed,
|
29
|
+
:after_enter => :after_enter_closed,
|
30
|
+
:before_exit => :before_exit_closed,
|
31
|
+
:exit => :exit_closed,
|
32
|
+
:after_exit => :after_exit_closed
|
33
|
+
|
34
|
+
event :close,
|
35
|
+
:before => :before_event,
|
36
|
+
:after => :after_event,
|
37
|
+
:guard => :event_guard,
|
38
|
+
:before_success => :event_before_success,
|
39
|
+
:after_commit => :event_after_commit,
|
40
|
+
:ensure => :ensure_event do
|
41
|
+
transitions :to => :closed, :from => [:open],
|
42
|
+
:guard => :transition_guard,
|
43
|
+
:after => :after_transition,
|
44
|
+
:success => :success_transition
|
45
|
+
end
|
46
|
+
|
47
|
+
event :open, :before => :before_event, :after => :after_event do
|
48
|
+
transitions :to => :open, :from => :closed
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def log(text)
|
53
|
+
@data ||= []
|
54
|
+
@data << text
|
55
|
+
#puts text
|
56
|
+
end
|
57
|
+
|
58
|
+
def aasm_write_state(*args); log('aasm_write_state'); true; end
|
59
|
+
def before_enter_open; log('before_enter_open'); end
|
60
|
+
def enter_open; log('enter_open'); end
|
61
|
+
def before_exit_open; log('before_exit_open'); end
|
62
|
+
def after_enter_open; log('after_enter_open'); end
|
63
|
+
def exit_open; log('exit_open'); end
|
64
|
+
def after_exit_open; log('after_exit_open'); end
|
65
|
+
|
66
|
+
def before_enter_closed; log('before_enter_closed'); end
|
67
|
+
def enter_closed; log('enter_closed'); end
|
68
|
+
def before_exit_closed; log('before_exit_closed'); end
|
69
|
+
def exit_closed; log('exit_closed'); end
|
70
|
+
def after_enter_closed; log('after_enter_closed'); end
|
71
|
+
def after_exit_closed; log('after_exit_closed'); end
|
72
|
+
|
73
|
+
def event_guard; log('event_guard'); !@fail_event_guard; end
|
74
|
+
def transition_guard; log('transition_guard'); !@fail_transition_guard; end
|
75
|
+
|
76
|
+
def event_before_success; log('event_before_success'); end
|
77
|
+
|
78
|
+
def after_transition; log('after_transition'); end
|
79
|
+
def after_all_transitions; log('after_all_transitions'); end
|
80
|
+
|
81
|
+
def before_all_events; log('before_all_events') end
|
82
|
+
def before_event; log('before_event'); end
|
83
|
+
def after_event; log('after_event'); end
|
84
|
+
def after_all_events; log('after_all_events'); end
|
85
|
+
|
86
|
+
def after_transition; log('after_transition'); end
|
87
|
+
def success_transition; log('transition_success'); end
|
88
|
+
|
89
|
+
def ensure_event; log('ensure'); end
|
90
|
+
def ensure_on_all_events; log('ensure'); end
|
91
|
+
|
92
|
+
def event_after_commit; log('after_commit'); end
|
93
|
+
end
|