activerecord 5.2.1.1 → 6.0.1
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 +738 -445
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/association_relation.rb +18 -9
- data/lib/active_record/associations.rb +20 -15
- data/lib/active_record/associations/association.rb +69 -20
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +15 -29
- data/lib/active_record/associations/collection_proxy.rb +19 -48
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +11 -10
- data/lib/active_record/associations/has_many_through_association.rb +42 -25
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +28 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +39 -31
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +114 -38
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +27 -13
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +6 -20
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +55 -45
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -77
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
- data/lib/active_record/connection_handling.rb +155 -26
- data/lib/active_record/core.rb +104 -59
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +38 -7
- data/lib/active_record/errors.rb +30 -16
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +91 -64
- data/lib/active_record/model_schema.rb +34 -10
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +233 -28
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +33 -21
- data/lib/active_record/railtie.rb +81 -46
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +42 -44
- data/lib/active_record/relation.rb +320 -70
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +67 -57
- data/lib/active_record/relation/delegation.rb +48 -35
- data/lib/active_record/relation/finder_methods.rb +30 -30
- data/lib/active_record/relation/merger.rb +19 -25
- data/lib/active_record/relation/predicate_builder.rb +18 -15
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +17 -10
- data/lib/active_record/relation/query_methods.rb +236 -73
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +21 -15
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +195 -26
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +58 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +111 -27
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -2,11 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# object, known as the <tt>@target</tt>. The kind of association any proxy is
|
8
|
-
# about is available in <tt>@reflection</tt>. That's an instance of the class
|
9
|
-
# ActiveRecord::Reflection::AssociationReflection.
|
5
|
+
# Collection proxies in Active Record are middlemen between an
|
6
|
+
# <tt>association</tt>, and its <tt>target</tt> result set.
|
10
7
|
#
|
11
8
|
# For example, given
|
12
9
|
#
|
@@ -16,14 +13,14 @@ module ActiveRecord
|
|
16
13
|
#
|
17
14
|
# blog = Blog.first
|
18
15
|
#
|
19
|
-
#
|
20
|
-
# <tt
|
21
|
-
#
|
16
|
+
# The collection proxy returned by <tt>blog.posts</tt> is built from a
|
17
|
+
# <tt>:has_many</tt> <tt>association</tt>, and delegates to a collection
|
18
|
+
# of posts as the <tt>target</tt>.
|
22
19
|
#
|
23
|
-
# This class delegates unknown methods to <tt
|
24
|
-
#
|
20
|
+
# This class delegates unknown methods to the <tt>association</tt>'s
|
21
|
+
# relation class via a delegate cache.
|
25
22
|
#
|
26
|
-
# The <tt
|
23
|
+
# The <tt>target</tt> result set is not loaded until needed. For example,
|
27
24
|
#
|
28
25
|
# blog.posts.count
|
29
26
|
#
|
@@ -366,34 +363,6 @@ module ActiveRecord
|
|
366
363
|
@association.create!(attributes, &block)
|
367
364
|
end
|
368
365
|
|
369
|
-
# Add one or more records to the collection by setting their foreign keys
|
370
|
-
# to the association's primary key. Since #<< flattens its argument list and
|
371
|
-
# inserts each record, +push+ and #concat behave identically. Returns +self+
|
372
|
-
# so method calls may be chained.
|
373
|
-
#
|
374
|
-
# class Person < ActiveRecord::Base
|
375
|
-
# has_many :pets
|
376
|
-
# end
|
377
|
-
#
|
378
|
-
# person.pets.size # => 0
|
379
|
-
# person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
|
380
|
-
# person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
|
381
|
-
# person.pets.size # => 3
|
382
|
-
#
|
383
|
-
# person.id # => 1
|
384
|
-
# person.pets
|
385
|
-
# # => [
|
386
|
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
387
|
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
388
|
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
389
|
-
# # ]
|
390
|
-
#
|
391
|
-
# person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
|
392
|
-
# person.pets.size # => 5
|
393
|
-
def concat(*records)
|
394
|
-
@association.concat(*records)
|
395
|
-
end
|
396
|
-
|
397
366
|
# Replaces this collection with +other_array+. This will perform a diff
|
398
367
|
# and delete/add only records that have changed.
|
399
368
|
#
|
@@ -500,7 +469,7 @@ module ActiveRecord
|
|
500
469
|
# Pet.find(1, 2, 3)
|
501
470
|
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
502
471
|
def delete_all(dependent = nil)
|
503
|
-
@association.delete_all(dependent)
|
472
|
+
@association.delete_all(dependent).tap { reset_scope }
|
504
473
|
end
|
505
474
|
|
506
475
|
# Deletes the records of the collection directly from the database
|
@@ -527,7 +496,7 @@ module ActiveRecord
|
|
527
496
|
#
|
528
497
|
# Pet.find(1) # => Couldn't find Pet with id=1
|
529
498
|
def destroy_all
|
530
|
-
@association.destroy_all
|
499
|
+
@association.destroy_all.tap { reset_scope }
|
531
500
|
end
|
532
501
|
|
533
502
|
# Deletes the +records+ supplied from the collection according to the strategy
|
@@ -646,7 +615,7 @@ module ActiveRecord
|
|
646
615
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
647
616
|
# # ]
|
648
617
|
def delete(*records)
|
649
|
-
@association.delete(*records)
|
618
|
+
@association.delete(*records).tap { reset_scope }
|
650
619
|
end
|
651
620
|
|
652
621
|
# Destroys the +records+ supplied and removes them from the collection.
|
@@ -718,7 +687,7 @@ module ActiveRecord
|
|
718
687
|
#
|
719
688
|
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
|
720
689
|
def destroy(*records)
|
721
|
-
@association.destroy(*records)
|
690
|
+
@association.destroy(*records).tap { reset_scope }
|
722
691
|
end
|
723
692
|
|
724
693
|
##
|
@@ -1033,8 +1002,9 @@ module ActiveRecord
|
|
1033
1002
|
end
|
1034
1003
|
|
1035
1004
|
# Adds one or more +records+ to the collection by setting their foreign keys
|
1036
|
-
# to the association's primary key.
|
1037
|
-
#
|
1005
|
+
# to the association's primary key. Since <tt><<</tt> flattens its argument list and
|
1006
|
+
# inserts each record, +push+ and +concat+ behave identically. Returns +self+
|
1007
|
+
# so several appends may be chained together.
|
1038
1008
|
#
|
1039
1009
|
# class Person < ActiveRecord::Base
|
1040
1010
|
# has_many :pets
|
@@ -1057,8 +1027,9 @@ module ActiveRecord
|
|
1057
1027
|
end
|
1058
1028
|
alias_method :push, :<<
|
1059
1029
|
alias_method :append, :<<
|
1030
|
+
alias_method :concat, :<<
|
1060
1031
|
|
1061
|
-
def prepend(*args)
|
1032
|
+
def prepend(*args) # :nodoc:
|
1062
1033
|
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
1063
1034
|
end
|
1064
1035
|
|
@@ -1088,7 +1059,7 @@ module ActiveRecord
|
|
1088
1059
|
# person.pets.reload # fetches pets from the database
|
1089
1060
|
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1090
1061
|
def reload
|
1091
|
-
proxy_association.reload
|
1062
|
+
proxy_association.reload(true)
|
1092
1063
|
reset_scope
|
1093
1064
|
end
|
1094
1065
|
|
@@ -1125,7 +1096,7 @@ module ActiveRecord
|
|
1125
1096
|
SpawnMethods,
|
1126
1097
|
].flat_map { |klass|
|
1127
1098
|
klass.public_instance_methods(false)
|
1128
|
-
} - self.public_instance_methods(false) - [:select] + [:scoping]
|
1099
|
+
} - self.public_instance_methods(false) - [:select] + [:scoping, :values]
|
1129
1100
|
|
1130
1101
|
delegate(*delegate_methods, to: :scope)
|
1131
1102
|
|
@@ -36,14 +36,6 @@ module ActiveRecord
|
|
36
36
|
super
|
37
37
|
end
|
38
38
|
|
39
|
-
def empty?
|
40
|
-
if reflection.has_cached_counter?
|
41
|
-
size.zero?
|
42
|
-
else
|
43
|
-
super
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
39
|
private
|
48
40
|
|
49
41
|
# Returns the number of records in this collection.
|
@@ -69,7 +61,7 @@ module ActiveRecord
|
|
69
61
|
# If there's nothing in the database and @target has no new records
|
70
62
|
# we are certain the current target is an empty array. This is a
|
71
63
|
# documented side-effect of the method that may avoid an extra SELECT.
|
72
|
-
|
64
|
+
loaded! if count == 0
|
73
65
|
|
74
66
|
[association_scope.limit_value, count].compact.min
|
75
67
|
end
|
@@ -92,13 +84,14 @@ module ActiveRecord
|
|
92
84
|
if method == :delete_all
|
93
85
|
scope.delete_all
|
94
86
|
else
|
95
|
-
scope.update_all(
|
87
|
+
scope.update_all(nullified_owner_attributes)
|
96
88
|
end
|
97
89
|
end
|
98
90
|
|
99
91
|
def delete_or_nullify_all_records(method)
|
100
92
|
count = delete_count(method, scope)
|
101
93
|
update_counter(-count)
|
94
|
+
count
|
102
95
|
end
|
103
96
|
|
104
97
|
# Deletes the records according to the <tt>:dependent</tt> option.
|
@@ -130,6 +123,14 @@ module ActiveRecord
|
|
130
123
|
end
|
131
124
|
saved_successfully
|
132
125
|
end
|
126
|
+
|
127
|
+
def difference(a, b)
|
128
|
+
a - b
|
129
|
+
end
|
130
|
+
|
131
|
+
def intersection(a, b)
|
132
|
+
a & b
|
133
|
+
end
|
133
134
|
end
|
134
135
|
end
|
135
136
|
end
|
@@ -21,20 +21,6 @@ module ActiveRecord
|
|
21
21
|
super
|
22
22
|
end
|
23
23
|
|
24
|
-
def concat_records(records)
|
25
|
-
ensure_not_nested
|
26
|
-
|
27
|
-
records = super(records, true)
|
28
|
-
|
29
|
-
if owner.new_record? && records
|
30
|
-
records.flatten.each do |record|
|
31
|
-
build_through_record(record)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
records
|
36
|
-
end
|
37
|
-
|
38
24
|
def insert_record(record, validate = true, raise = false)
|
39
25
|
ensure_not_nested
|
40
26
|
|
@@ -48,6 +34,20 @@ module ActiveRecord
|
|
48
34
|
end
|
49
35
|
|
50
36
|
private
|
37
|
+
def concat_records(records)
|
38
|
+
ensure_not_nested
|
39
|
+
|
40
|
+
records = super(records, true)
|
41
|
+
|
42
|
+
if owner.new_record? && records
|
43
|
+
records.flatten.each do |record|
|
44
|
+
build_through_record(record)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
records
|
49
|
+
end
|
50
|
+
|
51
51
|
# The through record (built with build_record) is temporarily cached
|
52
52
|
# so that it may be reused if insert_record is subsequently called.
|
53
53
|
#
|
@@ -57,21 +57,14 @@ module ActiveRecord
|
|
57
57
|
@through_records[record.object_id] ||= begin
|
58
58
|
ensure_mutable
|
59
59
|
|
60
|
-
|
61
|
-
|
60
|
+
attributes = through_scope_attributes
|
61
|
+
attributes[source_reflection.name] = record
|
62
|
+
attributes[source_reflection.foreign_type] = options[:source_type] if options[:source_type]
|
62
63
|
|
63
|
-
|
64
|
-
through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
|
65
|
-
end
|
66
|
-
|
67
|
-
through_record
|
64
|
+
through_association.build(attributes)
|
68
65
|
end
|
69
66
|
end
|
70
67
|
|
71
|
-
def options_for_through_record
|
72
|
-
[through_scope_attributes]
|
73
|
-
end
|
74
|
-
|
75
68
|
def through_scope_attributes
|
76
69
|
scope.where_values_hash(through_association.reflection.name.to_s).
|
77
70
|
except!(through_association.reflection.foreign_key,
|
@@ -161,6 +154,30 @@ module ActiveRecord
|
|
161
154
|
else
|
162
155
|
update_counter(-count)
|
163
156
|
end
|
157
|
+
|
158
|
+
count
|
159
|
+
end
|
160
|
+
|
161
|
+
def difference(a, b)
|
162
|
+
distribution = distribution(b)
|
163
|
+
|
164
|
+
a.reject { |record| mark_occurrence(distribution, record) }
|
165
|
+
end
|
166
|
+
|
167
|
+
def intersection(a, b)
|
168
|
+
distribution = distribution(b)
|
169
|
+
|
170
|
+
a.select { |record| mark_occurrence(distribution, record) }
|
171
|
+
end
|
172
|
+
|
173
|
+
def mark_occurrence(distribution, record)
|
174
|
+
distribution[record] > 0 && distribution[record] -= 1
|
175
|
+
end
|
176
|
+
|
177
|
+
def distribution(array)
|
178
|
+
array.each_with_object(Hash.new(0)) do |record, distribution|
|
179
|
+
distribution[record] += 1
|
180
|
+
end
|
164
181
|
end
|
165
182
|
|
166
183
|
def through_records_for(record)
|
@@ -23,35 +23,6 @@ module ActiveRecord
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def replace(record, save = true)
|
27
|
-
raise_on_type_mismatch!(record) if record
|
28
|
-
load_target
|
29
|
-
|
30
|
-
return target unless target || record
|
31
|
-
|
32
|
-
assigning_another_record = target != record
|
33
|
-
if assigning_another_record || record.has_changes_to_save?
|
34
|
-
save &&= owner.persisted?
|
35
|
-
|
36
|
-
transaction_if(save) do
|
37
|
-
remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
|
38
|
-
|
39
|
-
if record
|
40
|
-
set_owner_attributes(record)
|
41
|
-
set_inverse_instance(record)
|
42
|
-
|
43
|
-
if save && !record.save
|
44
|
-
nullify_owner_attributes(record)
|
45
|
-
set_owner_attributes(target) if target
|
46
|
-
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
self.target = record
|
53
|
-
end
|
54
|
-
|
55
26
|
def delete(method = options[:dependent])
|
56
27
|
if load_target
|
57
28
|
case method
|
@@ -62,12 +33,39 @@ module ActiveRecord
|
|
62
33
|
target.destroy
|
63
34
|
throw(:abort) unless target.destroyed?
|
64
35
|
when :nullify
|
65
|
-
target.update_columns(
|
36
|
+
target.update_columns(nullified_owner_attributes) if target.persisted?
|
66
37
|
end
|
67
38
|
end
|
68
39
|
end
|
69
40
|
|
70
41
|
private
|
42
|
+
def replace(record, save = true)
|
43
|
+
raise_on_type_mismatch!(record) if record
|
44
|
+
|
45
|
+
return target unless load_target || record
|
46
|
+
|
47
|
+
assigning_another_record = target != record
|
48
|
+
if assigning_another_record || record.has_changes_to_save?
|
49
|
+
save &&= owner.persisted?
|
50
|
+
|
51
|
+
transaction_if(save) do
|
52
|
+
remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
|
53
|
+
|
54
|
+
if record
|
55
|
+
set_owner_attributes(record)
|
56
|
+
set_inverse_instance(record)
|
57
|
+
|
58
|
+
if save && !record.save
|
59
|
+
nullify_owner_attributes(record)
|
60
|
+
set_owner_attributes(target) if target
|
61
|
+
raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
self.target = record
|
68
|
+
end
|
71
69
|
|
72
70
|
# The reason that the save param for replace is false, if for create (not just build),
|
73
71
|
# is because the setting of the foreign keys is actually handled by the scoping when
|
@@ -6,12 +6,12 @@ module ActiveRecord
|
|
6
6
|
class HasOneThroughAssociation < HasOneAssociation #:nodoc:
|
7
7
|
include ThroughAssociation
|
8
8
|
|
9
|
-
def replace(record, save = true)
|
10
|
-
create_through_record(record, save)
|
11
|
-
self.target = record
|
12
|
-
end
|
13
|
-
|
14
9
|
private
|
10
|
+
def replace(record, save = true)
|
11
|
+
create_through_record(record, save)
|
12
|
+
self.target = record
|
13
|
+
end
|
14
|
+
|
15
15
|
def create_through_record(record, save)
|
16
16
|
ensure_not_nested
|
17
17
|
|
@@ -14,10 +14,8 @@ module ActiveRecord
|
|
14
14
|
i[column.name] = column.alias
|
15
15
|
}
|
16
16
|
}
|
17
|
-
@
|
18
|
-
h[table.node] = table.columns
|
19
|
-
[column.name, column.alias]
|
20
|
-
}
|
17
|
+
@columns_cache = tables.each_with_object({}) { |table, h|
|
18
|
+
h[table.node] = table.columns
|
21
19
|
}
|
22
20
|
end
|
23
21
|
|
@@ -25,9 +23,8 @@ module ActiveRecord
|
|
25
23
|
@tables.flat_map(&:column_aliases)
|
26
24
|
end
|
27
25
|
|
28
|
-
# An array of [column_name, alias] pairs for the table
|
29
26
|
def column_aliases(node)
|
30
|
-
@
|
27
|
+
@columns_cache[node]
|
31
28
|
end
|
32
29
|
|
33
30
|
def column_alias(node, column)
|
@@ -67,16 +64,21 @@ module ActiveRecord
|
|
67
64
|
end
|
68
65
|
end
|
69
66
|
|
70
|
-
def initialize(base, table, associations)
|
67
|
+
def initialize(base, table, associations, join_type)
|
71
68
|
tree = self.class.make_tree associations
|
72
69
|
@join_root = JoinBase.new(base, table, build(tree, base))
|
70
|
+
@join_type = join_type
|
71
|
+
end
|
72
|
+
|
73
|
+
def base_klass
|
74
|
+
join_root.base_klass
|
73
75
|
end
|
74
76
|
|
75
77
|
def reflections
|
76
78
|
join_root.drop(1).map!(&:reflection)
|
77
79
|
end
|
78
80
|
|
79
|
-
def join_constraints(joins_to_add,
|
81
|
+
def join_constraints(joins_to_add, alias_tracker)
|
80
82
|
@alias_tracker = alias_tracker
|
81
83
|
|
82
84
|
construct_tables!(join_root)
|
@@ -85,9 +87,9 @@ module ActiveRecord
|
|
85
87
|
joins.concat joins_to_add.flat_map { |oj|
|
86
88
|
construct_tables!(oj.join_root)
|
87
89
|
if join_root.match? oj.join_root
|
88
|
-
walk join_root, oj.
|
90
|
+
walk(join_root, oj.join_root, oj.join_type)
|
89
91
|
else
|
90
|
-
make_join_constraints(oj.join_root, join_type)
|
92
|
+
make_join_constraints(oj.join_root, oj.join_type)
|
91
93
|
end
|
92
94
|
}
|
93
95
|
end
|
@@ -116,7 +118,7 @@ module ActiveRecord
|
|
116
118
|
result_set.each { |row_hash|
|
117
119
|
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
118
120
|
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
|
119
|
-
construct(parent, join_root, row_hash,
|
121
|
+
construct(parent, join_root, row_hash, seen, model_cache)
|
120
122
|
}
|
121
123
|
end
|
122
124
|
|
@@ -128,9 +130,11 @@ module ActiveRecord
|
|
128
130
|
end
|
129
131
|
|
130
132
|
protected
|
131
|
-
attr_reader :
|
133
|
+
attr_reader :join_root, :join_type
|
132
134
|
|
133
135
|
private
|
136
|
+
attr_reader :alias_tracker
|
137
|
+
|
134
138
|
def aliases
|
135
139
|
@aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
|
136
140
|
columns = join_part.column_names.each_with_index.map { |column_name, j|
|
@@ -152,7 +156,7 @@ module ActiveRecord
|
|
152
156
|
end
|
153
157
|
end
|
154
158
|
|
155
|
-
def make_constraints(parent, child, join_type
|
159
|
+
def make_constraints(parent, child, join_type)
|
156
160
|
foreign_table = parent.table
|
157
161
|
foreign_klass = parent.base_klass
|
158
162
|
joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
|
@@ -170,17 +174,17 @@ module ActiveRecord
|
|
170
174
|
end
|
171
175
|
|
172
176
|
def table_alias_for(reflection, parent, join)
|
173
|
-
name =
|
177
|
+
name = reflection.alias_candidate(parent.table_name)
|
174
178
|
join ? "#{name}_join" : name
|
175
179
|
end
|
176
180
|
|
177
|
-
def walk(left, right)
|
181
|
+
def walk(left, right, join_type)
|
178
182
|
intersection, missing = right.children.map { |node1|
|
179
183
|
[left.children.find { |node2| node1.match? node2 }, node1]
|
180
184
|
}.partition(&:first)
|
181
185
|
|
182
|
-
joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r) }
|
183
|
-
joins.concat missing.flat_map { |_, n| make_constraints(left, n) }
|
186
|
+
joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) }
|
187
|
+
joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) }
|
184
188
|
end
|
185
189
|
|
186
190
|
def find_reflection(klass, name)
|
@@ -202,7 +206,7 @@ module ActiveRecord
|
|
202
206
|
end
|
203
207
|
end
|
204
208
|
|
205
|
-
def construct(ar_parent, parent, row,
|
209
|
+
def construct(ar_parent, parent, row, seen, model_cache)
|
206
210
|
return if ar_parent.nil?
|
207
211
|
|
208
212
|
parent.children.each do |node|
|
@@ -211,7 +215,7 @@ module ActiveRecord
|
|
211
215
|
other.loaded!
|
212
216
|
elsif ar_parent.association_cached?(node.reflection.name)
|
213
217
|
model = ar_parent.association(node.reflection.name).target
|
214
|
-
construct(model, node, row,
|
218
|
+
construct(model, node, row, seen, model_cache)
|
215
219
|
next
|
216
220
|
end
|
217
221
|
|
@@ -226,22 +230,17 @@ module ActiveRecord
|
|
226
230
|
model = seen[ar_parent.object_id][node][id]
|
227
231
|
|
228
232
|
if model
|
229
|
-
construct(model, node, row,
|
233
|
+
construct(model, node, row, seen, model_cache)
|
230
234
|
else
|
231
|
-
model = construct_model(ar_parent, node, row, model_cache, id
|
232
|
-
|
233
|
-
if node.reflection.scope &&
|
234
|
-
node.reflection.scope_for(node.base_klass.unscoped).readonly_value
|
235
|
-
model.readonly!
|
236
|
-
end
|
235
|
+
model = construct_model(ar_parent, node, row, model_cache, id)
|
237
236
|
|
238
237
|
seen[ar_parent.object_id][node][id] = model
|
239
|
-
construct(model, node, row,
|
238
|
+
construct(model, node, row, seen, model_cache)
|
240
239
|
end
|
241
240
|
end
|
242
241
|
end
|
243
242
|
|
244
|
-
def construct_model(record, node, row, model_cache, id
|
243
|
+
def construct_model(record, node, row, model_cache, id)
|
245
244
|
other = record.association(node.reflection.name)
|
246
245
|
|
247
246
|
model = model_cache[node][id] ||=
|
@@ -255,6 +254,7 @@ module ActiveRecord
|
|
255
254
|
other.target = model
|
256
255
|
end
|
257
256
|
|
257
|
+
model.readonly! if node.readonly?
|
258
258
|
model
|
259
259
|
end
|
260
260
|
end
|