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,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/filters"
|
4
|
+
require "concurrent/map"
|
3
5
|
|
4
6
|
module ActiveRecord
|
5
7
|
# = Active Record Reflection
|
@@ -7,41 +9,42 @@ module ActiveRecord
|
|
7
9
|
extend ActiveSupport::Concern
|
8
10
|
|
9
11
|
included do
|
10
|
-
class_attribute :_reflections
|
11
|
-
class_attribute :aggregate_reflections
|
12
|
-
self._reflections = {}
|
13
|
-
self.aggregate_reflections = {}
|
12
|
+
class_attribute :_reflections, instance_writer: false, default: {}
|
13
|
+
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.create(macro, name, scope, options, ar)
|
17
|
-
klass =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
17
|
+
klass = \
|
18
|
+
case macro
|
19
|
+
when :composed_of
|
20
|
+
AggregateReflection
|
21
|
+
when :has_many
|
22
|
+
HasManyReflection
|
23
|
+
when :has_one
|
24
|
+
HasOneReflection
|
25
|
+
when :belongs_to
|
26
|
+
BelongsToReflection
|
27
|
+
else
|
28
|
+
raise "Unsupported Macro: #{macro}"
|
29
|
+
end
|
29
30
|
|
30
31
|
reflection = klass.new(name, scope, options, ar)
|
31
32
|
options[:through] ? ThroughReflection.new(reflection) : reflection
|
32
33
|
end
|
33
34
|
|
34
35
|
def self.add_reflection(ar, name, reflection)
|
35
|
-
ar.
|
36
|
+
ar.clear_reflections_cache
|
37
|
+
name = name.to_s
|
38
|
+
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
36
39
|
end
|
37
40
|
|
38
41
|
def self.add_aggregate_reflection(ar, name, reflection)
|
39
42
|
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
|
40
43
|
end
|
41
44
|
|
42
|
-
# \Reflection enables
|
43
|
-
#
|
44
|
-
#
|
45
|
+
# \Reflection enables the ability to examine the associations and aggregations of
|
46
|
+
# Active Record classes and objects. This information, for example,
|
47
|
+
# can be used in a form builder that takes an Active Record object
|
45
48
|
# and creates input fields for all of the attributes depending on their type
|
46
49
|
# and displays the associations to other objects.
|
47
50
|
#
|
@@ -61,22 +64,27 @@ module ActiveRecord
|
|
61
64
|
aggregate_reflections[aggregation.to_s]
|
62
65
|
end
|
63
66
|
|
64
|
-
# Returns a Hash of name of the reflection as the key and
|
67
|
+
# Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
|
65
68
|
#
|
66
69
|
# Account.reflections # => {"balance" => AggregateReflection}
|
67
70
|
#
|
68
|
-
# @api public
|
69
71
|
def reflections
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
@__reflections ||= begin
|
73
|
+
ref = {}
|
74
|
+
|
75
|
+
_reflections.each do |name, reflection|
|
76
|
+
parent_reflection = reflection.parent_reflection
|
77
|
+
|
78
|
+
if parent_reflection
|
79
|
+
parent_name = parent_reflection.name
|
80
|
+
ref[parent_name.to_s] = parent_reflection
|
81
|
+
else
|
82
|
+
ref[name] = reflection
|
83
|
+
end
|
77
84
|
end
|
85
|
+
|
86
|
+
ref
|
78
87
|
end
|
79
|
-
ref
|
80
88
|
end
|
81
89
|
|
82
90
|
# Returns an array of AssociationReflection objects for all the
|
@@ -89,10 +97,10 @@ module ActiveRecord
|
|
89
97
|
# Account.reflect_on_all_associations # returns an array of all associations
|
90
98
|
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
91
99
|
#
|
92
|
-
# @api public
|
93
100
|
def reflect_on_all_associations(macro = nil)
|
94
101
|
association_reflections = reflections.values
|
95
|
-
|
102
|
+
association_reflections.select! { |reflection| reflection.macro == macro } if macro
|
103
|
+
association_reflections
|
96
104
|
end
|
97
105
|
|
98
106
|
# Returns the AssociationReflection object for the +association+ (use the symbol).
|
@@ -100,27 +108,42 @@ module ActiveRecord
|
|
100
108
|
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
101
109
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
102
110
|
#
|
103
|
-
# @api public
|
104
111
|
def reflect_on_association(association)
|
105
112
|
reflections[association.to_s]
|
106
113
|
end
|
107
114
|
|
108
|
-
# @api private
|
109
115
|
def _reflect_on_association(association) #:nodoc:
|
110
116
|
_reflections[association.to_s]
|
111
117
|
end
|
112
118
|
|
113
119
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
114
|
-
#
|
115
|
-
# @api public
|
116
120
|
def reflect_on_all_autosave_associations
|
117
121
|
reflections.values.select { |reflection| reflection.options[:autosave] }
|
118
122
|
end
|
123
|
+
|
124
|
+
def clear_reflections_cache # :nodoc:
|
125
|
+
@__reflections = nil
|
126
|
+
end
|
119
127
|
end
|
120
128
|
|
121
|
-
# Holds all the methods that are shared between MacroReflection
|
122
|
-
#
|
129
|
+
# Holds all the methods that are shared between MacroReflection and ThroughReflection.
|
130
|
+
#
|
131
|
+
# AbstractReflection
|
132
|
+
# MacroReflection
|
133
|
+
# AggregateReflection
|
134
|
+
# AssociationReflection
|
135
|
+
# HasManyReflection
|
136
|
+
# HasOneReflection
|
137
|
+
# BelongsToReflection
|
138
|
+
# HasAndBelongsToManyReflection
|
139
|
+
# ThroughReflection
|
140
|
+
# PolymorphicReflection
|
141
|
+
# RuntimeReflection
|
123
142
|
class AbstractReflection # :nodoc:
|
143
|
+
def through_reflection?
|
144
|
+
false
|
145
|
+
end
|
146
|
+
|
124
147
|
def table_name
|
125
148
|
klass.table_name
|
126
149
|
end
|
@@ -131,14 +154,6 @@ module ActiveRecord
|
|
131
154
|
klass.new(attributes, &block)
|
132
155
|
end
|
133
156
|
|
134
|
-
def quoted_table_name
|
135
|
-
klass.quoted_table_name
|
136
|
-
end
|
137
|
-
|
138
|
-
def primary_key_type
|
139
|
-
klass.type_for_attribute(klass.primary_key)
|
140
|
-
end
|
141
|
-
|
142
157
|
# Returns the class name for the macro.
|
143
158
|
#
|
144
159
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
@@ -149,29 +164,161 @@ module ActiveRecord
|
|
149
164
|
|
150
165
|
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
|
151
166
|
|
152
|
-
def join_keys
|
153
|
-
|
167
|
+
def join_keys
|
168
|
+
@join_keys ||= get_join_keys(klass)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns a list of scopes that should be applied for this Reflection
|
172
|
+
# object when querying the database.
|
173
|
+
def scopes
|
174
|
+
scope ? [scope] : []
|
175
|
+
end
|
176
|
+
|
177
|
+
def join_scope(table, foreign_table, foreign_klass)
|
178
|
+
predicate_builder = predicate_builder(table)
|
179
|
+
scope_chain_items = join_scopes(table, predicate_builder)
|
180
|
+
klass_scope = klass_join_scope(table, predicate_builder)
|
181
|
+
|
182
|
+
key = join_keys.key
|
183
|
+
foreign_key = join_keys.foreign_key
|
184
|
+
|
185
|
+
klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
|
186
|
+
|
187
|
+
if type
|
188
|
+
klass_scope.where!(type => foreign_klass.polymorphic_name)
|
189
|
+
end
|
190
|
+
|
191
|
+
if klass.finder_needs_type_condition?
|
192
|
+
klass_scope.where!(klass.send(:type_condition, table))
|
193
|
+
end
|
194
|
+
|
195
|
+
scope_chain_items.inject(klass_scope, &:merge!)
|
196
|
+
end
|
197
|
+
|
198
|
+
def join_scopes(table, predicate_builder) # :nodoc:
|
199
|
+
if scope
|
200
|
+
[scope_for(build_scope(table, predicate_builder))]
|
201
|
+
else
|
202
|
+
[]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def klass_join_scope(table, predicate_builder) # :nodoc:
|
207
|
+
relation = build_scope(table, predicate_builder)
|
208
|
+
klass.scope_for_association(relation)
|
209
|
+
end
|
210
|
+
|
211
|
+
def constraints
|
212
|
+
chain.flat_map(&:scopes)
|
213
|
+
end
|
214
|
+
|
215
|
+
def counter_cache_column
|
216
|
+
if belongs_to?
|
217
|
+
if options[:counter_cache] == true
|
218
|
+
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
219
|
+
elsif options[:counter_cache]
|
220
|
+
options[:counter_cache].to_s
|
221
|
+
end
|
222
|
+
else
|
223
|
+
options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def inverse_of
|
228
|
+
return unless inverse_name
|
229
|
+
|
230
|
+
@inverse_of ||= klass._reflect_on_association inverse_name
|
231
|
+
end
|
232
|
+
|
233
|
+
def check_validity_of_inverse!
|
234
|
+
unless polymorphic?
|
235
|
+
if has_inverse? && inverse_of.nil?
|
236
|
+
raise InverseOfAssociationNotFoundError.new(self)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# This shit is nasty. We need to avoid the following situation:
|
242
|
+
#
|
243
|
+
# * An associated record is deleted via record.destroy
|
244
|
+
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
245
|
+
# :counter_cache options which points back at our owner. So they update the
|
246
|
+
# counter cache.
|
247
|
+
# * In which case, we must make sure to *not* update the counter cache, or else
|
248
|
+
# it will be decremented twice.
|
249
|
+
#
|
250
|
+
# Hence this method.
|
251
|
+
def inverse_which_updates_counter_cache
|
252
|
+
return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
|
253
|
+
@inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
|
254
|
+
inverse.counter_cache_column == counter_cache_column
|
255
|
+
end
|
256
|
+
end
|
257
|
+
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
258
|
+
|
259
|
+
def inverse_updates_counter_in_memory?
|
260
|
+
inverse_of && inverse_which_updates_counter_cache == inverse_of
|
261
|
+
end
|
262
|
+
|
263
|
+
# Returns whether a counter cache should be used for this association.
|
264
|
+
#
|
265
|
+
# The counter_cache option must be given on either the owner or inverse
|
266
|
+
# association, and the column must be present on the owner.
|
267
|
+
def has_cached_counter?
|
268
|
+
options[:counter_cache] ||
|
269
|
+
inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
|
270
|
+
!!active_record.columns_hash[counter_cache_column]
|
271
|
+
end
|
272
|
+
|
273
|
+
def counter_must_be_updated_by_has_many?
|
274
|
+
!inverse_updates_counter_in_memory? && has_cached_counter?
|
275
|
+
end
|
276
|
+
|
277
|
+
def alias_candidate(name)
|
278
|
+
"#{plural_name}_#{name}"
|
279
|
+
end
|
280
|
+
|
281
|
+
def chain
|
282
|
+
collect_join_chain
|
283
|
+
end
|
284
|
+
|
285
|
+
def get_join_keys(association_klass)
|
286
|
+
JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
|
154
287
|
end
|
155
288
|
|
156
|
-
def
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
289
|
+
def build_scope(table, predicate_builder = predicate_builder(table))
|
290
|
+
Relation.create(
|
291
|
+
klass,
|
292
|
+
table: table,
|
293
|
+
predicate_builder: predicate_builder
|
294
|
+
)
|
295
|
+
end
|
296
|
+
|
297
|
+
def join_primary_key(*)
|
298
|
+
foreign_key
|
299
|
+
end
|
161
300
|
|
162
|
-
|
301
|
+
def join_foreign_key
|
302
|
+
active_record_primary_key
|
163
303
|
end
|
304
|
+
|
305
|
+
protected
|
306
|
+
def actual_source_reflection # FIXME: this is a horrible name
|
307
|
+
self
|
308
|
+
end
|
309
|
+
|
310
|
+
private
|
311
|
+
def predicate_builder(table)
|
312
|
+
PredicateBuilder.new(TableMetadata.new(klass, table))
|
313
|
+
end
|
314
|
+
|
315
|
+
def primary_key(klass)
|
316
|
+
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
317
|
+
end
|
164
318
|
end
|
319
|
+
|
165
320
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
166
321
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
167
|
-
#
|
168
|
-
# MacroReflection
|
169
|
-
# AssociationReflection
|
170
|
-
# AggregateReflection
|
171
|
-
# HasManyReflection
|
172
|
-
# HasOneReflection
|
173
|
-
# BelongsToReflection
|
174
|
-
# ThroughReflection
|
175
322
|
class MacroReflection < AbstractReflection
|
176
323
|
# Returns the name of the macro.
|
177
324
|
#
|
@@ -196,15 +343,14 @@ module ActiveRecord
|
|
196
343
|
@scope = scope
|
197
344
|
@options = options
|
198
345
|
@active_record = active_record
|
199
|
-
@klass = options[:
|
346
|
+
@klass = options[:anonymous_class]
|
200
347
|
@plural_name = active_record.pluralize_table_names ?
|
201
348
|
name.to_s.pluralize : name.to_s
|
202
349
|
end
|
203
350
|
|
204
351
|
def autosave=(autosave)
|
205
|
-
@automatic_inverse_of = false
|
206
352
|
@options[:autosave] = autosave
|
207
|
-
|
353
|
+
parent_reflection = self.parent_reflection
|
208
354
|
if parent_reflection
|
209
355
|
parent_reflection.autosave = autosave
|
210
356
|
end
|
@@ -214,6 +360,17 @@ module ActiveRecord
|
|
214
360
|
#
|
215
361
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
216
362
|
# <tt>has_many :clients</tt> returns the Client class
|
363
|
+
#
|
364
|
+
# class Company < ActiveRecord::Base
|
365
|
+
# has_many :clients
|
366
|
+
# end
|
367
|
+
#
|
368
|
+
# Company.reflect_on_association(:clients).klass
|
369
|
+
# # => Client
|
370
|
+
#
|
371
|
+
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
|
372
|
+
# a new association object. Use +build_association+ or +create_association+
|
373
|
+
# instead. This allows plugins to hook into association object creation.
|
217
374
|
def klass
|
218
375
|
@klass ||= compute_class(class_name)
|
219
376
|
end
|
@@ -232,14 +389,17 @@ module ActiveRecord
|
|
232
389
|
active_record == other_aggregation.active_record
|
233
390
|
end
|
234
391
|
|
392
|
+
def scope_for(relation, owner = nil)
|
393
|
+
relation.instance_exec(owner, &scope) || relation
|
394
|
+
end
|
395
|
+
|
235
396
|
private
|
236
397
|
def derive_class_name
|
237
398
|
name.to_s.camelize
|
238
399
|
end
|
239
400
|
end
|
240
401
|
|
241
|
-
|
242
|
-
# Holds all the meta-data about an aggregation as it was specified in the
|
402
|
+
# Holds all the metadata about an aggregation as it was specified in the
|
243
403
|
# Active Record class.
|
244
404
|
class AggregateReflection < MacroReflection #:nodoc:
|
245
405
|
def mapping
|
@@ -248,50 +408,37 @@ module ActiveRecord
|
|
248
408
|
end
|
249
409
|
end
|
250
410
|
|
251
|
-
# Holds all the
|
411
|
+
# Holds all the metadata about an association as it was specified in the
|
252
412
|
# Active Record class.
|
253
413
|
class AssociationReflection < MacroReflection #:nodoc:
|
254
|
-
# Returns the target association's class.
|
255
|
-
#
|
256
|
-
# class Author < ActiveRecord::Base
|
257
|
-
# has_many :books
|
258
|
-
# end
|
259
|
-
#
|
260
|
-
# Author.reflect_on_association(:books).klass
|
261
|
-
# # => Book
|
262
|
-
#
|
263
|
-
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
|
264
|
-
# a new association object. Use +build_association+ or +create_association+
|
265
|
-
# instead. This allows plugins to hook into association object creation.
|
266
|
-
def klass
|
267
|
-
@klass ||= compute_class(class_name)
|
268
|
-
end
|
269
|
-
|
270
414
|
def compute_class(name)
|
415
|
+
if polymorphic?
|
416
|
+
raise ArgumentError, "Polymorphic association does not support to compute class."
|
417
|
+
end
|
271
418
|
active_record.send(:compute_type, name)
|
272
419
|
end
|
273
420
|
|
274
421
|
attr_reader :type, :foreign_type
|
275
|
-
attr_accessor :parent_reflection #
|
422
|
+
attr_accessor :parent_reflection # Reflection
|
276
423
|
|
277
424
|
def initialize(name, scope, options, active_record)
|
278
425
|
super
|
279
|
-
@automatic_inverse_of = nil
|
280
426
|
@type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
|
281
|
-
@foreign_type = options[:foreign_type] || "#{name}_type"
|
427
|
+
@foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
|
282
428
|
@constructable = calculate_constructable(macro, options)
|
283
|
-
@association_scope_cache =
|
284
|
-
|
429
|
+
@association_scope_cache = Concurrent::Map.new
|
430
|
+
|
431
|
+
if options[:class_name] && options[:class_name].class == Class
|
432
|
+
raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
|
433
|
+
end
|
285
434
|
end
|
286
435
|
|
287
|
-
def association_scope_cache(conn, owner)
|
436
|
+
def association_scope_cache(conn, owner, &block)
|
288
437
|
key = conn.prepared_statements
|
289
438
|
if polymorphic?
|
290
439
|
key = [key, owner._read_attribute(@foreign_type)]
|
291
440
|
end
|
292
|
-
@association_scope_cache
|
293
|
-
@association_scope_cache[key] ||= yield
|
294
|
-
}
|
441
|
+
@association_scope_cache.compute_if_absent(key) { StatementCache.create(conn, &block) }
|
295
442
|
end
|
296
443
|
|
297
444
|
def constructable? # :nodoc:
|
@@ -303,7 +450,7 @@ module ActiveRecord
|
|
303
450
|
end
|
304
451
|
|
305
452
|
def foreign_key
|
306
|
-
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
453
|
+
@foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
|
307
454
|
end
|
308
455
|
|
309
456
|
def association_foreign_key
|
@@ -319,44 +466,25 @@ module ActiveRecord
|
|
319
466
|
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
|
320
467
|
end
|
321
468
|
|
322
|
-
def counter_cache_column
|
323
|
-
if options[:counter_cache] == true
|
324
|
-
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
325
|
-
elsif options[:counter_cache]
|
326
|
-
options[:counter_cache].to_s
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
469
|
def check_validity!
|
331
470
|
check_validity_of_inverse!
|
332
471
|
end
|
333
472
|
|
334
|
-
def check_validity_of_inverse!
|
335
|
-
unless polymorphic?
|
336
|
-
if has_inverse? && inverse_of.nil?
|
337
|
-
raise InverseOfAssociationNotFoundError.new(self)
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
473
|
def check_preloadable!
|
343
474
|
return unless scope
|
344
475
|
|
345
476
|
if scope.arity > 0
|
346
|
-
|
477
|
+
raise ArgumentError, <<-MSG.squish
|
347
478
|
The association scope '#{name}' is instance dependent (the scope
|
348
|
-
block takes an argument). Preloading
|
349
|
-
|
350
|
-
passed to the association scope. This will most likely result in
|
351
|
-
broken or incorrect behavior. Joining, Preloading and eager loading
|
352
|
-
of these associations is deprecated and will be removed in the future.
|
479
|
+
block takes an argument). Preloading instance dependent scopes is
|
480
|
+
not supported.
|
353
481
|
MSG
|
354
482
|
end
|
355
483
|
end
|
356
484
|
alias :check_eager_loadable! :check_preloadable!
|
357
485
|
|
358
486
|
def join_id_for(owner) # :nodoc:
|
359
|
-
owner[
|
487
|
+
owner[join_foreign_key]
|
360
488
|
end
|
361
489
|
|
362
490
|
def through_reflection
|
@@ -369,30 +497,28 @@ module ActiveRecord
|
|
369
497
|
|
370
498
|
# A chain of reflections from this one back to the owner. For more see the explanation in
|
371
499
|
# ThroughReflection.
|
372
|
-
def
|
500
|
+
def collect_join_chain
|
373
501
|
[self]
|
374
502
|
end
|
375
503
|
|
504
|
+
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
505
|
+
# SQL queries on associations.
|
506
|
+
def clear_association_scope_cache # :nodoc:
|
507
|
+
@association_scope_cache.clear
|
508
|
+
end
|
509
|
+
|
376
510
|
def nested?
|
377
511
|
false
|
378
512
|
end
|
379
513
|
|
380
|
-
|
381
|
-
|
382
|
-
def scope_chain
|
383
|
-
scope ? [[scope]] : [[]]
|
514
|
+
def has_scope?
|
515
|
+
scope
|
384
516
|
end
|
385
517
|
|
386
518
|
def has_inverse?
|
387
519
|
inverse_name
|
388
520
|
end
|
389
521
|
|
390
|
-
def inverse_of
|
391
|
-
return unless inverse_name
|
392
|
-
|
393
|
-
@inverse_of ||= klass._reflect_on_association inverse_name
|
394
|
-
end
|
395
|
-
|
396
522
|
def polymorphic_inverse_of(associated_class)
|
397
523
|
if has_inverse?
|
398
524
|
if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
|
@@ -434,72 +560,52 @@ module ActiveRecord
|
|
434
560
|
# Returns +true+ if +self+ is a +has_one+ reflection.
|
435
561
|
def has_one?; false; end
|
436
562
|
|
437
|
-
def association_class
|
438
|
-
case macro
|
439
|
-
when :belongs_to
|
440
|
-
if polymorphic?
|
441
|
-
Associations::BelongsToPolymorphicAssociation
|
442
|
-
else
|
443
|
-
Associations::BelongsToAssociation
|
444
|
-
end
|
445
|
-
when :has_many
|
446
|
-
if options[:through]
|
447
|
-
Associations::HasManyThroughAssociation
|
448
|
-
else
|
449
|
-
Associations::HasManyAssociation
|
450
|
-
end
|
451
|
-
when :has_one
|
452
|
-
if options[:through]
|
453
|
-
Associations::HasOneThroughAssociation
|
454
|
-
else
|
455
|
-
Associations::HasOneAssociation
|
456
|
-
end
|
457
|
-
end
|
458
|
-
end
|
563
|
+
def association_class; raise NotImplementedError; end
|
459
564
|
|
460
565
|
def polymorphic?
|
461
566
|
options[:polymorphic]
|
462
567
|
end
|
463
568
|
|
464
569
|
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
|
465
|
-
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:
|
570
|
+
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:through, :foreign_key]
|
466
571
|
|
467
|
-
|
572
|
+
def add_as_source(seed)
|
573
|
+
seed
|
574
|
+
end
|
468
575
|
|
469
|
-
|
470
|
-
|
471
|
-
|
576
|
+
def add_as_polymorphic_through(reflection, seed)
|
577
|
+
seed + [PolymorphicReflection.new(self, reflection)]
|
578
|
+
end
|
579
|
+
|
580
|
+
def add_as_through(seed)
|
581
|
+
seed + [self]
|
582
|
+
end
|
583
|
+
|
584
|
+
def extensions
|
585
|
+
Array(options[:extend])
|
586
|
+
end
|
472
587
|
|
473
588
|
private
|
474
589
|
|
475
590
|
def calculate_constructable(macro, options)
|
476
|
-
|
477
|
-
when :belongs_to
|
478
|
-
!polymorphic?
|
479
|
-
when :has_one
|
480
|
-
!options[:through]
|
481
|
-
else
|
482
|
-
true
|
483
|
-
end
|
591
|
+
true
|
484
592
|
end
|
485
593
|
|
486
594
|
# Attempts to find the inverse association name automatically.
|
487
595
|
# If it cannot find a suitable inverse association name, it returns
|
488
|
-
# nil
|
596
|
+
# +nil+.
|
489
597
|
def inverse_name
|
490
|
-
|
491
|
-
|
492
|
-
nil
|
493
|
-
else
|
494
|
-
@automatic_inverse_of ||= automatic_inverse_of
|
495
|
-
end
|
598
|
+
unless defined?(@inverse_name)
|
599
|
+
@inverse_name = options.fetch(:inverse_of) { automatic_inverse_of }
|
496
600
|
end
|
601
|
+
|
602
|
+
@inverse_name
|
497
603
|
end
|
498
604
|
|
499
|
-
# returns either nil or the inverse association name that it finds.
|
605
|
+
# returns either +nil+ or the inverse association name that it finds.
|
500
606
|
def automatic_inverse_of
|
501
607
|
if can_find_inverse_of_automatically?(self)
|
502
|
-
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name).to_sym
|
608
|
+
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
|
503
609
|
|
504
610
|
begin
|
505
611
|
reflection = klass._reflect_on_association(inverse_name)
|
@@ -513,20 +619,15 @@ module ActiveRecord
|
|
513
619
|
return inverse_name
|
514
620
|
end
|
515
621
|
end
|
516
|
-
|
517
|
-
false
|
518
622
|
end
|
519
623
|
|
520
624
|
# Checks if the inverse reflection that is returned from the
|
521
625
|
# +automatic_inverse_of+ method is a valid reflection. We must
|
522
626
|
# make sure that the reflection's active_record name matches up
|
523
627
|
# with the current reflection's klass name.
|
524
|
-
#
|
525
|
-
# Note: klass will always be valid because when there's a NameError
|
526
|
-
# from calling +klass+, +reflection+ will already be set to false.
|
527
628
|
def valid_inverse_reflection?(reflection)
|
528
629
|
reflection &&
|
529
|
-
klass
|
630
|
+
klass <= reflection.active_record &&
|
530
631
|
can_find_inverse_of_automatically?(reflection)
|
531
632
|
end
|
532
633
|
|
@@ -534,9 +635,8 @@ module ActiveRecord
|
|
534
635
|
# us from being able to guess the inverse automatically. First, the
|
535
636
|
# <tt>inverse_of</tt> option cannot be set to false. Second, we must
|
536
637
|
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
|
537
|
-
# Third, we must not have options such as <tt>:
|
538
|
-
#
|
539
|
-
# inverse association.
|
638
|
+
# Third, we must not have options such as <tt>:foreign_key</tt>
|
639
|
+
# which prevent us from correctly guessing the inverse association.
|
540
640
|
#
|
541
641
|
# Anything with a scope can additionally ruin our attempt at finding an
|
542
642
|
# inverse, so we exclude reflections with scopes.
|
@@ -566,56 +666,78 @@ module ActiveRecord
|
|
566
666
|
def derive_join_table
|
567
667
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
568
668
|
end
|
569
|
-
|
570
|
-
def primary_key(klass)
|
571
|
-
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
572
|
-
end
|
573
669
|
end
|
574
670
|
|
575
671
|
class HasManyReflection < AssociationReflection # :nodoc:
|
576
|
-
def initialize(name, scope, options, active_record)
|
577
|
-
super(name, scope, options, active_record)
|
578
|
-
end
|
579
|
-
|
580
672
|
def macro; :has_many; end
|
581
673
|
|
582
674
|
def collection?; true; end
|
583
|
-
end
|
584
675
|
|
585
|
-
|
586
|
-
|
587
|
-
|
676
|
+
def association_class
|
677
|
+
if options[:through]
|
678
|
+
Associations::HasManyThroughAssociation
|
679
|
+
else
|
680
|
+
Associations::HasManyAssociation
|
681
|
+
end
|
588
682
|
end
|
589
683
|
|
684
|
+
def association_primary_key(klass = nil)
|
685
|
+
primary_key(klass || self.klass)
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
class HasOneReflection < AssociationReflection # :nodoc:
|
590
690
|
def macro; :has_one; end
|
591
691
|
|
592
692
|
def has_one?; true; end
|
593
|
-
end
|
594
693
|
|
595
|
-
|
596
|
-
|
597
|
-
|
694
|
+
def association_class
|
695
|
+
if options[:through]
|
696
|
+
Associations::HasOneThroughAssociation
|
697
|
+
else
|
698
|
+
Associations::HasOneAssociation
|
699
|
+
end
|
598
700
|
end
|
599
701
|
|
702
|
+
private
|
703
|
+
|
704
|
+
def calculate_constructable(macro, options)
|
705
|
+
!options[:through]
|
706
|
+
end
|
707
|
+
end
|
708
|
+
|
709
|
+
class BelongsToReflection < AssociationReflection # :nodoc:
|
600
710
|
def macro; :belongs_to; end
|
601
711
|
|
602
712
|
def belongs_to?; true; end
|
603
713
|
|
604
|
-
def
|
605
|
-
|
606
|
-
|
714
|
+
def association_class
|
715
|
+
if polymorphic?
|
716
|
+
Associations::BelongsToPolymorphicAssociation
|
717
|
+
else
|
718
|
+
Associations::BelongsToAssociation
|
719
|
+
end
|
607
720
|
end
|
608
721
|
|
609
|
-
def
|
610
|
-
|
722
|
+
def join_primary_key(klass = nil)
|
723
|
+
polymorphic? ? association_primary_key(klass) : association_primary_key
|
611
724
|
end
|
612
|
-
end
|
613
725
|
|
614
|
-
|
615
|
-
|
616
|
-
super
|
726
|
+
def join_foreign_key
|
727
|
+
foreign_key
|
617
728
|
end
|
618
729
|
|
730
|
+
private
|
731
|
+
def can_find_inverse_of_automatically?(_)
|
732
|
+
!polymorphic? && super
|
733
|
+
end
|
734
|
+
|
735
|
+
def calculate_constructable(macro, options)
|
736
|
+
!polymorphic?
|
737
|
+
end
|
738
|
+
end
|
739
|
+
|
740
|
+
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
619
741
|
def macro; :has_and_belongs_to_many; end
|
620
742
|
|
621
743
|
def collection?
|
@@ -623,19 +745,22 @@ module ActiveRecord
|
|
623
745
|
end
|
624
746
|
end
|
625
747
|
|
626
|
-
# Holds all the
|
748
|
+
# Holds all the metadata about a :through association as it was specified
|
627
749
|
# in the Active Record class.
|
628
750
|
class ThroughReflection < AbstractReflection #:nodoc:
|
629
|
-
|
630
|
-
|
631
|
-
:active_record_primary_key, :type, :to => :source_reflection
|
751
|
+
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
|
752
|
+
:active_record_primary_key, :type, :get_join_keys, to: :source_reflection
|
632
753
|
|
633
754
|
def initialize(delegate_reflection)
|
634
755
|
@delegate_reflection = delegate_reflection
|
635
|
-
@klass
|
756
|
+
@klass = delegate_reflection.options[:anonymous_class]
|
636
757
|
@source_reflection_name = delegate_reflection.options[:source]
|
637
758
|
end
|
638
759
|
|
760
|
+
def through_reflection?
|
761
|
+
true
|
762
|
+
end
|
763
|
+
|
639
764
|
def klass
|
640
765
|
@klass ||= delegate_reflection.compute_class(class_name)
|
641
766
|
end
|
@@ -694,74 +819,35 @@ module ActiveRecord
|
|
694
819
|
# # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
|
695
820
|
# <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
|
696
821
|
#
|
697
|
-
def
|
698
|
-
|
699
|
-
a = source_reflection.chain
|
700
|
-
b = through_reflection.chain
|
701
|
-
chain = a + b
|
702
|
-
chain[0] = self # Use self so we don't lose the information from :source_type
|
703
|
-
chain
|
704
|
-
end
|
822
|
+
def collect_join_chain
|
823
|
+
collect_join_reflections [self]
|
705
824
|
end
|
706
825
|
|
707
|
-
#
|
708
|
-
#
|
709
|
-
#
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
#
|
714
|
-
# class Article
|
715
|
-
# has_many :comments
|
716
|
-
# has_many :comment_tags, through: :comments, source: :tags
|
717
|
-
# end
|
718
|
-
#
|
719
|
-
# class Comment
|
720
|
-
# has_many :tags
|
721
|
-
# end
|
722
|
-
#
|
723
|
-
# There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
|
724
|
-
# but only Comment.tags will be represented in the #chain. So this method creates an array
|
725
|
-
# of scopes corresponding to the chain.
|
726
|
-
def scope_chain
|
727
|
-
@scope_chain ||= begin
|
728
|
-
scope_chain = source_reflection.scope_chain.map(&:dup)
|
729
|
-
|
730
|
-
# Add to it the scope from this reflection (if any)
|
731
|
-
scope_chain.first << scope if scope
|
732
|
-
|
733
|
-
through_scope_chain = through_reflection.scope_chain.map(&:dup)
|
734
|
-
|
735
|
-
if options[:source_type]
|
736
|
-
type = foreign_type
|
737
|
-
source_type = options[:source_type]
|
738
|
-
through_scope_chain.first << lambda { |object|
|
739
|
-
where(type => source_type)
|
740
|
-
}
|
741
|
-
end
|
742
|
-
|
743
|
-
# Recursively fill out the rest of the array from the through reflection
|
744
|
-
scope_chain + through_scope_chain
|
745
|
-
end
|
826
|
+
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
827
|
+
# SQL queries on associations.
|
828
|
+
def clear_association_scope_cache # :nodoc:
|
829
|
+
delegate_reflection.clear_association_scope_cache
|
830
|
+
source_reflection.clear_association_scope_cache
|
831
|
+
through_reflection.clear_association_scope_cache
|
746
832
|
end
|
747
833
|
|
748
|
-
def
|
749
|
-
source_reflection.
|
834
|
+
def scopes
|
835
|
+
source_reflection.scopes + super
|
750
836
|
end
|
751
837
|
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
ActiveRecord::Base.source_macro is deprecated and will be removed
|
756
|
-
without replacement.
|
757
|
-
MSG
|
838
|
+
def join_scopes(table, predicate_builder) # :nodoc:
|
839
|
+
source_reflection.join_scopes(table, predicate_builder) + super
|
840
|
+
end
|
758
841
|
|
759
|
-
|
842
|
+
def has_scope?
|
843
|
+
scope || options[:source_type] ||
|
844
|
+
source_reflection.has_scope? ||
|
845
|
+
through_reflection.has_scope?
|
760
846
|
end
|
761
847
|
|
762
848
|
# A through association is nested if there would be more than one join table
|
763
849
|
def nested?
|
764
|
-
|
850
|
+
source_reflection.through_reflection? || through_reflection.through_reflection?
|
765
851
|
end
|
766
852
|
|
767
853
|
# We want to use the klass from this reflection, rather than just delegate straight to
|
@@ -791,21 +877,19 @@ module ActiveRecord
|
|
791
877
|
def source_reflection_name # :nodoc:
|
792
878
|
return @source_reflection_name if @source_reflection_name
|
793
879
|
|
794
|
-
names = [name.to_s.singularize, name].collect
|
880
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
795
881
|
names = names.find_all { |n|
|
796
882
|
through_reflection.klass._reflect_on_association(n)
|
797
883
|
}
|
798
884
|
|
799
885
|
if names.length > 1
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
" #{macro} :#{name}, #{example_options}\n" \
|
808
|
-
" end"
|
886
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
887
|
+
active_record.name,
|
888
|
+
macro,
|
889
|
+
name,
|
890
|
+
options,
|
891
|
+
source_reflection_names
|
892
|
+
)
|
809
893
|
end
|
810
894
|
|
811
895
|
@source_reflection_name = names.first
|
@@ -819,10 +903,6 @@ module ActiveRecord
|
|
819
903
|
through_reflection.options
|
820
904
|
end
|
821
905
|
|
822
|
-
def join_id_for(owner) # :nodoc:
|
823
|
-
source_reflection.join_id_for(owner)
|
824
|
-
end
|
825
|
-
|
826
906
|
def check_validity!
|
827
907
|
if through_reflection.nil?
|
828
908
|
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
|
@@ -852,20 +932,56 @@ module ActiveRecord
|
|
852
932
|
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
|
853
933
|
end
|
854
934
|
|
935
|
+
if parent_reflection.nil?
|
936
|
+
reflections = active_record.reflections.keys.map(&:to_sym)
|
937
|
+
|
938
|
+
if reflections.index(through_reflection.name) > reflections.index(name)
|
939
|
+
raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
|
940
|
+
end
|
941
|
+
end
|
942
|
+
|
855
943
|
check_validity_of_inverse!
|
856
944
|
end
|
857
945
|
|
946
|
+
def constraints
|
947
|
+
scope_chain = source_reflection.constraints
|
948
|
+
scope_chain << scope if scope
|
949
|
+
scope_chain
|
950
|
+
end
|
951
|
+
|
952
|
+
def add_as_source(seed)
|
953
|
+
collect_join_reflections seed
|
954
|
+
end
|
955
|
+
|
956
|
+
def add_as_polymorphic_through(reflection, seed)
|
957
|
+
collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
|
958
|
+
end
|
959
|
+
|
960
|
+
def add_as_through(seed)
|
961
|
+
collect_join_reflections(seed + [self])
|
962
|
+
end
|
963
|
+
|
964
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
965
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
858
966
|
protected
|
967
|
+
attr_reader :delegate_reflection
|
859
968
|
|
860
969
|
def actual_source_reflection # FIXME: this is a horrible name
|
861
|
-
source_reflection.
|
970
|
+
source_reflection.actual_source_reflection
|
862
971
|
end
|
863
972
|
|
864
|
-
|
865
|
-
|
973
|
+
private
|
974
|
+
def collect_join_reflections(seed)
|
975
|
+
a = source_reflection.add_as_source seed
|
976
|
+
if options[:source_type]
|
977
|
+
through_reflection.add_as_polymorphic_through self, a
|
978
|
+
else
|
979
|
+
through_reflection.add_as_through a
|
980
|
+
end
|
866
981
|
end
|
867
982
|
|
868
|
-
|
983
|
+
def inverse_name; delegate_reflection.send(:inverse_name); end
|
984
|
+
|
869
985
|
def derive_class_name
|
870
986
|
# get the class_name of the belongs_to association of the through reflection
|
871
987
|
options[:source_type] || source_reflection.class_name
|
@@ -875,7 +991,50 @@ module ActiveRecord
|
|
875
991
|
public_instance_methods
|
876
992
|
|
877
993
|
delegate(*delegate_methods, to: :delegate_reflection)
|
994
|
+
end
|
995
|
+
|
996
|
+
class PolymorphicReflection < AbstractReflection # :nodoc:
|
997
|
+
delegate :klass, :scope, :plural_name, :type, :get_join_keys, :scope_for, to: :@reflection
|
998
|
+
|
999
|
+
def initialize(reflection, previous_reflection)
|
1000
|
+
@reflection = reflection
|
1001
|
+
@previous_reflection = previous_reflection
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
def join_scopes(table, predicate_builder) # :nodoc:
|
1005
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
|
1006
|
+
scopes << build_scope(table, predicate_builder).instance_exec(nil, &source_type_scope)
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
def constraints
|
1010
|
+
@reflection.constraints + [source_type_scope]
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
private
|
1014
|
+
def source_type_scope
|
1015
|
+
type = @previous_reflection.foreign_type
|
1016
|
+
source_type = @previous_reflection.options[:source_type]
|
1017
|
+
lambda { |object| where(type => source_type) }
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
class RuntimeReflection < AbstractReflection # :nodoc:
|
1022
|
+
delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
|
1023
|
+
|
1024
|
+
def initialize(reflection, association)
|
1025
|
+
@reflection = reflection
|
1026
|
+
@association = association
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
def klass
|
1030
|
+
@association.klass
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
def aliased_table
|
1034
|
+
@aliased_table ||= Arel::Table.new(table_name, type_caster: klass.type_caster)
|
1035
|
+
end
|
878
1036
|
|
1037
|
+
def all_includes; yield; end
|
879
1038
|
end
|
880
1039
|
end
|
881
1040
|
end
|