active_interaction-extras 1.1.0 → 2.0.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 +4 -4
- data/.github/workflows/ci.yml +3 -2
- data/CHANGELOG.md +24 -0
- data/README.md +325 -0
- data/active_interaction-extras.gemspec +6 -5
- data/lib/active_interaction/extras/active_job.rb +36 -5
- data/lib/active_interaction/extras/add_to_batch.rb +13 -0
- data/lib/active_interaction/extras/after_initialize.rb +20 -0
- data/lib/active_interaction/extras/all.rb +16 -1
- data/lib/active_interaction/extras/all_runner.rb +91 -0
- data/lib/active_interaction/extras/append_inputs.rb +60 -0
- data/lib/active_interaction/extras/current.rb +23 -0
- data/lib/active_interaction/extras/file_blobs.rb +80 -0
- data/lib/active_interaction/extras/filter_alias.rb +28 -2
- data/lib/active_interaction/extras/filter_extensions.rb +8 -0
- data/lib/active_interaction/extras/filters/ar_relation_filter.rb +22 -0
- data/lib/active_interaction/extras/filters/uuid_filter.rb +1 -1
- data/lib/active_interaction/extras/form_for.rb +33 -0
- data/lib/active_interaction/extras/good_job.rb +45 -0
- data/lib/active_interaction/extras/include_errors.rb +10 -0
- data/lib/active_interaction/extras/initialize_with.rb +16 -0
- data/lib/active_interaction/extras/instance_runnable.rb +35 -0
- data/lib/active_interaction/extras/model_fields.rb +68 -33
- data/lib/active_interaction/extras/model_names.rb +20 -0
- data/lib/active_interaction/extras/named_callbacks.rb +54 -0
- data/lib/active_interaction/extras/nested_attributes.rb +126 -0
- data/lib/active_interaction/extras/strong_params.rb +48 -10
- data/lib/active_interaction/extras/transaction.rb +27 -5
- data/lib/active_interaction/extras/version.rb +1 -1
- data/lib/active_interaction/extras.rb +10 -23
- metadata +41 -15
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# inspired by store_model/nested_attributes.rb
|
|
2
|
+
module ActiveInteraction::Extras::NestedAttributes
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
module InputsExtension
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
def normalize(inputs)
|
|
9
|
+
if @base.class.respond_to? :nested_attribute_options
|
|
10
|
+
@base.class.nested_attribute_options.each do |attribute, options|
|
|
11
|
+
alias_name = "#{attribute}_attributes"
|
|
12
|
+
next if !inputs.key?(alias_name) && !inputs.key?(alias_name.to_sym)
|
|
13
|
+
|
|
14
|
+
value = inputs[alias_name] || inputs[alias_name.to_sym]
|
|
15
|
+
value = @base.class.process_nested_collection(value, options)
|
|
16
|
+
|
|
17
|
+
inputs[attribute.to_s] = value
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
included do
|
|
26
|
+
ActiveInteraction::Inputs.prepend InputsExtension
|
|
27
|
+
|
|
28
|
+
class_attribute :nested_attribute_options, default: {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class_methods do
|
|
32
|
+
def accepts_nested_attributes_for(*attributes)
|
|
33
|
+
options = attributes.extract_options!
|
|
34
|
+
options.reverse_merge!(allow_destroy: false, update_only: false)
|
|
35
|
+
|
|
36
|
+
if options.key?(:permit)
|
|
37
|
+
if options[:permit].is_a?(Array)
|
|
38
|
+
options[:permit] |= [:id, :_destroy]
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
raise ArgumentError, "missing :permit option"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
attributes.each do |attribute|
|
|
45
|
+
nested_attribute_options[attribute] = options
|
|
46
|
+
|
|
47
|
+
case filters[attribute]
|
|
48
|
+
when ActiveInteraction::ArrayFilter
|
|
49
|
+
define_association_setter_for_many attribute, options
|
|
50
|
+
else
|
|
51
|
+
define_association_setter_for_single(attribute, options)
|
|
52
|
+
define_method "#{attribute}_attributes=" do |*args, **kwargs|
|
|
53
|
+
send("#{attribute}=", *args, **kwargs)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def define_association_setter_for_many(association, options)
|
|
60
|
+
define_method "#{association}_attributes=" do |attributes|
|
|
61
|
+
attributes = self.class.process_nested_collection(attributes, options)
|
|
62
|
+
send("#{association}=", attributes)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def define_association_setter_for_single(association, options)
|
|
67
|
+
return unless options&.dig(:allow_destroy)
|
|
68
|
+
|
|
69
|
+
define_method "#{association}=" do |attributes|
|
|
70
|
+
if ActiveRecord::Type::Boolean.new.cast(attributes.stringify_keys.dig("_destroy"))
|
|
71
|
+
super(nil)
|
|
72
|
+
else
|
|
73
|
+
super(attributes)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def process_nested_collection(attributes, options = nil)
|
|
79
|
+
attributes = attributes.values if attributes.is_a?(Hash)
|
|
80
|
+
if options&.dig(:allow_destroy)
|
|
81
|
+
attributes.reject! do |attribute|
|
|
82
|
+
ActiveRecord::Type::Boolean.new.cast(attribute.stringify_keys.dig("_destroy"))
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
attributes.reject! { |attribute| call_reject_if(attribute, options[:reject_if]) } if options&.dig(:reject_if)
|
|
87
|
+
|
|
88
|
+
attributes
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def call_reject_if(attributes, callback)
|
|
92
|
+
callback = ActiveRecord::NestedAttributes::ClassMethods::REJECT_ALL_BLANK_PROC if callback == :all_blank
|
|
93
|
+
|
|
94
|
+
case callback
|
|
95
|
+
when Symbol
|
|
96
|
+
method(callback).arity.zero? ? send(callback) : send(callback, attributes)
|
|
97
|
+
when Proc
|
|
98
|
+
callback.call(attributes)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def assign_collection_association(model, association_name, attributes_collection, destroy_missing: false, **options)
|
|
104
|
+
|
|
105
|
+
if attributes_collection.is_a?(ActiveRecord::Associations::CollectionProxy)
|
|
106
|
+
return attributes_collection
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
model.instance_exec do
|
|
110
|
+
begin
|
|
111
|
+
old_options = nested_attributes_options[association_name]
|
|
112
|
+
nested_attributes_options[association_name] = options
|
|
113
|
+
|
|
114
|
+
list = assign_nested_attributes_for_collection_association(association_name, attributes_collection)
|
|
115
|
+
|
|
116
|
+
if destroy_missing
|
|
117
|
+
association(association_name).scope.excluding(list).each(&:destroy!)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
list
|
|
121
|
+
ensure
|
|
122
|
+
nested_attributes_options[association_name] = old_options
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -29,18 +29,56 @@ module ActiveInteraction::Extras::StrongParams
|
|
|
29
29
|
|
|
30
30
|
class_methods do
|
|
31
31
|
def permitted_params
|
|
32
|
-
filters.map do |filter_name, filter|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
permissions = filters.map do |filter_name, filter|
|
|
33
|
+
[
|
|
34
|
+
permission_for_filter(filter, filter_name),
|
|
35
|
+
# (permission_for_filter(filter, filter.options[:as]) if filter.options[:as])
|
|
36
|
+
]
|
|
37
|
+
end.flatten(1).compact
|
|
38
|
+
|
|
39
|
+
if respond_to?(:nested_attribute_options)
|
|
40
|
+
nested_attribute_options.each do |attribute, opts|
|
|
41
|
+
permitted_list = opts.fetch(:permit)
|
|
42
|
+
permissions << {"#{attribute}_attributes": permitted_list}
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
permissions
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def permission_for_filter(filter, name = filter.name)
|
|
50
|
+
permit = filter.options[:permit]
|
|
51
|
+
return unless permit
|
|
52
|
+
|
|
53
|
+
case filter
|
|
54
|
+
when ActiveInteraction::ArrayFilter
|
|
55
|
+
value =
|
|
56
|
+
if permit == true
|
|
57
|
+
nested_type = filter.filters.values.first
|
|
58
|
+
case nested_type
|
|
59
|
+
when ActiveInteraction::HashFilter, ActiveInteraction::ObjectFilter
|
|
60
|
+
{}
|
|
61
|
+
else
|
|
62
|
+
[]
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
permit
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
{ name => value }
|
|
69
|
+
when ActiveInteraction::HashFilter, ActiveInteraction::ObjectFilter
|
|
70
|
+
if permit == true
|
|
71
|
+
{ name => {} }
|
|
40
72
|
else
|
|
41
|
-
|
|
73
|
+
{ name => permit }
|
|
42
74
|
end
|
|
43
|
-
|
|
75
|
+
else
|
|
76
|
+
if permit == true
|
|
77
|
+
name
|
|
78
|
+
else
|
|
79
|
+
{ name => permit }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
44
82
|
end
|
|
45
83
|
end
|
|
46
84
|
end
|
|
@@ -6,22 +6,44 @@ module ActiveInteraction::Extras::Transaction
|
|
|
6
6
|
|
|
7
7
|
included do
|
|
8
8
|
class_attribute :run_in_transaction_options
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
around_execute if: :run_in_transaction_options do |_interaction, block|
|
|
10
11
|
ActiveRecord::Base.transaction(**run_in_transaction_options) do
|
|
11
12
|
block.call
|
|
12
13
|
raise ActiveRecord::Rollback if _interaction.errors.any?
|
|
13
14
|
end
|
|
14
|
-
|
|
15
|
+
end
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
class_methods do
|
|
18
19
|
# https://pragtob.wordpress.com/2017/12/12/surprises-with-nested-transactions-rollbacks-and-activerecord/
|
|
19
|
-
def run_in_transaction!(requires_new: true)
|
|
20
|
-
|
|
20
|
+
def run_in_transaction!(enabled = true, requires_new: true)
|
|
21
|
+
value =
|
|
22
|
+
if enabled
|
|
23
|
+
{ requires_new: requires_new }
|
|
24
|
+
else
|
|
25
|
+
false
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
self.run_in_transaction_options = value
|
|
21
29
|
end
|
|
22
30
|
|
|
23
31
|
def skip_run_in_transaction!
|
|
24
|
-
self.run_in_transaction_options =
|
|
32
|
+
self.run_in_transaction_options = false
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def force_explicit_transactions!
|
|
36
|
+
before_filter do
|
|
37
|
+
if run_in_transaction_options.nil?
|
|
38
|
+
raise "Missing transaction definition. Add `run_in_transaction!`."
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def list_missing_transaction_definition
|
|
44
|
+
Zeitwerk::Loader.eager_load_all
|
|
45
|
+
descendants
|
|
46
|
+
.select { |klass| klass.run_in_transaction_options.nil? }
|
|
25
47
|
end
|
|
26
48
|
end
|
|
27
49
|
end
|
|
@@ -1,38 +1,25 @@
|
|
|
1
1
|
require "active_interaction/extras/version"
|
|
2
2
|
|
|
3
3
|
require 'active_support'
|
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
|
4
5
|
require 'active_interaction'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
module Extras
|
|
8
|
-
module Filters
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
module FilterExtensions
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
module Jobs
|
|
15
|
-
autoload(:Core, "active_interaction/extras/jobs/core")
|
|
16
|
-
end
|
|
7
|
+
require 'zeitwerk'
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
autoload(:RunCallback, "active_interaction/extras/run_callback")
|
|
24
|
-
autoload(:StrongParams, "active_interaction/extras/strong_params")
|
|
25
|
-
autoload(:Transaction, "active_interaction/extras/transaction")
|
|
9
|
+
loader = Zeitwerk::Loader.for_gem_extension(ActiveInteraction)
|
|
10
|
+
loader.inflector.inflect(
|
|
11
|
+
'uuid_filter' => 'UUIDFilter',
|
|
12
|
+
)
|
|
13
|
+
loader.setup
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
module ActiveInteraction
|
|
16
|
+
module Extras
|
|
29
17
|
|
|
30
|
-
autoload(:ActiveJob, "active_interaction/extras/active_job")
|
|
31
|
-
autoload(:Sidekiq, "active_interaction/extras/sidekiq")
|
|
32
18
|
end
|
|
33
19
|
end
|
|
34
20
|
|
|
35
21
|
require 'active_interaction/extras/filters/anything_filter'
|
|
22
|
+
require 'active_interaction/extras/filters/ar_relation_filter'
|
|
36
23
|
require 'active_interaction/extras/filters/uuid_filter'
|
|
37
24
|
|
|
38
25
|
I18n.load_path.unshift(
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_interaction-extras
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Anton Katunin
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: active_interaction
|
|
@@ -36,40 +35,54 @@ dependencies:
|
|
|
36
35
|
requirements:
|
|
37
36
|
- - ">="
|
|
38
37
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
38
|
+
version: '7.1'
|
|
40
39
|
type: :runtime
|
|
41
40
|
prerelease: false
|
|
42
41
|
version_requirements: !ruby/object:Gem::Requirement
|
|
43
42
|
requirements:
|
|
44
43
|
- - ">="
|
|
45
44
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
45
|
+
version: '7.1'
|
|
47
46
|
- !ruby/object:Gem::Dependency
|
|
48
47
|
name: activesupport
|
|
49
48
|
requirement: !ruby/object:Gem::Requirement
|
|
50
49
|
requirements:
|
|
51
50
|
- - ">="
|
|
52
51
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
52
|
+
version: '7.1'
|
|
54
53
|
type: :runtime
|
|
55
54
|
prerelease: false
|
|
56
55
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
56
|
requirements:
|
|
58
57
|
- - ">="
|
|
59
58
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
59
|
+
version: '7.1'
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: zeitwerk
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '2'
|
|
67
|
+
type: :runtime
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '2'
|
|
61
74
|
- !ruby/object:Gem::Dependency
|
|
62
75
|
name: bundler
|
|
63
76
|
requirement: !ruby/object:Gem::Requirement
|
|
64
77
|
requirements:
|
|
65
|
-
- - "
|
|
78
|
+
- - ">="
|
|
66
79
|
- !ruby/object:Gem::Version
|
|
67
80
|
version: '2.2'
|
|
68
81
|
type: :development
|
|
69
82
|
prerelease: false
|
|
70
83
|
version_requirements: !ruby/object:Gem::Requirement
|
|
71
84
|
requirements:
|
|
72
|
-
- - "
|
|
85
|
+
- - ">="
|
|
73
86
|
- !ruby/object:Gem::Version
|
|
74
87
|
version: '2.2'
|
|
75
88
|
- !ruby/object:Gem::Dependency
|
|
@@ -120,14 +133,14 @@ dependencies:
|
|
|
120
133
|
requirements:
|
|
121
134
|
- - ">="
|
|
122
135
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '
|
|
136
|
+
version: '2'
|
|
124
137
|
type: :development
|
|
125
138
|
prerelease: false
|
|
126
139
|
version_requirements: !ruby/object:Gem::Requirement
|
|
127
140
|
requirements:
|
|
128
141
|
- - ">="
|
|
129
142
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '
|
|
143
|
+
version: '2'
|
|
131
144
|
- !ruby/object:Gem::Dependency
|
|
132
145
|
name: activerecord
|
|
133
146
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -191,18 +204,33 @@ files:
|
|
|
191
204
|
- bin/setup
|
|
192
205
|
- lib/active_interaction/extras.rb
|
|
193
206
|
- lib/active_interaction/extras/active_job.rb
|
|
207
|
+
- lib/active_interaction/extras/add_to_batch.rb
|
|
208
|
+
- lib/active_interaction/extras/after_initialize.rb
|
|
194
209
|
- lib/active_interaction/extras/all.rb
|
|
210
|
+
- lib/active_interaction/extras/all_runner.rb
|
|
211
|
+
- lib/active_interaction/extras/append_inputs.rb
|
|
212
|
+
- lib/active_interaction/extras/current.rb
|
|
213
|
+
- lib/active_interaction/extras/file_blobs.rb
|
|
195
214
|
- lib/active_interaction/extras/filter_alias.rb
|
|
196
215
|
- lib/active_interaction/extras/filter_extensions.rb
|
|
197
216
|
- lib/active_interaction/extras/filter_extensions/hash_auto_strip.rb
|
|
198
217
|
- lib/active_interaction/extras/filter_extensions/object_classes.rb
|
|
199
218
|
- lib/active_interaction/extras/filter_extensions/timezone_support.rb
|
|
200
219
|
- lib/active_interaction/extras/filters/anything_filter.rb
|
|
220
|
+
- lib/active_interaction/extras/filters/ar_relation_filter.rb
|
|
201
221
|
- lib/active_interaction/extras/filters/uuid_filter.rb
|
|
222
|
+
- lib/active_interaction/extras/form_for.rb
|
|
223
|
+
- lib/active_interaction/extras/good_job.rb
|
|
202
224
|
- lib/active_interaction/extras/halt.rb
|
|
225
|
+
- lib/active_interaction/extras/include_errors.rb
|
|
226
|
+
- lib/active_interaction/extras/initialize_with.rb
|
|
227
|
+
- lib/active_interaction/extras/instance_runnable.rb
|
|
203
228
|
- lib/active_interaction/extras/jobs/core.rb
|
|
204
229
|
- lib/active_interaction/extras/locale/en.yml
|
|
205
230
|
- lib/active_interaction/extras/model_fields.rb
|
|
231
|
+
- lib/active_interaction/extras/model_names.rb
|
|
232
|
+
- lib/active_interaction/extras/named_callbacks.rb
|
|
233
|
+
- lib/active_interaction/extras/nested_attributes.rb
|
|
206
234
|
- lib/active_interaction/extras/rspec.rb
|
|
207
235
|
- lib/active_interaction/extras/run_callback.rb
|
|
208
236
|
- lib/active_interaction/extras/sidekiq.rb
|
|
@@ -214,7 +242,6 @@ licenses:
|
|
|
214
242
|
- MIT
|
|
215
243
|
metadata:
|
|
216
244
|
changelog_uri: https://github.com/antulik/active_interaction-extras/blob/master/CHANGELOG.md
|
|
217
|
-
post_install_message:
|
|
218
245
|
rdoc_options: []
|
|
219
246
|
require_paths:
|
|
220
247
|
- lib
|
|
@@ -222,15 +249,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
222
249
|
requirements:
|
|
223
250
|
- - ">="
|
|
224
251
|
- !ruby/object:Gem::Version
|
|
225
|
-
version: '
|
|
252
|
+
version: '3.0'
|
|
226
253
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
227
254
|
requirements:
|
|
228
255
|
- - ">="
|
|
229
256
|
- !ruby/object:Gem::Version
|
|
230
257
|
version: '0'
|
|
231
258
|
requirements: []
|
|
232
|
-
rubygems_version:
|
|
233
|
-
signing_key:
|
|
259
|
+
rubygems_version: 4.0.14
|
|
234
260
|
specification_version: 4
|
|
235
261
|
summary: Extensions for active_interaction gem
|
|
236
262
|
test_files: []
|