activerecord-virtual_attributes 6.1.2 → 7.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/.codeclimate.yml +1 -1
- data/.github/workflows/ci.yaml +2 -2
- data/CHANGELOG.md +15 -1
- data/Gemfile +2 -2
- data/activerecord-virtual_attributes.gemspec +13 -7
- data/bin/console +5 -2
- data/lib/active_record/virtual_attributes/rspec/have_virtual_attribute.rb +1 -1
- data/lib/active_record/virtual_attributes/version.rb +1 -1
- data/lib/active_record/virtual_attributes/virtual_arel.rb +16 -6
- data/lib/active_record/virtual_attributes/virtual_delegates.rb +4 -3
- data/lib/active_record/virtual_attributes/virtual_fields.rb +106 -106
- data/lib/active_record/virtual_attributes/virtual_reflections.rb +4 -3
- data/lib/active_record/virtual_attributes/virtual_total.rb +2 -31
- data/lib/active_record/virtual_attributes.rb +0 -8
- data/renovate.json +6 -0
- data/seed.rb +3 -0
- metadata +50 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf8f363320713146ad286d76999f5640a9fc44f3e139f6e5377ac8220e476217
|
4
|
+
data.tar.gz: cbabad277bfef0e01da4d14b8942671770eb283c9692ef9eb7a07cbac5f25b68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10295799b997217903b155c7ed84e9ee82bad2cf6c208b6aa140dc94e66df9b4ed8fbcf13418a2feadf075e7f93b2c550d7a94b698aae0c08b6d29a4d5ebe159
|
7
|
+
data.tar.gz: 1663fd7ab8af93e3419b6a1ae608c94d55df957b45d904e38b31a3b5f93667159e6ee10e667f560174b12cc2b66e936e20bcadc4ac98a45953ed3923f21cac94
|
data/.codeclimate.yml
CHANGED
data/.github/workflows/ci.yaml
CHANGED
@@ -25,7 +25,7 @@ jobs:
|
|
25
25
|
ports:
|
26
26
|
- 5432:5432
|
27
27
|
mysql:
|
28
|
-
image: mysql:8.
|
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@
|
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/
|
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
@@ -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 =
|
13
|
-
spec.description =
|
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
|
17
|
-
|
18
|
-
|
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
|
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", "~>
|
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
|
-
|
8
|
-
|
10
|
+
Database.new.setup.migrate
|
11
|
+
require_relative "../seed"
|
9
12
|
|
10
13
|
require "irb"
|
11
14
|
IRB.start(__FILE__)
|
@@ -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
|
-
|
14
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
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
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
185
|
+
h
|
186
|
+
end
|
153
187
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
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
|
-
|
178
|
-
|
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
|
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?(
|
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 -
|
223
|
-
|
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
|
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
|
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.
|
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
|
-
|
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
data/seed.rb
ADDED
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:
|
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:
|
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:
|
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:
|
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.
|
229
|
+
rubygems_version: 3.5.9
|
185
230
|
signing_key:
|
186
231
|
specification_version: 4
|
187
232
|
summary: Access non-sql attributes from sql
|