activerecord-virtual_attributes 1.5.0 → 1.6.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/.travis.yml +0 -8
- data/CHANGELOG.md +8 -1
- data/README.md +5 -0
- data/lib/active_record/virtual_attributes/arel_groups.rb +2 -1
- data/lib/active_record/virtual_attributes/version.rb +1 -1
- data/lib/active_record/virtual_attributes/virtual_arel.rb +10 -3
- data/lib/active_record/virtual_attributes/virtual_fields.rb +107 -51
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0b56d15edaf7d124492c47903569158354ff7ca1887735c706fb342c847e0a2
|
4
|
+
data.tar.gz: 4674998a27f163fcb2b387ace6f71d3c9c70adf6dcd64332d59219e8e34dc7d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70dbabafd878355e63957c7511b1c982716b7412da8fe14ae35c24f348fa676463ef7b1894fd33380f64835b89927643e6f4b11f77703138d6d9d5bb0cad5fb5
|
7
|
+
data.tar.gz: 7adae59e71766f84d6ce31dc78ed633d7fb449ebf63453c40b75650eef04fbec263465b96efcf24509faba5e42cd1a8d05ce7286dfbb1235dde771a2cd65d57b
|
data/.travis.yml
CHANGED
@@ -28,11 +28,3 @@ before_script:
|
|
28
28
|
- sh -c "if [ '$DB' = 'mysql2' ]; then mysql -e 'DROP DATABASE IF EXISTS virtual_attributes; CREATE DATABASE virtual_attributes;'; fi"
|
29
29
|
after_script:
|
30
30
|
- "./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT"
|
31
|
-
jobs:
|
32
|
-
allow_failures:
|
33
|
-
- gemfile: gemfiles/gemfile_52.gemfile
|
34
|
-
env: DB=mysql2
|
35
|
-
- gemfile: gemfiles/gemfile_52.gemfile
|
36
|
-
env: DB=pg
|
37
|
-
- gemfile: gemfiles/gemfile_52.gemfile
|
38
|
-
env: DB=sqlite3
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,12 @@ a nice looking [Changelog](http://keepachangelog.com).
|
|
5
5
|
|
6
6
|
## Version [Unreleased]
|
7
7
|
|
8
|
+
## Version [1.6.0] <small>2019-12-02</small>
|
9
|
+
|
10
|
+
* rails 5.2 support
|
11
|
+
* fix Arel#name error
|
12
|
+
* Display deprecation notices for invalid associations (rather than throw an error)
|
13
|
+
|
8
14
|
## Version [1.5.0] <small>2019-12-02</small>
|
9
15
|
|
10
16
|
* `select()` no longer modifies `select_values`. It understands virtual attributes at a lower level.
|
@@ -54,7 +60,8 @@ a nice looking [Changelog](http://keepachangelog.com).
|
|
54
60
|
* Initial Release
|
55
61
|
* Extracted from ManageIQ/manageiq
|
56
62
|
|
57
|
-
[Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.
|
63
|
+
[Unreleased]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.6.0...HEAD
|
64
|
+
[1.6.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.5.0...v1.6.0
|
58
65
|
[1.5.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.4.0...v1.5.0
|
59
66
|
[1.4.0]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.3.1...v1.4.0
|
60
67
|
[1.3.1]: https://github.com/ManageIQ/activerecord-virtual_attributes/compare/v1.3.0...v1.3.1
|
data/README.md
CHANGED
@@ -42,6 +42,11 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
42
42
|
|
43
43
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
44
44
|
|
45
|
+
|
46
|
+
To test with different versions of ruby, use `wwtd` gem or
|
47
|
+
|
48
|
+
DB=pg BUNDLE_GEMFILE=gemfiles/gemfile_${version-52}.gemfile beer "$@"
|
49
|
+
|
45
50
|
## Contributing
|
46
51
|
|
47
52
|
Bug reports and pull requests are welcome on GitHub at https://github.com/ManageIQ/activerecord-virtual_attributes .
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# this is from https://github.com/rails/arel/pull/435
|
2
2
|
# this allows sorting and where clauses to work with virtual_attribute columns
|
3
|
-
|
3
|
+
# no longer needed for rails 6.0 and up (change was merged)
|
4
|
+
if ActiveRecord.version.to_s < "6.0" && defined?(Arel::Nodes::Grouping)
|
4
5
|
module Arel
|
5
6
|
module Nodes
|
6
7
|
class Grouping
|
@@ -2,13 +2,17 @@ module ActiveRecord
|
|
2
2
|
module VirtualAttributes
|
3
3
|
# VirtualArel associates arel with an attribute
|
4
4
|
#
|
5
|
-
# Model.virtual_attribute :field, :string, :arel => ->
|
5
|
+
# Model.virtual_attribute :field, :string, :arel => ->(t) { t.grouping(t[:field2]) } }
|
6
6
|
# Model.select(:field)
|
7
7
|
#
|
8
8
|
# is equivalent to:
|
9
9
|
#
|
10
10
|
# Model.select(Model.arel_table.grouping(Model.arel_table[:field2]).as(:field))
|
11
11
|
# Model.attribute_supported_by_sql?(:field) # => true
|
12
|
+
class Arel::Nodes::Grouping
|
13
|
+
attr_accessor :name
|
14
|
+
end
|
15
|
+
|
12
16
|
module VirtualArel
|
13
17
|
extend ActiveSupport::Concern
|
14
18
|
|
@@ -21,8 +25,11 @@ module ActiveRecord
|
|
21
25
|
def arel_attribute(column_name, arel_table = self.arel_table)
|
22
26
|
load_schema
|
23
27
|
if virtual_attribute?(column_name) && !attribute_alias?(column_name)
|
24
|
-
col = _virtual_arel[column_name.to_s]
|
25
|
-
|
28
|
+
if (col = _virtual_arel[column_name.to_s])
|
29
|
+
arel = col.call(arel_table)
|
30
|
+
arel.name = column_name if arel.kind_of?(Arel::Nodes::Grouping)
|
31
|
+
arel
|
32
|
+
end
|
26
33
|
else
|
27
34
|
super
|
28
35
|
end
|
@@ -102,6 +102,9 @@ module ActiveRecord
|
|
102
102
|
class Preloader
|
103
103
|
prepend(Module.new {
|
104
104
|
if ActiveRecord.version.to_s >= "6.0"
|
105
|
+
# preloader.rb active record 6.0
|
106
|
+
# changed:
|
107
|
+
# since grouped_records can return a hash/array, we need to handle those 2 new cases
|
105
108
|
def preloaders_for_reflection(reflection, records, scope, polymorphic_parent)
|
106
109
|
case reflection
|
107
110
|
when Array
|
@@ -112,11 +115,43 @@ module ActiveRecord
|
|
112
115
|
super(reflection, records, scope)
|
113
116
|
end
|
114
117
|
end
|
118
|
+
elsif ActiveRecord.version.to_s >= "5.2" # < 6.0
|
119
|
+
# preloader.rb active record 6.0
|
120
|
+
# else block changed to reflect how 5.2 preloaders_for_one works
|
121
|
+
def preloaders_for_reflection(reflection, records, scope, polymorphic_parent)
|
122
|
+
case reflection
|
123
|
+
when Array
|
124
|
+
reflection.flat_map { |ref| preloaders_on(ref, records, scope, polymorphic_parent) }
|
125
|
+
when Hash
|
126
|
+
preloaders_on(reflection, records, scope, polymorphic_parent)
|
127
|
+
else
|
128
|
+
records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
|
129
|
+
loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
|
130
|
+
loader.run(self)
|
131
|
+
loader
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# preloader.rb active record 6.0
|
137
|
+
# since this deals with polymorphic_parent, it makes everything easier to just define it
|
138
|
+
def preloaders_on(association, records, scope, polymorphic_parent = false)
|
139
|
+
case association
|
140
|
+
when Hash
|
141
|
+
preloaders_for_hash(association, records, scope, polymorphic_parent)
|
142
|
+
when Symbol, String
|
143
|
+
preloaders_for_one(association.to_sym, records, scope, polymorphic_parent)
|
144
|
+
else
|
145
|
+
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
115
149
|
|
150
|
+
if ActiveRecord.version.to_s >= "5.2"
|
116
151
|
# rubocop:disable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
|
117
152
|
# preloader.rb active record 6.0
|
118
153
|
# changed:
|
119
|
-
#
|
154
|
+
# passing polymorphic around (and makes 5.2 more similar to 6.0)
|
120
155
|
def preloaders_for_hash(association, records, scope, polymorphic_parent)
|
121
156
|
association.flat_map { |parent, child|
|
122
157
|
grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
|
@@ -133,7 +168,7 @@ module ActiveRecord
|
|
133
168
|
|
134
169
|
# preloader.rb active record 6.0
|
135
170
|
# changed:
|
136
|
-
#
|
171
|
+
# passing polymorphic_parent to preloaders_for_reflection
|
137
172
|
def preloaders_for_one(association, records, scope, polymorphic_parent)
|
138
173
|
grouped_records(association, records, polymorphic_parent)
|
139
174
|
.flat_map do |reflection, reflection_records|
|
@@ -143,9 +178,11 @@ module ActiveRecord
|
|
143
178
|
|
144
179
|
# preloader.rb active record 6.0
|
145
180
|
# changed:
|
181
|
+
# different from 5.2. But not called outside these redefined methods here, so it works fine
|
182
|
+
# did add compact to fix a 5.2 double preload nil bug
|
146
183
|
def grouped_records(orig_association, records, polymorphic_parent)
|
147
184
|
h = {}
|
148
|
-
records.each do |record|
|
185
|
+
records.compact.each do |record|
|
149
186
|
# each class can resolve virtual_{attributes,includes} differently
|
150
187
|
association = record.class.replace_virtual_fields(orig_association)
|
151
188
|
# 1 line optimization for single element array:
|
@@ -153,8 +190,10 @@ module ActiveRecord
|
|
153
190
|
|
154
191
|
case association
|
155
192
|
when Symbol, String
|
193
|
+
# 4/24/20 we want to revert #67 once we handle all these error cases in our codebase.
|
156
194
|
reflection = record.class._reflect_on_association(association)
|
157
|
-
|
195
|
+
display_virtual_attribute_deprecation("#{record.class.name}.#{association} does not exist") if !reflection && !polymorphic_parent
|
196
|
+
next if !reflection || !record.association(association).klass
|
158
197
|
when nil
|
159
198
|
next
|
160
199
|
else # need parent (preloaders_for_{hash,one}) to handle this Array/Hash
|
@@ -165,6 +204,21 @@ module ActiveRecord
|
|
165
204
|
h
|
166
205
|
end
|
167
206
|
# rubocop:enable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
|
207
|
+
|
208
|
+
def display_virtual_attribute_deprecation(str)
|
209
|
+
short_caller = caller
|
210
|
+
# if debugging is turned on, don't prune the backtrace.
|
211
|
+
# if debugging is off, prune down to the line where the sql is executed
|
212
|
+
# this defaults to false and only displays 1 line number.
|
213
|
+
unless ActiveSupport::Deprecation.debug
|
214
|
+
bc = ActiveSupport::BacktraceCleaner.new
|
215
|
+
bc.add_silencer { |line| line =~ /virtual_fields/ }
|
216
|
+
bc.add_silencer { |line| line =~ /active_record/ }
|
217
|
+
short_caller = bc.clean(caller)
|
218
|
+
end
|
219
|
+
|
220
|
+
ActiveSupport::Deprecation.warn(str, short_caller)
|
221
|
+
end
|
168
222
|
else
|
169
223
|
def preloaders_for_one(association, records, scope)
|
170
224
|
klass_map = records.compact.group_by(&:class)
|
@@ -194,7 +248,7 @@ module ActiveRecord
|
|
194
248
|
# syntax from the original codebase.
|
195
249
|
#
|
196
250
|
# rubocop:disable Style/BlockDelimiters, Layout/SpaceAfterComma, Style/HashSyntax
|
197
|
-
# rubocop:disable Layout/AlignHash
|
251
|
+
# rubocop:disable Layout/AlignHash
|
198
252
|
class JoinDependency
|
199
253
|
def instantiate(result_set, *_, &block)
|
200
254
|
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
@@ -210,51 +264,7 @@ module ActiveRecord
|
|
210
264
|
column_aliases = aliases.column_aliases(join_root)
|
211
265
|
|
212
266
|
# New Code
|
213
|
-
|
214
|
-
# This monkey patches the ActiveRecord::Associations::JoinDependency to
|
215
|
-
# include columns into the main record that might have been added
|
216
|
-
# through a `select` clause.
|
217
|
-
#
|
218
|
-
# This can be seen with the following:
|
219
|
-
#
|
220
|
-
# Vm.select(Vm.arel_table[Arel.star]).select(:some_vm_virtual_col)
|
221
|
-
# .includes(:tags => {}).references(:tags)
|
222
|
-
#
|
223
|
-
# Which will produce a SQL SELECT statement kind of like this:
|
224
|
-
#
|
225
|
-
# SELECT "vms".*,
|
226
|
-
# (<virtual_attribute_arel>) AS some_vm_virtual_col,
|
227
|
-
# "vms"."id" AS t0_r0
|
228
|
-
# "vms"."vendor" AS t0_r1
|
229
|
-
# "vms"."format" AS t0_r1
|
230
|
-
# "vms"."version" AS t0_r1
|
231
|
-
# ...
|
232
|
-
# "tags"."id" AS t1_r0
|
233
|
-
# "tags"."name" AS t1_r1
|
234
|
-
#
|
235
|
-
# This is because rails is trying to reduce the number of queries
|
236
|
-
# needed to fetch all of the records in the include, so it grabs the
|
237
|
-
# columns for both of the tables together to do it. Unfortuantely (or
|
238
|
-
# fortunately... depending on how you look at it), it does not remove
|
239
|
-
# any `.select` columns from the query that is run in the process, so
|
240
|
-
# that is brought along for the ride, but never used when this method
|
241
|
-
# instanciates the objects.
|
242
|
-
#
|
243
|
-
# The "New Code" here simply also instanciates any extra rows that
|
244
|
-
# might have been included in the select (virtual_columns) as well and
|
245
|
-
# brought back with the result set.
|
246
|
-
unless result_set.empty?
|
247
|
-
join_dep_keys = aliases.columns.map(&:right)
|
248
|
-
join_root_aliases = column_aliases.map(&:first)
|
249
|
-
additional_attributes = result_set.first.keys
|
250
|
-
.reject { |k| join_dep_keys.include?(k) }
|
251
|
-
.reject { |k| join_root_aliases.include?(k) }
|
252
|
-
column_aliases += if ActiveRecord.version.to_s >= "6.0"
|
253
|
-
additional_attributes.map { |k| Aliases::Column.new(k, k) }
|
254
|
-
else
|
255
|
-
additional_attributes.map { |k| [k, k] }
|
256
|
-
end
|
257
|
-
end
|
267
|
+
column_aliases += select_values_from_references(column_aliases, result_set) if result_set.present?
|
258
268
|
# End of New Code
|
259
269
|
|
260
270
|
message_bus = ActiveSupport::Notifications.instrumenter
|
@@ -279,7 +289,53 @@ module ActiveRecord
|
|
279
289
|
parents.values
|
280
290
|
end
|
281
291
|
# rubocop:enable Style/BlockDelimiters, Layout/SpaceAfterComma, Style/HashSyntax
|
282
|
-
# rubocop:enable Layout/AlignHash
|
292
|
+
# rubocop:enable Layout/AlignHash
|
293
|
+
|
294
|
+
#
|
295
|
+
# This monkey patches the ActiveRecord::Associations::JoinDependency to
|
296
|
+
# include columns into the main record that might have been added
|
297
|
+
# through a `select` clause.
|
298
|
+
#
|
299
|
+
# This can be seen with the following:
|
300
|
+
#
|
301
|
+
# Vm.select(Vm.arel_table[Arel.star]).select(:some_vm_virtual_col)
|
302
|
+
# .includes(:tags => {}).references(:tags)
|
303
|
+
#
|
304
|
+
# Which will produce a SQL SELECT statement kind of like this:
|
305
|
+
#
|
306
|
+
# SELECT "vms".*,
|
307
|
+
# (<virtual_attribute_arel>) AS some_vm_virtual_col,
|
308
|
+
# "vms"."id" AS t0_r0
|
309
|
+
# "vms"."vendor" AS t0_r1
|
310
|
+
# "vms"."format" AS t0_r1
|
311
|
+
# "vms"."version" AS t0_r1
|
312
|
+
# ...
|
313
|
+
# "tags"."id" AS t1_r0
|
314
|
+
# "tags"."name" AS t1_r1
|
315
|
+
#
|
316
|
+
# This is because rails is trying to reduce the number of queries
|
317
|
+
# needed to fetch all of the records in the include, so it grabs the
|
318
|
+
# columns for both of the tables together to do it. Unfortunately (or
|
319
|
+
# fortunately... depending on how you look at it), it does not remove
|
320
|
+
# any `.select` columns from the query that is run in the process, so
|
321
|
+
# that is brought along for the ride, but never used when this method
|
322
|
+
# instanciates the objects.
|
323
|
+
#
|
324
|
+
# The "New Code" here simply also instanciates any extra rows that
|
325
|
+
# might have been included in the select (virtual_columns) as well and
|
326
|
+
# brought back with the result set.
|
327
|
+
def select_values_from_references(column_aliases, result_set)
|
328
|
+
join_dep_keys = aliases.columns.map(&:right)
|
329
|
+
join_root_aliases = column_aliases.map(&:first)
|
330
|
+
additional_attributes = result_set.first.keys
|
331
|
+
.reject { |k| join_dep_keys.include?(k) }
|
332
|
+
.reject { |k| join_root_aliases.include?(k) }
|
333
|
+
if ActiveRecord.version.to_s >= "6.0"
|
334
|
+
additional_attributes.map { |k| Aliases::Column.new(k, k) }
|
335
|
+
else
|
336
|
+
additional_attributes.map { |k| [k, k] }
|
337
|
+
end
|
338
|
+
end
|
283
339
|
end
|
284
340
|
end
|
285
341
|
|
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: 1.
|
4
|
+
version: 1.6.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: 2020-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|