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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3 -2
  3. data/CHANGELOG.md +24 -0
  4. data/README.md +325 -0
  5. data/active_interaction-extras.gemspec +6 -5
  6. data/lib/active_interaction/extras/active_job.rb +36 -5
  7. data/lib/active_interaction/extras/add_to_batch.rb +13 -0
  8. data/lib/active_interaction/extras/after_initialize.rb +20 -0
  9. data/lib/active_interaction/extras/all.rb +16 -1
  10. data/lib/active_interaction/extras/all_runner.rb +91 -0
  11. data/lib/active_interaction/extras/append_inputs.rb +60 -0
  12. data/lib/active_interaction/extras/current.rb +23 -0
  13. data/lib/active_interaction/extras/file_blobs.rb +80 -0
  14. data/lib/active_interaction/extras/filter_alias.rb +28 -2
  15. data/lib/active_interaction/extras/filter_extensions.rb +8 -0
  16. data/lib/active_interaction/extras/filters/ar_relation_filter.rb +22 -0
  17. data/lib/active_interaction/extras/filters/uuid_filter.rb +1 -1
  18. data/lib/active_interaction/extras/form_for.rb +33 -0
  19. data/lib/active_interaction/extras/good_job.rb +45 -0
  20. data/lib/active_interaction/extras/include_errors.rb +10 -0
  21. data/lib/active_interaction/extras/initialize_with.rb +16 -0
  22. data/lib/active_interaction/extras/instance_runnable.rb +35 -0
  23. data/lib/active_interaction/extras/model_fields.rb +68 -33
  24. data/lib/active_interaction/extras/model_names.rb +20 -0
  25. data/lib/active_interaction/extras/named_callbacks.rb +54 -0
  26. data/lib/active_interaction/extras/nested_attributes.rb +126 -0
  27. data/lib/active_interaction/extras/strong_params.rb +48 -10
  28. data/lib/active_interaction/extras/transaction.rb +27 -5
  29. data/lib/active_interaction/extras/version.rb +1 -1
  30. data/lib/active_interaction/extras.rb +10 -23
  31. 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
- next unless filter.options[:permit]
34
-
35
- case filter
36
- when ActiveInteraction::ArrayFilter
37
- { filter_name => [] }
38
- when ActiveInteraction::HashFilter
39
- { filter_name => {} }
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
- filter_name
73
+ { name => permit }
42
74
  end
43
- end.compact
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
- set_callback :execute, :around, ->(_interaction, block) {
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
- }, if: :run_in_transaction_options
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
- self.run_in_transaction_options = {requires_new: requires_new}
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 = nil
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,5 +1,5 @@
1
1
  module ActiveInteraction
2
2
  module Extras
3
- VERSION = "1.1.0"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  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
- module ActiveInteraction
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
- autoload(:All, "active_interaction/extras/all")
19
-
20
- autoload(:FilterAlias, "active_interaction/extras/filter_alias")
21
- autoload(:Halt, "active_interaction/extras/halt")
22
- autoload(:ModelFields, "active_interaction/extras/model_fields")
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
- autoload(:TimezoneSupport, "active_interaction/extras/timezone_support")
28
- autoload(:Rspec, "active_interaction/extras/rspec")
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: 1.1.0
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: 2023-03-08 00:00:00.000000000 Z
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: '6.0'
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: '6.0'
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: '6.0'
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: '6.0'
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: '0'
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: '0'
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: '2.7'
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: 3.3.26
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: []