activerecord-virtual_attributes 2.0.0 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -101,242 +101,76 @@ module ActiveRecord
101
101
  module Associations
102
102
  class Preloader
103
103
  prepend(Module.new {
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
108
- def preloaders_for_reflection(reflection, records, scope, polymorphic_parent)
109
- case reflection
110
- when Array
111
- reflection.flat_map { |ref| preloaders_on(ref, records, scope, polymorphic_parent) }
112
- when Hash
113
- preloaders_on(reflection, records, scope, polymorphic_parent)
114
- else
115
- super(reflection, records, scope)
116
- end
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"
104
+ # preloader.rb active record 6.0
105
+ # changed:
106
+ # since grouped_records can return a hash/array, we need to handle those 2 new cases
107
+ def preloaders_for_reflection(reflection, records, scope, polymorphic_parent)
108
+ case reflection
109
+ when Array
110
+ reflection.flat_map { |ref| preloaders_on(ref, records, scope, polymorphic_parent) }
111
+ when Hash
112
+ preloaders_on(reflection, records, scope, polymorphic_parent)
113
+ else
114
+ super(reflection, records, scope)
115
+ end
116
+ end
117
+
118
+ # rubocop:disable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
119
+ # preloader.rb active record 6.0
120
+ # changed:
121
+ # passing polymorphic around (and makes 5.2 more similar to 6.0)
122
+ def preloaders_for_hash(association, records, scope, polymorphic_parent)
123
+ association.flat_map { |parent, child|
124
+ grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
125
+ loaders = preloaders_for_reflection(reflection, reflection_records, scope, polymorphic_parent)
126
+ recs = loaders.flat_map(&:preloaded_records).uniq
127
+ child_polymorphic_parent = reflection && reflection.respond_to?(:options) && reflection.options[:polymorphic]
128
+ loaders.concat Array.wrap(child).flat_map { |assoc|
129
+ preloaders_on assoc, recs, scope, child_polymorphic_parent
130
+ }
131
+ loaders
146
132
  end
147
- end
133
+ }
148
134
  end
149
135
 
150
- if ActiveRecord.version.to_s >= "5.2"
151
- # rubocop:disable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
152
- # preloader.rb active record 6.0
153
- # changed:
154
- # passing polymorphic around (and makes 5.2 more similar to 6.0)
155
- def preloaders_for_hash(association, records, scope, polymorphic_parent)
156
- association.flat_map { |parent, child|
157
- grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
158
- loaders = preloaders_for_reflection(reflection, reflection_records, scope, polymorphic_parent)
159
- recs = loaders.flat_map(&:preloaded_records).uniq
160
- child_polymorphic_parent = reflection && reflection.respond_to?(:options) && reflection.options[:polymorphic]
161
- loaders.concat Array.wrap(child).flat_map { |assoc|
162
- preloaders_on assoc, recs, scope, child_polymorphic_parent
163
- }
164
- loaders
165
- end
166
- }
167
- end
168
-
169
- # preloader.rb active record 6.0
170
- # changed:
171
- # passing polymorphic_parent to preloaders_for_reflection
172
- def preloaders_for_one(association, records, scope, polymorphic_parent)
173
- grouped_records(association, records, polymorphic_parent)
174
- .flat_map do |reflection, reflection_records|
175
- preloaders_for_reflection(reflection, reflection_records, scope, polymorphic_parent)
176
- end
177
- end
178
-
179
- # preloader.rb active record 6.0
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
183
- def grouped_records(orig_association, records, polymorphic_parent)
184
- h = {}
185
- records.compact.each do |record|
186
- # each class can resolve virtual_{attributes,includes} differently
187
- association = record.class.replace_virtual_fields(orig_association)
188
- # 1 line optimization for single element array:
189
- association = association.first if association.kind_of?(Array) && association.size == 1
190
-
191
- case association
192
- when Symbol, String
193
- # 4/24/20 we want to revert #67 once we handle all these error cases in our codebase.
194
- reflection = record.class._reflect_on_association(association)
195
- display_virtual_attribute_deprecation("#{record.class.name}.#{association} does not exist") if !reflection && !polymorphic_parent
196
- next if !reflection || !record.association(association).klass
197
- when nil
198
- next
199
- else # need parent (preloaders_for_{hash,one}) to handle this Array/Hash
200
- reflection = association
201
- end
202
- (h[reflection] ||= []) << record
203
- end
204
- h
205
- end
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)
136
+ # preloader.rb active record 6.0
137
+ # changed:
138
+ # passing polymorphic_parent to preloaders_for_reflection
139
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
140
+ grouped_records(association, records, polymorphic_parent)
141
+ .flat_map do |reflection, reflection_records|
142
+ preloaders_for_reflection(reflection, reflection_records, scope, polymorphic_parent)
218
143
  end
144
+ end
219
145
 
220
- ActiveSupport::Deprecation.warn(str, short_caller)
221
- end
222
- else
223
- def preloaders_for_one(association, records, scope)
224
- klass_map = records.compact.group_by(&:class)
146
+ # preloader.rb active record 6.0, 6.1
147
+ def grouped_records(orig_association, records, polymorphic_parent)
148
+ h = {}
149
+ records.each do |record|
150
+ # The virtual_field lookup can return Symbol/Nil/Other (typically a Hash)
151
+ # so the case statement and the cases for Nil/Other are new
225
152
 
226
- # new logic: preload virtual fields / virtual includes
227
- loaders = klass_map.keys.group_by { |klass| klass.virtual_includes(association) }.flat_map do |virtuals, klasses|
228
- subset = klasses.flat_map { |klass| klass_map[klass] }
229
- preload(subset, virtuals)
230
- end
231
- # /new logic
153
+ # each class can resolve virtual_{attributes,includes} differently
154
+ association = record.class.replace_virtual_fields(orig_association)
155
+ # 1 line optimization for single element array:
156
+ association = association.first if association.kind_of?(Array) && association.size == 1
232
157
 
233
- records_with_association = klass_map.select { |k, _rs| k.reflect_on_association(association) }.flat_map { |_k, rs| rs }
234
- if records_with_association.any?
235
- loaders.concat(super(association, records_with_association, scope))
158
+ case association
159
+ when Symbol, String
160
+ reflection = record.class._reflect_on_association(association)
161
+ next if polymorphic_parent && !reflection || !record.association(association).klass
162
+ when nil
163
+ next
164
+ else # need parent (preloaders_for_{hash,one}) to handle this Array/Hash
165
+ reflection = association
236
166
  end
237
-
238
- loaders
167
+ (h[reflection] ||= []) << record
239
168
  end
169
+ h
240
170
  end
171
+ # rubocop:enable Style/BlockDelimiters, Lint/AmbiguousBlockAssociation, Style/MethodCallWithArgsParentheses
241
172
  })
242
173
  end
243
-
244
- # FIXME: Hopefully we can get this into Rails core so this is no longer
245
- # required in our codebase, but the rule that are broken here are mostly
246
- # due to the style of the Rails codebase conflicting with our own.
247
- # Ignoring them to avoid noise in RuboCop, but allow us to keep the same
248
- # syntax from the original codebase.
249
- #
250
- # rubocop:disable Style/BlockDelimiters, Layout/SpaceAfterComma, Style/HashSyntax
251
- # rubocop:disable Layout/AlignHash
252
- class JoinDependency
253
- def instantiate(result_set, *_, &block)
254
- primary_key = aliases.column_alias(join_root, join_root.primary_key)
255
-
256
- seen = Hash.new { |i, object_id|
257
- i[object_id] = Hash.new { |j, child_class|
258
- j[child_class] = {}
259
- }
260
- }
261
-
262
- model_cache = Hash.new { |h,klass| h[klass] = {} }
263
- parents = model_cache[join_root]
264
- column_aliases = aliases.column_aliases(join_root)
265
-
266
- # New Code
267
- column_aliases += select_values_from_references(column_aliases, result_set) if result_set.present?
268
- # End of New Code
269
-
270
- message_bus = ActiveSupport::Notifications.instrumenter
271
-
272
- payload = {
273
- record_count: result_set.length,
274
- class_name: join_root.base_klass.name
275
- }
276
-
277
- message_bus.instrument('instantiation.active_record', payload) do
278
- result_set.each { |row_hash|
279
- parent_key = primary_key ? row_hash[primary_key] : row_hash
280
- parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
281
- if ActiveRecord.version.to_s < "6.0"
282
- construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
283
- else
284
- construct(parent, join_root, row_hash, seen, model_cache)
285
- end
286
- }
287
- end
288
-
289
- parents.values
290
- end
291
- # rubocop:enable Style/BlockDelimiters, Layout/SpaceAfterComma, Style/HashSyntax
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
339
- end
340
174
  end
