activerecord-virtual_attributes 7.1.1 → 7.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26048751ad2fe218660a4b62fa6c89d6467e7d33404c463154a041cf638ff43c
4
- data.tar.gz: 8c2302076104393e4e4eac994e854a951ee6c0588b3fbfefc6c95f3d8d905861
3
+ metadata.gz: 0f9b4d5589eb768e0f866cd4d4cdf54e937284a98aea2ff1644e8dddb56413f9
4
+ data.tar.gz: 981261df2b4e1092f1206eb633b064d7605ada874ac1c56946524b89461f7779
5
5
  SHA512:
6
- metadata.gz: 34b460bb1293c44743f26c0b91d62d857f53158022f2777ffb29f97a2279b6a9bbd3d19e4cd83dafaee035061093da669da4ac2bb749f1524258ae5db8d5577c
7
- data.tar.gz: 2b7876b8d6f5c0eeafaf2f2f7740943ead1cc25b0c211ee84e44365609d86c659c328103c432d0c01184d1c09f13f2af5226c9d05614ac7c875392e503ae981a
6
+ metadata.gz: 5bf6835a1bc40e8fd619b1f5b00a84d2f39f86ce51de170941d71e9880a1ae9c6c061b523c43f801664c8ec5789363f55d016207acde9e66655207c22fa923bf
7
+ data.tar.gz: 52a5dff2126f009791c7f9dad66647b0d589d9f0c659a184c79307fb087505b37bb23e8ad3c95c82c4939531d86ed8f11cc874f0b141c34cd5836cc122897dc6
data/CHANGELOG.md CHANGED
@@ -4,6 +4,10 @@ The versioning of this gem follows ActiveRecord versioning, and does not follow
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [7.1.2] - 2025-06-20
8
+
9
+ * Introduce virtual_has_many :through [#191](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/191)
10
+
7
11
  ## [7.1.1] - 2025-06-18
8
12
 
9
13
  * Deprecate virtual_delegate without a type [#188](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/188)
@@ -115,7 +119,8 @@ The versioning of this gem follows ActiveRecord versioning, and does not follow
115
119
  * Initial Release
116
120
  * Extracted from ManageIQ/manageiq
117
121
 
118
- [Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v7.1.1...HEAD
122
+ [Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v7.1.2...HEAD
123
+ [7.1.2]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v7.1.1...v7.1.2
119
124
  [7.1.1]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v7.1.0...v7.1.1
120
125
  [7.1.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v7.0.0...v7.1.0
121
126
  [7.0.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.2...v7.0.0
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module VirtualAttributes
3
- VERSION = "7.1.1".freeze
3
+ VERSION = "7.1.2".freeze
4
4
  end
5
5
  end
@@ -111,10 +111,9 @@ module ActiveRecord
111
111
  private
112
112
 
113
113
  def define_virtual_arel(name, arel) # :nodoc:
114
- self._virtual_arel = _virtual_arel.merge(name => arel)
114
+ self._virtual_arel = _virtual_arel.merge(name.to_s => arel)
115
115
  end
116
116
  end
117
117
  end
118
118
  end
119
119
  end
120
-
@@ -18,42 +18,28 @@ module ActiveRecord
18
18
  # Definition
19
19
  #
20
20
 
21
- def virtual_delegate(*methods)
22
- options = methods.extract_options!
23
- unless (to = options[:to])
24
- raise ArgumentError, 'Delegation needs an association. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
25
- end
26
-
27
- unless options[:type]
21
+ def virtual_delegate(*methods, to:, type: nil, prefix: nil, allow_nil: nil, default: nil, **options) # rubocop:disable Naming/MethodParameterName
22
+ unless type
28
23
  ActiveRecord::VirtualAttributes.deprecator.warn("Calling virtual_delegate without :type is now deprecated", caller)
29
24
  end
30
25
 
31
26
  to = to.to_s
32
- if to.include?(".") && methods.size > 1
33
- raise ArgumentError, 'Delegation only supports specifying a method name when defining a single virtual method'
27
+ if to.include?(".") && (methods.size > 1 || prefix)
28
+ raise ArgumentError, 'Delegation only supports specifying a target method name when defining a single virtual method with no prefix'
34
29
  end
35
30
 
36
31
  if to.count(".") > 1
37
- raise ArgumentError, 'Delegation needs a single association. Supply an option hash with a :to key with only 1 period (e.g. delegate :hello, to: "greeter.greeting")'
32
+ raise ArgumentError, 'Delegation needs a single association. Supply keyword :to with only 1 period (e.g. delegate :hello, to: "greeter.greeting")'
38
33
  end
39
34
 
40
- allow_nil = options[:allow_nil]
41
- default = options[:default]
42
-
43
35
  # put method entry per method name.
44
36
  # This better supports reloading of the class and changing the definitions
45
37
  methods.each do |method|
46
- method_prefix = virtual_delegate_name_prefix(options[:prefix], to)
47
- method_name = "#{method_prefix}#{method}"
48
- if to.include?(".") # to => "target.method"
49
- to, method = to.split(".").map(&:to_sym)
50
- options[:to] = to
51
- end
52
-
38
+ method_name, to, method = determine_method_names(method, to, prefix)
53
39
  define_delegate(method_name, method, :to => to, :allow_nil => allow_nil, :default => default)
54
40
 
55
41
  self.virtual_delegates_to_define =
56
- virtual_delegates_to_define.merge(method_name => [method, options])
42
+ virtual_delegates_to_define.merge(method_name.to_s => [method, options.merge(:to => to, :type => type)])
57
43
  end
58
44
  end
59
45
 
@@ -83,6 +69,7 @@ module ActiveRecord
83
69
  end
84
70
 
85
71
  # see activesupport module/delegation.rb
72
+ # rubocop:disable Style/TernaryParentheses
86
73
  def define_delegate(method_name, method, to: nil, allow_nil: nil, default: nil) # rubocop:disable Naming/MethodParameterName
87
74
  location = caller_locations(2, 1).first
88
75
  file, line = location.path, location.lineno
@@ -128,9 +115,27 @@ module ActiveRecord
128
115
  method_def = method_def.split("\n").map(&:strip).join(';')
129
116
  module_eval(method_def, file, line)
130
117
  end
118
+ # rubocop:enable Style/TernaryParentheses
119
+
120
+ # Sometimes the `to` contains the column name target.column, split it up to the source method_name and target column
121
+ # If `to` does specify the column name, `to` becomes the target (i.e.: association)
122
+ #
123
+ # @param column [Symbol|String] the name of the column
124
+ # @param to [Symbol|String]
125
+ # @param prefix [Boolean|Nil|Symbol]
126
+ # @return [Symbol, Symbol, Symbol] method_name, relation, relation's column name
127
+ def determine_method_names(column, to, prefix) # rubocop:disable Naming/MethodParameterName
128
+ method_name = column = column.to_sym
129
+
130
+ tos = to.to_s
131
+ if tos.include?(".") # to => "target.method"
132
+ to, column = tos.split(".").map(&:to_sym)
133
+ end
134
+
135
+ method_prefix = "#{prefix == true ? to : prefix}_" if prefix
136
+ method_name = "#{method_prefix}#{method_name}".to_sym
131
137
 
132
- def virtual_delegate_name_prefix(prefix, to) # rubocop:disable Naming/MethodParameterName
133
- "#{prefix == true ? to : prefix}_" if prefix
138
+ [method_name, to.to_sym, column]
134
139
  end
135
140
 
136
141
  # @param col [String] attribute name
@@ -143,7 +143,7 @@ module ActiveRecord
143
143
 
144
144
  module Associations
145
145
  class Preloader
146
- prepend(Module.new {
146
+ prepend(Module.new do
147
147
  # preloader is called with virtual attributes - need to resolve
148
148
  def call
149
149
  # Possibly overkill since all records probably have the same class and associations
@@ -165,13 +165,14 @@ module ActiveRecord
165
165
  end
166
166
  end
167
167
  end
168
- })
168
+ end)
169
169
 
170
170
  class Branch
171
- prepend(Module.new {
171
+ prepend(Module.new do
172
172
  # from branched.rb 7.0
173
173
  # not going to modify rails code for rubocops
174
174
  # rubocop:disable Lint/AmbiguousOperatorPrecedence
175
+ # rubocop:disable Layout/EmptyLineAfterGuardClause
175
176
  def grouped_records
176
177
  h = {}
177
178
  polymorphic_parent = !root? && parent.polymorphic?
@@ -186,6 +187,7 @@ module ActiveRecord
186
187
  end
187
188
  h
188
189
  end
190
+ # rubocop:enable Layout/EmptyLineAfterGuardClause
189
191
  # rubocop:enable Lint/AmbiguousOperatorPrecedence
190
192
 
191
193
  # branched.rb 7.0
@@ -209,13 +211,13 @@ module ActiveRecord
209
211
  preloader_for(reflection).new(rhs_klass, rs, reflection, scope, reflection_scope, associate_by_default)
210
212
  end
211
213
  end
212
- })
214
+ end)
213
215
  end
214
216
  end
215
217
  end
216
218
 
217
219
  class Relation
218
- include(Module.new {
220
+ include(Module.new do
219
221
  # From ActiveRecord::QueryMethods (rails 5.2 - 6.1)
220
222
  def build_select(arel)
221
223
  if select_values.any?
@@ -246,6 +248,6 @@ module ActiveRecord
246
248
  associations = klass.replace_virtual_fields(associations) || {}
247
249
  super
248
250
  end
249
- })
251
+ end)
250
252
  end
251
253
  end
@@ -25,7 +25,7 @@ module ActiveRecord
25
25
  private
26
26
 
27
27
  def define_virtual_include(name, uses)
28
- self._virtual_includes = _virtual_includes.merge(name => uses)
28
+ self._virtual_includes = _virtual_includes.merge(name.to_s => uses)
29
29
  end
30
30
  end
31
31
  end
@@ -5,31 +5,28 @@ module ActiveRecord
5
5
  include ActiveRecord::VirtualAttributes::VirtualIncludes
6
6
 
7
7
  module ClassMethods
8
-
9
8
  #
10
9
  # Definition
11
10
  #
12
11
 
13
- def virtual_has_one(name, options = {})
14
- uses = options.delete(:uses)
12
+ def virtual_has_one(name, uses: nil, **options)
15
13
  reflection = ActiveRecord::Associations::Builder::HasOne.build(self, name, nil, options)
16
- add_virtual_reflection(reflection, name, uses, options)
14
+ add_virtual_reflection(reflection, name, uses)
17
15
  end
18
16
 
19
- def virtual_has_many(name, options = {})
17
+ def virtual_has_many(name, uses: nil, source: nil, through: nil, **options)
20
18
  define_method(:"#{name.to_s.singularize}_ids") do
21
19
  records = send(name)
22
20
  records.respond_to?(:ids) ? records.ids : records.collect(&:id)
23
21
  end
24
- uses = options.delete(:uses)
22
+ define_delegate(name, source || name, :to => through, :allow_nil => true, :default => []) if through
25
23
  reflection = ActiveRecord::Associations::Builder::HasMany.build(self, name, nil, options)
26
- add_virtual_reflection(reflection, name, uses, options)
24
+ add_virtual_reflection(reflection, name, uses)
27
25
  end
28
26
 
29
- def virtual_belongs_to(name, options = {})
30
- uses = options.delete(:uses)
27
+ def virtual_belongs_to(name, uses: nil, **options)
31
28
  reflection = ActiveRecord::Associations::Builder::BelongsTo.build(self, name, nil, options)
32
- add_virtual_reflection(reflection, name, uses, options)
29
+ add_virtual_reflection(reflection, name, uses)
33
30
  end
34
31
 
35
32
  def virtual_reflection?(name)
@@ -95,7 +92,7 @@ module ActiveRecord
95
92
 
96
93
  private
97
94
 
98
- def add_virtual_reflection(reflection, name, uses, _options)
95
+ def add_virtual_reflection(reflection, name, uses)
99
96
  raise ArgumentError, "macro must be specified" unless reflection
100
97
 
101
98
  reset_virtual_reflection_information
@@ -52,10 +52,7 @@ module ActiveRecord
52
52
  #
53
53
 
54
54
  # Compatibility method: `virtual_attribute` is a more accurate name
55
- def virtual_column(name, **options)
56
- type = options.delete(:type)
57
- raise ArgumentError, "missing :type attribute" unless type
58
-
55
+ def virtual_column(name, type:, **options)
59
56
  virtual_attribute(name, type, **options)
60
57
  end
61
58
 
@@ -105,7 +102,7 @@ module ActiveRecord
105
102
  end
106
103
 
107
104
  def define_virtual_attribute(name, cast_type, uses: nil, arel: nil)
108
- attribute_types[name] = cast_type
105
+ attribute_types[name.to_s] = cast_type
109
106
  define_virtual_include(name, uses) if uses
110
107
  define_virtual_arel(name, arel) if arel
111
108
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-virtual_attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.1
4
+ version: 7.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keenan Brock
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-17 00:00:00.000000000 Z
11
+ date: 2025-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord