activerecord 7.0.8 → 7.2.0
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 +530 -2004
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- 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 +26 -14
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- 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 +21 -14
- 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 +5 -5
- 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 +1 -3
- 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 +328 -471
- 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 +131 -32
- 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 +148 -33
- data/lib/active_record/attributes.rb +58 -45
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +10 -24
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- 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 +317 -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 +188 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- 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 +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -128
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +274 -125
- 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 +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- 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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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 +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- 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 +368 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +364 -198
- 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 +45 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -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 +96 -104
- data/lib/active_record/core.rb +217 -174
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -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 +87 -34
- data/lib/active_record/delegated_type.rb +39 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -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 +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +44 -20
- data/lib/active_record/encryption/encrypted_attribute_type.rb +45 -10
- data/lib/active_record/encryption/encryptor.rb +17 -2
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- 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_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 +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +122 -29
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- 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 +167 -97
- 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 +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- 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 +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- 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 +234 -117
- data/lib/active_record/model_schema.rb +88 -103
- data/lib/active_record/nested_attributes.rb +35 -9
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +135 -86
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- 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 +259 -68
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +196 -61
- data/lib/active_record/relation/calculations.rb +249 -92
- data/lib/active_record/relation/delegation.rb +30 -19
- 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 +18 -3
- 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 +2 -1
- data/lib/active_record/relation/query_methods.rb +548 -94
- 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 +580 -90
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +63 -14
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -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/signed_id.rb +27 -6
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +180 -119
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- 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 +106 -24
- data/lib/active_record/translation.rb +0 -2
- 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 +1 -3
- 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 +9 -3
- 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 +60 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- 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/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/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} +5 -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 +56 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module ActiveRecord
|
|
4
|
+
class ReadonlyAttributeError < ActiveRecordError
|
|
5
|
+
end
|
|
6
|
+
|
|
4
7
|
module ReadonlyAttributes
|
|
5
8
|
extend ActiveSupport::Concern
|
|
6
9
|
|
|
@@ -9,10 +12,11 @@ module ActiveRecord
|
|
|
9
12
|
end
|
|
10
13
|
|
|
11
14
|
module ClassMethods
|
|
12
|
-
# Attributes listed as readonly will be used to create a new record
|
|
13
|
-
#
|
|
15
|
+
# Attributes listed as readonly will be used to create a new record.
|
|
16
|
+
# Assigning a new value to a readonly attribute on a persisted record raises an error.
|
|
14
17
|
#
|
|
15
|
-
#
|
|
18
|
+
# By setting +config.active_record.raise_on_assign_to_attr_readonly+ to +false+, it will
|
|
19
|
+
# not raise. The value will change in memory, but will not be persisted on +save+.
|
|
16
20
|
#
|
|
17
21
|
# ==== Examples
|
|
18
22
|
#
|
|
@@ -21,9 +25,14 @@ module ActiveRecord
|
|
|
21
25
|
# end
|
|
22
26
|
#
|
|
23
27
|
# post = Post.create!(title: "Introducing Ruby on Rails!")
|
|
24
|
-
# post.
|
|
28
|
+
# post.title = "a different title" # raises ActiveRecord::ReadonlyAttributeError
|
|
29
|
+
# post.update(title: "a different title") # raises ActiveRecord::ReadonlyAttributeError
|
|
25
30
|
def attr_readonly(*attributes)
|
|
26
|
-
self._attr_readonly
|
|
31
|
+
self._attr_readonly |= attributes.map(&:to_s)
|
|
32
|
+
|
|
33
|
+
if ActiveRecord.raise_on_assign_to_attr_readonly
|
|
34
|
+
include(HasReadonlyAttributes)
|
|
35
|
+
end
|
|
27
36
|
end
|
|
28
37
|
|
|
29
38
|
# Returns an array of all the attributes that have been specified as readonly.
|
|
@@ -35,5 +44,23 @@ module ActiveRecord
|
|
|
35
44
|
_attr_readonly.include?(name)
|
|
36
45
|
end
|
|
37
46
|
end
|
|
47
|
+
|
|
48
|
+
module HasReadonlyAttributes # :nodoc:
|
|
49
|
+
def write_attribute(attr_name, value)
|
|
50
|
+
if !new_record? && self.class.readonly_attribute?(attr_name.to_s)
|
|
51
|
+
raise ReadonlyAttributeError.new(attr_name)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
super
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def _write_attribute(attr_name, value)
|
|
58
|
+
if !new_record? && self.class.readonly_attribute?(attr_name.to_s)
|
|
59
|
+
raise ReadonlyAttributeError.new(attr_name)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
38
65
|
end
|
|
39
66
|
end
|
|
@@ -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
|
|
@@ -346,9 +394,10 @@ module ActiveRecord
|
|
|
346
394
|
attr_reader :plural_name # :nodoc:
|
|
347
395
|
|
|
348
396
|
def initialize(name, scope, options, active_record)
|
|
397
|
+
super()
|
|
349
398
|
@name = name
|
|
350
399
|
@scope = scope
|
|
351
|
-
@options = options
|
|
400
|
+
@options = normalize_options(options)
|
|
352
401
|
@active_record = active_record
|
|
353
402
|
@klass = options[:anonymous_class]
|
|
354
403
|
@plural_name = active_record.pluralize_table_names ?
|
|
@@ -379,13 +428,17 @@ module ActiveRecord
|
|
|
379
428
|
# a new association object. Use +build_association+ or +create_association+
|
|
380
429
|
# instead. This allows plugins to hook into association object creation.
|
|
381
430
|
def klass
|
|
382
|
-
@klass ||= compute_class(class_name)
|
|
431
|
+
@klass ||= compute_class(compute_name(class_name))
|
|
383
432
|
end
|
|
384
433
|
|
|
385
434
|
def compute_class(name)
|
|
386
435
|
name.constantize
|
|
387
436
|
end
|
|
388
437
|
|
|
438
|
+
def compute_name(name) # :nodoc:
|
|
439
|
+
active_record.name.demodulize == name ? "::#{name}" : name
|
|
440
|
+
end
|
|
441
|
+
|
|
389
442
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
|
390
443
|
# and +other_aggregation+ has an options hash assigned to it.
|
|
391
444
|
def ==(other_aggregation)
|
|
@@ -404,6 +457,26 @@ module ActiveRecord
|
|
|
404
457
|
def derive_class_name
|
|
405
458
|
name.to_s.camelize
|
|
406
459
|
end
|
|
460
|
+
|
|
461
|
+
def normalize_options(options)
|
|
462
|
+
counter_cache = options.delete(:counter_cache)
|
|
463
|
+
|
|
464
|
+
if counter_cache
|
|
465
|
+
active = true
|
|
466
|
+
|
|
467
|
+
case counter_cache
|
|
468
|
+
when String, Symbol
|
|
469
|
+
column = -counter_cache.to_s
|
|
470
|
+
when Hash
|
|
471
|
+
active = counter_cache.fetch(:active, true)
|
|
472
|
+
column = counter_cache[:column]&.to_s
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
options[:counter_cache] = { active: active, column: column }
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
options
|
|
479
|
+
end
|
|
407
480
|
end
|
|
408
481
|
|
|
409
482
|
# Holds all the metadata about an aggregation as it was specified in the
|
|
@@ -423,23 +496,23 @@ module ActiveRecord
|
|
|
423
496
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
|
424
497
|
end
|
|
425
498
|
|
|
426
|
-
msg = <<-MSG.squish
|
|
427
|
-
Rails couldn't find a valid model for #{name} association.
|
|
428
|
-
Please provide the :class_name option on the association declaration.
|
|
429
|
-
If :class_name is already provided, make sure it's an ActiveRecord::Base subclass.
|
|
430
|
-
MSG
|
|
431
|
-
|
|
432
499
|
begin
|
|
433
500
|
klass = active_record.send(:compute_type, name)
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
501
|
+
rescue NameError => error
|
|
502
|
+
if error.name.match?(/(?:\A|::)#{name}\z/)
|
|
503
|
+
message = "Missing model class #{name} for the #{active_record}##{self.name} association."
|
|
504
|
+
message += " You can specify a different model class with the :class_name option." unless options[:class_name]
|
|
505
|
+
raise NameError.new(message, name)
|
|
506
|
+
else
|
|
507
|
+
raise
|
|
437
508
|
end
|
|
509
|
+
end
|
|
438
510
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
raise NameError, msg
|
|
511
|
+
unless klass < ActiveRecord::Base
|
|
512
|
+
raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass."
|
|
442
513
|
end
|
|
514
|
+
|
|
515
|
+
klass
|
|
443
516
|
end
|
|
444
517
|
|
|
445
518
|
attr_reader :type, :foreign_type
|
|
@@ -449,6 +522,21 @@ module ActiveRecord
|
|
|
449
522
|
super
|
|
450
523
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
|
451
524
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
|
525
|
+
@join_table = nil
|
|
526
|
+
@foreign_key = nil
|
|
527
|
+
@association_foreign_key = nil
|
|
528
|
+
@association_primary_key = nil
|
|
529
|
+
if options[:query_constraints]
|
|
530
|
+
ActiveRecord.deprecator.warn <<~MSG.squish
|
|
531
|
+
Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is deprecated.
|
|
532
|
+
To maintain current behavior, use the `foreign_key` option instead.
|
|
533
|
+
MSG
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# If the foreign key is an array, set query constraints options and don't use the foreign key
|
|
537
|
+
if options[:foreign_key].is_a?(Array)
|
|
538
|
+
options[:query_constraints] = options.delete(:foreign_key)
|
|
539
|
+
end
|
|
452
540
|
|
|
453
541
|
ensure_option_not_given_as_class!(:class_name)
|
|
454
542
|
end
|
|
@@ -458,15 +546,33 @@ module ActiveRecord
|
|
|
458
546
|
if polymorphic?
|
|
459
547
|
key = [key, owner._read_attribute(@foreign_type)]
|
|
460
548
|
end
|
|
461
|
-
klass.
|
|
549
|
+
klass.with_connection do |connection|
|
|
550
|
+
klass.cached_find_by_statement(connection, key, &block)
|
|
551
|
+
end
|
|
462
552
|
end
|
|
463
553
|
|
|
464
554
|
def join_table
|
|
465
555
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
|
466
556
|
end
|
|
467
557
|
|
|
468
|
-
def foreign_key
|
|
469
|
-
@foreign_key ||=
|
|
558
|
+
def foreign_key(infer_from_inverse_of: true)
|
|
559
|
+
@foreign_key ||= if options[:foreign_key]
|
|
560
|
+
if options[:foreign_key].is_a?(Array)
|
|
561
|
+
options[:foreign_key].map { |fk| fk.to_s.freeze }.freeze
|
|
562
|
+
else
|
|
563
|
+
options[:foreign_key].to_s.freeze
|
|
564
|
+
end
|
|
565
|
+
elsif options[:query_constraints]
|
|
566
|
+
options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
|
|
567
|
+
else
|
|
568
|
+
derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
|
|
569
|
+
|
|
570
|
+
if active_record.has_query_constraints?
|
|
571
|
+
derived_fk = derive_fk_query_constraints(derived_fk)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
derived_fk
|
|
575
|
+
end
|
|
470
576
|
end
|
|
471
577
|
|
|
472
578
|
def association_foreign_key
|
|
@@ -478,7 +584,22 @@ module ActiveRecord
|
|
|
478
584
|
end
|
|
479
585
|
|
|
480
586
|
def active_record_primary_key
|
|
481
|
-
|
|
587
|
+
custom_primary_key = options[:primary_key]
|
|
588
|
+
@active_record_primary_key ||= if custom_primary_key
|
|
589
|
+
if custom_primary_key.is_a?(Array)
|
|
590
|
+
custom_primary_key.map { |pk| pk.to_s.freeze }.freeze
|
|
591
|
+
else
|
|
592
|
+
custom_primary_key.to_s.freeze
|
|
593
|
+
end
|
|
594
|
+
elsif active_record.has_query_constraints? || options[:query_constraints]
|
|
595
|
+
active_record.query_constraints_list
|
|
596
|
+
elsif active_record.composite_primary_key?
|
|
597
|
+
# If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
|
598
|
+
primary_key = primary_key(active_record)
|
|
599
|
+
primary_key.include?("id") ? "id" : primary_key.freeze
|
|
600
|
+
else
|
|
601
|
+
primary_key(active_record).freeze
|
|
602
|
+
end
|
|
482
603
|
end
|
|
483
604
|
|
|
484
605
|
def join_primary_key(klass = nil)
|
|
@@ -495,6 +616,14 @@ module ActiveRecord
|
|
|
495
616
|
|
|
496
617
|
def check_validity!
|
|
497
618
|
check_validity_of_inverse!
|
|
619
|
+
|
|
620
|
+
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
|
621
|
+
if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length
|
|
622
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
|
623
|
+
elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length
|
|
624
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
|
625
|
+
end
|
|
626
|
+
end
|
|
498
627
|
end
|
|
499
628
|
|
|
500
629
|
def check_eager_loadable!
|
|
@@ -510,7 +639,7 @@ module ActiveRecord
|
|
|
510
639
|
end
|
|
511
640
|
|
|
512
641
|
def join_id_for(owner) # :nodoc:
|
|
513
|
-
owner
|
|
642
|
+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
|
|
514
643
|
end
|
|
515
644
|
|
|
516
645
|
def through_reflection
|
|
@@ -631,14 +760,20 @@ module ActiveRecord
|
|
|
631
760
|
|
|
632
761
|
begin
|
|
633
762
|
reflection = klass._reflect_on_association(inverse_name)
|
|
634
|
-
|
|
763
|
+
if !reflection && active_record.automatically_invert_plural_associations
|
|
764
|
+
plural_inverse_name = ActiveSupport::Inflector.pluralize(inverse_name)
|
|
765
|
+
reflection = klass._reflect_on_association(plural_inverse_name)
|
|
766
|
+
end
|
|
767
|
+
rescue NameError => error
|
|
768
|
+
raise unless error.name.to_s == class_name
|
|
769
|
+
|
|
635
770
|
# Give up: we couldn't compute the klass type so we won't be able
|
|
636
771
|
# to find any associations either.
|
|
637
772
|
reflection = false
|
|
638
773
|
end
|
|
639
774
|
|
|
640
775
|
if valid_inverse_reflection?(reflection)
|
|
641
|
-
|
|
776
|
+
reflection.name
|
|
642
777
|
end
|
|
643
778
|
end
|
|
644
779
|
end
|
|
@@ -688,16 +823,58 @@ module ActiveRecord
|
|
|
688
823
|
class_name.camelize
|
|
689
824
|
end
|
|
690
825
|
|
|
691
|
-
def derive_foreign_key
|
|
826
|
+
def derive_foreign_key(infer_from_inverse_of: true)
|
|
692
827
|
if belongs_to?
|
|
693
828
|
"#{name}_id"
|
|
694
829
|
elsif options[:as]
|
|
695
830
|
"#{options[:as]}_id"
|
|
831
|
+
elsif options[:inverse_of] && infer_from_inverse_of
|
|
832
|
+
inverse_of.foreign_key(infer_from_inverse_of: false)
|
|
696
833
|
else
|
|
697
834
|
active_record.model_name.to_s.foreign_key
|
|
698
835
|
end
|
|
699
836
|
end
|
|
700
837
|
|
|
838
|
+
def derive_fk_query_constraints(foreign_key)
|
|
839
|
+
primary_query_constraints = active_record.query_constraints_list
|
|
840
|
+
owner_pk = active_record.primary_key
|
|
841
|
+
|
|
842
|
+
if primary_query_constraints.size > 2
|
|
843
|
+
raise ArgumentError, <<~MSG.squish
|
|
844
|
+
The query constraints list on the `#{active_record}` model has more than 2
|
|
845
|
+
attributes. Active Record is unable to derive the query constraints
|
|
846
|
+
for the association. You need to explicitly define the query constraints
|
|
847
|
+
for this association.
|
|
848
|
+
MSG
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
if !primary_query_constraints.include?(owner_pk)
|
|
852
|
+
raise ArgumentError, <<~MSG.squish
|
|
853
|
+
The query constraints on the `#{active_record}` model does not include the primary
|
|
854
|
+
key so Active Record is unable to derive the foreign key constraints for
|
|
855
|
+
the association. You need to explicitly define the query constraints for this
|
|
856
|
+
association.
|
|
857
|
+
MSG
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
return foreign_key if primary_query_constraints.include?(foreign_key)
|
|
861
|
+
|
|
862
|
+
first_key, last_key = primary_query_constraints
|
|
863
|
+
|
|
864
|
+
if first_key == owner_pk
|
|
865
|
+
[foreign_key, last_key.to_s]
|
|
866
|
+
elsif last_key == owner_pk
|
|
867
|
+
[first_key.to_s, foreign_key]
|
|
868
|
+
else
|
|
869
|
+
raise ArgumentError, <<~MSG.squish
|
|
870
|
+
Active Record couldn't correctly interpret the query constraints
|
|
871
|
+
for the `#{active_record}` model. The query constraints on `#{active_record}` are
|
|
872
|
+
`#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
|
|
873
|
+
You need to explicitly set the query constraints for this association.
|
|
874
|
+
MSG
|
|
875
|
+
end
|
|
876
|
+
end
|
|
877
|
+
|
|
701
878
|
def derive_join_table
|
|
702
879
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
|
703
880
|
end
|
|
@@ -747,7 +924,17 @@ module ActiveRecord
|
|
|
747
924
|
# klass option is necessary to support loading polymorphic associations
|
|
748
925
|
def association_primary_key(klass = nil)
|
|
749
926
|
if primary_key = options[:primary_key]
|
|
750
|
-
@association_primary_key ||=
|
|
927
|
+
@association_primary_key ||= if primary_key.is_a?(Array)
|
|
928
|
+
primary_key.map { |pk| pk.to_s.freeze }.freeze
|
|
929
|
+
else
|
|
930
|
+
-primary_key.to_s
|
|
931
|
+
end
|
|
932
|
+
elsif (klass || self.klass).has_query_constraints? || options[:query_constraints]
|
|
933
|
+
(klass || self.klass).composite_query_constraints_list
|
|
934
|
+
elsif (klass || self.klass).composite_primary_key?
|
|
935
|
+
# If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
|
936
|
+
primary_key = (klass || self.klass).primary_key
|
|
937
|
+
primary_key.include?("id") ? "id" : primary_key
|
|
751
938
|
else
|
|
752
939
|
primary_key(klass || self.klass)
|
|
753
940
|
end
|
|
@@ -786,6 +973,7 @@ module ActiveRecord
|
|
|
786
973
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
|
787
974
|
|
|
788
975
|
def initialize(delegate_reflection)
|
|
976
|
+
super()
|
|
789
977
|
@delegate_reflection = delegate_reflection
|
|
790
978
|
@klass = delegate_reflection.options[:anonymous_class]
|
|
791
979
|
@source_reflection_name = delegate_reflection.options[:source]
|
|
@@ -798,7 +986,7 @@ module ActiveRecord
|
|
|
798
986
|
end
|
|
799
987
|
|
|
800
988
|
def klass
|
|
801
|
-
@klass ||= delegate_reflection.compute_class(class_name)
|
|
989
|
+
@klass ||= delegate_reflection.compute_class(compute_name(class_name))
|
|
802
990
|
end
|
|
803
991
|
|
|
804
992
|
# Returns the source of the through reflection. It checks both a singularized
|
|
@@ -819,6 +1007,8 @@ module ActiveRecord
|
|
|
819
1007
|
# # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
|
|
820
1008
|
#
|
|
821
1009
|
def source_reflection
|
|
1010
|
+
return unless source_reflection_name
|
|
1011
|
+
|
|
822
1012
|
through_reflection.klass._reflect_on_association(source_reflection_name)
|
|
823
1013
|
end
|
|
824
1014
|
|
|
@@ -919,24 +1109,23 @@ module ActiveRecord
|
|
|
919
1109
|
end
|
|
920
1110
|
|
|
921
1111
|
def source_reflection_name # :nodoc:
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1112
|
+
@source_reflection_name ||= begin
|
|
1113
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
|
1114
|
+
names = names.find_all { |n|
|
|
1115
|
+
through_reflection.klass._reflect_on_association(n)
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
if names.length > 1
|
|
1119
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
|
1120
|
+
active_record.name,
|
|
1121
|
+
macro,
|
|
1122
|
+
name,
|
|
1123
|
+
options,
|
|
1124
|
+
source_reflection_names
|
|
1125
|
+
)
|
|
1126
|
+
end
|
|
1127
|
+
names.first
|
|
937
1128
|
end
|
|
938
|
-
|
|
939
|
-
@source_reflection_name = names.first
|
|
940
1129
|
end
|
|
941
1130
|
|
|
942
1131
|
def source_options
|
|
@@ -977,7 +1166,7 @@ module ActiveRecord
|
|
|
977
1166
|
end
|
|
978
1167
|
|
|
979
1168
|
if parent_reflection.nil?
|
|
980
|
-
reflections = active_record.
|
|
1169
|
+
reflections = active_record.normalized_reflections.keys
|
|
981
1170
|
|
|
982
1171
|
if reflections.index(through_reflection.name) > reflections.index(name)
|
|
983
1172
|
raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
|
|
@@ -1040,6 +1229,7 @@ module ActiveRecord
|
|
|
1040
1229
|
:name, :scope_for, to: :@reflection
|
|
1041
1230
|
|
|
1042
1231
|
def initialize(reflection, previous_reflection)
|
|
1232
|
+
super()
|
|
1043
1233
|
@reflection = reflection
|
|
1044
1234
|
@previous_reflection = previous_reflection
|
|
1045
1235
|
end
|
|
@@ -1065,6 +1255,7 @@ module ActiveRecord
|
|
|
1065
1255
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
|
1066
1256
|
|
|
1067
1257
|
def initialize(reflection, association)
|
|
1258
|
+
super()
|
|
1068
1259
|
@reflection = reflection
|
|
1069
1260
|
@association = association
|
|
1070
1261
|
end
|