activerecord 4.2.0 → 5.2.8.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 +5 -5
- data/CHANGELOG.md +640 -928
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +264 -247
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +87 -41
- data/lib/active_record/associations/association_scope.rb +106 -132
- data/lib/active_record/associations/belongs_to_association.rb +55 -36
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +14 -23
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +145 -266
- data/lib/active_record/associations/collection_proxy.rb +242 -138
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +35 -75
- data/lib/active_record/associations/has_many_through_association.rb +51 -69
- data/lib/active_record/associations/has_one_association.rb +39 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +134 -154
- data/lib/active_record/associations/preloader/association.rb +85 -116
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +83 -93
- data/lib/active_record/associations/singular_association.rb +27 -40
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1732 -1596
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +12 -5
- data/lib/active_record/attribute_methods/dirty.rb +94 -125
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
- data/lib/active_record/attribute_methods/write.rb +31 -46
- data/lib/active_record/attribute_methods.rb +170 -117
- data/lib/active_record/attributes.rb +201 -74
- data/lib/active_record/autosave_association.rb +118 -45
- data/lib/active_record/base.rb +60 -48
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +37 -13
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
- data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +42 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +466 -280
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +439 -330
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -324
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +205 -202
- data/lib/active_record/counter_cache.rb +80 -37
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +136 -90
- data/lib/active_record/errors.rb +180 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +193 -135
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +92 -98
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +594 -267
- data/lib/active_record/model_schema.rb +292 -111
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +8 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +350 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +117 -35
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +160 -174
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +447 -288
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +259 -244
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +290 -253
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +91 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +118 -92
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +446 -389
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +287 -339
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -19
- data/lib/active_record/scoping/default.rb +102 -84
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +136 -95
- data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +208 -123
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +30 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +41 -32
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -21
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -6
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +77 -53
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,49 +1,41 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/conversions"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
|
-
# Keeps track of table aliases for ActiveRecord::Associations::
|
6
|
-
# ActiveRecord::Associations::ThroughAssociationScope
|
7
|
+
# Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
|
7
8
|
class AliasTracker # :nodoc:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
new connection, Hash.new(0)
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.create(connection, table_joins)
|
15
|
-
if table_joins.empty?
|
16
|
-
empty connection
|
9
|
+
def self.create(connection, initial_table, joins)
|
10
|
+
if joins.empty?
|
11
|
+
aliases = Hash.new(0)
|
17
12
|
else
|
18
|
-
aliases = Hash.new { |h,k|
|
19
|
-
h[k] = initial_count_for(connection, k,
|
13
|
+
aliases = Hash.new { |h, k|
|
14
|
+
h[k] = initial_count_for(connection, k, joins)
|
20
15
|
}
|
21
|
-
new connection, aliases
|
22
16
|
end
|
17
|
+
aliases[initial_table] = 1
|
18
|
+
new(connection, aliases)
|
23
19
|
end
|
24
20
|
|
25
21
|
def self.initial_count_for(connection, name, table_joins)
|
26
|
-
|
27
|
-
quoted_name = connection.quote_table_name(name).downcase
|
22
|
+
quoted_name = nil
|
28
23
|
|
29
24
|
counts = table_joins.map do |join|
|
30
25
|
if join.is_a?(Arel::Nodes::StringJoin)
|
26
|
+
# quoted_name should be case ignored as some database adapters (Oracle) return quoted name in uppercase
|
27
|
+
quoted_name ||= connection.quote_table_name(name)
|
28
|
+
|
31
29
|
# Table names + table aliases
|
32
|
-
join.left.
|
33
|
-
/
|
30
|
+
join.left.scan(
|
31
|
+
/JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
|
34
32
|
).size
|
35
|
-
elsif join.
|
36
|
-
join.left.
|
33
|
+
elsif join.is_a?(Arel::Nodes::Join)
|
34
|
+
join.left.name == name ? 1 : 0
|
35
|
+
elsif join.is_a?(Hash)
|
36
|
+
join[name]
|
37
37
|
else
|
38
|
-
|
39
|
-
#
|
40
|
-
# activerecord/test/cases/associations/cascaded_eager_loading_test.rb:37
|
41
|
-
# with :posts
|
42
|
-
#
|
43
|
-
# activerecord/test/cases/associations/eager_test.rb:1133
|
44
|
-
# with :comments
|
45
|
-
#
|
46
|
-
0
|
38
|
+
raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
|
47
39
|
end
|
48
40
|
end
|
49
41
|
|
@@ -56,14 +48,14 @@ module ActiveRecord
|
|
56
48
|
@connection = connection
|
57
49
|
end
|
58
50
|
|
59
|
-
def aliased_table_for(table_name, aliased_name)
|
51
|
+
def aliased_table_for(table_name, aliased_name, type_caster)
|
60
52
|
if aliases[table_name].zero?
|
61
53
|
# If it's zero, we can have our table_name
|
62
54
|
aliases[table_name] = 1
|
63
|
-
Arel::Table.new(table_name)
|
55
|
+
Arel::Table.new(table_name, type_caster: type_caster)
|
64
56
|
else
|
65
57
|
# Otherwise, we need to use an alias
|
66
|
-
aliased_name = connection.table_alias_for(aliased_name)
|
58
|
+
aliased_name = @connection.table_alias_for(aliased_name)
|
67
59
|
|
68
60
|
# Update the count
|
69
61
|
aliases[aliased_name] += 1
|
@@ -73,14 +65,16 @@ module ActiveRecord
|
|
73
65
|
else
|
74
66
|
aliased_name
|
75
67
|
end
|
76
|
-
Arel::Table.new(table_name).alias(table_alias)
|
68
|
+
Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
|
77
69
|
end
|
78
70
|
end
|
79
71
|
|
72
|
+
attr_reader :aliases
|
73
|
+
|
80
74
|
private
|
81
75
|
|
82
76
|
def truncate(name)
|
83
|
-
name.slice(0, connection.table_alias_length - 2)
|
77
|
+
name.slice(0, @connection.table_alias_length - 2)
|
84
78
|
end
|
85
79
|
end
|
86
80
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array/wrap"
|
2
4
|
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
@@ -8,18 +10,17 @@ module ActiveRecord
|
|
8
10
|
#
|
9
11
|
# Association
|
10
12
|
# SingularAssociation
|
11
|
-
# HasOneAssociation
|
13
|
+
# HasOneAssociation + ForeignAssociation
|
12
14
|
# HasOneThroughAssociation + ThroughAssociation
|
13
15
|
# BelongsToAssociation
|
14
16
|
# BelongsToPolymorphicAssociation
|
15
17
|
# CollectionAssociation
|
16
|
-
# HasManyAssociation
|
18
|
+
# HasManyAssociation + ForeignAssociation
|
17
19
|
# HasManyThroughAssociation + ThroughAssociation
|
18
20
|
class Association #:nodoc:
|
19
21
|
attr_reader :owner, :target, :reflection
|
20
|
-
attr_accessor :inversed
|
21
22
|
|
22
|
-
delegate :options, :
|
23
|
+
delegate :options, to: :reflection
|
23
24
|
|
24
25
|
def initialize(owner, reflection)
|
25
26
|
reflection.check_validity!
|
@@ -30,14 +31,6 @@ module ActiveRecord
|
|
30
31
|
reset_scope
|
31
32
|
end
|
32
33
|
|
33
|
-
# Returns the name of the table of the associated class:
|
34
|
-
#
|
35
|
-
# post.comments.aliased_table_name # => "comments"
|
36
|
-
#
|
37
|
-
def aliased_table_name
|
38
|
-
klass.table_name
|
39
|
-
end
|
40
|
-
|
41
34
|
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
42
35
|
def reset
|
43
36
|
@loaded = false
|
@@ -73,7 +66,7 @@ module ActiveRecord
|
|
73
66
|
#
|
74
67
|
# Note that if the target has not been loaded, it is not considered stale.
|
75
68
|
def stale_target?
|
76
|
-
|
69
|
+
!@inversed && loaded? && @stale_state != stale_state
|
77
70
|
end
|
78
71
|
|
79
72
|
# Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
|
@@ -83,7 +76,7 @@ module ActiveRecord
|
|
83
76
|
end
|
84
77
|
|
85
78
|
def scope
|
86
|
-
target_scope.merge(association_scope)
|
79
|
+
target_scope.merge!(association_scope)
|
87
80
|
end
|
88
81
|
|
89
82
|
# The scope for this association.
|
@@ -94,7 +87,7 @@ module ActiveRecord
|
|
94
87
|
# actually gets built.
|
95
88
|
def association_scope
|
96
89
|
if klass
|
97
|
-
@association_scope ||= AssociationScope.scope(self
|
90
|
+
@association_scope ||= AssociationScope.scope(self)
|
98
91
|
end
|
99
92
|
end
|
100
93
|
|
@@ -104,14 +97,32 @@ module ActiveRecord
|
|
104
97
|
|
105
98
|
# Set the inverse association, if possible
|
106
99
|
def set_inverse_instance(record)
|
107
|
-
if
|
108
|
-
inverse
|
109
|
-
|
110
|
-
|
100
|
+
if inverse = inverse_association_for(record)
|
101
|
+
inverse.inversed_from(owner)
|
102
|
+
end
|
103
|
+
record
|
104
|
+
end
|
105
|
+
|
106
|
+
def set_inverse_instance_from_queries(record)
|
107
|
+
if inverse = inverse_association_for(record)
|
108
|
+
inverse.inversed_from_queries(owner)
|
111
109
|
end
|
112
110
|
record
|
113
111
|
end
|
114
112
|
|
113
|
+
# Remove the inverse association, if possible
|
114
|
+
def remove_inverse_instance(record)
|
115
|
+
if inverse = inverse_association_for(record)
|
116
|
+
inverse.inversed_from(nil)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def inversed_from(record)
|
121
|
+
self.target = record
|
122
|
+
@inversed = !!record
|
123
|
+
end
|
124
|
+
alias :inversed_from_queries :inversed_from
|
125
|
+
|
115
126
|
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
|
116
127
|
# polymorphic_type field on the owner.
|
117
128
|
def klass
|
@@ -121,7 +132,17 @@ module ActiveRecord
|
|
121
132
|
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
|
122
133
|
# through association's scope)
|
123
134
|
def target_scope
|
124
|
-
AssociationRelation.create(klass,
|
135
|
+
AssociationRelation.create(klass, self).merge!(klass.all)
|
136
|
+
end
|
137
|
+
|
138
|
+
def extensions
|
139
|
+
extensions = klass.default_extensions | reflection.extensions
|
140
|
+
|
141
|
+
if reflection.scope
|
142
|
+
extensions |= reflection.scope_for(klass.unscoped, owner).extensions
|
143
|
+
end
|
144
|
+
|
145
|
+
extensions
|
125
146
|
end
|
126
147
|
|
127
148
|
# Loads the \target if needed and returns it.
|
@@ -143,17 +164,9 @@ module ActiveRecord
|
|
143
164
|
reset
|
144
165
|
end
|
145
166
|
|
146
|
-
|
147
|
-
if sql.respond_to?(:to_proc)
|
148
|
-
owner.instance_exec(record, &sql)
|
149
|
-
else
|
150
|
-
sql
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
# We can't dump @reflection since it contains the scope proc
|
167
|
+
# We can't dump @reflection and @through_reflection since it contains the scope proc
|
155
168
|
def marshal_dump
|
156
|
-
ivars = (instance_variables - [:@reflection]).map { |name| [name, instance_variable_get(name)] }
|
169
|
+
ivars = (instance_variables - [:@reflection, :@through_reflection]).map { |name| [name, instance_variable_get(name)] }
|
157
170
|
[@reflection.name, ivars]
|
158
171
|
end
|
159
172
|
|
@@ -163,14 +176,28 @@ module ActiveRecord
|
|
163
176
|
@reflection = @owner.class._reflect_on_association(reflection_name)
|
164
177
|
end
|
165
178
|
|
166
|
-
def initialize_attributes(record) #:nodoc:
|
179
|
+
def initialize_attributes(record, except_from_scope_attributes = nil) #:nodoc:
|
180
|
+
except_from_scope_attributes ||= {}
|
167
181
|
skip_assign = [reflection.foreign_key, reflection.type].compact
|
168
|
-
|
169
|
-
|
182
|
+
assigned_keys = record.changed_attribute_names_to_save
|
183
|
+
assigned_keys += except_from_scope_attributes.keys.map(&:to_s)
|
184
|
+
attributes = scope_for_create.except!(*(assigned_keys - skip_assign))
|
185
|
+
record.send(:_assign_attributes, attributes) if attributes.any?
|
170
186
|
set_inverse_instance(record)
|
171
187
|
end
|
172
188
|
|
189
|
+
def create(attributes = {}, &block)
|
190
|
+
_create_record(attributes, &block)
|
191
|
+
end
|
192
|
+
|
193
|
+
def create!(attributes = {}, &block)
|
194
|
+
_create_record(attributes, true, &block)
|
195
|
+
end
|
196
|
+
|
173
197
|
private
|
198
|
+
def scope_for_create
|
199
|
+
scope.scope_for_create
|
200
|
+
end
|
174
201
|
|
175
202
|
def find_target?
|
176
203
|
!loaded? && (!owner.new_record? || foreign_key_present?) && klass
|
@@ -182,8 +209,8 @@ module ActiveRecord
|
|
182
209
|
if (reflection.has_one? || reflection.collection?) && !options[:through]
|
183
210
|
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
|
184
211
|
|
185
|
-
if reflection.
|
186
|
-
attributes[reflection.type] = owner.class.
|
212
|
+
if reflection.type
|
213
|
+
attributes[reflection.type] = owner.class.polymorphic_name
|
187
214
|
end
|
188
215
|
end
|
189
216
|
|
@@ -211,9 +238,19 @@ module ActiveRecord
|
|
211
238
|
# the kind of the class of the associated objects. Meant to be used as
|
212
239
|
# a sanity check when you are about to assign an associated record.
|
213
240
|
def raise_on_type_mismatch!(record)
|
214
|
-
unless record.is_a?(reflection.klass)
|
215
|
-
|
216
|
-
|
241
|
+
unless record.is_a?(reflection.klass)
|
242
|
+
fresh_class = reflection.class_name.safe_constantize
|
243
|
+
unless fresh_class && record.is_a?(fresh_class)
|
244
|
+
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, "\
|
245
|
+
"got #{record.inspect} which is an instance of #{record.class}(##{record.class.object_id})"
|
246
|
+
raise ActiveRecord::AssociationTypeMismatch, message
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def inverse_association_for(record)
|
252
|
+
if invertible_for?(record)
|
253
|
+
record.association(inverse_reflection_for(record).name)
|
217
254
|
end
|
218
255
|
end
|
219
256
|
|
@@ -239,15 +276,24 @@ module ActiveRecord
|
|
239
276
|
# so that when stale_state is different from the value stored on the last find_target,
|
240
277
|
# the target is stale.
|
241
278
|
#
|
242
|
-
# This is only relevant to certain associations, which is why it returns nil by default.
|
279
|
+
# This is only relevant to certain associations, which is why it returns +nil+ by default.
|
243
280
|
def stale_state
|
244
281
|
end
|
245
282
|
|
246
283
|
def build_record(attributes)
|
247
284
|
reflection.build_association(attributes) do |record|
|
248
|
-
initialize_attributes(record)
|
285
|
+
initialize_attributes(record, attributes)
|
286
|
+
yield(record) if block_given?
|
249
287
|
end
|
250
288
|
end
|
289
|
+
|
290
|
+
# Returns true if statement cache should be skipped on the association reader.
|
291
|
+
def skip_statement_cache?(scope)
|
292
|
+
reflection.has_scope? ||
|
293
|
+
scope.eager_loading? ||
|
294
|
+
klass.scope_attributes? ||
|
295
|
+
reflection.source_reflection.active_record.default_scopes.any?
|
296
|
+
end
|
251
297
|
end
|
252
298
|
end
|
253
299
|
end
|
@@ -1,46 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
class AssociationScope #:nodoc:
|
4
|
-
def self.scope(association
|
5
|
-
INSTANCE.scope
|
6
|
-
end
|
7
|
-
|
8
|
-
class BindSubstitution
|
9
|
-
def initialize(block)
|
10
|
-
@block = block
|
11
|
-
end
|
12
|
-
|
13
|
-
def bind_value(scope, column, value, alias_tracker)
|
14
|
-
substitute = alias_tracker.connection.substitute_at(column)
|
15
|
-
scope.bind_values += [[column, @block.call(value)]]
|
16
|
-
substitute
|
17
|
-
end
|
6
|
+
def self.scope(association)
|
7
|
+
INSTANCE.scope(association)
|
18
8
|
end
|
19
9
|
|
20
10
|
def self.create(&block)
|
21
|
-
block
|
22
|
-
new
|
11
|
+
block ||= lambda { |val| val }
|
12
|
+
new(block)
|
23
13
|
end
|
24
14
|
|
25
|
-
def initialize(
|
26
|
-
@
|
15
|
+
def initialize(value_transformation)
|
16
|
+
@value_transformation = value_transformation
|
27
17
|
end
|
28
18
|
|
29
19
|
INSTANCE = create
|
30
20
|
|
31
|
-
def scope(association
|
32
|
-
klass
|
33
|
-
reflection
|
34
|
-
scope
|
35
|
-
owner
|
36
|
-
|
21
|
+
def scope(association)
|
22
|
+
klass = association.klass
|
23
|
+
reflection = association.reflection
|
24
|
+
scope = klass.unscoped
|
25
|
+
owner = association.owner
|
26
|
+
chain = get_chain(reflection, association, scope.alias_tracker)
|
37
27
|
|
38
|
-
scope.extending!
|
39
|
-
add_constraints(scope, owner,
|
40
|
-
end
|
41
|
-
|
42
|
-
def join_type
|
43
|
-
Arel::Nodes::InnerJoin
|
28
|
+
scope.extending! reflection.extensions
|
29
|
+
add_constraints(scope, owner, chain)
|
44
30
|
end
|
45
31
|
|
46
32
|
def self.get_bind_values(owner, chain)
|
@@ -49,146 +35,134 @@ module ActiveRecord
|
|
49
35
|
|
50
36
|
binds << last_reflection.join_id_for(owner)
|
51
37
|
if last_reflection.type
|
52
|
-
binds << owner.class.
|
38
|
+
binds << owner.class.polymorphic_name
|
53
39
|
end
|
54
40
|
|
55
41
|
chain.each_cons(2).each do |reflection, next_reflection|
|
56
42
|
if reflection.type
|
57
|
-
binds << next_reflection.klass.
|
43
|
+
binds << next_reflection.klass.polymorphic_name
|
58
44
|
end
|
59
45
|
end
|
60
46
|
binds
|
61
47
|
end
|
62
48
|
|
63
|
-
private
|
49
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
50
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
51
|
+
protected
|
64
52
|
|
65
|
-
|
66
|
-
chain.map do |reflection|
|
67
|
-
alias_tracker.aliased_table_for(
|
68
|
-
table_name_for(reflection, klass, refl),
|
69
|
-
table_alias_for(reflection, refl, reflection != refl)
|
70
|
-
)
|
71
|
-
end
|
72
|
-
end
|
53
|
+
attr_reader :value_transformation
|
73
54
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
def join(table, constraint)
|
81
|
-
table.create_join(table, table.create_on(constraint), join_type)
|
82
|
-
end
|
83
|
-
|
84
|
-
def column_for(table_name, column_name, alias_tracker)
|
85
|
-
columns = alias_tracker.connection.schema_cache.columns_hash(table_name)
|
86
|
-
columns[column_name]
|
87
|
-
end
|
88
|
-
|
89
|
-
def bind_value(scope, column, value, alias_tracker)
|
90
|
-
@bind_substitution.bind_value scope, column, value, alias_tracker
|
91
|
-
end
|
55
|
+
private
|
56
|
+
def join(table, constraint)
|
57
|
+
table.create_join(table, table.create_on(constraint))
|
58
|
+
end
|
92
59
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
60
|
+
def last_chain_scope(scope, reflection, owner)
|
61
|
+
join_keys = reflection.join_keys
|
62
|
+
key = join_keys.key
|
63
|
+
foreign_key = join_keys.foreign_key
|
97
64
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
foreign_key = join_keys.foreign_key
|
65
|
+
table = reflection.aliased_table
|
66
|
+
value = transform_value(owner[foreign_key])
|
67
|
+
scope = apply_scope(scope, table, key, value)
|
102
68
|
|
103
|
-
|
104
|
-
|
69
|
+
if reflection.type
|
70
|
+
polymorphic_type = transform_value(owner.class.polymorphic_name)
|
71
|
+
scope = apply_scope(scope, table, reflection.type, polymorphic_type)
|
72
|
+
end
|
105
73
|
|
106
|
-
if reflection.type
|
107
|
-
value = owner.class.base_class.name
|
108
|
-
bind_val = bind scope, table.table_name, reflection.type, value, tracker
|
109
|
-
scope = scope.where(table[reflection.type].eq(bind_val))
|
110
|
-
else
|
111
74
|
scope
|
112
75
|
end
|
113
|
-
end
|
114
76
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
77
|
+
def transform_value(value)
|
78
|
+
value_transformation.call(value)
|
79
|
+
end
|
80
|
+
|
81
|
+
def next_chain_scope(scope, reflection, next_reflection)
|
82
|
+
join_keys = reflection.join_keys
|
83
|
+
key = join_keys.key
|
84
|
+
foreign_key = join_keys.foreign_key
|
119
85
|
|
120
|
-
|
86
|
+
table = reflection.aliased_table
|
87
|
+
foreign_table = next_reflection.aliased_table
|
88
|
+
constraint = table[key].eq(foreign_table[foreign_key])
|
89
|
+
|
90
|
+
if reflection.type
|
91
|
+
value = transform_value(next_reflection.klass.polymorphic_name)
|
92
|
+
scope = apply_scope(scope, table, reflection.type, value)
|
93
|
+
end
|
121
94
|
|
122
|
-
|
123
|
-
value = next_reflection.klass.base_class.name
|
124
|
-
bind_val = bind scope, table.table_name, reflection.type, value, tracker
|
125
|
-
scope = scope.where(table[reflection.type].eq(bind_val))
|
95
|
+
scope.joins!(join(foreign_table, constraint))
|
126
96
|
end
|
127
97
|
|
128
|
-
|
129
|
-
|
98
|
+
class ReflectionProxy < SimpleDelegator # :nodoc:
|
99
|
+
attr_reader :aliased_table
|
130
100
|
|
131
|
-
|
132
|
-
|
133
|
-
|
101
|
+
def initialize(reflection, aliased_table)
|
102
|
+
super(reflection)
|
103
|
+
@aliased_table = aliased_table
|
104
|
+
end
|
134
105
|
|
135
|
-
|
106
|
+
def all_includes; nil; end
|
107
|
+
end
|
136
108
|
|
137
|
-
|
138
|
-
|
139
|
-
|
109
|
+
def get_chain(reflection, association, tracker)
|
110
|
+
name = reflection.name
|
111
|
+
chain = [Reflection::RuntimeReflection.new(reflection, association)]
|
112
|
+
reflection.chain.drop(1).each do |refl|
|
113
|
+
aliased_table = tracker.aliased_table_for(
|
114
|
+
refl.table_name,
|
115
|
+
refl.alias_candidate(name),
|
116
|
+
refl.klass.type_caster
|
117
|
+
)
|
118
|
+
chain << ReflectionProxy.new(refl, aliased_table)
|
119
|
+
end
|
120
|
+
chain
|
121
|
+
end
|
140
122
|
|
141
|
-
|
142
|
-
|
123
|
+
def add_constraints(scope, owner, chain)
|
124
|
+
scope = last_chain_scope(scope, chain.last, owner)
|
143
125
|
|
144
|
-
|
145
|
-
|
146
|
-
scope = next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
|
126
|
+
chain.each_cons(2) do |reflection, next_reflection|
|
127
|
+
scope = next_chain_scope(scope, reflection, next_reflection)
|
147
128
|
end
|
148
129
|
|
149
|
-
|
150
|
-
|
130
|
+
chain_head = chain.first
|
131
|
+
chain.reverse_each do |reflection|
|
132
|
+
# Exclude the scope of the association itself, because that
|
133
|
+
# was already merged in the #scope method.
|
134
|
+
reflection.constraints.each do |scope_chain_item|
|
135
|
+
item = eval_scope(reflection, scope_chain_item, owner)
|
151
136
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
item = eval_scope(klass, scope_chain_item, owner)
|
137
|
+
if scope_chain_item == chain_head.scope
|
138
|
+
scope.merge! item.except(:where, :includes, :unscope, :order)
|
139
|
+
end
|
156
140
|
|
157
|
-
|
158
|
-
|
159
|
-
|
141
|
+
reflection.all_includes do
|
142
|
+
scope.includes! item.includes_values
|
143
|
+
end
|
160
144
|
|
161
|
-
|
162
|
-
scope.
|
145
|
+
scope.unscope!(*item.unscope_values)
|
146
|
+
scope.where_clause += item.where_clause
|
147
|
+
scope.order_values = item.order_values | scope.order_values
|
163
148
|
end
|
164
|
-
|
165
|
-
scope.where_values += item.where_values
|
166
|
-
scope.bind_values += item.bind_values
|
167
|
-
scope.order_values |= item.order_values
|
168
149
|
end
|
169
|
-
end
|
170
|
-
|
171
|
-
scope
|
172
|
-
end
|
173
150
|
|
174
|
-
|
175
|
-
|
176
|
-
end
|
151
|
+
scope
|
152
|
+
end
|
177
153
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
else
|
185
|
-
reflection.table_name
|
154
|
+
def apply_scope(scope, table, key, value)
|
155
|
+
if scope.table == table
|
156
|
+
scope.where!(key => value)
|
157
|
+
else
|
158
|
+
scope.where!(table.name => { key => value })
|
159
|
+
end
|
186
160
|
end
|
187
|
-
end
|
188
161
|
|
189
|
-
|
190
|
-
|
191
|
-
|
162
|
+
def eval_scope(reflection, scope, owner)
|
163
|
+
relation = reflection.build_scope(reflection.aliased_table)
|
164
|
+
relation.instance_exec(owner, &scope) || relation
|
165
|
+
end
|
192
166
|
end
|
193
167
|
end
|
194
168
|
end
|