activerecord-virtual_attributes 6.1.2 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e19652ea9b4883f4acae661b63dcc69b4fa4b0bdd0b286a0eb8b17a87892278e
4
- data.tar.gz: 2ba220783720e1bc5b67e0af787c1396127b3fef2f2e0813deb7e34b766579b8
3
+ metadata.gz: cf8f363320713146ad286d76999f5640a9fc44f3e139f6e5377ac8220e476217
4
+ data.tar.gz: cbabad277bfef0e01da4d14b8942671770eb283c9692ef9eb7a07cbac5f25b68
5
5
  SHA512:
6
- metadata.gz: 2b60fcf44b86210f1dbe3ab9be5585158c027bc9c81c0f2ccad77a0079a77b0918bc58feca99f550f4ad2aff4e61defaf7229b125f6091dd770df0242af645fe
7
- data.tar.gz: e691ea9679a9fda6db72fc3f057dd7d5aee163e9fd1d4aff035292da1e03c23eb711920bad8cf79ad41054bb28c2ba29ac9a03a4a38d7b44a52b9ca322245e16
6
+ metadata.gz: 10295799b997217903b155c7ed84e9ee82bad2cf6c208b6aa140dc94e66df9b4ed8fbcf13418a2feadf075e7f93b2c550d7a94b698aae0c08b6d29a4d5ebe159
7
+ data.tar.gz: 1663fd7ab8af93e3419b6a1ae608c94d55df957b45d904e38b31a3b5f93667159e6ee10e667f560174b12cc2b66e936e20bcadc4ac98a45953ed3923f21cac94
data/.codeclimate.yml CHANGED
@@ -31,4 +31,4 @@ plugins:
31
31
  rubocop:
32
32
  enabled: true
33
33
  config: ".rubocop_cc.yml"
34
- channel: rubocop-0-82
34
+ channel: rubocop-1-56-3
@@ -25,7 +25,7 @@ jobs:
25
25
  ports:
26
26
  - 5432:5432
27
27
  mysql:
28
- image: mysql:8.0
28
+ image: mysql:8.4
29
29
  env:
30
30
  MYSQL_ROOT_PASSWORD: password
31
31
  MYSQL_DATABASE: virtual_attributes
@@ -66,6 +66,6 @@ jobs:
66
66
  - name: Report code coverage
67
67
  if: ${{ github.ref == 'refs/heads/master' && matrix.ruby-version == '3.1' }}
68
68
  continue-on-error: true
69
- uses: paambaati/codeclimate-action@v5
69
+ uses: paambaati/codeclimate-action@v8
70
70
  env:
71
71
  CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
