activerecord 7.0.0 → 7.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +515 -1268
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +28 -17
- data/lib/active_record/associations/collection_proxy.rb +36 -13
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +18 -14
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +2 -4
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +378 -491
- data/lib/active_record/attribute_assignment.rb +1 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +153 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -40
- data/lib/active_record/attributes.rb +63 -48
- data/lib/active_record/autosave_association.rb +70 -38
- data/lib/active_record/base.rb +12 -8
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +124 -132
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +297 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +215 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +319 -135
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +27 -140
- data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +101 -48
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +64 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +98 -106
- data/lib/active_record/core.rb +220 -177
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +88 -35
- data/lib/active_record/delegated_type.rb +40 -11
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +47 -25
- data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
- data/lib/active_record/encryption/encryptor.rb +25 -10
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +23 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +131 -27
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +169 -99
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +39 -24
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +28 -27
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +110 -13
- data/lib/active_record/migration/compatibility.rb +174 -64
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +292 -125
- data/lib/active_record/model_schema.rb +113 -112
- data/lib/active_record/nested_attributes.rb +35 -9
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +177 -345
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +102 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +34 -9
- data/lib/active_record/railtie.rb +153 -100
- data/lib/active_record/railties/controller_runtime.rb +24 -10
- data/lib/active_record/railties/databases.rake +148 -152
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +278 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +293 -108
- data/lib/active_record/relation/delegation.rb +31 -20
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +625 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +602 -96
- data/lib/active_record/result.rb +55 -52
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +76 -30
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +82 -30
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +29 -8
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +191 -121
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +174 -152
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +109 -27
- data/lib/active_record/translation.rb +1 -3
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +12 -6
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +63 -14
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +266 -30
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +59 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -11,6 +11,7 @@ module ActiveRecord
|
|
|
11
11
|
class_attribute :_reflections, instance_writer: false, default: {}
|
|
12
12
|
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
|
13
13
|
class_attribute :automatic_scope_inversing, instance_writer: false, default: false
|
|
14
|
+
class_attribute :automatically_invert_plural_associations, instance_writer: false, default: false
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
class << self
|
|
@@ -21,12 +22,12 @@ module ActiveRecord
|
|
|
21
22
|
|
|
22
23
|
def add_reflection(ar, name, reflection)
|
|
23
24
|
ar.clear_reflections_cache
|
|
24
|
-
name =
|
|
25
|
+
name = name.to_sym
|
|
25
26
|
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def add_aggregate_reflection(ar, name, reflection)
|
|
29
|
-
ar.aggregate_reflections = ar.aggregate_reflections.merge(
|
|
30
|
+
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_sym => reflection)
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
private
|
|
@@ -46,6 +47,8 @@ module ActiveRecord
|
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
49
|
|
|
50
|
+
# = Active Record Reflection
|
|
51
|
+
#
|
|
49
52
|
# \Reflection enables the ability to examine the associations and aggregations of
|
|
50
53
|
# Active Record classes and objects. This information, for example,
|
|
51
54
|
# can be used in a form builder that takes an Active Record object
|
|
@@ -65,7 +68,7 @@ module ActiveRecord
|
|
|
65
68
|
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
|
66
69
|
#
|
|
67
70
|
def reflect_on_aggregation(aggregation)
|
|
68
|
-
aggregate_reflections[aggregation.
|
|
71
|
+
aggregate_reflections[aggregation.to_sym]
|
|
69
72
|
end
|
|
70
73
|
|
|
71
74
|
# Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
|
|
@@ -73,6 +76,10 @@ module ActiveRecord
|
|
|
73
76
|
# Account.reflections # => {"balance" => AggregateReflection}
|
|
74
77
|
#
|
|
75
78
|
def reflections
|
|
79
|
+
normalized_reflections.stringify_keys
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def normalized_reflections # :nodoc
|
|
76
83
|
@__reflections ||= begin
|
|
77
84
|
ref = {}
|
|
78
85
|
|
|
@@ -81,13 +88,13 @@ module ActiveRecord
|
|
|
81
88
|
|
|
82
89
|
if parent_reflection
|
|
83
90
|
parent_name = parent_reflection.name
|
|
84
|
-
ref[parent_name
|
|
91
|
+
ref[parent_name] = parent_reflection
|
|
85
92
|
else
|
|
86
93
|
ref[name] = reflection
|
|
87
94
|
end
|
|
88
95
|
end
|
|
89
96
|
|
|
90
|
-
ref
|
|
97
|
+
ref.freeze
|
|
91
98
|
end
|
|
92
99
|
end
|
|
93
100
|
|
|
@@ -102,7 +109,7 @@ module ActiveRecord
|
|
|
102
109
|
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
|
103
110
|
#
|
|
104
111
|
def reflect_on_all_associations(macro = nil)
|
|
105
|
-
association_reflections =
|
|
112
|
+
association_reflections = normalized_reflections.values
|
|
106
113
|
association_reflections.select! { |reflection| reflection.macro == macro } if macro
|
|
107
114
|
association_reflections
|
|
108
115
|
end
|
|
@@ -113,21 +120,31 @@ module ActiveRecord
|
|
|
113
120
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
|
114
121
|
#
|
|
115
122
|
def reflect_on_association(association)
|
|
116
|
-
|
|
123
|
+
normalized_reflections[association.to_sym]
|
|
117
124
|
end
|
|
118
125
|
|
|
119
126
|
def _reflect_on_association(association) # :nodoc:
|
|
120
|
-
_reflections[association.
|
|
127
|
+
_reflections[association.to_sym]
|
|
121
128
|
end
|
|
122
129
|
|
|
123
130
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
|
124
131
|
def reflect_on_all_autosave_associations
|
|
125
|
-
reflections
|
|
132
|
+
reflections = normalized_reflections.values
|
|
133
|
+
reflections.select! { |reflection| reflection.options[:autosave] }
|
|
134
|
+
reflections
|
|
126
135
|
end
|
|
127
136
|
|
|
128
137
|
def clear_reflections_cache # :nodoc:
|
|
129
138
|
@__reflections = nil
|
|
130
139
|
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
def inherited(subclass)
|
|
143
|
+
super
|
|
144
|
+
subclass.class_eval do
|
|
145
|
+
@__reflections = nil
|
|
146
|
+
end
|
|
147
|
+
end
|
|
131
148
|
end
|
|
132
149
|
|
|
133
150
|
# Holds all the methods that are shared between MacroReflection and ThroughReflection.
|
|
@@ -144,6 +161,14 @@ module ActiveRecord
|
|
|
144
161
|
# PolymorphicReflection
|
|
145
162
|
# RuntimeReflection
|
|
146
163
|
class AbstractReflection # :nodoc:
|
|
164
|
+
def initialize
|
|
165
|
+
@class_name = nil
|
|
166
|
+
@counter_cache_column = nil
|
|
167
|
+
@inverse_of = nil
|
|
168
|
+
@inverse_which_updates_counter_cache_defined = false
|
|
169
|
+
@inverse_which_updates_counter_cache = nil
|
|
170
|
+
end
|
|
171
|
+
|
|
147
172
|
def through_reflection?
|
|
148
173
|
false
|
|
149
174
|
end
|
|
@@ -183,10 +208,14 @@ module ActiveRecord
|
|
|
183
208
|
|
|
184
209
|
scope_chain_items.inject(klass_scope, &:merge!)
|
|
185
210
|
|
|
186
|
-
|
|
187
|
-
|
|
211
|
+
primary_key_column_names = Array(join_primary_key)
|
|
212
|
+
foreign_key_column_names = Array(join_foreign_key)
|
|
188
213
|
|
|
189
|
-
|
|
214
|
+
primary_foreign_key_pairs = primary_key_column_names.zip(foreign_key_column_names)
|
|
215
|
+
|
|
216
|
+
primary_foreign_key_pairs.each do |primary_key_column_name, foreign_key_column_name|
|
|
217
|
+
klass_scope.where!(table[primary_key_column_name].eq(foreign_table[foreign_key_column_name]))
|
|
218
|
+
end
|
|
190
219
|
|
|
191
220
|
if klass.finder_needs_type_condition?
|
|
192
221
|
klass_scope.where!(klass.send(:type_condition, table))
|
|
@@ -213,14 +242,16 @@ module ActiveRecord
|
|
|
213
242
|
end
|
|
214
243
|
|
|
215
244
|
def counter_cache_column
|
|
216
|
-
@counter_cache_column ||=
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
245
|
+
@counter_cache_column ||= begin
|
|
246
|
+
counter_cache = options[:counter_cache]
|
|
247
|
+
|
|
248
|
+
if belongs_to?
|
|
249
|
+
if counter_cache
|
|
250
|
+
counter_cache[:column] || -"#{active_record.name.demodulize.underscore.pluralize}_count"
|
|
251
|
+
end
|
|
252
|
+
else
|
|
253
|
+
-((counter_cache && -counter_cache[:column]) || "#{name}_count")
|
|
221
254
|
end
|
|
222
|
-
else
|
|
223
|
-
-(options[:counter_cache]&.to_s || "#{name}_count")
|
|
224
255
|
end
|
|
225
256
|
end
|
|
226
257
|
|
|
@@ -231,11 +262,11 @@ module ActiveRecord
|
|
|
231
262
|
end
|
|
232
263
|
|
|
233
264
|
def check_validity_of_inverse!
|
|
234
|
-
|
|
235
|
-
if
|
|
265
|
+
if !polymorphic? && has_inverse?
|
|
266
|
+
if inverse_of.nil?
|
|
236
267
|
raise InverseOfAssociationNotFoundError.new(self)
|
|
237
268
|
end
|
|
238
|
-
if
|
|
269
|
+
if inverse_of == self
|
|
239
270
|
raise InverseOfAssociationRecursiveError.new(self)
|
|
240
271
|
end
|
|
241
272
|
end
|
|
@@ -252,10 +283,16 @@ module ActiveRecord
|
|
|
252
283
|
#
|
|
253
284
|
# Hence this method.
|
|
254
285
|
def inverse_which_updates_counter_cache
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
286
|
+
unless @inverse_which_updates_counter_cache_defined
|
|
287
|
+
if counter_cache_column
|
|
288
|
+
inverse_candidates = inverse_of ? [inverse_of] : klass.reflect_on_all_associations(:belongs_to)
|
|
289
|
+
@inverse_which_updates_counter_cache = inverse_candidates.find do |inverse|
|
|
290
|
+
inverse.counter_cache_column == counter_cache_column && (inverse.polymorphic? || inverse.klass == active_record)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
@inverse_which_updates_counter_cache_defined = true
|
|
258
294
|
end
|
|
295
|
+
@inverse_which_updates_counter_cache
|
|
259
296
|
end
|
|
260
297
|
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
|
261
298
|
|
|
@@ -263,7 +300,7 @@ module ActiveRecord
|
|
|
263
300
|
inverse_of && inverse_which_updates_counter_cache == inverse_of
|
|
264
301
|
end
|
|
265
302
|
|
|
266
|
-
# Returns whether a counter cache
|
|
303
|
+
# Returns whether this association has a counter cache.
|
|
267
304
|
#
|
|
268
305
|
# The counter_cache option must be given on either the owner or inverse
|
|
269
306
|
# association, and the column must be present on the owner.
|
|
@@ -273,6 +310,17 @@ module ActiveRecord
|
|
|
273
310
|
active_record.has_attribute?(counter_cache_column)
|
|
274
311
|
end
|
|
275
312
|
|
|
313
|
+
# Returns whether this association has a counter cache and its column values were backfilled
|
|
314
|
+
# (and so it is used internally by methods like +size+/+any?+/etc).
|
|
315
|
+
def has_active_cached_counter?
|
|
316
|
+
return false unless has_cached_counter?
|
|
317
|
+
|
|
318
|
+
counter_cache = options[:counter_cache] ||
|
|
319
|
+
(inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache])
|
|
320
|
+
|
|
321
|
+
counter_cache[:active] != false
|
|
322
|
+
end
|
|
323
|
+
|
|
276
324
|
def counter_must_be_updated_by_has_many?
|
|
277
325
|
!inverse_updates_counter_in_memory? && has_cached_counter?
|
|
278
326
|
end
|
|
@@ -297,6 +345,12 @@ module ActiveRecord
|
|
|
297
345
|
options[:strict_loading]
|
|
298
346
|
end
|
|
299
347
|
|
|
348
|
+
def strict_loading_violation_message(owner)
|
|
349
|
+
message = +"`#{owner}` is marked for strict_loading."
|
|
350
|
+
message << " The #{polymorphic? ? "polymorphic association" : "#{klass} association"}"
|
|
351
|
+
message << " named `:#{name}` cannot be lazily loaded."
|
|
352
|
+
end
|
|
353
|
+
|
|
300
354
|
protected
|
|
301
355
|
def actual_source_reflection # FIXME: this is a horrible name
|
|
302
356
|
self
|
|
@@ -340,9 +394,10 @@ module ActiveRecord
|
|
|
340
394
|
attr_reader :plural_name # :nodoc:
|
|
341
395
|
|
|
342
396
|
def initialize(name, scope, options, active_record)
|
|
397
|
+
super()
|
|
343
398
|
@name = name
|
|
344
399
|
@scope = scope
|
|
345
|
-
@options = options
|
|
400
|
+
@options = normalize_options(options)
|
|
346
401
|
@active_record = active_record
|
|
347
402
|
@klass = options[:anonymous_class]
|
|
348
403
|
@plural_name = active_record.pluralize_table_names ?
|
|
@@ -373,7 +428,15 @@ module ActiveRecord
|
|
|
373
428
|
# a new association object. Use +build_association+ or +create_association+
|
|
374
429
|
# instead. This allows plugins to hook into association object creation.
|
|
375
430
|
def klass
|
|
376
|
-
@klass ||=
|
|
431
|
+
@klass ||= _klass(class_name)
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def _klass(class_name) # :nodoc:
|
|
435
|
+
if active_record.name.demodulize == class_name
|
|
436
|
+
return compute_class("::#{class_name}") rescue NameError
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
compute_class(class_name)
|
|
377
440
|
end
|
|
378
441
|
|
|
379
442
|
def compute_class(name)
|
|
@@ -398,6 +461,26 @@ module ActiveRecord
|
|
|
398
461
|
def derive_class_name
|
|
399
462
|
name.to_s.camelize
|
|
400
463
|
end
|
|
464
|
+
|
|
465
|
+
def normalize_options(options)
|
|
466
|
+
counter_cache = options.delete(:counter_cache)
|
|
467
|
+
|
|
468
|
+
if counter_cache
|
|
469
|
+
active = true
|
|
470
|
+
|
|
471
|
+
case counter_cache
|
|
472
|
+
when String, Symbol
|
|
473
|
+
column = -counter_cache.to_s
|
|
474
|
+
when Hash
|
|
475
|
+
active = counter_cache.fetch(:active, true)
|
|
476
|
+
column = counter_cache[:column]&.to_s
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
options[:counter_cache] = { active: active, column: column }
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
options
|
|
483
|
+
end
|
|
401
484
|
end
|
|
402
485
|
|
|
403
486
|
# Holds all the metadata about an aggregation as it was specified in the
|
|
@@ -417,23 +500,23 @@ module ActiveRecord
|
|
|
417
500
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
|
418
501
|
end
|
|
419
502
|
|
|
420
|
-
msg = <<-MSG.squish
|
|
421
|
-
Rails couldn't find a valid model for #{name} association.
|
|
422
|
-
Please provide the :class_name option on the association declaration.
|
|
423
|
-
If :class_name is already provided, make sure it's an ActiveRecord::Base subclass.
|
|
424
|
-
MSG
|
|
425
|
-
|
|
426
503
|
begin
|
|
427
504
|
klass = active_record.send(:compute_type, name)
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
505
|
+
rescue NameError => error
|
|
506
|
+
if error.name.match?(/(?:\A|::)#{name}\z/)
|
|
507
|
+
message = "Missing model class #{name} for the #{active_record}##{self.name} association."
|
|
508
|
+
message += " You can specify a different model class with the :class_name option." unless options[:class_name]
|
|
509
|
+
raise NameError.new(message, name)
|
|
510
|
+
else
|
|
511
|
+
raise
|
|
431
512
|
end
|
|
513
|
+
end
|
|
432
514
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
raise NameError, msg
|
|
515
|
+
unless klass < ActiveRecord::Base
|
|
516
|
+
raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass."
|
|
436
517
|
end
|
|
518
|
+
|
|
519
|
+
klass
|
|
437
520
|
end
|
|
438
521
|
|
|
439
522
|
attr_reader :type, :foreign_type
|
|
@@ -443,6 +526,21 @@ module ActiveRecord
|
|
|
443
526
|
super
|
|
444
527
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
|
445
528
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
|
529
|
+
@join_table = nil
|
|
530
|
+
@foreign_key = nil
|
|
531
|
+
@association_foreign_key = nil
|
|
532
|
+
@association_primary_key = nil
|
|
533
|
+
if options[:query_constraints]
|
|
534
|
+
ActiveRecord.deprecator.warn <<~MSG.squish
|
|
535
|
+
Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is deprecated.
|
|
536
|
+
To maintain current behavior, use the `foreign_key` option instead.
|
|
537
|
+
MSG
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# If the foreign key is an array, set query constraints options and don't use the foreign key
|
|
541
|
+
if options[:foreign_key].is_a?(Array)
|
|
542
|
+
options[:query_constraints] = options.delete(:foreign_key)
|
|
543
|
+
end
|
|
446
544
|
|
|
447
545
|
ensure_option_not_given_as_class!(:class_name)
|
|
448
546
|
end
|
|
@@ -452,15 +550,33 @@ module ActiveRecord
|
|
|
452
550
|
if polymorphic?
|
|
453
551
|
key = [key, owner._read_attribute(@foreign_type)]
|
|
454
552
|
end
|
|
455
|
-
klass.
|
|
553
|
+
klass.with_connection do |connection|
|
|
554
|
+
klass.cached_find_by_statement(connection, key, &block)
|
|
555
|
+
end
|
|
456
556
|
end
|
|
457
557
|
|
|
458
558
|
def join_table
|
|
459
559
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
|
460
560
|
end
|
|
461
561
|
|
|
462
|
-
def foreign_key
|
|
463
|
-
@foreign_key ||=
|
|
562
|
+
def foreign_key(infer_from_inverse_of: true)
|
|
563
|
+
@foreign_key ||= if options[:foreign_key]
|
|
564
|
+
if options[:foreign_key].is_a?(Array)
|
|
565
|
+
options[:foreign_key].map { |fk| fk.to_s.freeze }.freeze
|
|
566
|
+
else
|
|
567
|
+
options[:foreign_key].to_s.freeze
|
|
568
|
+
end
|
|
569
|
+
elsif options[:query_constraints]
|
|
570
|
+
options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
|
|
571
|
+
else
|
|
572
|
+
derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
|
|
573
|
+
|
|
574
|
+
if active_record.has_query_constraints?
|
|
575
|
+
derived_fk = derive_fk_query_constraints(derived_fk)
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
derived_fk
|
|
579
|
+
end
|
|
464
580
|
end
|
|
465
581
|
|
|
466
582
|
def association_foreign_key
|
|
@@ -472,19 +588,46 @@ module ActiveRecord
|
|
|
472
588
|
end
|
|
473
589
|
|
|
474
590
|
def active_record_primary_key
|
|
475
|
-
|
|
591
|
+
custom_primary_key = options[:primary_key]
|
|
592
|
+
@active_record_primary_key ||= if custom_primary_key
|
|
593
|
+
if custom_primary_key.is_a?(Array)
|
|
594
|
+
custom_primary_key.map { |pk| pk.to_s.freeze }.freeze
|
|
595
|
+
else
|
|
596
|
+
custom_primary_key.to_s.freeze
|
|
597
|
+
end
|
|
598
|
+
elsif active_record.has_query_constraints? || options[:query_constraints]
|
|
599
|
+
active_record.query_constraints_list
|
|
600
|
+
elsif active_record.composite_primary_key?
|
|
601
|
+
# If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
|
602
|
+
primary_key = primary_key(active_record)
|
|
603
|
+
primary_key.include?("id") ? "id" : primary_key.freeze
|
|
604
|
+
else
|
|
605
|
+
primary_key(active_record).freeze
|
|
606
|
+
end
|
|
476
607
|
end
|
|
477
608
|
|
|
478
609
|
def join_primary_key(klass = nil)
|
|
479
610
|
foreign_key
|
|
480
611
|
end
|
|
481
612
|
|
|
613
|
+
def join_primary_type
|
|
614
|
+
type
|
|
615
|
+
end
|
|
616
|
+
|
|
482
617
|
def join_foreign_key
|
|
483
618
|
active_record_primary_key
|
|
484
619
|
end
|
|
485
620
|
|
|
486
621
|
def check_validity!
|
|
487
622
|
check_validity_of_inverse!
|
|
623
|
+
|
|
624
|
+
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
|
625
|
+
if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length
|
|
626
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
|
627
|
+
elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length
|
|
628
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
|
629
|
+
end
|
|
630
|
+
end
|
|
488
631
|
end
|
|
489
632
|
|
|
490
633
|
def check_eager_loadable!
|
|
@@ -500,7 +643,7 @@ module ActiveRecord
|
|
|
500
643
|
end
|
|
501
644
|
|
|
502
645
|
def join_id_for(owner) # :nodoc:
|
|
503
|
-
owner
|
|
646
|
+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
|
|
504
647
|
end
|
|
505
648
|
|
|
506
649
|
def through_reflection
|
|
@@ -582,6 +725,10 @@ module ActiveRecord
|
|
|
582
725
|
options[:polymorphic]
|
|
583
726
|
end
|
|
584
727
|
|
|
728
|
+
def polymorphic_name
|
|
729
|
+
active_record.polymorphic_name
|
|
730
|
+
end
|
|
731
|
+
|
|
585
732
|
def add_as_source(seed)
|
|
586
733
|
seed
|
|
587
734
|
end
|
|
@@ -617,14 +764,20 @@ module ActiveRecord
|
|
|
617
764
|
|
|
618
765
|
begin
|
|
619
766
|
reflection = klass._reflect_on_association(inverse_name)
|
|
620
|
-
|
|
767
|
+
if !reflection && active_record.automatically_invert_plural_associations
|
|
768
|
+
plural_inverse_name = ActiveSupport::Inflector.pluralize(inverse_name)
|
|
769
|
+
reflection = klass._reflect_on_association(plural_inverse_name)
|
|
770
|
+
end
|
|
771
|
+
rescue NameError => error
|
|
772
|
+
raise unless error.name.to_s == class_name
|
|
773
|
+
|
|
621
774
|
# Give up: we couldn't compute the klass type so we won't be able
|
|
622
775
|
# to find any associations either.
|
|
623
776
|
reflection = false
|
|
624
777
|
end
|
|
625
778
|
|
|
626
779
|
if valid_inverse_reflection?(reflection)
|
|
627
|
-
|
|
780
|
+
reflection.name
|
|
628
781
|
end
|
|
629
782
|
end
|
|
630
783
|
end
|
|
@@ -674,16 +827,58 @@ module ActiveRecord
|
|
|
674
827
|
class_name.camelize
|
|
675
828
|
end
|
|
676
829
|
|
|
677
|
-
def derive_foreign_key
|
|
830
|
+
def derive_foreign_key(infer_from_inverse_of: true)
|
|
678
831
|
if belongs_to?
|
|
679
832
|
"#{name}_id"
|
|
680
833
|
elsif options[:as]
|
|
681
834
|
"#{options[:as]}_id"
|
|
835
|
+
elsif options[:inverse_of] && infer_from_inverse_of
|
|
836
|
+
inverse_of.foreign_key(infer_from_inverse_of: false)
|
|
682
837
|
else
|
|
683
838
|
active_record.model_name.to_s.foreign_key
|
|
684
839
|
end
|
|
685
840
|
end
|
|
686
841
|
|
|
842
|
+
def derive_fk_query_constraints(foreign_key)
|
|
843
|
+
primary_query_constraints = active_record.query_constraints_list
|
|
844
|
+
owner_pk = active_record.primary_key
|
|
845
|
+
|
|
846
|
+
if primary_query_constraints.size > 2
|
|
847
|
+
raise ArgumentError, <<~MSG.squish
|
|
848
|
+
The query constraints list on the `#{active_record}` model has more than 2
|
|
849
|
+
attributes. Active Record is unable to derive the query constraints
|
|
850
|
+
for the association. You need to explicitly define the query constraints
|
|
851
|
+
for this association.
|
|
852
|
+
MSG
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
if !primary_query_constraints.include?(owner_pk)
|
|
856
|
+
raise ArgumentError, <<~MSG.squish
|
|
857
|
+
The query constraints on the `#{active_record}` model does not include the primary
|
|
858
|
+
key so Active Record is unable to derive the foreign key constraints for
|
|
859
|
+
the association. You need to explicitly define the query constraints for this
|
|
860
|
+
association.
|
|
861
|
+
MSG
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
return foreign_key if primary_query_constraints.include?(foreign_key)
|
|
865
|
+
|
|
866
|
+
first_key, last_key = primary_query_constraints
|
|
867
|
+
|
|
868
|
+
if first_key == owner_pk
|
|
869
|
+
[foreign_key, last_key.to_s]
|
|
870
|
+
elsif last_key == owner_pk
|
|
871
|
+
[first_key.to_s, foreign_key]
|
|
872
|
+
else
|
|
873
|
+
raise ArgumentError, <<~MSG.squish
|
|
874
|
+
Active Record couldn't correctly interpret the query constraints
|
|
875
|
+
for the `#{active_record}` model. The query constraints on `#{active_record}` are
|
|
876
|
+
`#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
|
|
877
|
+
You need to explicitly set the query constraints for this association.
|
|
878
|
+
MSG
|
|
879
|
+
end
|
|
880
|
+
end
|
|
881
|
+
|
|
687
882
|
def derive_join_table
|
|
688
883
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
|
689
884
|
end
|
|
@@ -733,7 +928,17 @@ module ActiveRecord
|
|
|
733
928
|
# klass option is necessary to support loading polymorphic associations
|
|
734
929
|
def association_primary_key(klass = nil)
|
|
735
930
|
if primary_key = options[:primary_key]
|
|
736
|
-
@association_primary_key ||=
|
|
931
|
+
@association_primary_key ||= if primary_key.is_a?(Array)
|
|
932
|
+
primary_key.map { |pk| pk.to_s.freeze }.freeze
|
|
933
|
+
else
|
|
934
|
+
-primary_key.to_s
|
|
935
|
+
end
|
|
936
|
+
elsif (klass || self.klass).has_query_constraints? || options[:query_constraints]
|
|
937
|
+
(klass || self.klass).composite_query_constraints_list
|
|
938
|
+
elsif (klass || self.klass).composite_primary_key?
|
|
939
|
+
# If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
|
940
|
+
primary_key = (klass || self.klass).primary_key
|
|
941
|
+
primary_key.include?("id") ? "id" : primary_key
|
|
737
942
|
else
|
|
738
943
|
primary_key(klass || self.klass)
|
|
739
944
|
end
|
|
@@ -772,6 +977,7 @@ module ActiveRecord
|
|
|
772
977
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
|
773
978
|
|
|
774
979
|
def initialize(delegate_reflection)
|
|
980
|
+
super()
|
|
775
981
|
@delegate_reflection = delegate_reflection
|
|
776
982
|
@klass = delegate_reflection.options[:anonymous_class]
|
|
777
983
|
@source_reflection_name = delegate_reflection.options[:source]
|
|
@@ -784,7 +990,7 @@ module ActiveRecord
|
|
|
784
990
|
end
|
|
785
991
|
|
|
786
992
|
def klass
|
|
787
|
-
@klass ||= delegate_reflection.
|
|
993
|
+
@klass ||= delegate_reflection._klass(class_name)
|
|
788
994
|
end
|
|
789
995
|
|
|
790
996
|
# Returns the source of the through reflection. It checks both a singularized
|
|
@@ -805,6 +1011,8 @@ module ActiveRecord
|
|
|
805
1011
|
# # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
|
|
806
1012
|
#
|
|
807
1013
|
def source_reflection
|
|
1014
|
+
return unless source_reflection_name
|
|
1015
|
+
|
|
808
1016
|
through_reflection.klass._reflect_on_association(source_reflection_name)
|
|
809
1017
|
end
|
|
810
1018
|
|
|
@@ -905,24 +1113,23 @@ module ActiveRecord
|
|
|
905
1113
|
end
|
|
906
1114
|
|
|
907
1115
|
def source_reflection_name # :nodoc:
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1116
|
+
@source_reflection_name ||= begin
|
|
1117
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
|
1118
|
+
names = names.find_all { |n|
|
|
1119
|
+
through_reflection.klass._reflect_on_association(n)
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if names.length > 1
|
|
1123
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
|
1124
|
+
active_record.name,
|
|
1125
|
+
macro,
|
|
1126
|
+
name,
|
|
1127
|
+
options,
|
|
1128
|
+
source_reflection_names
|
|
1129
|
+
)
|
|
1130
|
+
end
|
|
1131
|
+
names.first
|
|
923
1132
|
end
|
|
924
|
-
|
|
925
|
-
@source_reflection_name = names.first
|
|
926
1133
|
end
|
|
927
1134
|
|
|
928
1135
|
def source_options
|
|
@@ -963,7 +1170,7 @@ module ActiveRecord
|
|
|
963
1170
|
end
|
|
964
1171
|
|
|
965
1172
|
if parent_reflection.nil?
|
|
966
|
-
reflections = active_record.
|
|
1173
|
+
reflections = active_record.normalized_reflections.keys
|
|
967
1174
|
|
|
968
1175
|
if reflections.index(through_reflection.name) > reflections.index(name)
|
|
969
1176
|
raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
|
|
@@ -1026,12 +1233,13 @@ module ActiveRecord
|
|
|
1026
1233
|
:name, :scope_for, to: :@reflection
|
|
1027
1234
|
|
|
1028
1235
|
def initialize(reflection, previous_reflection)
|
|
1236
|
+
super()
|
|
1029
1237
|
@reflection = reflection
|
|
1030
1238
|
@previous_reflection = previous_reflection
|
|
1031
1239
|
end
|
|
1032
1240
|
|
|
1033
1241
|
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
|
1034
|
-
scopes = @previous_reflection.join_scopes(table, predicate_builder, record) + super
|
|
1242
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
|
1035
1243
|
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
|
1036
1244
|
end
|
|
1037
1245
|
|
|
@@ -1051,6 +1259,7 @@ module ActiveRecord
|
|
|
1051
1259
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
|
1052
1260
|
|
|
1053
1261
|
def initialize(reflection, association)
|
|
1262
|
+
super()
|
|
1054
1263
|
@reflection = reflection
|
|
1055
1264
|
@association = association
|
|
1056
1265
|
end
|