activerecord-virtual_attributes 1.5.0 → 1.6.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: 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