data/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ The versioning of this gem follows ActiveRecord versioning, and does not follow
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [7.0.0] - 2024-08-01
8
+
9
+ * Use Arel.literal [#154](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/154)
10
+ * drop attribute_builder [#153](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/153)
11
+ * dropped virtual_aggregate [#152](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/152)
12
+ * resolve rubocops (also fix bin/console) [#151](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/151)
13
+ * Rails 7.0 support / dropping 6.1 [#150](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/150)
14
+ * condense includes produced by replace_virtual_fields [#149](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/149)
15
+ * fix bin/console [#148](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/148)
16
+ * Rails 7.0 support pt1 [#146](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/146)
17
+ * Fix sqlite3 v2 and rails [#140](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/140)
18
+ * Use custom Arel node [#114](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/114)
19
+
7
20
  ## [6.1.2] - 2023-10-26
8
21
 
9
22
  * Fix bind variables for joins with static strings [#124](https://github.com/ManageIQ/activerecord-virtual_attributes/pull/124)
@@ -93,7 +106,8 @@ The versioning of this gem follows ActiveRecord versioning, and does not follow
93
106
  * Initial Release
94
107
  * Extracted from ManageIQ/manageiq
95
108
 
96
- [Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.2...HEAD
109
+ [Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v7.0.0...HEAD
110
+ [7.0.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.2...v7.0.0
97
111
  [6.1.2]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.1...v6.1.2
98
112
  [6.1.1]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v6.1.0...v6.1.1
99
113
  [6.1.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v3.0.0...v6.1.0
data/Gemfile CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "activerecord", "~> 6.1.4"
5
+ gem "activerecord", "~>7.0.8"
6
6
  gem "mysql2"
7
7
  gem "pg"
8
- gem "sqlite3"
8
+ gem "sqlite3", "< 2"
9
9
 
10
10
  gemspec
@@ -9,29 +9,35 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Keenan Brock"]
10
10
  spec.email = ["keenan@thebrocks.net"]
11
11
 
12
- spec.summary = %q{Access non-sql attributes from sql}
13
- spec.description = %q{Define attributes in arel}
12
+ spec.summary = "Access non-sql attributes from sql"
13
+ spec.description = "Define attributes in arel"
14
14
  spec.homepage = "https://github.com/ManageIQ/activerecord-virtual_attributes"
15
15
  spec.license = "Apache 2.0"
16
- spec.metadata["homepage_uri"] = spec.homepage
17
- spec.metadata["source_code_uri"] = "https://github.com/ManageIQ/activerecord-virtual_attributes"
18
- spec.metadata["changelog_uri"] = "https://github.com/ManageIQ/activerecord-virtual_attributes/blob/master/CHANGELOG.md"
16
+ spec.metadata = {
17
+ "homepage_uri" => spec.homepage,
18
+ "source_code_uri" => "https://github.com/ManageIQ/activerecord-virtual_attributes",
19
+ "changelog_uri" => "https://github.com/ManageIQ/activerecord-virtual_attributes/blob/master/CHANGELOG.md",
20
+ "rubygems_mfa_required" => "true"
21
+ }
19
22
 
20
23
  # Specify which files should be added to the gem when it is released.
21
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
25
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
26
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
27
  end
25
28
 
26
29
  spec.require_paths = ["lib"]
27
30
 
28
- spec.add_runtime_dependency "activerecord", "~> 6.1.0"
31
+ spec.add_runtime_dependency "activerecord", "~> 7.0"
29
32
 
30
33
  spec.add_development_dependency "byebug"
31
34
  spec.add_development_dependency "database_cleaner-active_record", "~> 2.1"
32
35
  spec.add_development_dependency "db-query-matchers"
33
36
  spec.add_development_dependency "manageiq-style"
37
+ spec.add_development_dependency "mysql2"
38
+ spec.add_development_dependency "pg"
34
39
  spec.add_development_dependency "rake", "~> 13.0"
35
40
  spec.add_development_dependency "rspec", "~> 3.0"
36
41
  spec.add_development_dependency "simplecov", ">= 0.21.2"
42
+ spec.add_development_dependency "sqlite3"
37
43
  end
data/bin/console CHANGED
@@ -3,9 +3,12 @@
3
3
  require "bundler/setup"
4
4
  require "active_record-virtual_attributes"
5
5
 
6
+ # any helper that is not rspec specific
7
+ Dir['./spec/support/**/*.rb'].sort.select { |f| !File.read(f).include?("RSpec") }.each { |f| require f }
8
+
6
9
  # models for local testing
7
- require "rspec"
8
- Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
10
+ Database.new.setup.migrate
11
+ require_relative "../seed"
9
12
 
10
13
  require "irb"
11
14
  IRB.start(__FILE__)
@@ -1,4 +1,4 @@
1
- RSpec::Matchers.define :have_virtual_attribute do |name, type|
1
+ RSpec::Matchers.define(:have_virtual_attribute) do |name, type|
2
2
  match do |klass|
3
3
  expect(klass.has_attribute?(name)).to be_truthy
4
4
  expect(klass.virtual_attribute?(name)).to be_truthy
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module VirtualAttributes
3
- VERSION = "6.1.2".freeze
3
+ VERSION = "7.0.0".freeze
4
4
  end
5
5
  end
@@ -10,8 +10,13 @@ module ActiveRecord
10
10
  # Model.select(Model.arel_table.grouping(Model.arel_table[:field2]).as(:field))
11
11
  # Model.attribute_supported_by_sql?(:field) # => true
12
12
 
13
- # in essence, this is our Arel::Nodes::VirtualAttribute
14
- class Arel::Nodes::Grouping
13
+ class VirtualAttribute < Arel::Nodes::Grouping
14
+ def initialize(arel, name = nil, relation = nil)
15
+ super(arel)
16
+ @name = name
17
+ @relation = relation
18
+ end
19
+
15
20
  attr_accessor :name, :relation
16
21
 
17
22
  # methods from Arel::Nodes::Attribute
@@ -95,10 +100,12 @@ module ActiveRecord
95
100
  return unless arel_lambda
96
101
 
97
102
  arel = arel_lambda.call(table)
98
- arel = Arel::Nodes::Grouping.new(arel) unless arel.kind_of?(Arel::Nodes::Grouping)
99
- arel.name = column_name
100
- arel.relation = table
101
- arel
103
+ # By convention, all attributes are defined with a grouping.
104
+ # Since we're adding a VirtualAttribute node, which is essentially a
105
+ # grouping, there is no need to keep both and end up with double parens
106
+ arel = arel.expr if arel.kind_of?(Arel::Nodes::Grouping)
107
+
108
+ VirtualAttribute.new(arel, column_name, table)
102
109
  end
103
110
 
104
111
  private
@@ -111,6 +118,8 @@ module ActiveRecord
111
118
  end
112
119
  end
113
120
 
121
+ # fixed in https://github.com/rails/rails/pull/45642
122
+ if ActiveRecord.version < Gem::Version.new(7.1)
114
123
  module Arel # :nodoc: all
115
124
  # rubocop:disable Naming/MethodName
116
125
  # rubocop:disable Naming/MethodParameterName
@@ -150,3 +159,4 @@ module Arel # :nodoc: all
150
159
  # rubocop:enable Naming/MethodParameterName
151
160
  # rubocop:enable Style/ConditionalAssignment
152
161
  end
162
+ end
@@ -74,12 +74,13 @@ module ActiveRecord
74
74
  type = options[:type] || to_ref.klass.type_for_attribute(col)
75
75
  type = ActiveRecord::Type.lookup(type) if type.kind_of?(Symbol)
76
76
  raise "unknown attribute #{to}##{col} referenced in #{name}" unless type
77
+
77
78
  arel = virtual_delegate_arel(col, to_ref)
78
79
  define_virtual_attribute(method_name, type, :uses => (options[:uses] || to), :arel => arel)
79
80
  end
80
81
 
81
82
  # see activesupport module/delegation.rb
82
- def define_delegate(method_name, method, to: nil, allow_nil: nil, default: nil)
83
+ def define_delegate(method_name, method, to: nil, allow_nil: nil, default: nil) # rubocop:disable Naming/MethodParameterName
83
84
  location = caller_locations(2, 1).first
84
85
  file, line = location.path, location.lineno
85
86
 
@@ -125,7 +126,7 @@ module ActiveRecord
125
126
  module_eval(method_def, file, line)
126
127
  end
127
128
 
128
- def virtual_delegate_name_prefix(prefix, to)
129
+ def virtual_delegate_name_prefix(prefix, to) # rubocop:disable Naming/MethodParameterName
129
130
  "#{prefix == true ? to : prefix}_" if prefix
130
131
  end
131
132
 
@@ -260,7 +261,7 @@ module ActiveRecord
260
261
 
261
262
  yield arel if block_given?
262
263
 
263
- ::Arel::Nodes::Grouping.new(arel)
264
+ arel
264
265
  end
265
266
 
266
267
  # determine table reference to use for a sub query
@@ -5,6 +5,7 @@ module ActiveRecord
5
5
  include ActiveRecord::VirtualAttributes
6
6
  include ActiveRecord::VirtualAttributes::VirtualReflections
7
7
 
8
+ # rubocop:disable Style/SingleLineMethods
8
9
  module NonARModels
9
10
  def dangerous_attribute_method?(_); false; end
10
11
 
@@ -14,6 +15,7 @@ module ActiveRecord
14
15
 
15
16
  def belongs_to_required_by_default; false; end
16
17
  end
18
+ # rubocop:enable Style/SingleLineMethods
17
19
 
18
20
  included do
19
21
  unless respond_to?(:dangerous_attribute_method?)
@@ -31,18 +33,20 @@ module ActiveRecord
31
33
  end
32
34
 
33
35
  def replace_virtual_fields(associations)
34
- return associations if associations.blank?
35
-
36
- case associations
37
- when String, Symbol
38
- virtual_field?(associations) ? replace_virtual_fields(virtual_includes(associations)) : associations.to_sym
39
- when Array
40
- associations.collect { |association| replace_virtual_fields(association) }.compact
41
- when Hash
42
- replace_virtual_field_hash(associations)
43
- else
44
- associations
45
- end
36
+ return nil if associations.blank?
37
+
38
+ ret =
39
+ case associations
40
+ when String, Symbol
41
+ virtual_field?(associations) ? replace_virtual_fields(virtual_includes(associations)) : associations.to_sym
42
+ when Array
43
+ associations.filter_map { |association| replace_virtual_fields(association) }
44
+ when Hash
45
+ replace_virtual_field_hash(associations)
46
+ else
47
+ associations
48
+ end
49
+ simplify_includes(ret)
46
50
  end
47
51
 
48
52
  def replace_virtual_field_hash(associations)
@@ -96,11 +100,42 @@ module ActiveRecord
96
100
  merge_includes(include_to_hash(v1), v2)
97
101
  end
98
102
  end
103
+
104
+ # @param [Hash|Array|Symbol|nil]
105
+ def simplify_includes(ret)
106
+ case ret
107
+ when Hash
108
+ ret.size <= 1 && ret.values.first.blank? ? ret.keys.first : ret
109
+ when Array
110
+ ret.size <= 1 ? ret.first : ret
111
+ else
112
+ ret
113
+ end
114
+ end
99
115
  end
100
116
  end
101
117
  end
102
118
  end
103
119
 
120
+ def assert_klass_has_instance_method(klass, instance_method)
121
+ klass.instance_method(instance_method)
122
+ rescue NameError => err
123
+ msg = "#{klass} is missing the method our prepended code is expecting to patch. Was the undefined method removed or renamed upstream?\nSee: #{__FILE__}.\nThe NameError was: #{err}. "
124
+ raise NameError, msg
125
+ end
126
+
127
+ # Expect these methods to exist. (Otherwise we are patching the wrong methods)
128
+ %w[
129
+ grouped_records
130
+ preloaders_for_reflection
131
+ ].each { |method| assert_klass_has_instance_method(ActiveRecord::Associations::Preloader::Branch, method) }
132
+
133
+ %w[
134
+ build_select
135
+ arel_column
136
+ construct_join_dependency
137
+ ].each { |method| assert_klass_has_instance_method(ActiveRecord::Relation, method) }
138
+
104
139
  module ActiveRecord
105
140
  class Base
106
141
  include ActiveRecord::VirtualAttributes::VirtualFields
@@ -109,105 +144,81 @@ module ActiveRecord
109
144
  module Associations
110
145
  class Preloader
111
146
  prepend(Module.new {
112
- # preloader.rb active record 6.0
113
- # changed:
114
- # since grouped_records can return a hash/array, we need to handle those 2 new cases
115
- def preloaders_for_reflection(reflection, records, scope, polymorphic_parent)
116
- case reflection
117
- when Array
118
- reflection.flat_map { |ref| preloaders_on(ref, records, scope, polymorphic_parent) }
119
- when Hash
120
- preloaders_on(reflection, records, scope, polymorphic_parent)
121
- else
122
- super(reflection, records, scope)
147
+ # preloader is called with virtual attributes - need to resolve
148
+ def call
149
+ # Possibly overkill since all records probably have the same class and associations
150
+ # use a cache so we only convert includes once per base class
151
+ assoc_cache = Hash.new { |h, klass| h[klass] = klass.replace_virtual_fields(associations) }
152
+
153
+ # convert the includes with virtual attributes to includes with proper associations
154
+ records_by_assoc = records.group_by { |rec| assoc_cache[rec.class] }
155
+ # if these are the same includes, then do the preloader work
156
+ return super if records_by_assoc.size == 1 && records_by_assoc.keys.first == associations
157
+
158
+ # for each of the associations, run a preloader
159
+ records_by_assoc.each do |klass_associations, klass_records|
160
+ next if klass_associations.blank?
161
+
162
+ Array[klass_associations].each do |klass_association| # rubocop:disable Style/RedundantArrayConstructor
163
+ # this calls back into itself, but it will take the short circuit
164
+ Preloader.new(:records => klass_records, :associations => klass_association, :scope => scope).call
165
+ end
123
166
  end
124
167
  end
168
+ })
125
169
 
126
- # rubocop:disable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
127
- # preloader.rb active record 6.0
128
- # changed:
129
- # passing polymorphic around (and makes 5.2 more similar to 6.0)
130
- def preloaders_for_hash(association, records, scope, polymorphic_parent)
131
- association.flat_map { |parent, child|
132
- grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
133
- loaders = preloaders_for_reflection(reflection, reflection_records, scope, polymorphic_parent)
134
- recs = loaders.flat_map(&:preloaded_records).uniq
135
- child_polymorphic_parent = reflection && reflection.respond_to?(:options) && reflection.options[:polymorphic]
136
- loaders.concat Array.wrap(child).flat_map { |assoc|
137
- preloaders_on assoc, recs, scope, child_polymorphic_parent
138
- }
139
- loaders
140
- end
141
- }
142
- end
170
+ class Branch
171
+ prepend(Module.new {
172
+ # from branched.rb 7.0
173
+ def grouped_records
174
+ h = {}
175
+ polymorphic_parent = !root? && parent.polymorphic?
176
+ source_records.each do |record|
177
+ # begin virtual_attributes changes
178
+ association = record.class.replace_virtual_fields(self.association)
179
+ # end virtual_attributes changes
143
180
 
144
- # preloader.rb active record 6.0
145
- # changed:
146
- # passing polymorphic_parent to preloaders_for_reflection
147
- def preloaders_for_one(association, records, scope, polymorphic_parent)
148
- grouped_records(association, records, polymorphic_parent)
149
- .flat_map do |reflection, reflection_records|
150
- preloaders_for_reflection(reflection, reflection_records, scope, polymorphic_parent)
181
+ reflection = record.class._reflect_on_association(association)
182
+ next if polymorphic_parent && !reflection || !record.association(association).klass
183
+ (h[reflection] ||= []) << record
151
184
  end
152
- end
185
+ h
186
+ end
153
187
 
154
- # preloader.rb active record 6.0, 6.1
155
- def grouped_records(orig_association, records, polymorphic_parent)
156
- h = {}
157
- records.each do |record|
158
- # The virtual_field lookup can return Symbol/Nil/Other (typically a Hash)
159
- # so the case statement and the cases for Nil/Other are new
188
+ # branched.rb 7.0
189
+ def preloaders_for_reflection(reflection, reflection_records)
190
+ reflection_records.group_by do |record|
191
+ # begin virtual_attributes changes
192
+ needed_association = record.class.replace_virtual_fields(association)
193
+ # end virtual_attributes changes
160
194
 
161
- # each class can resolve virtual_{attributes,includes} differently
162
- association = record.class.replace_virtual_fields(orig_association)
163
- # 1 line optimization for single element array:
164
- association = association.first if association.kind_of?(Array) && association.size == 1
195
+ klass = record.association(needed_association).klass
165
196
 
166
- case association
167
- when Symbol, String
168
- reflection = record.class._reflect_on_association(association)
169
- next if polymorphic_parent && !reflection || !record.association(association).klass
170
- when nil
171
- next
172
- else # need parent (preloaders_for_{hash,one}) to handle this Array/Hash
173
- reflection = association
197
+ if reflection.scope && reflection.scope.arity != 0
198
+ # For instance dependent scopes, the scope is potentially
199
+ # different for each record. To allow this we'll group each
200
+ # object separately into its own preloader
201
+ reflection_scope = reflection.join_scopes(klass.arel_table, klass.predicate_builder, klass, record).inject(&:merge!)
202
+ end
203
+
204
+ [klass, reflection_scope]
205
+ end.map do |(rhs_klass, reflection_scope), rs|
206
+ preloader_for(reflection).new(rhs_klass, rs, reflection, scope, reflection_scope, associate_by_default)
174
207
  end
175
- (h[reflection] ||= []) << record
176
208
  end
177
- h
178
- end
179
- # rubocop:enable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
180
- })
209
+ })
210
+ end
181
211
  end
182
212
  end
183
213
 
184
214
  class Relation
185
- def without_virtual_includes
186
- filtered_includes = includes_values && klass.replace_virtual_fields(includes_values)
187
- if filtered_includes != includes_values
188
- spawn.tap { |other| other.includes_values = filtered_includes }
189
- else
190
- self
191
- end
192
- end
193
-
194
215
  include(Module.new {
195
- # From ActiveRecord::FinderMethods
196
- def apply_join_dependency(*args, **kargs, &block)
197
- real = without_virtual_includes
198
- if real.equal?(self)
199
- super
200
- else
201
- real.apply_join_dependency(*args, **kargs, &block)
202
- end
203
- end
204
-
205
216
  # From ActiveRecord::QueryMethods (rails 5.2 - 6.1)
206
217
  def build_select(arel)
207
218
  if select_values.any?
208
- cols = arel_columns(select_values.uniq).map do |col|
219
+ cols = arel_columns(select_values).map do |col|
209
220
  # if it is a virtual attribute, then add aliases to those columns
210
- if col.kind_of?(Arel::Nodes::Grouping) && col.name
221
+ if col.kind_of?(VirtualAttributes::VirtualAttribute)
211
222
  col.as(connection.quote_column_name(col.name))
212
223
  else
213
224
  col
@@ -219,9 +230,8 @@ module ActiveRecord
219
230
  end
220
231
  end
221
232
 
222
- # from ActiveRecord::QueryMethods (rails 5.2 - 6.0)
223
- # TODO: remove from rails 7.0
224
- def arel_column(field, &block)
233
+ # from ActiveRecord::QueryMethods (rails 5.2 - 7.0)
234
+ def arel_column(field)
225
235
  if virtual_attribute?(field) && (arel = table[field])
226
236
  arel
227
237
  else
@@ -230,19 +240,9 @@ module ActiveRecord
230
240
  end
231
241
 
232
242
  def construct_join_dependency(associations, join_type) # :nodoc:
233
- associations = klass.replace_virtual_fields(associations)
243
+ associations = klass.replace_virtual_fields(associations) || {}
234
244
  super
235
245
  end
236
-
237
- # From ActiveRecord::Calculations
238
- # introduces virtual includes support for calculate (we mostly use COUNT(*))
239
- def calculate(operation, attribute_name)
240
- # allow calculate to work with includes and a virtual attribute
241
- real = without_virtual_includes
242
- return super if real.equal?(self)
243
-
244
- real.calculate(operation, attribute_name)
245
- end
246
246
  })
247
247
  end
248
248
  end
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  end
18
18
 
19
19
  def virtual_has_many(name, options = {})
20
- define_method("#{name.to_s.singularize}_ids") do
20
+ define_method(:"#{name.to_s.singularize}_ids") do
21
21
  records = send(name)
22
22
  records.respond_to?(:ids) ? records.ids : records.collect(&:id)
23
23
  end
@@ -57,11 +57,11 @@ module ActiveRecord
57
57
  end
58
58
 
59
59
  def follow_associations(association_names)
60
- association_names.inject(self) { |klass, name| klass.try!(:reflect_on_association, name).try!(:klass) }
60
+ association_names.inject(self) { |klass, name| klass&.reflect_on_association(name)&.klass }
61
61
  end
62
62
 
63
63
  def follow_associations_with_virtual(association_names)
64
- association_names.inject(self) { |klass, name| klass.try!(:reflection_with_virtual, name).try!(:klass) }
64
+ association_names.inject(self) { |klass, name| klass&.reflection_with_virtual(name)&.klass }
65
65
  end
66
66
 
67
67
  # invalid associations return a nil
@@ -97,6 +97,7 @@ module ActiveRecord
97
97
 
98
98
  def add_virtual_reflection(reflection, name, uses, _options)
99
99
  raise ArgumentError, "macro must be specified" unless reflection
100
+
100
101
  reset_virtual_reflection_information
101
102
  _virtual_reflections[name.to_sym] = reflection
102
103
  define_virtual_include(name.to_s, uses)
@@ -49,35 +49,6 @@ module VirtualAttributes
49
49
  define_virtual_aggregate_method(name, relation, column, :average) { |values| values.count == 0 ? 0 : values.sum / values.count }
50
50
  end
51
51
 
52
- # @param method_name
53
- # :count :average :minimum :maximum :sum
54
- #
55
- # example:
56
- #
57
- # class Hardware
58
- # has_many :disks
59
- # virtual_sum :allocated_disk_storage, :disks, :size
60
- # end
61
- #
62
- # generates:
63
- #
64
- # def allocated_disk_storage
65
- # if disks.loaded?
66
- # disks.map(&:size).compact.sum
67
- # else
68
- # disks.sum(:size) || 0
69
- # end
70
- # end
71
- #
72
- # virtual_attribute :allocated_disk_storage, :integer, :uses => :disks, :arel => ...
73
- #
74
- # # arel => (SELECT sum("disks"."size") where "hardware"."id" = "disks"."hardware_id")
75
-
76
- def virtual_aggregate(name, relation, method_name = :sum, column = nil, options = {})
77
- return virtual_total(name, relation, options) if method_name == :size
78
- return virtual_sum(name, relation, column, options) if method_name == :sum
79
- end
80
-
81
52
  def define_virtual_aggregate_attribute(name, relation, method_name, column, options)
82
53
  reflection = reflect_on_association(relation)
83
54
 
@@ -101,7 +72,7 @@ module VirtualAttributes
101
72
  if has_attribute?(name)
102
73
  self[name] || 0
103
74
  elsif (rel = send(relation)).loaded?
104
- values = rel.map { |t| t.send(column) }.compact
75
+ values = rel.filter_map { |t| t.send(column) }
105
76
  if block_given?
106
77
  yield values
107
78
  else
@@ -138,7 +109,7 @@ module VirtualAttributes
138
109
  query.where(join.right.expr)
139
110
 
140
111
  # add coalesce to ensure correct value comes out
141
- t.grouping(Arel::Nodes::NamedFunction.new('COALESCE', [t.grouping(query), Arel::Nodes::SqlLiteral.new("0")]))
112
+ Arel::Nodes::NamedFunction.new('COALESCE', [t.grouping(query), Arel.sql("0")])
142
113
  end
143
114
  end
144
115
  end
@@ -83,14 +83,6 @@ module ActiveRecord
83
83
  end
84
84
  end
85
85
 
86
- def attributes_builder # :nodoc:
87
- unless defined?(@attributes_builder) && @attributes_builder
88
- defaults = _default_attributes.except(*(column_names - [primary_key]))
89
- @attributes_builder = ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
90
- end
91
- @attributes_builder
92
- end
93
-
94
86
  private
95
87
 
96
88
  def load_schema!
data/renovate.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "config:recommended"
5
+ ]
6
+ }
data/seed.rb ADDED
@@ -0,0 +1,3 @@
1
+ Author.create_with_books(3)
2
+ Author.create_with_books(4)
3
+ Author.create_with_books(2)
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: 6.1.2
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keenan Brock
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-26 00:00:00.000000000 Z
11
+ date: 2024-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 6.1.0
19
+ version: '7.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 6.1.0
26
+ version: '7.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: mysql2
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pg
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: rake
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +150,20 @@ dependencies:
122
150
  - - ">="
123
151
  - !ruby/object:Gem::Version
124
152
  version: 0.21.2
153
+ - !ruby/object:Gem::Dependency
154
+ name: sqlite3
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
125
167
  description: Define attributes in arel
126
168
  email:
127
169
  - keenan@thebrocks.net
@@ -159,6 +201,8 @@ files:
159
201
  - lib/active_record/virtual_attributes/virtual_reflections.rb
160
202
  - lib/active_record/virtual_attributes/virtual_total.rb
161
203
  - lib/activerecord-virtual_attributes.rb
204
+ - renovate.json
205
+ - seed.rb
162
206
  homepage: https://github.com/ManageIQ/activerecord-virtual_attributes
163
207
  licenses:
164
208
  - Apache 2.0
@@ -166,6 +210,7 @@ metadata:
166
210
  homepage_uri: https://github.com/ManageIQ/activerecord-virtual_attributes
167
211
  source_code_uri: https://github.com/ManageIQ/activerecord-virtual_attributes
168
212
  changelog_uri: https://github.com/ManageIQ/activerecord-virtual_attributes/blob/master/CHANGELOG.md
213
+ rubygems_mfa_required: 'true'
169
214
  post_install_message:
170
215
  rdoc_options: []
171
216
  require_paths:
@@ -181,7 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
181
226
  - !ruby/object:Gem::Version
182
227
  version: '0'
183
228
  requirements: []
184
- rubygems_version: 3.2.33
229
+ rubygems_version: 3.5.9
185
230
  signing_key:
186
231
  specification_version: 4
187
232
  summary: Access non-sql attributes from sql