activerecord 5.0.7 → 5.1.7
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 +5 -5
- data/CHANGELOG.md +657 -2080
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations/alias_tracker.rb +10 -11
- data/lib/active_record/associations/association.rb +23 -5
- data/lib/active_record/associations/association_scope.rb +95 -81
- data/lib/active_record/associations/belongs_to_association.rb +7 -4
- data/lib/active_record/associations/builder/belongs_to.rb +30 -16
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +36 -205
- data/lib/active_record/associations/collection_proxy.rb +132 -63
- data/lib/active_record/associations/has_many_association.rb +10 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -4
- data/lib/active_record/associations/has_one_association.rb +24 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +121 -118
- data/lib/active_record/associations/preloader/association.rb +64 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +41 -41
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +2 -5
- data/lib/active_record/associations.rb +1591 -1562
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +229 -46
- data/lib/active_record/attribute_methods/primary_key.rb +74 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +30 -33
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_mutation_tracker.rb +63 -11
- data/lib/active_record/attribute_set/builder.rb +27 -33
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attributes.rb +22 -22
- data/lib/active_record/autosave_association.rb +18 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +56 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +3 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
- data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
- data/lib/active_record/connection_adapters/column.rb +26 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +109 -93
- data/lib/active_record/counter_cache.rb +60 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +64 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +69 -74
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +23 -28
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +100 -47
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/migration.rb +153 -155
- data/lib/active_record/model_schema.rb +94 -107
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +11 -34
- data/lib/active_record/persistence.rb +65 -50
- data/lib/active_record/query_cache.rb +2 -6
- data/lib/active_record/querying.rb +3 -4
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +105 -133
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +154 -108
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/batches.rb +80 -51
- data/lib/active_record/relation/calculations.rb +169 -162
- data/lib/active_record/relation/delegation.rb +32 -31
- data/lib/active_record/relation/finder_methods.rb +197 -231
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +255 -293
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +80 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/relation.rb +93 -119
- data/lib/active_record/result.rb +41 -32
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +176 -192
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +15 -38
- data/lib/active_record/schema_migration.rb +8 -4
- data/lib/active_record/scoping/default.rb +90 -90
- data/lib/active_record/scoping/named.rb +11 -11
- data/lib/active_record/scoping.rb +6 -6
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +65 -55
- data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
- data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +46 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +97 -109
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +13 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/internal/abstract_json.rb +4 -0
- data/lib/active_record/type/serialized.rb +14 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +20 -20
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/migration.rb +1 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- data/lib/rails/generators/active_record.rb +4 -4
- metadata +24 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -10,9 +10,9 @@ module ActiveRecord
|
|
10
10
|
# HasManyAssociation => has_many
|
11
11
|
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
12
12
|
#
|
13
|
-
# CollectionAssociation class provides common methods to the collections
|
13
|
+
# The CollectionAssociation class provides common methods to the collections
|
14
14
|
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
15
|
-
# +:through association+ option.
|
15
|
+
# the +:through association+ option.
|
16
16
|
#
|
17
17
|
# You need to be careful with assumptions regarding the target: The proxy
|
18
18
|
# does not fetch records from the database until it needs them, but new
|
@@ -24,18 +24,9 @@ module ActiveRecord
|
|
24
24
|
# If you need to work on all current children, new and existing records,
|
25
25
|
# +load_target+ and the +loaded+ flag are your friends.
|
26
26
|
class CollectionAssociation < Association #:nodoc:
|
27
|
-
|
28
27
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
29
|
-
def reader
|
30
|
-
if
|
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
|
-
|
37
|
-
klass.uncached { reload }
|
38
|
-
elsif stale_target?
|
28
|
+
def reader
|
29
|
+
if stale_target?
|
39
30
|
reload
|
40
31
|
end
|
41
32
|
|
@@ -51,14 +42,9 @@ module ActiveRecord
|
|
51
42
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
52
43
|
def ids_reader
|
53
44
|
if loaded?
|
54
|
-
|
55
|
-
record.send(reflection.association_primary_key)
|
56
|
-
end
|
45
|
+
target.pluck(reflection.association_primary_key)
|
57
46
|
else
|
58
|
-
@association_ids ||= (
|
59
|
-
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
|
60
|
-
scope.pluck(column)
|
61
|
-
)
|
47
|
+
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
62
48
|
end
|
63
49
|
end
|
64
50
|
|
@@ -85,14 +71,6 @@ module ActiveRecord
|
|
85
71
|
@target = []
|
86
72
|
end
|
87
73
|
|
88
|
-
def select(*fields)
|
89
|
-
if block_given?
|
90
|
-
load_target.select.each { |e| yield e }
|
91
|
-
else
|
92
|
-
scope.select(*fields)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
74
|
def find(*args)
|
97
75
|
if block_given?
|
98
76
|
load_target.find(*args) { |*block_args| yield(*block_args) }
|
@@ -114,52 +92,6 @@ module ActiveRecord
|
|
114
92
|
end
|
115
93
|
end
|
116
94
|
|
117
|
-
def first(*args)
|
118
|
-
first_nth_or_last(:first, *args)
|
119
|
-
end
|
120
|
-
|
121
|
-
def second(*args)
|
122
|
-
first_nth_or_last(:second, *args)
|
123
|
-
end
|
124
|
-
|
125
|
-
def third(*args)
|
126
|
-
first_nth_or_last(:third, *args)
|
127
|
-
end
|
128
|
-
|
129
|
-
def fourth(*args)
|
130
|
-
first_nth_or_last(:fourth, *args)
|
131
|
-
end
|
132
|
-
|
133
|
-
def fifth(*args)
|
134
|
-
first_nth_or_last(:fifth, *args)
|
135
|
-
end
|
136
|
-
|
137
|
-
def forty_two(*args)
|
138
|
-
first_nth_or_last(:forty_two, *args)
|
139
|
-
end
|
140
|
-
|
141
|
-
def third_to_last(*args)
|
142
|
-
first_nth_or_last(:third_to_last, *args)
|
143
|
-
end
|
144
|
-
|
145
|
-
def second_to_last(*args)
|
146
|
-
first_nth_or_last(:second_to_last, *args)
|
147
|
-
end
|
148
|
-
|
149
|
-
def last(*args)
|
150
|
-
first_nth_or_last(:last, *args)
|
151
|
-
end
|
152
|
-
|
153
|
-
def take(n = nil)
|
154
|
-
if loaded?
|
155
|
-
n ? target.take(n) : target.first
|
156
|
-
else
|
157
|
-
scope.take(n).tap do |record|
|
158
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
95
|
def build(attributes = {}, &block)
|
164
96
|
if attributes.is_a?(Array)
|
165
97
|
attributes.collect { |attr| build(attr, &block) }
|
@@ -170,14 +102,6 @@ module ActiveRecord
|
|
170
102
|
end
|
171
103
|
end
|
172
104
|
|
173
|
-
def create(attributes = {}, &block)
|
174
|
-
_create_record(attributes, &block)
|
175
|
-
end
|
176
|
-
|
177
|
-
def create!(attributes = {}, &block)
|
178
|
-
_create_record(attributes, true, &block)
|
179
|
-
end
|
180
|
-
|
181
105
|
# Add +records+ to this association. Returns +self+ so method calls may
|
182
106
|
# be chained. Since << flattens its argument list and inserts each record,
|
183
107
|
# +push+ and +concat+ behave identically.
|
@@ -225,12 +149,12 @@ module ActiveRecord
|
|
225
149
|
end
|
226
150
|
|
227
151
|
dependent = if dependent
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
152
|
+
dependent
|
153
|
+
elsif options[:dependent] == :destroy
|
154
|
+
:delete_all
|
155
|
+
else
|
156
|
+
options[:dependent]
|
157
|
+
end
|
234
158
|
|
235
159
|
delete_or_nullify_all_records(dependent).tap do
|
236
160
|
reset
|
@@ -248,28 +172,6 @@ module ActiveRecord
|
|
248
172
|
end
|
249
173
|
end
|
250
174
|
|
251
|
-
# Count all records using SQL. Construct options and pass them with
|
252
|
-
# scope to the target class's +count+.
|
253
|
-
def count(column_name = nil)
|
254
|
-
relation = scope
|
255
|
-
if association_scope.distinct_value
|
256
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
257
|
-
column_name ||= reflection.klass.primary_key
|
258
|
-
relation = relation.distinct
|
259
|
-
end
|
260
|
-
|
261
|
-
value = relation.count(column_name)
|
262
|
-
|
263
|
-
limit = options[:limit]
|
264
|
-
offset = options[:offset]
|
265
|
-
|
266
|
-
if limit || offset
|
267
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
268
|
-
else
|
269
|
-
value
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
175
|
# Removes +records+ from this association calling +before_remove+ and
|
274
176
|
# +after_remove+ callbacks.
|
275
177
|
#
|
@@ -279,11 +181,8 @@ module ActiveRecord
|
|
279
181
|
# +delete_records+. They are in any case removed from the collection.
|
280
182
|
def delete(*records)
|
281
183
|
return if records.empty?
|
282
|
-
_options = records.extract_options!
|
283
|
-
dependent = _options[:dependent] || options[:dependent]
|
284
|
-
|
285
184
|
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
286
|
-
delete_or_destroy(records, dependent)
|
185
|
+
delete_or_destroy(records, options[:dependent])
|
287
186
|
end
|
288
187
|
|
289
188
|
# Deletes the +records+ and removes them from this association calling
|
@@ -309,11 +208,7 @@ module ActiveRecord
|
|
309
208
|
# +count_records+, which is a method descendants have to provide.
|
310
209
|
def size
|
311
210
|
if !find_target? || loaded?
|
312
|
-
|
313
|
-
target.uniq.size
|
314
|
-
else
|
315
|
-
target.size
|
316
|
-
end
|
211
|
+
target.size
|
317
212
|
elsif !association_scope.group_values.empty?
|
318
213
|
load_target.size
|
319
214
|
elsif !association_scope.distinct_value && target.is_a?(Array)
|
@@ -324,15 +219,6 @@ module ActiveRecord
|
|
324
219
|
end
|
325
220
|
end
|
326
221
|
|
327
|
-
# Returns the size of the collection calling +size+ on the target.
|
328
|
-
#
|
329
|
-
# If the collection has been already loaded +length+ and +size+ are
|
330
|
-
# equivalent. If not and you are going to need the records anyway this
|
331
|
-
# method will take one less query. Otherwise +size+ is more efficient.
|
332
|
-
def length
|
333
|
-
load_target.size
|
334
|
-
end
|
335
|
-
|
336
222
|
# Returns true if the collection is empty.
|
337
223
|
#
|
338
224
|
# If the collection has been loaded
|
@@ -349,36 +235,6 @@ module ActiveRecord
|
|
349
235
|
end
|
350
236
|
end
|
351
237
|
|
352
|
-
# Returns true if the collections is not empty.
|
353
|
-
# If block given, loads all records and checks for one or more matches.
|
354
|
-
# Otherwise, equivalent to +!collection.empty?+.
|
355
|
-
def any?
|
356
|
-
if block_given?
|
357
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
358
|
-
else
|
359
|
-
!empty?
|
360
|
-
end
|
361
|
-
end
|
362
|
-
|
363
|
-
# Returns true if the collection has more than 1 record.
|
364
|
-
# If block given, loads all records and checks for two or more matches.
|
365
|
-
# Otherwise, equivalent to +collection.size > 1+.
|
366
|
-
def many?
|
367
|
-
if block_given?
|
368
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
369
|
-
else
|
370
|
-
size > 1
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
def distinct
|
375
|
-
seen = {}
|
376
|
-
load_target.find_all do |record|
|
377
|
-
seen[record.id] = true unless seen.key?(record.id)
|
378
|
-
end
|
379
|
-
end
|
380
|
-
alias uniq distinct
|
381
|
-
|
382
238
|
# Replace this collection with +other_array+. This will perform a diff
|
383
239
|
# and delete/add only records that have changed.
|
384
240
|
def replace(other_array)
|
@@ -435,24 +291,27 @@ module ActiveRecord
|
|
435
291
|
owner.new_record? && !foreign_key_present?
|
436
292
|
end
|
437
293
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
conn = klass.connection
|
443
|
-
sc = reflection.association_scope_cache(conn, owner) do
|
444
|
-
StatementCache.create(conn) { |params|
|
445
|
-
as = AssociationScope.create { params.bind }
|
446
|
-
target_scope.merge as.scope(self, conn)
|
447
|
-
}
|
448
|
-
end
|
449
|
-
|
450
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
451
|
-
sc.execute(binds, klass, klass.connection, &block)
|
294
|
+
def find_from_target?
|
295
|
+
loaded? ||
|
296
|
+
owner.new_record? ||
|
297
|
+
target.any? { |record| record.new_record? || record.changed? }
|
452
298
|
end
|
453
299
|
|
300
|
+
private
|
301
|
+
|
454
302
|
def find_target
|
455
|
-
|
303
|
+
return scope.to_a if skip_statement_cache?
|
304
|
+
|
305
|
+
conn = klass.connection
|
306
|
+
sc = reflection.association_scope_cache(conn, owner) do
|
307
|
+
StatementCache.create(conn) { |params|
|
308
|
+
as = AssociationScope.create { params.bind }
|
309
|
+
target_scope.merge as.scope(self, conn)
|
310
|
+
}
|
311
|
+
end
|
312
|
+
|
313
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
314
|
+
sc.execute(binds, klass, conn) do |record|
|
456
315
|
set_inverse_instance(record)
|
457
316
|
end
|
458
317
|
end
|
@@ -474,7 +333,7 @@ module ActiveRecord
|
|
474
333
|
persisted.map! do |record|
|
475
334
|
if mem_record = memory.delete(record)
|
476
335
|
|
477
|
-
((record.attribute_names & mem_record.attribute_names) - mem_record.
|
336
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
478
337
|
mem_record[name] = record[name]
|
479
338
|
end
|
480
339
|
|
@@ -538,8 +397,9 @@ module ActiveRecord
|
|
538
397
|
records.each { |record| callback(:after_remove, record) }
|
539
398
|
end
|
540
399
|
|
541
|
-
# Delete the given records from the association,
|
542
|
-
#
|
400
|
+
# Delete the given records from the association,
|
401
|
+
# using one of the methods +:destroy+, +:delete_all+
|
402
|
+
# or +:nullify+ (or +nil+, in which case a default is used).
|
543
403
|
def delete_records(records, method)
|
544
404
|
raise NotImplementedError
|
545
405
|
end
|
@@ -610,25 +470,6 @@ module ActiveRecord
|
|
610
470
|
owner.class.send(full_callback_name)
|
611
471
|
end
|
612
472
|
|
613
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
614
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
615
|
-
#
|
616
|
-
# If the args is just a non-empty options hash, go to the database.
|
617
|
-
#
|
618
|
-
# Otherwise, go to the database only if none of the following are true:
|
619
|
-
# * target already loaded
|
620
|
-
# * owner is new record
|
621
|
-
# * target contains new or changed record(s)
|
622
|
-
def fetch_first_nth_or_last_using_find?(args)
|
623
|
-
if args.first.is_a?(Hash)
|
624
|
-
true
|
625
|
-
else
|
626
|
-
!(loaded? ||
|
627
|
-
owner.new_record? ||
|
628
|
-
target.any? { |record| record.new_record? || record.changed? })
|
629
|
-
end
|
630
|
-
end
|
631
|
-
|
632
473
|
def include_in_memory?(record)
|
633
474
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
634
475
|
assoc = owner.association(reflection.through_reflection.name)
|
@@ -655,16 +496,6 @@ module ActiveRecord
|
|
655
496
|
load_target.select { |r| ids.include?(r.id.to_s) }
|
656
497
|
end
|
657
498
|
end
|
658
|
-
|
659
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
660
|
-
def first_nth_or_last(type, *args)
|
661
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
662
|
-
|
663
|
-
collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
|
664
|
-
collection.send(type, *args).tap do |record|
|
665
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
666
|
-
end
|
667
|
-
end
|
668
499
|
end
|
669
500
|
end
|
670
501
|
end
|
@@ -53,6 +53,12 @@ module ActiveRecord
|
|
53
53
|
@association.loaded?
|
54
54
|
end
|
55
55
|
|
56
|
+
##
|
57
|
+
# :method: select
|
58
|
+
#
|
59
|
+
# :call-seq:
|
60
|
+
# select(*fields, &block)
|
61
|
+
#
|
56
62
|
# Works in two ways.
|
57
63
|
#
|
58
64
|
# *First:* Specify a subset of fields to be selected from the result set.
|
@@ -75,7 +81,7 @@ module ActiveRecord
|
|
75
81
|
# # #<Pet id: nil, name: "Choo-Choo">
|
76
82
|
# # ]
|
77
83
|
#
|
78
|
-
# person.pets.select(:id, :name
|
84
|
+
# person.pets.select(:id, :name)
|
79
85
|
# # => [
|
80
86
|
# # #<Pet id: 1, name: "Fancy-Fancy">,
|
81
87
|
# # #<Pet id: 2, name: "Spook">,
|
@@ -100,15 +106,6 @@ module ActiveRecord
|
|
100
106
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
101
107
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
102
108
|
# # ]
|
103
|
-
#
|
104
|
-
# person.pets.select(:name) { |pet| pet.name =~ /oo/ }
|
105
|
-
# # => [
|
106
|
-
# # #<Pet id: 2, name: "Spook">,
|
107
|
-
# # #<Pet id: 3, name: "Choo-Choo">
|
108
|
-
# # ]
|
109
|
-
def select(*fields, &block)
|
110
|
-
@association.select(*fields, &block)
|
111
|
-
end
|
112
109
|
|
113
110
|
# Finds an object in the collection responding to the +id+. Uses the same
|
114
111
|
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
|
@@ -140,6 +137,12 @@ module ActiveRecord
|
|
140
137
|
@association.find(*args, &block)
|
141
138
|
end
|
142
139
|
|
140
|
+
##
|
141
|
+
# :method: first
|
142
|
+
#
|
143
|
+
# :call-seq:
|
144
|
+
# first(limit = nil)
|
145
|
+
#
|
143
146
|
# Returns the first record, or the first +n+ records, from the collection.
|
144
147
|
# If the collection is empty, the first form returns +nil+, and the second
|
145
148
|
# form returns an empty array.
|
@@ -166,45 +169,63 @@ module ActiveRecord
|
|
166
169
|
# another_person_without.pets # => []
|
167
170
|
# another_person_without.pets.first # => nil
|
168
171
|
# another_person_without.pets.first(3) # => []
|
169
|
-
def first(*args)
|
170
|
-
@association.first(*args)
|
171
|
-
end
|
172
172
|
|
173
|
+
##
|
174
|
+
# :method: second
|
175
|
+
#
|
176
|
+
# :call-seq:
|
177
|
+
# second()
|
178
|
+
#
|
173
179
|
# Same as #first except returns only the second record.
|
174
|
-
def second(*args)
|
175
|
-
@association.second(*args)
|
176
|
-
end
|
177
180
|
|
181
|
+
##
|
182
|
+
# :method: third
|
183
|
+
#
|
184
|
+
# :call-seq:
|
185
|
+
# third()
|
186
|
+
#
|
178
187
|
# Same as #first except returns only the third record.
|
179
|
-
def third(*args)
|
180
|
-
@association.third(*args)
|
181
|
-
end
|
182
188
|
|
189
|
+
##
|
190
|
+
# :method: fourth
|
191
|
+
#
|
192
|
+
# :call-seq:
|
193
|
+
# fourth()
|
194
|
+
#
|
183
195
|
# Same as #first except returns only the fourth record.
|
184
|
-
def fourth(*args)
|
185
|
-
@association.fourth(*args)
|
186
|
-
end
|
187
196
|
|
197
|
+
##
|
198
|
+
# :method: fifth
|
199
|
+
#
|
200
|
+
# :call-seq:
|
201
|
+
# fifth()
|
202
|
+
#
|
188
203
|
# Same as #first except returns only the fifth record.
|
189
|
-
def fifth(*args)
|
190
|
-
@association.fifth(*args)
|
191
|
-
end
|
192
204
|
|
205
|
+
##
|
206
|
+
# :method: forty_two
|
207
|
+
#
|
208
|
+
# :call-seq:
|
209
|
+
# forty_two()
|
210
|
+
#
|
193
211
|
# Same as #first except returns only the forty second record.
|
194
212
|
# Also known as accessing "the reddit".
|
195
|
-
def forty_two(*args)
|
196
|
-
@association.forty_two(*args)
|
197
|
-
end
|
198
213
|
|
214
|
+
##
|
215
|
+
# :method: third_to_last
|
216
|
+
#
|
217
|
+
# :call-seq:
|
218
|
+
# third_to_last()
|
219
|
+
#
|
199
220
|
# Same as #first except returns only the third-to-last record.
|
200
|
-
def third_to_last(*args)
|
201
|
-
@association.third_to_last(*args)
|
202
|
-
end
|
203
221
|
|
222
|
+
##
|
223
|
+
# :method: second_to_last
|
224
|
+
#
|
225
|
+
# :call-seq:
|
226
|
+
# second_to_last()
|
227
|
+
#
|
204
228
|
# Same as #first except returns only the second-to-last record.
|
205
|
-
def second_to_last(*args)
|
206
|
-
@association.second_to_last(*args)
|
207
|
-
end
|
208
229
|
|
209
230
|
# Returns the last record, or the last +n+ records, from the collection.
|
210
231
|
# If the collection is empty, the first form returns +nil+, and the second
|
@@ -232,8 +253,9 @@ module ActiveRecord
|
|
232
253
|
# another_person_without.pets # => []
|
233
254
|
# another_person_without.pets.last # => nil
|
234
255
|
# another_person_without.pets.last(3) # => []
|
235
|
-
def last(
|
236
|
-
|
256
|
+
def last(limit = nil)
|
257
|
+
load_target if find_from_target?
|
258
|
+
super
|
237
259
|
end
|
238
260
|
|
239
261
|
# Gives a record (or N records if a parameter is supplied) from the collection
|
@@ -261,8 +283,9 @@ module ActiveRecord
|
|
261
283
|
# another_person_without.pets # => []
|
262
284
|
# another_person_without.pets.take # => nil
|
263
285
|
# another_person_without.pets.take(2) # => []
|
264
|
-
def take(
|
265
|
-
|
286
|
+
def take(limit = nil)
|
287
|
+
load_target if find_from_target?
|
288
|
+
super
|
266
289
|
end
|
267
290
|
|
268
291
|
# Returns a new object of the collection type that has been instantiated
|
@@ -695,6 +718,12 @@ module ActiveRecord
|
|
695
718
|
@association.destroy(*records)
|
696
719
|
end
|
697
720
|
|
721
|
+
##
|
722
|
+
# :method: distinct
|
723
|
+
#
|
724
|
+
# :call-seq:
|
725
|
+
# distinct(value = true)
|
726
|
+
#
|
698
727
|
# Specifies whether the records should be unique or not.
|
699
728
|
#
|
700
729
|
# class Person < ActiveRecord::Base
|
@@ -709,17 +738,39 @@ module ActiveRecord
|
|
709
738
|
#
|
710
739
|
# person.pets.select(:name).distinct
|
711
740
|
# # => [#<Pet name: "Fancy-Fancy">]
|
712
|
-
|
713
|
-
|
741
|
+
#
|
742
|
+
# person.pets.select(:name).distinct.distinct(false)
|
743
|
+
# # => [
|
744
|
+
# # #<Pet name: "Fancy-Fancy">,
|
745
|
+
# # #<Pet name: "Fancy-Fancy">
|
746
|
+
# # ]
|
747
|
+
|
748
|
+
#--
|
749
|
+
def uniq
|
750
|
+
load_target.uniq
|
751
|
+
end
|
752
|
+
|
753
|
+
def calculate(operation, column_name)
|
754
|
+
null_scope? ? scope.calculate(operation, column_name) : super
|
755
|
+
end
|
756
|
+
|
757
|
+
def pluck(*column_names)
|
758
|
+
null_scope? ? scope.pluck(*column_names) : super
|
714
759
|
end
|
715
|
-
alias uniq distinct
|
716
760
|
|
717
|
-
|
761
|
+
##
|
762
|
+
# :method: count
|
763
|
+
#
|
764
|
+
# :call-seq:
|
765
|
+
# count(column_name = nil, &block)
|
766
|
+
#
|
767
|
+
# Count all records.
|
718
768
|
#
|
719
769
|
# class Person < ActiveRecord::Base
|
720
770
|
# has_many :pets
|
721
771
|
# end
|
722
772
|
#
|
773
|
+
# # This will perform the count using SQL.
|
723
774
|
# person.pets.count # => 3
|
724
775
|
# person.pets
|
725
776
|
# # => [
|
@@ -727,17 +778,11 @@ module ActiveRecord
|
|
727
778
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
728
779
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
729
780
|
# # ]
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
null_scope? ? scope.calculate(operation, column_name) : super
|
736
|
-
end
|
737
|
-
|
738
|
-
def pluck(*column_names)
|
739
|
-
null_scope? ? scope.pluck(*column_names) : super
|
740
|
-
end
|
781
|
+
#
|
782
|
+
# Passing a block will select all of a person's pets in SQL and then
|
783
|
+
# perform the count using Ruby.
|
784
|
+
#
|
785
|
+
# person.pets.count { |pet| pet.name.include?('-') } # => 2
|
741
786
|
|
742
787
|
# Returns the size of the collection. If the collection hasn't been loaded,
|
743
788
|
# it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
|
@@ -767,6 +812,12 @@ module ActiveRecord
|
|
767
812
|
@association.size
|
768
813
|
end
|
769
814
|
|
815
|
+
##
|
816
|
+
# :method: length
|
817
|
+
#
|
818
|
+
# :call-seq:
|
819
|
+
# length()
|
820
|
+
#
|
770
821
|
# Returns the size of the collection calling +size+ on the target.
|
771
822
|
# If the collection has been already loaded, +length+ and +size+ are
|
772
823
|
# equivalent. If not and you are going to need the records anyway this
|
@@ -787,9 +838,6 @@ module ActiveRecord
|
|
787
838
|
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
788
839
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
789
840
|
# # ]
|
790
|
-
def length
|
791
|
-
@association.length
|
792
|
-
end
|
793
841
|
|
794
842
|
# Returns +true+ if the collection is empty. If the collection has been
|
795
843
|
# loaded it is equivalent
|
@@ -813,6 +861,12 @@ module ActiveRecord
|
|
813
861
|
@association.empty?
|
814
862
|
end
|
815
863
|
|
864
|
+
##
|
865
|
+
# :method: any?
|
866
|
+
#
|
867
|
+
# :call-seq:
|
868
|
+
# any?()
|
869
|
+
#
|
816
870
|
# Returns +true+ if the collection is not empty.
|
817
871
|
#
|
818
872
|
# class Person < ActiveRecord::Base
|
@@ -842,10 +896,13 @@ module ActiveRecord
|
|
842
896
|
# pet.group == 'dogs'
|
843
897
|
# end
|
844
898
|
# # => true
|
845
|
-
def any?(&block)
|
846
|
-
@association.any?(&block)
|
847
|
-
end
|
848
899
|
|
900
|
+
##
|
901
|
+
# :method: many?
|
902
|
+
#
|
903
|
+
# :call-seq:
|
904
|
+
# many?()
|
905
|
+
#
|
849
906
|
# Returns true if the collection has more than one record.
|
850
907
|
# Equivalent to <tt>collection.size > 1</tt>.
|
851
908
|
#
|
@@ -880,9 +937,6 @@ module ActiveRecord
|
|
880
937
|
# pet.group == 'cats'
|
881
938
|
# end
|
882
939
|
# # => true
|
883
|
-
def many?(&block)
|
884
|
-
@association.many?(&block)
|
885
|
-
end
|
886
940
|
|
887
941
|
# Returns +true+ if the given +record+ is present in the collection.
|
888
942
|
#
|
@@ -1064,6 +1118,7 @@ module ActiveRecord
|
|
1064
1118
|
end
|
1065
1119
|
|
1066
1120
|
def reset_scope # :nodoc:
|
1121
|
+
@offsets = {}
|
1067
1122
|
@scope = nil
|
1068
1123
|
self
|
1069
1124
|
end
|
@@ -1073,16 +1128,30 @@ module ActiveRecord
|
|
1073
1128
|
SpawnMethods,
|
1074
1129
|
].flat_map { |klass|
|
1075
1130
|
klass.public_instance_methods(false)
|
1076
|
-
} - self.public_instance_methods(false) + [:scoping]
|
1131
|
+
} - self.public_instance_methods(false) - [:select] + [:scoping]
|
1077
1132
|
|
1078
1133
|
delegate(*delegate_methods, to: :scope)
|
1079
1134
|
|
1080
1135
|
private
|
1081
1136
|
|
1137
|
+
def find_nth_with_limit(index, limit)
|
1138
|
+
load_target if find_from_target?
|
1139
|
+
super
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
def find_nth_from_last(index)
|
1143
|
+
load_target if find_from_target?
|
1144
|
+
super
|
1145
|
+
end
|
1146
|
+
|
1082
1147
|
def null_scope?
|
1083
1148
|
@association.null_scope?
|
1084
1149
|
end
|
1085
1150
|
|
1151
|
+
def find_from_target?
|
1152
|
+
@association.find_from_target?
|
1153
|
+
end
|
1154
|
+
|
1086
1155
|
def exec_queries
|
1087
1156
|
load_target
|
1088
1157
|
end
|