aasm 4.12.3 → 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 +48 -18
- data/Appraisals +50 -26
- data/CHANGELOG.md +75 -3
- data/Dockerfile +44 -0
- data/Gemfile +2 -3
- data/README.md +216 -110
- data/aasm.gemspec +2 -0
- data/docker-compose.yml +40 -0
- data/gemfiles/norails.gemfile +10 -0
- data/gemfiles/rails_4.2.gemfile +9 -8
- data/gemfiles/rails_4.2_mongoid_5.gemfile +6 -5
- data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
- data/gemfiles/rails_5.0.gemfile +6 -6
- data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
- data/gemfiles/rails_5.1.gemfile +14 -0
- data/gemfiles/rails_5.2.gemfile +14 -0
- data/lib/aasm.rb +5 -2
- data/lib/aasm/aasm.rb +30 -27
- data/lib/aasm/base.rb +25 -7
- data/lib/aasm/core/event.rb +14 -24
- 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 +47 -0
- data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
- data/lib/aasm/core/state.rb +10 -9
- data/lib/aasm/core/transition.rb +7 -68
- data/lib/aasm/errors.rb +4 -3
- data/lib/aasm/instance_base.rb +16 -4
- data/lib/aasm/persistence.rb +3 -0
- data/lib/aasm/persistence/active_record_persistence.rb +25 -5
- data/lib/aasm/persistence/base.rb +1 -1
- 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 +105 -0
- data/lib/aasm/persistence/orm.rb +23 -19
- data/lib/aasm/persistence/plain_persistence.rb +2 -1
- data/lib/aasm/persistence/redis_persistence.rb +1 -1
- data/lib/aasm/persistence/sequel_persistence.rb +0 -1
- 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 +5 -1
- data/lib/aasm/version.rb +1 -1
- data/lib/generators/aasm/orm_helpers.rb +6 -0
- data/lib/generators/active_record/aasm_generator.rb +3 -1
- data/lib/generators/nobrainer/aasm_generator.rb +28 -0
- data/lib/motion-aasm.rb +1 -0
- data/spec/database.rb +16 -1
- data/spec/en.yml +0 -3
- data/spec/generators/active_record_generator_spec.rb +6 -0
- data/spec/generators/no_brainer_generator_spec.rb +29 -0
- 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/active_record/person.rb +23 -0
- data/spec/models/active_record/simple_new_dsl.rb +15 -0
- data/spec/models/active_record/work.rb +3 -0
- 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/nobrainer/complex_no_brainer_example.rb +36 -0
- data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
- data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
- data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
- data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
- data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
- data/spec/models/nobrainer/simple_no_brainer.rb +23 -0
- data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
- data/spec/models/simple_example.rb +8 -0
- data/spec/models/simple_example_with_guard_args.rb +17 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/spec_helpers/active_record.rb +2 -1
- data/spec/spec_helpers/dynamoid.rb +7 -5
- data/spec/spec_helpers/mongoid.rb +20 -1
- data/spec/spec_helpers/nobrainer.rb +15 -0
- data/spec/spec_helpers/redis.rb +5 -2
- data/spec/spec_helpers/sequel.rb +1 -1
- data/spec/unit/abstract_class_spec.rb +27 -0
- data/spec/unit/api_spec.rb +4 -0
- data/spec/unit/callback_multiple_spec.rb +7 -3
- data/spec/unit/callbacks_spec.rb +32 -2
- data/spec/unit/complex_example_spec.rb +0 -1
- data/spec/unit/event_spec.rb +13 -0
- data/spec/unit/exception_spec.rb +1 -1
- data/spec/unit/inspection_multiple_spec.rb +9 -5
- data/spec/unit/inspection_spec.rb +7 -3
- data/spec/unit/invoker_spec.rb +189 -0
- data/spec/unit/invokers/base_invoker_spec.rb +72 -0
- data/spec/unit/invokers/class_invoker_spec.rb +95 -0
- data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
- data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
- 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 +109 -4
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -4
- data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -4
- data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
- data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
- data/spec/unit/rspec_matcher_spec.rb +9 -0
- data/spec/unit/simple_example_spec.rb +15 -0
- data/spec/unit/state_spec.rb +23 -7
- data/spec/unit/transition_spec.rb +1 -1
- data/test/minitest_helper.rb +2 -2
- data/test/unit/minitest_matcher_test.rb +1 -1
- metadata +106 -12
- data/callbacks.txt +0 -51
- data/gemfiles/rails_3.2.gemfile +0 -13
- data/gemfiles/rails_4.0.gemfile +0 -15
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/docker-compose.yml
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
version: "2"
|
2
|
+
|
3
|
+
services:
|
4
|
+
aasm:
|
5
|
+
image: aasm/aasm
|
6
|
+
build: .
|
7
|
+
command: bash -c 'bundle exec appraisal install && bundle exec appraisal rspec'
|
8
|
+
environment:
|
9
|
+
- DYNAMODB_HOST=dynamodb
|
10
|
+
- DYNAMODB_PORT=8000
|
11
|
+
- MONGODB_HOST=mongo
|
12
|
+
- MONGODB_PORT=27017
|
13
|
+
- RAILS_ENV=development
|
14
|
+
- REDIS_HOST=redis
|
15
|
+
- REDIS_PORT=6379
|
16
|
+
- RETHINKDB_DB=rethinkdb_test
|
17
|
+
- RETHINKDB_HOST=rethinkdb
|
18
|
+
- RETHINKDB_PORT=28015
|
19
|
+
depends_on:
|
20
|
+
- dynamodb
|
21
|
+
- mongo
|
22
|
+
- redis
|
23
|
+
- rethinkdb
|
24
|
+
volumes:
|
25
|
+
- .:/application
|
26
|
+
volumes_from:
|
27
|
+
- bundle
|
28
|
+
bundle:
|
29
|
+
image: aasm/aasm
|
30
|
+
command: echo Bundler data container
|
31
|
+
volumes:
|
32
|
+
- /bundle
|
33
|
+
dynamodb:
|
34
|
+
image: cnadiminti/dynamodb-local:2017-02-16
|
35
|
+
mongo:
|
36
|
+
image: mongo:3.6.1
|
37
|
+
redis:
|
38
|
+
image: redis:4.0.6-alpine
|
39
|
+
rethinkdb:
|
40
|
+
image: rethinkdb:2.3.6
|
data/gemfiles/rails_4.2.gemfile
CHANGED
@@ -2,15 +2,16 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "sqlite3",
|
6
|
-
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
7
6
|
gem "rails", "4.2.5"
|
8
|
-
gem "nokogiri", "1.6.8.1", :
|
9
|
-
gem "mime-types", "~> 2", :
|
10
|
-
gem "mongoid", "~>4.0"
|
7
|
+
gem "nokogiri", "1.6.8.1", platforms: [:ruby_19]
|
8
|
+
gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
|
9
|
+
gem "mongoid", "~> 4.0"
|
11
10
|
gem "sequel"
|
12
|
-
gem "dynamoid", "~> 1", :
|
13
|
-
gem "aws-sdk", "~>2", :
|
11
|
+
gem "dynamoid", "~> 1", platforms: :ruby
|
12
|
+
gem "aws-sdk", "~> 2", platforms: :ruby
|
14
13
|
gem "redis-objects"
|
14
|
+
gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
|
15
|
+
gem "after_commit_everywhere", "~> 0.1", ">= 0.1.5"
|
15
16
|
|
16
|
-
gemspec :
|
17
|
+
gemspec path: "../"
|
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "sqlite3",
|
6
|
-
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
7
6
|
gem "rails", "4.2.5"
|
8
|
-
gem "mime-types", "~> 2", :
|
9
|
-
gem "mongoid", "~>5.0"
|
7
|
+
gem "mime-types", "~> 2", platforms: [:ruby_19, :jruby]
|
8
|
+
gem "mongoid", "~> 5.0"
|
9
|
+
gem "activerecord-jdbcsqlite3-adapter", "1.3.24", platforms: :jruby
|
10
|
+
gem "after_commit_everywhere", "~> 0.1", ">= 0.1.5"
|
10
11
|
|
11
|
-
gemspec :
|
12
|
+
gemspec path: "../"
|
data/gemfiles/rails_5.0.gemfile
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
-
gem "sqlite3",
|
6
|
-
gem "activerecord-jdbcsqlite3-adapter", :platforms => :jruby
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
7
6
|
gem "rails", "5.0.0"
|
8
|
-
gem "mongoid", "~>6.0"
|
7
|
+
gem "mongoid", "~> 6.0"
|
9
8
|
gem "sequel"
|
10
|
-
gem "dynamoid", "~> 1", :
|
11
|
-
gem "aws-sdk", "~>2", :
|
9
|
+
gem "dynamoid", "~> 1.3", platforms: :ruby
|
10
|
+
gem "aws-sdk", "~> 2", platforms: :ruby
|
12
11
|
gem "redis-objects"
|
12
|
+
gem "after_commit_everywhere", "~> 0.1", ">= 0.1.5"
|
13
13
|
|
14
|
-
gemspec :
|
14
|
+
gemspec path: "../"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
6
|
+
gem "rails", "5.1"
|
7
|
+
gem "mongoid", "~>6.0"
|
8
|
+
gem "sequel"
|
9
|
+
gem "dynamoid", "~> 1.3", platforms: :ruby
|
10
|
+
gem "aws-sdk", "~>2", platforms: :ruby
|
11
|
+
gem "redis-objects"
|
12
|
+
gem "after_commit_everywhere", "~> 0.1", ">= 0.1.5"
|
13
|
+
|
14
|
+
gemspec path: "../"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "sqlite3", "~> 1.3.5", platforms: :ruby
|
6
|
+
gem "rails", "5.2"
|
7
|
+
gem "mongoid", "~>6.0"
|
8
|
+
gem "sequel"
|
9
|
+
gem "dynamoid", "~>2.2", platforms: :ruby
|
10
|
+
gem "aws-sdk", "~>2", platforms: :ruby
|
11
|
+
gem "redis-objects"
|
12
|
+
gem "after_commit_everywhere", "~> 0.1", ">= 0.1.5"
|
13
|
+
|
14
|
+
gemspec path: "../"
|
data/lib/aasm.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
|
3
1
|
require 'aasm/version'
|
4
2
|
require 'aasm/errors'
|
5
3
|
require 'aasm/configuration'
|
@@ -9,6 +7,11 @@ require 'aasm/instance_base'
|
|
9
7
|
require 'aasm/core/transition'
|
10
8
|
require 'aasm/core/event'
|
11
9
|
require 'aasm/core/state'
|
10
|
+
require 'aasm/core/invoker'
|
11
|
+
require 'aasm/core/invokers/base_invoker'
|
12
|
+
require 'aasm/core/invokers/class_invoker'
|
13
|
+
require 'aasm/core/invokers/literal_invoker'
|
14
|
+
require 'aasm/core/invokers/proc_invoker'
|
12
15
|
require 'aasm/localizer'
|
13
16
|
require 'aasm/state_machine_store'
|
14
17
|
require 'aasm/state_machine'
|
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
|
@@ -130,31 +115,51 @@ private
|
|
130
115
|
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
131
116
|
event.fire_global_callbacks(:error_on_all_events, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) ||
|
132
117
|
raise(e)
|
118
|
+
false
|
133
119
|
ensure
|
134
120
|
event.fire_callbacks(:ensure, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
135
121
|
event.fire_global_callbacks(:ensure_on_all_events, self, *process_args(event, aasm(state_machine_name).current_state, *args))
|
136
122
|
end
|
137
123
|
end
|
138
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
|
+
|
139
145
|
def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
|
140
146
|
persist = options[:persist]
|
141
147
|
|
142
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)
|
143
150
|
|
144
|
-
new_state.fire_callbacks(:before_enter, self,
|
145
|
-
*process_args(event, aasm(state_machine_name).current_state, *args))
|
151
|
+
new_state.fire_callbacks(:before_enter, self, *callback_args)
|
146
152
|
|
147
|
-
new_state.fire_callbacks(:enter, self,
|
148
|
-
*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?
|
149
154
|
|
150
155
|
persist_successful = true
|
151
156
|
if persist
|
152
157
|
persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
|
153
158
|
if persist_successful
|
154
159
|
yield if block_given?
|
155
|
-
event.fire_callbacks(:before_success, self)
|
160
|
+
event.fire_callbacks(:before_success, self, *callback_args)
|
156
161
|
event.fire_transition_callbacks(self, *process_args(event, old_state.name, *args))
|
157
|
-
event.fire_callbacks(:success, self)
|
162
|
+
event.fire_callbacks(:success, self, *callback_args)
|
158
163
|
end
|
159
164
|
else
|
160
165
|
aasm(state_machine_name).current_state = new_state_name
|
@@ -167,10 +172,8 @@ private
|
|
167
172
|
end
|
168
173
|
|
169
174
|
if persist_successful
|
170
|
-
old_state.fire_callbacks(:after_exit, self,
|
171
|
-
|
172
|
-
new_state.fire_callbacks(:after_enter, self,
|
173
|
-
*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)
|
174
177
|
event.fire_callbacks(
|
175
178
|
:after,
|
176
179
|
self,
|
data/lib/aasm/base.rb
CHANGED
@@ -54,13 +54,14 @@ module AASM
|
|
54
54
|
# make sure to raise an error if no_direct_assignment is enabled
|
55
55
|
# and attribute is directly assigned though
|
56
56
|
aasm_name = @name
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
57
|
+
|
58
|
+
if @state_machine.config.no_direct_assignment
|
59
|
+
@klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name|
|
60
|
+
if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment
|
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)
|
64
|
+
end
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|
@@ -134,6 +135,8 @@ module AASM
|
|
134
135
|
aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block)
|
135
136
|
end
|
136
137
|
|
138
|
+
skip_instance_level_validation(event, name, aasm_name, klass)
|
139
|
+
|
137
140
|
# Create aliases for the event methods. Keep the old names to maintain backwards compatibility.
|
138
141
|
if namespace?
|
139
142
|
klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?")
|
@@ -249,5 +252,20 @@ module AASM
|
|
249
252
|
end
|
250
253
|
end
|
251
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
|
+
|
252
270
|
end
|
253
271
|
end
|
data/lib/aasm/core/event.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AASM::Core
|
2
4
|
class Event
|
3
5
|
include DslHelper
|
@@ -123,18 +125,19 @@ module AASM::Core
|
|
123
125
|
|
124
126
|
def _fire(obj, options={}, to_state=::AASM::NO_VALUE, *args)
|
125
127
|
result = options[:test_only] ? false : nil
|
128
|
+
clear_failed_callbacks
|
126
129
|
transitions = @transitions.select { |t| t.from == obj.aasm(state_machine.name).current_state || t.from == nil}
|
127
130
|
return result if transitions.size == 0
|
128
131
|
|
129
132
|
if to_state == ::AASM::NO_VALUE
|
130
133
|
to_state = nil
|
131
|
-
elsif to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym)
|
132
|
-
# nop, to_state is a valid to-state
|
133
|
-
else
|
134
|
+
elsif !(to_state.respond_to?(:to_sym) && transitions.map(&:to).flatten.include?(to_state.to_sym))
|
134
135
|
# to_state is an argument
|
135
136
|
args.unshift(to_state)
|
136
137
|
to_state = nil
|
137
138
|
end
|
139
|
+
|
140
|
+
# nop, to_state is a valid to-state
|
138
141
|
|
139
142
|
transitions.each do |transition|
|
140
143
|
next if to_state and !Array(transition.to).include?(to_state)
|
@@ -155,28 +158,15 @@ module AASM::Core
|
|
155
158
|
result
|
156
159
|
end
|
157
160
|
|
158
|
-
def
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
raise NoMethodError.new("NoMethodError: undefined method `#{code}' for #{record.inspect}:#{record.class}")
|
163
|
-
end
|
164
|
-
arity = record.__send__(:method, code.to_sym).arity
|
165
|
-
record.__send__(code, *(arity < 0 ? args : args[0...arity]))
|
166
|
-
true
|
167
|
-
|
168
|
-
when Proc
|
169
|
-
arity = code.arity
|
170
|
-
record.instance_exec(*(arity < 0 ? args : args[0...arity]), &code)
|
171
|
-
true
|
172
|
-
|
173
|
-
when Array
|
174
|
-
code.each {|a| invoke_callbacks(a, record, args)}
|
175
|
-
true
|
161
|
+
def clear_failed_callbacks
|
162
|
+
# https://github.com/aasm/aasm/issues/383, https://github.com/aasm/aasm/issues/599
|
163
|
+
transitions.each { |transition| transition.failures.clear }
|
164
|
+
end
|
176
165
|
|
177
|
-
|
178
|
-
|
179
|
-
|
166
|
+
def invoke_callbacks(code, record, args)
|
167
|
+
Invoker.new(code, record, args)
|
168
|
+
.with_default_return_value(false)
|
169
|
+
.invoke
|
180
170
|
end
|
181
171
|
end
|
182
172
|
end # AASM
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AASM
|
4
|
+
module Core
|
5
|
+
##
|
6
|
+
# main invoker class which encapsulates the logic
|
7
|
+
# for invoking literal-based, proc-based, class-based
|
8
|
+
# and array-based callbacks for different entities.
|
9
|
+
class Invoker
|
10
|
+
DEFAULT_RETURN_VALUE = true
|
11
|
+
|
12
|
+
##
|
13
|
+
# Initialize a new invoker instance.
|
14
|
+
# NOTE that invoker must be used per-subject/record
|
15
|
+
# (one instance per subject/record)
|
16
|
+
#
|
17
|
+
# ==Options:
|
18
|
+
#
|
19
|
+
# +subject+ - invoking subject, may be Proc,
|
20
|
+
# Class, String, Symbol or Array
|
21
|
+
# +record+ - invoking record
|
22
|
+
# +args+ - arguments which will be passed to the callback
|
23
|
+
|
24
|
+
def initialize(subject, record, args)
|
25
|
+
@subject = subject
|
26
|
+
@record = record
|
27
|
+
@args = args
|
28
|
+
@options = {}
|
29
|
+
@failures = []
|
30
|
+
@default_return_value = DEFAULT_RETURN_VALUE
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Pass additional options to concrete invoker
|
35
|
+
#
|
36
|
+
# ==Options:
|
37
|
+
#
|
38
|
+
# +options+ - hash of options which will be passed to
|
39
|
+
# concrete invokers
|
40
|
+
#
|
41
|
+
# ==Example:
|
42
|
+
#
|
43
|
+
# with_options(guard: proc {...})
|
44
|
+
|
45
|
+
def with_options(options)
|
46
|
+
@options = options
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Collect failures to a specified buffer
|
52
|
+
#
|
53
|
+
# ==Options:
|
54
|
+
#
|
55
|
+
# +failures+ - failures buffer to collect failures
|
56
|
+
|
57
|
+
def with_failures(failures)
|
58
|
+
@failures = failures
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Change default return value of #invoke method
|
64
|
+
# if none of invokers processed the request.
|
65
|
+
#
|
66
|
+
# The default return value is #DEFAULT_RETURN_VALUE
|
67
|
+
#
|
68
|
+
# ==Options:
|
69
|
+
#
|
70
|
+
# +value+ - default return value for #invoke method
|
71
|
+
|
72
|
+
def with_default_return_value(value)
|
73
|
+
@default_return_value = value
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Find concrete invoker for specified subject and invoker it,
|
79
|
+
# or return default value set by #DEFAULT_RETURN_VALUE or
|
80
|
+
# overridden by #with_default_return_value
|
81
|
+
|
82
|
+
# rubocop:disable Metrics/AbcSize
|
83
|
+
def invoke
|
84
|
+
return invoke_array if subject.is_a?(Array)
|
85
|
+
return literal_invoker.invoke if literal_invoker.may_invoke?
|
86
|
+
return proc_invoker.invoke if proc_invoker.may_invoke?
|
87
|
+
return class_invoker.invoke if class_invoker.may_invoke?
|
88
|
+
default_return_value
|
89
|
+
end
|
90
|
+
# rubocop:enable Metrics/AbcSize
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
attr_reader :subject, :record, :args, :options, :failures,
|
95
|
+
:default_return_value
|
96
|
+
|
97
|
+
def invoke_array
|
98
|
+
return subject.all? { |item| sub_invoke(item) } if options[:guard]
|
99
|
+
return subject.all? { |item| !sub_invoke(item) } if options[:unless]
|
100
|
+
subject.map { |item| sub_invoke(item) }
|
101
|
+
end
|
102
|
+
|
103
|
+
def sub_invoke(new_subject)
|
104
|
+
self.class.new(new_subject, record, args)
|
105
|
+
.with_failures(failures)
|
106
|
+
.with_options(options)
|
107
|
+
.invoke
|
108
|
+
end
|
109
|
+
|
110
|
+
def proc_invoker
|
111
|
+
@proc_invoker ||= Invokers::ProcInvoker
|
112
|
+
.new(subject, record, args)
|
113
|
+
.with_failures(failures)
|
114
|
+
end
|
115
|
+
|
116
|
+
def class_invoker
|
117
|
+
@class_invoker ||= Invokers::ClassInvoker
|
118
|
+
.new(subject, record, args)
|
119
|
+
.with_failures(failures)
|
120
|
+
end
|
121
|
+
|
122
|
+
def literal_invoker
|
123
|
+
@literal_invoker ||= Invokers::LiteralInvoker
|
124
|
+
.new(subject, record, args)
|
125
|
+
.with_failures(failures)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|