activerecord 5.2.6 → 6.1.3.2
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 +1038 -571
- data/MIT-LICENSE +3 -1
- data/README.rdoc +7 -5
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +13 -12
- data/lib/active_record/aggregations.rb +9 -8
- data/lib/active_record/association_relation.rb +30 -10
- data/lib/active_record/associations.rb +137 -25
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +95 -42
- data/lib/active_record/associations/association_scope.rb +23 -21
- data/lib/active_record/associations/belongs_to_association.rb +54 -46
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
- data/lib/active_record/associations/builder/association.rb +45 -22
- data/lib/active_record/associations/builder/belongs_to.rb +29 -59
- data/lib/active_record/associations/builder/collection_association.rb +8 -17
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -2
- data/lib/active_record/associations/builder/has_one.rb +33 -2
- data/lib/active_record/associations/builder/singular_association.rb +3 -1
- data/lib/active_record/associations/collection_association.rb +31 -29
- data/lib/active_record/associations/collection_proxy.rb +25 -21
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +26 -13
- data/lib/active_record/associations/has_many_through_association.rb +24 -18
- data/lib/active_record/associations/has_one_association.rb +43 -31
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +91 -60
- data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
- data/lib/active_record/associations/preloader.rb +47 -34
- data/lib/active_record/associations/preloader/association.rb +71 -43
- data/lib/active_record/associations/preloader/through_association.rb +49 -40
- data/lib/active_record/associations/singular_association.rb +3 -17
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +17 -19
- data/lib/active_record/attribute_methods.rb +81 -143
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -40
- data/lib/active_record/attribute_methods/primary_key.rb +20 -25
- data/lib/active_record/attribute_methods/query.rb +4 -8
- data/lib/active_record/attribute_methods/read.rb +14 -56
- data/lib/active_record/attribute_methods/serialization.rb +12 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +18 -34
- data/lib/active_record/attributes.rb +46 -9
- data/lib/active_record/autosave_association.rb +57 -42
- data/lib/active_record/base.rb +4 -17
- data/lib/active_record/callbacks.rb +158 -43
- data/lib/active_record/coders/yaml_column.rb +1 -2
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -90
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
- data/lib/active_record/connection_adapters/abstract/transaction.rb +155 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +228 -98
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
- data/lib/active_record/connection_adapters/column.rb +30 -12
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.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 +15 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
- data/lib/active_record/connection_adapters/schema_cache.rb +127 -21
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +19 -6
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_handling.rb +293 -33
- data/lib/active_record/core.rb +323 -97
- data/lib/active_record/counter_cache.rb +8 -30
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +3 -4
- data/lib/active_record/enum.rb +111 -37
- data/lib/active_record/errors.rb +62 -19
- data/lib/active_record/explain.rb +10 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +200 -481
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +53 -24
- data/lib/active_record/insert_all.rb +208 -0
- data/lib/active_record/integration.rb +67 -17
- data/lib/active_record/internal_metadata.rb +26 -9
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +37 -23
- data/lib/active_record/locking/pessimistic.rb +9 -5
- data/lib/active_record/log_subscriber.rb +35 -35
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/migration.rb +206 -157
- data/lib/active_record/migration/command_recorder.rb +96 -44
- data/lib/active_record/migration/compatibility.rb +142 -64
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/model_schema.rb +148 -22
- data/lib/active_record/nested_attributes.rb +4 -7
- data/lib/active_record/no_touching.rb +8 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +267 -59
- data/lib/active_record/query_cache.rb +21 -4
- data/lib/active_record/querying.rb +40 -23
- data/lib/active_record/railtie.rb +115 -58
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +408 -78
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +109 -93
- data/lib/active_record/relation.rb +374 -104
- data/lib/active_record/relation/batches.rb +44 -35
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +153 -90
- data/lib/active_record/relation/delegation.rb +35 -50
- data/lib/active_record/relation/finder_methods.rb +64 -39
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +32 -40
- data/lib/active_record/relation/predicate_builder.rb +62 -45
- data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +475 -186
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +9 -9
- data/lib/active_record/relation/where_clause.rb +111 -61
- data/lib/active_record/result.rb +64 -38
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +22 -41
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +54 -9
- data/lib/active_record/schema_migration.rb +7 -9
- data/lib/active_record/scoping.rb +8 -9
- data/lib/active_record/scoping/default.rb +4 -6
- data/lib/active_record/scoping/named.rb +17 -24
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +49 -6
- data/lib/active_record/store.rb +88 -9
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -43
- data/lib/active_record/tasks/database_tasks.rb +277 -81
- data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
- data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +246 -0
- data/lib/active_record/timestamp.rb +43 -32
- data/lib/active_record/touch_later.rb +23 -22
- data/lib/active_record/transactions.rb +62 -118
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +10 -5
- data/lib/active_record/type/adapter_specific_registry.rb +3 -13
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type_caster/connection.rb +15 -15
- data/lib/active_record/type_caster/map.rb +8 -8
- data/lib/active_record/validations.rb +4 -3
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +38 -30
- data/lib/arel.rb +54 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -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 +70 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -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 +15 -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 +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -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 +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -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 +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -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 +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration.rb +19 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +119 -34
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
- data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -6,9 +6,14 @@ module ActiveRecord
|
|
6
6
|
module Associations
|
7
7
|
# Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
|
8
8
|
class AliasTracker # :nodoc:
|
9
|
-
def self.create(connection, initial_table, joins)
|
9
|
+
def self.create(connection, initial_table, joins, aliases = nil)
|
10
10
|
if joins.empty?
|
11
|
-
aliases
|
11
|
+
aliases ||= Hash.new(0)
|
12
|
+
elsif aliases
|
13
|
+
default_proc = aliases.default_proc || proc { 0 }
|
14
|
+
aliases.default_proc = proc { |h, k|
|
15
|
+
h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
|
16
|
+
}
|
12
17
|
else
|
13
18
|
aliases = Hash.new { |h, k|
|
14
19
|
h[k] = initial_count_for(connection, k, joins)
|
@@ -32,8 +37,6 @@ module ActiveRecord
|
|
32
37
|
).size
|
33
38
|
elsif join.is_a?(Arel::Nodes::Join)
|
34
39
|
join.left.name == name ? 1 : 0
|
35
|
-
elsif join.is_a?(Hash)
|
36
|
-
join[name]
|
37
40
|
else
|
38
41
|
raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
|
39
42
|
end
|
@@ -48,31 +51,31 @@ module ActiveRecord
|
|
48
51
|
@connection = connection
|
49
52
|
end
|
50
53
|
|
51
|
-
def aliased_table_for(
|
52
|
-
|
54
|
+
def aliased_table_for(arel_table, table_name = nil)
|
55
|
+
table_name ||= arel_table.name
|
56
|
+
|
57
|
+
if aliases[table_name] == 0
|
53
58
|
# If it's zero, we can have our table_name
|
54
59
|
aliases[table_name] = 1
|
55
|
-
|
60
|
+
arel_table = arel_table.alias(table_name) if arel_table.name != table_name
|
56
61
|
else
|
57
62
|
# Otherwise, we need to use an alias
|
58
|
-
aliased_name = @connection.table_alias_for(
|
63
|
+
aliased_name = @connection.table_alias_for(yield)
|
59
64
|
|
60
65
|
# Update the count
|
61
|
-
aliases[aliased_name] += 1
|
66
|
+
count = aliases[aliased_name] += 1
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
aliased_name
|
67
|
-
end
|
68
|
-
Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
|
68
|
+
aliased_name = "#{truncate(aliased_name)}_#{count}" if count > 1
|
69
|
+
|
70
|
+
arel_table = arel_table.alias(aliased_name)
|
69
71
|
end
|
72
|
+
|
73
|
+
arel_table
|
70
74
|
end
|
71
75
|
|
72
76
|
attr_reader :aliases
|
73
77
|
|
74
78
|
private
|
75
|
-
|
76
79
|
def truncate(name)
|
77
80
|
name.slice(0, @connection.table_alias_length - 2)
|
78
81
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/array/wrap"
|
4
|
-
|
5
3
|
module ActiveRecord
|
6
4
|
module Associations
|
7
5
|
# = Active Record Associations
|
@@ -17,6 +15,23 @@ module ActiveRecord
|
|
17
15
|
# CollectionAssociation
|
18
16
|
# HasManyAssociation + ForeignAssociation
|
19
17
|
# HasManyThroughAssociation + ThroughAssociation
|
18
|
+
#
|
19
|
+
# Associations in Active Record are middlemen between the object that
|
20
|
+
# holds the association, known as the <tt>owner</tt>, and the associated
|
21
|
+
# result set, known as the <tt>target</tt>. Association metadata is available in
|
22
|
+
# <tt>reflection</tt>, which is an instance of <tt>ActiveRecord::Reflection::AssociationReflection</tt>.
|
23
|
+
#
|
24
|
+
# For example, given
|
25
|
+
#
|
26
|
+
# class Blog < ActiveRecord::Base
|
27
|
+
# has_many :posts
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# blog = Blog.first
|
31
|
+
#
|
32
|
+
# The association of <tt>blog.posts</tt> has the object +blog+ as its
|
33
|
+
# <tt>owner</tt>, the collection of its posts as <tt>target</tt>, and
|
34
|
+
# the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
|
20
35
|
class Association #:nodoc:
|
21
36
|
attr_reader :owner, :target, :reflection
|
22
37
|
|
@@ -39,8 +54,14 @@ module ActiveRecord
|
|
39
54
|
@inversed = false
|
40
55
|
end
|
41
56
|
|
57
|
+
def reset_negative_cache # :nodoc:
|
58
|
+
reset if loaded? && target.nil?
|
59
|
+
end
|
60
|
+
|
42
61
|
# Reloads the \target and returns +self+ on success.
|
43
|
-
|
62
|
+
# The QueryCache is cleared if +force+ is true.
|
63
|
+
def reload(force = false)
|
64
|
+
klass.connection.clear_query_cache if force && klass
|
44
65
|
reset
|
45
66
|
reset_scope
|
46
67
|
load_target
|
@@ -76,18 +97,10 @@ module ActiveRecord
|
|
76
97
|
end
|
77
98
|
|
78
99
|
def scope
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
#
|
84
|
-
# Note that the association_scope is merged into the target_scope only when the
|
85
|
-
# scope method is called. This is because at that point the call may be surrounded
|
86
|
-
# by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
|
87
|
-
# actually gets built.
|
88
|
-
def association_scope
|
89
|
-
if klass
|
90
|
-
@association_scope ||= AssociationScope.scope(self)
|
100
|
+
if (scope = klass.current_scope) && scope.try(:proxy_association) == self
|
101
|
+
scope.spawn
|
102
|
+
else
|
103
|
+
target_scope.merge!(association_scope)
|
91
104
|
end
|
92
105
|
end
|
93
106
|
|
@@ -121,7 +134,15 @@ module ActiveRecord
|
|
121
134
|
self.target = record
|
122
135
|
@inversed = !!record
|
123
136
|
end
|
124
|
-
|
137
|
+
|
138
|
+
def inversed_from_queries(record)
|
139
|
+
if inversable?(record)
|
140
|
+
self.target = record
|
141
|
+
@inversed = true
|
142
|
+
else
|
143
|
+
@inversed = false
|
144
|
+
end
|
145
|
+
end
|
125
146
|
|
126
147
|
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
|
127
148
|
# polymorphic_type field on the owner.
|
@@ -129,12 +150,6 @@ module ActiveRecord
|
|
129
150
|
reflection.klass
|
130
151
|
end
|
131
152
|
|
132
|
-
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
|
133
|
-
# through association's scope)
|
134
|
-
def target_scope
|
135
|
-
AssociationRelation.create(klass, self).merge!(klass.all)
|
136
|
-
end
|
137
|
-
|
138
153
|
def extensions
|
139
154
|
extensions = klass.default_extensions | reflection.extensions
|
140
155
|
|
@@ -186,40 +201,56 @@ module ActiveRecord
|
|
186
201
|
set_inverse_instance(record)
|
187
202
|
end
|
188
203
|
|
189
|
-
def create(attributes =
|
204
|
+
def create(attributes = nil, &block)
|
190
205
|
_create_record(attributes, &block)
|
191
206
|
end
|
192
207
|
|
193
|
-
def create!(attributes =
|
208
|
+
def create!(attributes = nil, &block)
|
194
209
|
_create_record(attributes, true, &block)
|
195
210
|
end
|
196
211
|
|
197
212
|
private
|
198
|
-
def
|
199
|
-
|
200
|
-
|
213
|
+
def find_target
|
214
|
+
if (owner.strict_loading? || reflection.strict_loading?) && owner.validation_context.nil?
|
215
|
+
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
|
216
|
+
end
|
201
217
|
|
202
|
-
|
203
|
-
|
204
|
-
end
|
218
|
+
scope = self.scope
|
219
|
+
return scope.to_a if skip_statement_cache?(scope)
|
205
220
|
|
206
|
-
|
207
|
-
|
221
|
+
sc = reflection.association_scope_cache(klass, owner) do |params|
|
222
|
+
as = AssociationScope.create { params.bind }
|
223
|
+
target_scope.merge!(as.scope(self))
|
224
|
+
end
|
208
225
|
|
209
|
-
|
210
|
-
|
226
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
227
|
+
sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) }
|
228
|
+
end
|
211
229
|
|
212
|
-
|
213
|
-
|
214
|
-
|
230
|
+
# The scope for this association.
|
231
|
+
#
|
232
|
+
# Note that the association_scope is merged into the target_scope only when the
|
233
|
+
# scope method is called. This is because at that point the call may be surrounded
|
234
|
+
# by scope.scoping { ... } or unscoped { ... } etc, which affects the scope which
|
235
|
+
# actually gets built.
|
236
|
+
def association_scope
|
237
|
+
if klass
|
238
|
+
@association_scope ||= AssociationScope.scope(self)
|
215
239
|
end
|
240
|
+
end
|
216
241
|
|
217
|
-
|
242
|
+
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
|
243
|
+
# through association's scope)
|
244
|
+
def target_scope
|
245
|
+
AssociationRelation.create(klass, self).merge!(klass.scope_for_association)
|
218
246
|
end
|
219
247
|
|
220
|
-
|
221
|
-
|
222
|
-
|
248
|
+
def scope_for_create
|
249
|
+
scope.scope_for_create
|
250
|
+
end
|
251
|
+
|
252
|
+
def find_target?
|
253
|
+
!loaded? && (!owner.new_record? || foreign_key_present?) && klass
|
223
254
|
end
|
224
255
|
|
225
256
|
# Returns true if there is a foreign key present on the owner which
|
@@ -269,7 +300,7 @@ module ActiveRecord
|
|
269
300
|
|
270
301
|
# Returns true if record contains the foreign_key
|
271
302
|
def foreign_key_for?(record)
|
272
|
-
record.
|
303
|
+
record._has_attribute?(reflection.foreign_key)
|
273
304
|
end
|
274
305
|
|
275
306
|
# This should be implemented to return the values of the relevant key(s) on the owner,
|
@@ -294,6 +325,28 @@ module ActiveRecord
|
|
294
325
|
klass.scope_attributes? ||
|
295
326
|
reflection.source_reflection.active_record.default_scopes.any?
|
296
327
|
end
|
328
|
+
|
329
|
+
def enqueue_destroy_association(options)
|
330
|
+
job_class = owner.class.destroy_association_async_job
|
331
|
+
|
332
|
+
if job_class
|
333
|
+
owner._after_commit_jobs.push([job_class, options])
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def inversable?(record)
|
338
|
+
record &&
|
339
|
+
((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
|
340
|
+
end
|
341
|
+
|
342
|
+
def matches_foreign_key?(record)
|
343
|
+
if foreign_key_for?(record)
|
344
|
+
record.read_attribute(reflection.foreign_key) == owner.id ||
|
345
|
+
(foreign_key_for?(owner) && owner.read_attribute(reflection.foreign_key) == record.id)
|
346
|
+
else
|
347
|
+
owner.read_attribute(reflection.foreign_key) == record.id
|
348
|
+
end
|
349
|
+
end
|
297
350
|
end
|
298
351
|
end
|
299
352
|
end
|
@@ -26,7 +26,9 @@ module ActiveRecord
|
|
26
26
|
chain = get_chain(reflection, association, scope.alias_tracker)
|
27
27
|
|
28
28
|
scope.extending! reflection.extensions
|
29
|
-
add_constraints(scope, owner, chain)
|
29
|
+
scope = add_constraints(scope, owner, chain)
|
30
|
+
scope.limit!(1) unless reflection.collection?
|
31
|
+
scope
|
30
32
|
end
|
31
33
|
|
32
34
|
def self.get_bind_values(owner, chain)
|
@@ -46,25 +48,20 @@ module ActiveRecord
|
|
46
48
|
binds
|
47
49
|
end
|
48
50
|
|
49
|
-
|
50
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
51
|
-
protected
|
52
|
-
|
51
|
+
private
|
53
52
|
attr_reader :value_transformation
|
54
53
|
|
55
|
-
private
|
56
54
|
def join(table, constraint)
|
57
|
-
|
55
|
+
Arel::Nodes::LeadingJoin.new(table, Arel::Nodes::On.new(constraint))
|
58
56
|
end
|
59
57
|
|
60
58
|
def last_chain_scope(scope, reflection, owner)
|
61
|
-
|
62
|
-
|
63
|
-
foreign_key = join_keys.foreign_key
|
59
|
+
primary_key = reflection.join_primary_key
|
60
|
+
foreign_key = reflection.join_foreign_key
|
64
61
|
|
65
62
|
table = reflection.aliased_table
|
66
63
|
value = transform_value(owner[foreign_key])
|
67
|
-
scope = apply_scope(scope, table,
|
64
|
+
scope = apply_scope(scope, table, primary_key, value)
|
68
65
|
|
69
66
|
if reflection.type
|
70
67
|
polymorphic_type = transform_value(owner.class.polymorphic_name)
|
@@ -79,13 +76,12 @@ module ActiveRecord
|
|
79
76
|
end
|
80
77
|
|
81
78
|
def next_chain_scope(scope, reflection, next_reflection)
|
82
|
-
|
83
|
-
|
84
|
-
foreign_key = join_keys.foreign_key
|
79
|
+
primary_key = reflection.join_primary_key
|
80
|
+
foreign_key = reflection.join_foreign_key
|
85
81
|
|
86
82
|
table = reflection.aliased_table
|
87
83
|
foreign_table = next_reflection.aliased_table
|
88
|
-
constraint = table[
|
84
|
+
constraint = table[primary_key].eq(foreign_table[foreign_key])
|
89
85
|
|
90
86
|
if reflection.type
|
91
87
|
value = transform_value(next_reflection.klass.polymorphic_name)
|
@@ -110,11 +106,9 @@ module ActiveRecord
|
|
110
106
|
name = reflection.name
|
111
107
|
chain = [Reflection::RuntimeReflection.new(reflection, association)]
|
112
108
|
reflection.chain.drop(1).each do |refl|
|
113
|
-
aliased_table = tracker.aliased_table_for(
|
114
|
-
refl.
|
115
|
-
|
116
|
-
refl.klass.type_caster
|
117
|
-
)
|
109
|
+
aliased_table = tracker.aliased_table_for(refl.klass.arel_table) do
|
110
|
+
refl.alias_candidate(name)
|
111
|
+
end
|
118
112
|
chain << ReflectionProxy.new(refl, aliased_table)
|
119
113
|
end
|
120
114
|
chain
|
@@ -136,10 +130,18 @@ module ActiveRecord
|
|
136
130
|
|
137
131
|
if scope_chain_item == chain_head.scope
|
138
132
|
scope.merge! item.except(:where, :includes, :unscope, :order)
|
133
|
+
elsif !item.references_values.empty?
|
134
|
+
scope.merge! item.only(:joins, :left_outer_joins)
|
135
|
+
|
136
|
+
associations = item.eager_load_values | item.includes_values
|
137
|
+
|
138
|
+
unless associations.empty?
|
139
|
+
scope.joins! item.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
|
140
|
+
end
|
139
141
|
end
|
140
142
|
|
141
143
|
reflection.all_includes do
|
142
|
-
scope.
|
144
|
+
scope.includes_values |= item.includes_values
|
143
145
|
end
|
144
146
|
|
145
147
|
scope.unscope!(*item.unscope_values)
|
@@ -11,26 +11,23 @@ module ActiveRecord
|
|
11
11
|
when :destroy
|
12
12
|
target.destroy
|
13
13
|
raise ActiveRecord::Rollback unless target.destroyed?
|
14
|
+
when :destroy_async
|
15
|
+
id = owner.public_send(reflection.foreign_key.to_sym)
|
16
|
+
primary_key_column = reflection.active_record_primary_key.to_sym
|
17
|
+
|
18
|
+
enqueue_destroy_association(
|
19
|
+
owner_model_name: owner.class.to_s,
|
20
|
+
owner_id: owner.id,
|
21
|
+
association_class: reflection.klass.to_s,
|
22
|
+
association_ids: [id],
|
23
|
+
association_primary_key_column: primary_key_column,
|
24
|
+
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
25
|
+
)
|
14
26
|
else
|
15
|
-
target.
|
27
|
+
target.public_send(options[:dependent])
|
16
28
|
end
|
17
29
|
end
|
18
30
|
|
19
|
-
def replace(record)
|
20
|
-
if record
|
21
|
-
raise_on_type_mismatch!(record)
|
22
|
-
update_counters_on_replace(record)
|
23
|
-
set_inverse_instance(record)
|
24
|
-
@updated = true
|
25
|
-
else
|
26
|
-
decrement_counters
|
27
|
-
end
|
28
|
-
|
29
|
-
replace_keys(record)
|
30
|
-
|
31
|
-
self.target = record
|
32
|
-
end
|
33
|
-
|
34
31
|
def inversed_from(record)
|
35
32
|
replace_keys(record)
|
36
33
|
super
|
@@ -49,30 +46,60 @@ module ActiveRecord
|
|
49
46
|
@updated
|
50
47
|
end
|
51
48
|
|
52
|
-
def decrement_counters
|
49
|
+
def decrement_counters
|
53
50
|
update_counters(-1)
|
54
51
|
end
|
55
52
|
|
56
|
-
def increment_counters
|
53
|
+
def increment_counters
|
57
54
|
update_counters(1)
|
58
55
|
end
|
59
56
|
|
57
|
+
def decrement_counters_before_last_save
|
58
|
+
if reflection.polymorphic?
|
59
|
+
model_was = owner.attribute_before_last_save(reflection.foreign_type)&.constantize
|
60
|
+
else
|
61
|
+
model_was = klass
|
62
|
+
end
|
63
|
+
|
64
|
+
foreign_key_was = owner.attribute_before_last_save(reflection.foreign_key)
|
65
|
+
|
66
|
+
if foreign_key_was && model_was < ActiveRecord::Base
|
67
|
+
update_counters_via_scope(model_was, foreign_key_was, -1)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
60
71
|
def target_changed?
|
61
72
|
owner.saved_change_to_attribute?(reflection.foreign_key)
|
62
73
|
end
|
63
74
|
|
64
75
|
private
|
76
|
+
def replace(record)
|
77
|
+
if record
|
78
|
+
raise_on_type_mismatch!(record)
|
79
|
+
set_inverse_instance(record)
|
80
|
+
@updated = true
|
81
|
+
end
|
82
|
+
|
83
|
+
replace_keys(record, force: true)
|
84
|
+
|
85
|
+
self.target = record
|
86
|
+
end
|
65
87
|
|
66
88
|
def update_counters(by)
|
67
89
|
if require_counter_update? && foreign_key_present?
|
68
90
|
if target && !stale_target?
|
69
91
|
target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
|
70
92
|
else
|
71
|
-
klass.
|
93
|
+
update_counters_via_scope(klass, owner._read_attribute(reflection.foreign_key), by)
|
72
94
|
end
|
73
95
|
end
|
74
96
|
end
|
75
97
|
|
98
|
+
def update_counters_via_scope(klass, foreign_key, by)
|
99
|
+
scope = klass.unscoped.where!(primary_key(klass) => foreign_key)
|
100
|
+
scope.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
|
101
|
+
end
|
102
|
+
|
76
103
|
def find_target?
|
77
104
|
!loaded? && foreign_key_present? && klass
|
78
105
|
end
|
@@ -81,44 +108,25 @@ module ActiveRecord
|
|
81
108
|
reflection.counter_cache_column && owner.persisted?
|
82
109
|
end
|
83
110
|
|
84
|
-
def
|
85
|
-
|
86
|
-
owner.instance_variable_set :@_after_replace_counter_called, true
|
87
|
-
record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
|
88
|
-
decrement_counters
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Checks whether record is different to the current target, without loading it
|
93
|
-
def different_target?(record)
|
94
|
-
record._read_attribute(primary_key(record)) != owner._read_attribute(reflection.foreign_key)
|
95
|
-
end
|
111
|
+
def replace_keys(record, force: false)
|
112
|
+
target_key = record ? record._read_attribute(primary_key(record.class)) : nil
|
96
113
|
|
97
|
-
|
98
|
-
|
114
|
+
if force || owner[reflection.foreign_key] != target_key
|
115
|
+
owner[reflection.foreign_key] = target_key
|
116
|
+
end
|
99
117
|
end
|
100
118
|
|
101
|
-
def primary_key(
|
102
|
-
reflection.association_primary_key(
|
119
|
+
def primary_key(klass)
|
120
|
+
reflection.association_primary_key(klass)
|
103
121
|
end
|
104
122
|
|
105
123
|
def foreign_key_present?
|
106
124
|
owner._read_attribute(reflection.foreign_key)
|
107
125
|
end
|
108
126
|
|
109
|
-
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
110
|
-
# has_one associations.
|
111
127
|
def invertible_for?(record)
|
112
128
|
inverse = inverse_reflection_for(record)
|
113
|
-
inverse && inverse.has_one?
|
114
|
-
end
|
115
|
-
|
116
|
-
def target_id
|
117
|
-
if options[:primary_key]
|
118
|
-
owner.send(reflection.name).try(:id)
|
119
|
-
else
|
120
|
-
owner._read_attribute(reflection.foreign_key)
|
121
|
-
end
|
129
|
+
inverse && (inverse.has_one? || ActiveRecord::Base.has_many_inversing)
|
122
130
|
end
|
123
131
|
|
124
132
|
def stale_state
|