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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90da25c212d7507da2ca2789b1151b2e7cb7cb3936411a7e92823518299141b9
4
- data.tar.gz: d2a3cc1662d29fa8625dab40610950a573c0014b9739dbb260184da47afdb2ac
3
+ metadata.gz: e0b56d15edaf7d124492c47903569158354ff7ca1887735c706fb342c847e0a2
4
+ data.tar.gz: 4674998a27f163fcb2b387ace6f71d3c9c70adf6dcd64332d59219e8e34dc7d3
5
5
  SHA512:
6
- metadata.gz: 78c1edd3d1a17f5f4fcd147315e37c788cdfc3add3137a4190d568cc93ab1823d9de0e46a6f57f266d248104823578d5f6a86e076228ae640c52c035922910f4
7
- data.tar.gz: 19ec2fafe3553b4ff809c1aae1aa6ccfd6f303216d1c35bc4a09f30e9dda2876a48fd531d9216e0a1a566de0434c90edd58936c12027b62bc9c70eb896235b50
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.5.0...HEAD
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
- if defined?(Arel::Nodes::Grouping)
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
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module VirtualAttributes
3
- VERSION = "1.5.0".freeze
3
+ VERSION = "1.6.0".freeze
4
4
  end
5
5
  end
@@ -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 => -> (t) { t.grouping(t[:field2]) } }
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
- col.call(arel_table) if col
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
- # since grouped_records can return a hash/array, we need to handle those 2 new cases
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
- # since grouped_records can return a hash/array, we need to handle those 2 new cases
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
- next if polymorphic_parent && !reflection || !record.association(association).klass
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, Metrics/AbcSize, Metrics/MethodLength
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, Metrics/AbcSize, Metrics/MethodLength
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.5.0
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: 2019-12-04 00:00:00.000000000 Z
11
+ date: 2020-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord