activerecord 4.2.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1537 -789
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +27 -14
- data/lib/active_record/associations/preloader.rb +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
- data/lib/active_record/attribute_methods/write.rb +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +176 -115
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -17
- data/lib/active_record/scoping/default.rb +24 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +159 -67
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +21 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +60 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -101
@@ -28,15 +28,21 @@ module ActiveRecord
|
|
28
28
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
29
29
|
def reader(force_reload = false)
|
30
30
|
if force_reload
|
31
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
32
|
+
Passing an argument to force an association to reload is now
|
33
|
+
deprecated and will be removed in Rails 5.1. Please call `reload`
|
34
|
+
on the result collection proxy instead.
|
35
|
+
MSG
|
36
|
+
|
31
37
|
klass.uncached { reload }
|
32
38
|
elsif stale_target?
|
33
39
|
reload
|
34
40
|
end
|
35
41
|
|
36
|
-
if
|
42
|
+
if null_scope?
|
37
43
|
# Cache the proxy separately before the owner has an id
|
38
44
|
# or else a post-save proxy will still lack the id
|
39
|
-
@
|
45
|
+
@null_proxy ||= CollectionProxy.create(klass, self)
|
40
46
|
else
|
41
47
|
@proxy ||= CollectionProxy.create(klass, self)
|
42
48
|
end
|
@@ -54,17 +60,22 @@ module ActiveRecord
|
|
54
60
|
record.send(reflection.association_primary_key)
|
55
61
|
end
|
56
62
|
else
|
57
|
-
|
58
|
-
|
63
|
+
@association_ids ||= (
|
64
|
+
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
|
65
|
+
scope.pluck(column)
|
66
|
+
)
|
59
67
|
end
|
60
68
|
end
|
61
69
|
|
62
70
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
71
|
def ids_writer(ids)
|
64
72
|
pk_type = reflection.primary_key_type
|
65
|
-
ids = Array(ids).reject
|
66
|
-
ids.map! { |i| pk_type.
|
67
|
-
|
73
|
+
ids = Array(ids).reject(&:blank?)
|
74
|
+
ids.map! { |i| pk_type.cast(i) }
|
75
|
+
records = klass.where(reflection.association_primary_key => ids).index_by do |r|
|
76
|
+
r.send(reflection.association_primary_key)
|
77
|
+
end.values_at(*ids)
|
78
|
+
replace(records)
|
68
79
|
end
|
69
80
|
|
70
81
|
def reset
|
@@ -125,10 +136,28 @@ module ActiveRecord
|
|
125
136
|
first_nth_or_last(:forty_two, *args)
|
126
137
|
end
|
127
138
|
|
139
|
+
def third_to_last(*args)
|
140
|
+
first_nth_or_last(:third_to_last, *args)
|
141
|
+
end
|
142
|
+
|
143
|
+
def second_to_last(*args)
|
144
|
+
first_nth_or_last(:second_to_last, *args)
|
145
|
+
end
|
146
|
+
|
128
147
|
def last(*args)
|
129
148
|
first_nth_or_last(:last, *args)
|
130
149
|
end
|
131
150
|
|
151
|
+
def take(n = nil)
|
152
|
+
if loaded?
|
153
|
+
n ? target.take(n) : target.first
|
154
|
+
else
|
155
|
+
scope.take(n).tap do |record|
|
156
|
+
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
132
161
|
def build(attributes = {}, &block)
|
133
162
|
if attributes.is_a?(Array)
|
134
163
|
attributes.collect { |attr| build(attr, &block) }
|
@@ -151,6 +180,7 @@ module ActiveRecord
|
|
151
180
|
# be chained. Since << flattens its argument list and inserts each record,
|
152
181
|
# +push+ and +concat+ behave identically.
|
153
182
|
def concat(*records)
|
183
|
+
records = records.flatten
|
154
184
|
if owner.new_record?
|
155
185
|
load_target
|
156
186
|
concat_records(records)
|
@@ -218,11 +248,7 @@ module ActiveRecord
|
|
218
248
|
|
219
249
|
# Count all records using SQL. Construct options and pass them with
|
220
250
|
# scope to the target class's +count+.
|
221
|
-
def count(column_name = nil
|
222
|
-
# TODO: Remove count_options argument as soon we remove support to
|
223
|
-
# activerecord-deprecated_finders.
|
224
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
225
|
-
|
251
|
+
def count(column_name = nil)
|
226
252
|
relation = scope
|
227
253
|
if association_scope.distinct_value
|
228
254
|
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
@@ -254,7 +280,7 @@ module ActiveRecord
|
|
254
280
|
_options = records.extract_options!
|
255
281
|
dependent = _options[:dependent] || options[:dependent]
|
256
282
|
|
257
|
-
records = find(records) if records.any? { |record| record.kind_of?(
|
283
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
258
284
|
delete_or_destroy(records, dependent)
|
259
285
|
end
|
260
286
|
|
@@ -265,7 +291,7 @@ module ActiveRecord
|
|
265
291
|
# +:dependent+ option.
|
266
292
|
def destroy(*records)
|
267
293
|
return if records.empty?
|
268
|
-
records = find(records) if records.any? { |record| record.kind_of?(
|
294
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
269
295
|
delete_or_destroy(records, :destroy)
|
270
296
|
end
|
271
297
|
|
@@ -289,7 +315,7 @@ module ActiveRecord
|
|
289
315
|
elsif !loaded? && !association_scope.group_values.empty?
|
290
316
|
load_target.size
|
291
317
|
elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
|
292
|
-
unsaved_records = target.select
|
318
|
+
unsaved_records = target.select(&:new_record?)
|
293
319
|
unsaved_records.size + count_records
|
294
320
|
else
|
295
321
|
count_records
|
@@ -322,7 +348,8 @@ module ActiveRecord
|
|
322
348
|
end
|
323
349
|
|
324
350
|
# Returns true if the collections is not empty.
|
325
|
-
#
|
351
|
+
# If block given, loads all records and checks for one or more matches.
|
352
|
+
# Otherwise, equivalent to +!collection.empty?+.
|
326
353
|
def any?
|
327
354
|
if block_given?
|
328
355
|
load_target.any? { |*block_args| yield(*block_args) }
|
@@ -332,7 +359,8 @@ module ActiveRecord
|
|
332
359
|
end
|
333
360
|
|
334
361
|
# Returns true if the collection has more than 1 record.
|
335
|
-
#
|
362
|
+
# If block given, loads all records and checks for two or more matches.
|
363
|
+
# Otherwise, equivalent to +collection.size > 1+.
|
336
364
|
def many?
|
337
365
|
if block_given?
|
338
366
|
load_target.many? { |*block_args| yield(*block_args) }
|
@@ -361,6 +389,8 @@ module ActiveRecord
|
|
361
389
|
replace_common_records_in_memory(other_array, original_target)
|
362
390
|
if other_array != original_target
|
363
391
|
transaction { replace_records(other_array, original_target) }
|
392
|
+
else
|
393
|
+
other_array
|
364
394
|
end
|
365
395
|
end
|
366
396
|
end
|
@@ -395,12 +425,16 @@ module ActiveRecord
|
|
395
425
|
|
396
426
|
def replace_on_target(record, index, skip_callbacks)
|
397
427
|
callback(:before_add, record) unless skip_callbacks
|
428
|
+
|
429
|
+
was_loaded = loaded?
|
398
430
|
yield(record) if block_given?
|
399
431
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
432
|
+
unless !was_loaded && loaded?
|
433
|
+
if index
|
434
|
+
@target[index] = record
|
435
|
+
else
|
436
|
+
@target << record
|
437
|
+
end
|
404
438
|
end
|
405
439
|
|
406
440
|
callback(:after_add, record) unless skip_callbacks
|
@@ -421,13 +455,7 @@ module ActiveRecord
|
|
421
455
|
|
422
456
|
private
|
423
457
|
def get_records
|
424
|
-
if
|
425
|
-
scope.eager_loading? ||
|
426
|
-
klass.current_scope ||
|
427
|
-
klass.default_scopes.any?
|
428
|
-
|
429
|
-
return scope.to_a
|
430
|
-
end
|
458
|
+
return scope.to_a if skip_statement_cache?
|
431
459
|
|
432
460
|
conn = klass.connection
|
433
461
|
sc = reflection.association_scope_cache(conn, owner) do
|
@@ -506,7 +534,7 @@ module ActiveRecord
|
|
506
534
|
def delete_or_destroy(records, method)
|
507
535
|
records = records.flatten
|
508
536
|
records.each { |record| raise_on_type_mismatch!(record) }
|
509
|
-
existing_records = records.reject
|
537
|
+
existing_records = records.reject(&:new_record?)
|
510
538
|
|
511
539
|
if existing_records.empty?
|
512
540
|
remove_records(existing_records, records, method)
|
@@ -553,7 +581,7 @@ module ActiveRecord
|
|
553
581
|
def concat_records(records, should_raise = false)
|
554
582
|
result = true
|
555
583
|
|
556
|
-
records.
|
584
|
+
records.each do |record|
|
557
585
|
raise_on_type_mismatch!(record)
|
558
586
|
add_to_target(record) do |rec|
|
559
587
|
result &&= insert_record(rec, true, should_raise) unless owner.new_record?
|
@@ -597,8 +625,8 @@ module ActiveRecord
|
|
597
625
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
598
626
|
assoc = owner.association(reflection.through_reflection.name)
|
599
627
|
assoc.reader.any? { |source|
|
600
|
-
|
601
|
-
|
628
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
629
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
602
630
|
} || target.include?(record)
|
603
631
|
else
|
604
632
|
target.include?(record)
|
@@ -609,7 +637,7 @@ module ActiveRecord
|
|
609
637
|
# specified, then #find scans the entire collection.
|
610
638
|
def find_by_scan(*args)
|
611
639
|
expects_array = args.first.kind_of?(Array)
|
612
|
-
ids = args.flatten.compact.map
|
640
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
613
641
|
|
614
642
|
if ids.size == 1
|
615
643
|
id = ids.first
|
@@ -29,10 +29,11 @@ module ActiveRecord
|
|
29
29
|
# instantiation of the actual post records.
|
30
30
|
class CollectionProxy < Relation
|
31
31
|
delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
|
32
|
+
delegate :find_nth, to: :scope
|
32
33
|
|
33
34
|
def initialize(klass, association) #:nodoc:
|
34
35
|
@association = association
|
35
|
-
super klass, klass.arel_table
|
36
|
+
super klass, klass.arel_table, klass.predicate_builder
|
36
37
|
merge! association.scope(nullify: false)
|
37
38
|
end
|
38
39
|
|
@@ -111,7 +112,7 @@ module ActiveRecord
|
|
111
112
|
end
|
112
113
|
|
113
114
|
# Finds an object in the collection responding to the +id+. Uses the same
|
114
|
-
# rules as
|
115
|
+
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
|
115
116
|
# error if the object cannot be found.
|
116
117
|
#
|
117
118
|
# class Person < ActiveRecord::Base
|
@@ -126,7 +127,7 @@ module ActiveRecord
|
|
126
127
|
# # ]
|
127
128
|
#
|
128
129
|
# person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
129
|
-
# person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4
|
130
|
+
# person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
|
130
131
|
#
|
131
132
|
# person.pets.find(2) { |pet| pet.name.downcase! }
|
132
133
|
# # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
|
@@ -170,32 +171,42 @@ module ActiveRecord
|
|
170
171
|
@association.first(*args)
|
171
172
|
end
|
172
173
|
|
173
|
-
# Same as
|
174
|
+
# Same as #first except returns only the second record.
|
174
175
|
def second(*args)
|
175
176
|
@association.second(*args)
|
176
177
|
end
|
177
178
|
|
178
|
-
# Same as
|
179
|
+
# Same as #first except returns only the third record.
|
179
180
|
def third(*args)
|
180
181
|
@association.third(*args)
|
181
182
|
end
|
182
183
|
|
183
|
-
# Same as
|
184
|
+
# Same as #first except returns only the fourth record.
|
184
185
|
def fourth(*args)
|
185
186
|
@association.fourth(*args)
|
186
187
|
end
|
187
188
|
|
188
|
-
# Same as
|
189
|
+
# Same as #first except returns only the fifth record.
|
189
190
|
def fifth(*args)
|
190
191
|
@association.fifth(*args)
|
191
192
|
end
|
192
193
|
|
193
|
-
# Same as
|
194
|
+
# Same as #first except returns only the forty second record.
|
194
195
|
# Also known as accessing "the reddit".
|
195
196
|
def forty_two(*args)
|
196
197
|
@association.forty_two(*args)
|
197
198
|
end
|
198
199
|
|
200
|
+
# Same as #first except returns only the third-to-last record.
|
201
|
+
def third_to_last(*args)
|
202
|
+
@association.third_to_last(*args)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Same as #first except returns only the second-to-last record.
|
206
|
+
def second_to_last(*args)
|
207
|
+
@association.second_to_last(*args)
|
208
|
+
end
|
209
|
+
|
199
210
|
# Returns the last record, or the last +n+ records, from the collection.
|
200
211
|
# If the collection is empty, the first form returns +nil+, and the second
|
201
212
|
# form returns an empty array.
|
@@ -226,6 +237,35 @@ module ActiveRecord
|
|
226
237
|
@association.last(*args)
|
227
238
|
end
|
228
239
|
|
240
|
+
# Gives a record (or N records if a parameter is supplied) from the collection
|
241
|
+
# using the same rules as <tt>ActiveRecord::Base.take</tt>.
|
242
|
+
#
|
243
|
+
# class Person < ActiveRecord::Base
|
244
|
+
# has_many :pets
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# person.pets
|
248
|
+
# # => [
|
249
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
250
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
251
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
252
|
+
# # ]
|
253
|
+
#
|
254
|
+
# person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
255
|
+
#
|
256
|
+
# person.pets.take(2)
|
257
|
+
# # => [
|
258
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
259
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>
|
260
|
+
# # ]
|
261
|
+
#
|
262
|
+
# another_person_without.pets # => []
|
263
|
+
# another_person_without.pets.take # => nil
|
264
|
+
# another_person_without.pets.take(2) # => []
|
265
|
+
def take(n = nil)
|
266
|
+
@association.take(n)
|
267
|
+
end
|
268
|
+
|
229
269
|
# Returns a new object of the collection type that has been instantiated
|
230
270
|
# with +attributes+ and linked to this object, but have not yet been saved.
|
231
271
|
# You can pass an array of attributes hashes, this will return an array
|
@@ -285,7 +325,7 @@ module ActiveRecord
|
|
285
325
|
@association.create(attributes, &block)
|
286
326
|
end
|
287
327
|
|
288
|
-
# Like
|
328
|
+
# Like #create, except that if the record is invalid, raises an exception.
|
289
329
|
#
|
290
330
|
# class Person
|
291
331
|
# has_many :pets
|
@@ -302,8 +342,8 @@ module ActiveRecord
|
|
302
342
|
end
|
303
343
|
|
304
344
|
# Add one or more records to the collection by setting their foreign keys
|
305
|
-
# to the association's primary key. Since
|
306
|
-
# inserts each record, +push+ and
|
345
|
+
# to the association's primary key. Since #<< flattens its argument list and
|
346
|
+
# inserts each record, +push+ and #concat behave identically. Returns +self+
|
307
347
|
# so method calls may be chained.
|
308
348
|
#
|
309
349
|
# class Person < ActiveRecord::Base
|
@@ -359,7 +399,7 @@ module ActiveRecord
|
|
359
399
|
# specified by the +:dependent+ option. If no +:dependent+ option is given,
|
360
400
|
# then it will follow the default strategy.
|
361
401
|
#
|
362
|
-
# For
|
402
|
+
# For <tt>has_many :through</tt> associations, the default deletion strategy is
|
363
403
|
# +:delete_all+.
|
364
404
|
#
|
365
405
|
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
@@ -394,7 +434,7 @@ module ActiveRecord
|
|
394
434
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
|
395
435
|
# # ]
|
396
436
|
#
|
397
|
-
# Both +has_many+ and
|
437
|
+
# Both +has_many+ and <tt>has_many :through</tt> dependencies default to the
|
398
438
|
# +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
|
399
439
|
# Records are not instantiated and callbacks will not be fired.
|
400
440
|
#
|
@@ -413,7 +453,7 @@ module ActiveRecord
|
|
413
453
|
# person.pets.delete_all
|
414
454
|
#
|
415
455
|
# Pet.find(1, 2, 3)
|
416
|
-
# # => ActiveRecord::RecordNotFound
|
456
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
417
457
|
#
|
418
458
|
# If it is set to <tt>:delete_all</tt>, all the objects are deleted
|
419
459
|
# *without* calling their +destroy+ method.
|
@@ -433,7 +473,7 @@ module ActiveRecord
|
|
433
473
|
# person.pets.delete_all
|
434
474
|
#
|
435
475
|
# Pet.find(1, 2, 3)
|
436
|
-
# # => ActiveRecord::RecordNotFound
|
476
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
437
477
|
def delete_all(dependent = nil)
|
438
478
|
@association.delete_all(dependent)
|
439
479
|
end
|
@@ -465,15 +505,16 @@ module ActiveRecord
|
|
465
505
|
@association.destroy_all
|
466
506
|
end
|
467
507
|
|
468
|
-
# Deletes the +records+ supplied
|
469
|
-
#
|
470
|
-
#
|
508
|
+
# Deletes the +records+ supplied from the collection according to the strategy
|
509
|
+
# specified by the +:dependent+ option. If no +:dependent+ option is given,
|
510
|
+
# then it will follow the default strategy. Returns an array with the
|
471
511
|
# deleted records.
|
472
512
|
#
|
473
|
-
#
|
474
|
-
#
|
475
|
-
#
|
476
|
-
# strategy is
|
513
|
+
# For <tt>has_many :through</tt> associations, the default deletion strategy is
|
514
|
+
# +:delete_all+.
|
515
|
+
#
|
516
|
+
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
517
|
+
# This sets the foreign keys to +NULL+.
|
477
518
|
#
|
478
519
|
# class Person < ActiveRecord::Base
|
479
520
|
# has_many :pets # dependent: :nullify option by default
|
@@ -526,7 +567,7 @@ module ActiveRecord
|
|
526
567
|
# # => [#<Pet id: 2, name: "Spook", person_id: 1>]
|
527
568
|
#
|
528
569
|
# Pet.find(1, 3)
|
529
|
-
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
570
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
|
530
571
|
#
|
531
572
|
# If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
|
532
573
|
# *without* calling their +destroy+ method.
|
@@ -554,9 +595,9 @@ module ActiveRecord
|
|
554
595
|
# # ]
|
555
596
|
#
|
556
597
|
# Pet.find(1)
|
557
|
-
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
|
598
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
|
558
599
|
#
|
559
|
-
# You can pass +
|
600
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
560
601
|
# responding to the +id+ and executes delete on them.
|
561
602
|
#
|
562
603
|
# class Person < ActiveRecord::Base
|
@@ -618,9 +659,9 @@ module ActiveRecord
|
|
618
659
|
# person.pets.size # => 0
|
619
660
|
# person.pets # => []
|
620
661
|
#
|
621
|
-
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
662
|
+
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
622
663
|
#
|
623
|
-
# You can pass +
|
664
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
624
665
|
# responding to the +id+ and then deletes them from the database.
|
625
666
|
#
|
626
667
|
# person.pets.size # => 3
|
@@ -650,7 +691,7 @@ module ActiveRecord
|
|
650
691
|
# person.pets.size # => 0
|
651
692
|
# person.pets # => []
|
652
693
|
#
|
653
|
-
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with
|
694
|
+
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
|
654
695
|
def destroy(*records)
|
655
696
|
@association.destroy(*records)
|
656
697
|
end
|
@@ -687,10 +728,8 @@ module ActiveRecord
|
|
687
728
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
688
729
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
689
730
|
# # ]
|
690
|
-
def count(column_name = nil
|
691
|
-
|
692
|
-
# activerecord-deprecated_finders.
|
693
|
-
@association.count(column_name, options)
|
731
|
+
def count(column_name = nil)
|
732
|
+
@association.count(column_name)
|
694
733
|
end
|
695
734
|
|
696
735
|
# Returns the size of the collection. If the collection hasn't been loaded,
|
@@ -777,7 +816,7 @@ module ActiveRecord
|
|
777
816
|
# person.pets.any? # => false
|
778
817
|
#
|
779
818
|
# person.pets << Pet.new(name: 'Snoop')
|
780
|
-
# person.pets.count # =>
|
819
|
+
# person.pets.count # => 1
|
781
820
|
# person.pets.any? # => true
|
782
821
|
#
|
783
822
|
# You can also pass a +block+ to define criteria. The behavior
|
@@ -852,7 +891,7 @@ module ActiveRecord
|
|
852
891
|
!!@association.include?(record)
|
853
892
|
end
|
854
893
|
|
855
|
-
def arel
|
894
|
+
def arel #:nodoc:
|
856
895
|
scope.arel
|
857
896
|
end
|
858
897
|
|
@@ -940,6 +979,10 @@ module ActiveRecord
|
|
940
979
|
end
|
941
980
|
alias_method :to_a, :to_ary
|
942
981
|
|
982
|
+
def records # :nodoc:
|
983
|
+
load_target
|
984
|
+
end
|
985
|
+
|
943
986
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
944
987
|
# to the association's primary key. Returns +self+, so several appends may be
|
945
988
|
# chained together.
|
@@ -967,12 +1010,15 @@ module ActiveRecord
|
|
967
1010
|
alias_method :append, :<<
|
968
1011
|
|
969
1012
|
def prepend(*args)
|
970
|
-
raise NoMethodError, "prepend on association is not defined. Please use
|
1013
|
+
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
971
1014
|
end
|
972
1015
|
|
973
1016
|
# Equivalent to +delete_all+. The difference is that returns +self+, instead
|
974
1017
|
# of an array with the deleted objects, so methods can be chained. See
|
975
1018
|
# +delete_all+ for more information.
|
1019
|
+
# Note that because +delete_all+ removes records by directly
|
1020
|
+
# running an SQL query into the database, the +updated_at+ column of
|
1021
|
+
# the object is not changed.
|
976
1022
|
def clear
|
977
1023
|
delete_all
|
978
1024
|
self
|
@@ -6,6 +6,7 @@ module ActiveRecord
|
|
6
6
|
# If the association has a <tt>:through</tt> option further specialization
|
7
7
|
# is provided by its child HasManyThroughAssociation.
|
8
8
|
class HasManyAssociation < CollectionAssociation #:nodoc:
|
9
|
+
include ForeignAssociation
|
9
10
|
|
10
11
|
def handle_dependency
|
11
12
|
case options[:dependent]
|
@@ -14,9 +15,16 @@ module ActiveRecord
|
|
14
15
|
|
15
16
|
when :restrict_with_error
|
16
17
|
unless empty?
|
17
|
-
record =
|
18
|
-
owner.errors.
|
19
|
-
|
18
|
+
record = owner.class.human_attribute_name(reflection.name).downcase
|
19
|
+
message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.many', record: record, raise: true) rescue nil
|
20
|
+
if message
|
21
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
22
|
+
The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1.
|
23
|
+
Please use `:'restrict_dependent_destroy.has_many'` instead.
|
24
|
+
MESSAGE
|
25
|
+
end
|
26
|
+
owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
|
27
|
+
throw(:abort)
|
20
28
|
end
|
21
29
|
|
22
30
|
else
|
@@ -42,7 +50,7 @@ module ActiveRecord
|
|
42
50
|
end
|
43
51
|
|
44
52
|
def empty?
|
45
|
-
if has_cached_counter?
|
53
|
+
if reflection.has_cached_counter?
|
46
54
|
size.zero?
|
47
55
|
else
|
48
56
|
super
|
@@ -65,8 +73,8 @@ module ActiveRecord
|
|
65
73
|
# If the collection is empty the target is set to an empty array and
|
66
74
|
# the loaded flag is set to true as well.
|
67
75
|
def count_records
|
68
|
-
count = if has_cached_counter?
|
69
|
-
owner._read_attribute
|
76
|
+
count = if reflection.has_cached_counter?
|
77
|
+
owner._read_attribute reflection.counter_cache_column
|
70
78
|
else
|
71
79
|
scope.count
|
72
80
|
end
|
@@ -79,56 +87,20 @@ module ActiveRecord
|
|
79
87
|
[association_scope.limit_value, count].compact.min
|
80
88
|
end
|
81
89
|
|
82
|
-
def has_cached_counter?(reflection = reflection())
|
83
|
-
owner.attribute_present?(cached_counter_attribute_name(reflection))
|
84
|
-
end
|
85
|
-
|
86
|
-
def cached_counter_attribute_name(reflection = reflection())
|
87
|
-
options[:counter_cache] || "#{reflection.name}_count"
|
88
|
-
end
|
89
|
-
|
90
90
|
def update_counter(difference, reflection = reflection())
|
91
|
-
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
def update_counter_in_database(difference, reflection = reflection())
|
96
|
-
if has_cached_counter?(reflection)
|
97
|
-
counter = cached_counter_attribute_name(reflection)
|
98
|
-
owner.class.update_counters(owner.id, counter => difference)
|
91
|
+
if reflection.has_cached_counter?
|
92
|
+
owner.increment!(reflection.counter_cache_column, difference)
|
99
93
|
end
|
100
94
|
end
|
101
95
|
|
102
96
|
def update_counter_in_memory(difference, reflection = reflection())
|
103
|
-
if
|
104
|
-
counter =
|
105
|
-
owner
|
106
|
-
owner.send(:
|
97
|
+
if reflection.counter_must_be_updated_by_has_many?
|
98
|
+
counter = reflection.counter_cache_column
|
99
|
+
owner.increment(counter, difference)
|
100
|
+
owner.send(:clear_attribute_change, counter) # eww
|
107
101
|
end
|
108
102
|
end
|
109
103
|
|
110
|
-
# This shit is nasty. We need to avoid the following situation:
|
111
|
-
#
|
112
|
-
# * An associated record is deleted via record.destroy
|
113
|
-
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
114
|
-
# :counter_cache options which points back at our owner. So they update the
|
115
|
-
# counter cache.
|
116
|
-
# * In which case, we must make sure to *not* update the counter cache, or else
|
117
|
-
# it will be decremented twice.
|
118
|
-
#
|
119
|
-
# Hence this method.
|
120
|
-
def inverse_updates_counter_cache?(reflection = reflection())
|
121
|
-
counter_name = cached_counter_attribute_name(reflection)
|
122
|
-
inverse_updates_counter_named?(counter_name, reflection)
|
123
|
-
end
|
124
|
-
|
125
|
-
def inverse_updates_counter_named?(counter_name, reflection = reflection())
|
126
|
-
reflection.klass._reflections.values.any? { |inverse_reflection|
|
127
|
-
inverse_reflection.belongs_to? &&
|
128
|
-
inverse_reflection.counter_cache_column == counter_name
|
129
|
-
}
|
130
|
-
end
|
131
|
-
|
132
104
|
def delete_count(method, scope)
|
133
105
|
if method == :delete_all
|
134
106
|
scope.delete_all
|
@@ -146,21 +118,13 @@ module ActiveRecord
|
|
146
118
|
def delete_records(records, method)
|
147
119
|
if method == :destroy
|
148
120
|
records.each(&:destroy!)
|
149
|
-
update_counter(-records.length) unless inverse_updates_counter_cache?
|
121
|
+
update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
|
150
122
|
else
|
151
123
|
scope = self.scope.where(reflection.klass.primary_key => records)
|
152
124
|
update_counter(-delete_count(method, scope))
|
153
125
|
end
|
154
126
|
end
|
155
127
|
|
156
|
-
def foreign_key_present?
|
157
|
-
if reflection.klass.primary_key
|
158
|
-
owner.attribute_present?(reflection.association_primary_key)
|
159
|
-
else
|
160
|
-
false
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
128
|
def concat_records(records, *)
|
165
129
|
update_counter_if_success(super, records.length)
|
166
130
|
end
|