341
175
 
342
176
  class Relation
@@ -351,99 +185,50 @@ module ActiveRecord
351
185
 
352
186
  include(Module.new {
353
187
  # From ActiveRecord::FinderMethods
354
- if ActiveRecord.version.to_s >= "5.2"
355
- def apply_join_dependency(*args, &block)
356
- real = without_virtual_includes
357
- if real.equal?(self)
358
- super
359
- else
360
- real.apply_join_dependency(*args, &block)
361
- end
362
- end
363
- else
364
- def find_with_associations(&block)
365
- real = without_virtual_includes
366
- if real.equal?(self)
367
- super
368
- else
369
- real.find_with_associations(&block)
370
- end
188
+ def apply_join_dependency(*args, **kargs, &block)
189
+ real = without_virtual_includes
190
+ if real.equal?(self)
191
+ super
192
+ else
193
+ real.apply_join_dependency(*args, **kargs, &block)
371
194
  end
372
195
  end
373
196
 
374
- # From ActiveRecord::QueryMethods (rails 5.2 - 6.0)
197
+ # From ActiveRecord::QueryMethods (rails 5.2 - 6.1)
375
198
  def build_select(arel)
376
199
  if select_values.any?
377
- arel.project(*arel_columns(select_values.uniq, true))
378
- elsif klass.ignored_columns.any?
379
- arel.project(*klass.column_names.map { |field| arel_attribute(field) })
380
- else
381
- arel.project(table[Arel.star])
382
- end
383
- end
384
-
385
- # from ActiveRecord::QueryMethods (rails 5.2 - 6.0)
386
- def arel_columns(columns, allow_alias = false)
387
- columns.flat_map do |field|
388
- case field
389
- when Symbol
390
- arel_column(field.to_s, allow_alias) do |attr_name|
391
- connection.quote_table_name(attr_name)
200
+ cols = arel_columns(select_values.uniq).map do |col|
201
+ # if it is a virtual attribute, then add aliases to those columns
202
+ if col.kind_of?(Arel::Nodes::Grouping) && col.name
203
+ col.as(connection.quote_column_name(col.name))
204
+ else
205
+ col
392
206
  end
393
- when String
394
- arel_column(field, allow_alias, &:itself)
395
- when Proc
396
- field.call
397
- else
398
- field
399
207
  end
400
- end
401
- end
402
-
403
- # from ActiveRecord::QueryMethods (rails 5.2 - 6.0)
404
- def arel_column(field, allow_alias = false, &block)
405
- field = klass.attribute_aliases[field] || field
406
- from = from_clause.name || from_clause.value
407
-
408
- if klass.columns_hash.key?(field) && (!from || table_name_matches?(from))
409
- arel_attribute(field)
410
- elsif virtual_attribute?(field)
411
- virtual_attribute_arel_column(field, allow_alias, &block)
208
+ arel.project(*cols)
412
209
  else
413
- yield field
210
+ super
414
211
  end
415
212
  end
416
213
 
417
- def virtual_attribute_arel_column(field, allow_alias)
418
- arel = arel_attribute(field)
419
- if arel.nil?
420
- yield field
421
- elsif allow_alias && arel && arel.respond_to?(:as) && !arel.kind_of?(Arel::Nodes::As) && !arel.try(:alias)
422
- arel.as(connection.quote_column_name(field.to_s))
423
- else
214
+ # from ActiveRecord::QueryMethods (rails 5.2 - 6.0)
215
+ # TODO: remove from rails 7.0
216
+ def arel_column(field, &block)
217
+ if virtual_attribute?(field) && (arel = table[field])
424
218
  arel
219
+ else
220
+ super
425
221
  end
426
222
  end
427
223
 
428
- # From ActiveRecord::QueryMethods
429
- def table_name_matches?(from)
430
- /(?:\A|(?<!FROM)\s)(?:\b#{table.name}\b|#{connection.quote_table_name(table.name)})(?!\.)/i.match?(from.to_s)
431
- end
432
-
433
- # From ActiveRecord::QueryMethods
434
- def build_left_outer_joins(manager, outer_joins, *rest)
435
- outer_joins = klass.replace_virtual_fields(outer_joins)
436
- super if outer_joins.present?
224
+ def construct_join_dependency(associations, join_type) # :nodoc:
225
+ associations = klass.replace_virtual_fields(associations)
226
+ super
437
227
  end
438
228
 
439
229
  # From ActiveRecord::Calculations
230
+ # introduces virtual includes support for calculate (we mostly use COUNT(*))
440
231
  def calculate(operation, attribute_name)
441
- if ActiveRecord.version.to_s < "5.1"
442
- if (arel = klass.arel_attribute(attribute_name)) && virtual_attribute?(attribute_name)
443
- attribute_name = arel
444
- end
445
- end
446
-
447
232
  # allow calculate to work with includes and a virtual attribute
448
233
  real = without_virtual_includes
449
234
  return super if real.equal?(self)
@@ -5,7 +5,7 @@ module VirtualAttributes
5
5
  module ClassMethods
6
6
  private
7
7
 
8
- # define an attribute to calculating the total of a has many relationship
8
+ # define an attribute to calculate the total of a has many relationship
9
9
  #
10
10
  # example:
11
11
  #
@@ -25,24 +25,45 @@ module VirtualAttributes
25
25
  # # arel == (SELECT COUNT(*) FROM vms where ems.id = vms.ems_id)
26
26
  #
27
27
  def virtual_total(name, relation, options = {})
28
- define_virtual_size_method(name, relation)
29
28
  define_virtual_aggregate_attribute(name, relation, :count, Arel.star, options)
29
+ define_method(name) { (has_attribute?(name) ? self[name] : send(relation).try(:size)) || 0 }
30
30
  end
31
31
 
32
- # define an attribute to calculate the sum of a has may relationship
32
+ def virtual_sum(name, relation, column, options = {})
33
+ define_virtual_aggregate_attribute(name, relation, :sum, column, options)
34
+ define_virtual_aggregate_method(name, relation, column, :sum)
35
+ end
36
+
37
+ def virtual_minimum(name, relation, column, options = {})
38
+ define_virtual_aggregate_attribute(name, relation, :minimum, column, options)
39
+ define_virtual_aggregate_method(name, relation, column, :min, :minimum)
40
+ end
41
+
42
+ def virtual_maximum(name, relation, column, options = {})
43
+ define_virtual_aggregate_attribute(name, relation, :maximum, column, options)
44
+ define_virtual_aggregate_method(name, relation, column, :max, :maximum)
45
+ end
46
+
47
+ def virtual_average(name, relation, column, options = {})
48
+ define_virtual_aggregate_attribute(name, relation, :average, column, options)
49
+ define_virtual_aggregate_method(name, relation, column, :average) { |values| values.count == 0 ? 0 : values.sum / values.count }
50
+ end
51
+
52
+ # @param method_name
53
+ # :count :average :minimum :maximum :sum
33
54
  #
34
55
  # example:
35
56
  #
36
57
  # class Hardware
37
58
  # has_many :disks
38
- # virtual_aggregate :allocated_disk_storage, :disks, :sum, :size
59
+ # virtual_sum :allocated_disk_storage, :disks, :size
39
60
  # end
40
61
  #
41
62
  # generates:
42
63
  #
43
64
  # def allocated_disk_storage
44
65
  # if disks.loaded?
45
- # disks.blank? ? nil : disks.map(&:size).compact.sum
66
+ # disks.map(&:size).compact.sum
46
67
  # else
47
68
  # disks.sum(:size) || 0
48
69
  # end
@@ -53,10 +74,8 @@ module VirtualAttributes
53
74
  # # arel => (SELECT sum("disks"."size") where "hardware"."id" = "disks"."hardware_id")
54
75
 
55
76
  def virtual_aggregate(name, relation, method_name = :sum, column = nil, options = {})
56
- return define_virtual_total(name, relation, options) if method_name == :size
57
-
58
- define_virtual_aggregate_method(name, relation, method_name, column)
59
- define_virtual_aggregate_attribute(name, relation, method_name, column, options)
77
+ return virtual_total(name, relation, options) if method_name == :size
78
+ return virtual_sum(name, relation, column, options) if method_name == :sum
60
79
  end
61
80
 
62
81
  def define_virtual_aggregate_attribute(name, relation, method_name, column, options)
@@ -77,20 +96,19 @@ module VirtualAttributes
77
96
  end
78
97
  end
79
98
 
80
- def define_virtual_size_method(name, relation)
81
- define_method(name) do
82
- (has_attribute?(name) ? self[name] : send(relation).try(:size)) || 0
83
- end
84
- end
85
-
86
- def define_virtual_aggregate_method(name, relation, method_name, column)
99
+ def define_virtual_aggregate_method(name, relation, column, ruby_method_name, arel_method_name = ruby_method_name)
87
100
  define_method(name) do
88
- if attribute_present?(name)
89
- self[name]
101
+ if has_attribute?(name)
102
+ self[name] || 0
90
103
  elsif (rel = send(relation)).loaded?
91
- rel.blank? ? nil : rel.map { |t| t.send(column) }.compact.send(method_name)
104
+ values = rel.map { |t| t.send(column) }.compact
105
+ if block_given?
106
+ yield values
107
+ else
108
+ values.blank? ? nil : values.send(ruby_method_name)
109
+ end
92
110
  else
93
- rel.try(method_name, column) || 0
111
+ rel.try(arel_method_name, column) || 0
94
112
  end
95
113
  end
96
114
  end
@@ -101,7 +119,7 @@ module VirtualAttributes
101
119
  # need db access for the reflection join_keys, so delaying all this key lookup until call time
102
120
  lambda do |t|
103
121
  # strings and symbols are converted across, arel objects are not
104
- column = reflection.klass.arel_attribute(column) unless column.respond_to?(:count)
122
+ column = reflection.klass.arel_table[column] unless column.respond_to?(:count)
105
123
 
106
124
  # query: SELECT COUNT(*) FROM main_table JOIN foreign_table ON main_table.id = foreign_table.id JOIN ...
107
125
  relation_query = joins(reflection.name).select(column.send(method_name))
@@ -121,18 +139,16 @@ module VirtualAttributes
121
139
 
122
140
  # convert bind variables from ? to actual values. otherwise, sql is incomplete
123
141
  conn = connection
124
- sql = if ActiveRecord.version.to_s >= "5.2"
125
- conn.unprepared_statement { conn.to_sql(query) }
126
- else
127
- conn.unprepared_statement { conn.to_sql(query, relation_query.bound_attributes) }
128
- end
142
+ sql = conn.unprepared_statement { conn.to_sql(query) }
129
143
 
130
144
  # add () around query
131
- t.grouping(Arel::Nodes::SqlLiteral.new(sql))
145
+ query = t.grouping(Arel::Nodes::SqlLiteral.new(sql))
146
+ # add coalesce to ensure correct value comes out
147
+ t.grouping(Arel::Nodes::NamedFunction.new('COALESCE', [query, Arel::Nodes::SqlLiteral.new("0")]))
132
148
  end
133
149
  end
134
150
  end
135
151
  end
136
152
  end
137
153
 
138
- ActiveRecord::Base.send(:include, VirtualAttributes::VirtualTotal)
154
+ ActiveRecord::Base.include VirtualAttributes::VirtualTotal
@@ -86,14 +86,7 @@ module ActiveRecord
86
86
  def attributes_builder # :nodoc:
87
87
  unless defined?(@attributes_builder) && @attributes_builder
88
88
  defaults = _default_attributes.except(*(column_names - [primary_key]))
89
- # change necessary for rails 5.0 and 5.1 - (changed/introduced in https://github.com/rails/rails/pull/31894)
90
- defaults = defaults.except(*virtual_attribute_names)
91
- # end change
92
- @attributes_builder = if ActiveRecord.version.to_s >= "5.2"
93
- ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
94
- else
95
- ActiveRecord::AttributeSet::Builder.new(attribute_types, defaults)
96
- end
89
+ @attributes_builder = ActiveModel::AttributeSet::Builder.new(attribute_types, defaults)
97
90
  end
98
91
  @attributes_builder
99
92
  end
@@ -130,22 +123,7 @@ require "active_record/virtual_attributes/virtual_fields"
130
123
  # Class extensions
131
124
  #
132
125
 
133
- # this patch is no longer necessary for 5.2
134
- if ActiveRecord.version.to_s < "5.2"
135
- require "active_record/attribute"
136
- module ActiveRecord
137
- # This is a bug in rails 5.0 and 5.1, but it is made much worse by virtual attributes
138
- class Attribute
139
- def with_value_from_database(value)
140
- # self.class.from_database(name, value, type)
141
- initialized? ? self.class.from_database(name, value, type) : self
142
- end
143
- end
144
- end
145
- end
146
-
147
126
  require "active_record/virtual_attributes/virtual_total"
148
- require "active_record/virtual_attributes/arel_groups"
149
127
 
150
128
  # legacy support for sql types
151
129
  module VirtualAttributes