activerecord 4.2.11.1 → 5.2.4
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 +579 -1635
- 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 +263 -249
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +77 -43
- data/lib/active_record/associations/association_scope.rb +106 -133
- data/lib/active_record/associations/belongs_to_association.rb +52 -41
- 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 +9 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
- 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 +139 -280
- data/lib/active_record/associations/collection_proxy.rb +231 -133
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_association.rb +34 -89
- data/lib/active_record/associations/has_many_through_association.rb +49 -76
- data/lib/active_record/associations/has_one_association.rb +38 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
- 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 +133 -159
- data/lib/active_record/associations/preloader/association.rb +85 -120
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +81 -91
- data/lib/active_record/associations/singular_association.rb +27 -34
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1732 -1597
- 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 +10 -8
- data/lib/active_record/attribute_methods/dirty.rb +94 -135
- 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 +58 -36
- data/lib/active_record/attribute_methods/write.rb +30 -45
- data/lib/active_record/attribute_methods.rb +166 -109
- data/lib/active_record/attributes.rb +201 -82
- data/lib/active_record/autosave_association.rb +94 -36
- data/lib/active_record/base.rb +57 -44
- 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 +24 -12
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
- data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
- data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
- 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 +41 -180
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- 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 +4 -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 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -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 +5 -7
- 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 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
- 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 +462 -284
- 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 +432 -323
- 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 -308
- 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 +178 -198
- data/lib/active_record/counter_cache.rb +79 -36
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +135 -88
- data/lib/active_record/errors.rb +179 -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 +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +188 -132
- 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 +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +88 -96
- 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 +581 -282
- data/lib/active_record/model_schema.rb +290 -111
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +7 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +347 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +94 -32
- 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 +149 -156
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +414 -267
- 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 +256 -248
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +288 -239
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +86 -86
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
- 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 +116 -119
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +448 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- 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 -340
- 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 -16
- data/lib/active_record/scoping/default.rb +102 -85
- 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 +134 -96
- data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
- 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 +199 -124
- 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 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- 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 +24 -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 +40 -41
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +34 -22
- 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 -3
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
- 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/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +72 -49
- 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 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- 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 -31
- data/lib/active_record/type/decimal.rb +0 -64
- 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 -59
- 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 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
@@ -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
|
-
inverse.target = owner
|
110
|
-
inverse.inversed = true
|
100
|
+
if inverse = inverse_association_for(record)
|
101
|
+
inverse.inversed_from(owner)
|
111
102
|
end
|
112
103
|
record
|
113
104
|
end
|
114
105
|
|
106
|
+
def set_inverse_instance_from_queries(record)
|
107
|
+
if inverse = inverse_association_for(record)
|
108
|
+
inverse.inversed_from_queries(owner)
|
109
|
+
end
|
110
|
+
record
|
111
|
+
end
|
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
|
|
@@ -214,12 +241,19 @@ module ActiveRecord
|
|
214
241
|
unless record.is_a?(reflection.klass)
|
215
242
|
fresh_class = reflection.class_name.safe_constantize
|
216
243
|
unless fresh_class && record.is_a?(fresh_class)
|
217
|
-
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected,
|
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})"
|
218
246
|
raise ActiveRecord::AssociationTypeMismatch, message
|
219
247
|
end
|
220
248
|
end
|
221
249
|
end
|
222
250
|
|
251
|
+
def inverse_association_for(record)
|
252
|
+
if invertible_for?(record)
|
253
|
+
record.association(inverse_reflection_for(record).name)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
223
257
|
# Can be redefined by subclasses, notably polymorphic belongs_to
|
224
258
|
# The record parameter is necessary to support polymorphic inverses as we must check for
|
225
259
|
# the association in the specific class of the record.
|
@@ -242,22 +276,22 @@ module ActiveRecord
|
|
242
276
|
# so that when stale_state is different from the value stored on the last find_target,
|
243
277
|
# the target is stale.
|
244
278
|
#
|
245
|
-
# 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.
|
246
280
|
def stale_state
|
247
281
|
end
|
248
282
|
|
249
283
|
def build_record(attributes)
|
250
284
|
reflection.build_association(attributes) do |record|
|
251
|
-
initialize_attributes(record)
|
285
|
+
initialize_attributes(record, attributes)
|
286
|
+
yield(record) if block_given?
|
252
287
|
end
|
253
288
|
end
|
254
289
|
|
255
290
|
# Returns true if statement cache should be skipped on the association reader.
|
256
|
-
def skip_statement_cache?
|
257
|
-
reflection.
|
291
|
+
def skip_statement_cache?(scope)
|
292
|
+
reflection.has_scope? ||
|
258
293
|
scope.eager_loading? ||
|
259
|
-
klass.
|
260
|
-
klass.default_scopes.any? ||
|
294
|
+
klass.scope_attributes? ||
|
261
295
|
reflection.source_reflection.active_record.default_scopes.any?
|
262
296
|
end
|
263
297
|
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,147 +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.unscope!(*item.unscope_values)
|
166
|
-
scope.where_values += item.where_values
|
167
|
-
scope.bind_values += item.bind_values
|
168
|
-
scope.order_values |= item.order_values
|
169
149
|
end
|
170
|
-
end
|
171
|
-
|
172
|
-
scope
|
173
|
-
end
|
174
150
|
|
175
|
-
|
176
|
-
|
177
|
-
end
|
151
|
+
scope
|
152
|
+
end
|
178
153
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
else
|
186
|
-
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
|
187
160
|
end
|
188
|
-
end
|
189
161
|
|
190
|
-
|
191
|
-
|
192
|
-
|
162
|
+
def eval_scope(reflection, scope, owner)
|
163
|
+
relation = reflection.build_scope(reflection.aliased_table)
|
164
|
+
relation.instance_exec(owner, &scope) || relation
|
165
|
+
end
|
193
166
|
end
|
194
167
|
end
|
195
168
|
end
|