activerecord 7.0.8.6 → 7.2.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 +631 -1939
- 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 +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -10
- 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 +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- 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 +11 -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 +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +11 -25
- 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 +323 -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 +217 -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 +307 -129
- 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 +278 -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 +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- 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 +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- 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 +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- 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/key_provider.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 +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 +3 -0
- data/lib/active_record/enum.rb +129 -28
- 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 +4 -4
- 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 +59 -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 +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- 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 +18 -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 +129 -85
- 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 +267 -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 +250 -93
- 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 +576 -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 +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 +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/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 +190 -118
- 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 +61 -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/sqlite.rb +25 -0
- 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,7 +428,15 @@ 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 ||=
|
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)
|
383
440
|
end
|
384
441
|
|
385
442
|
def compute_class(name)
|
@@ -404,6 +461,26 @@ module ActiveRecord
|
|
404
461
|
def derive_class_name
|
405
462
|
name.to_s.camelize
|
406
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
|
407
484
|
end
|
408
485
|
|
409
486
|
# Holds all the metadata about an aggregation as it was specified in the
|
@@ -423,23 +500,23 @@ module ActiveRecord
|
|
423
500
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
424
501
|
end
|
425
502
|
|
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
503
|
begin
|
433
504
|
klass = active_record.send(:compute_type, name)
|
434
|
-
|
435
|
-
|
436
|
-
|
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
|
437
512
|
end
|
513
|
+
end
|
438
514
|
|
439
|
-
|
440
|
-
|
441
|
-
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."
|
442
517
|
end
|
518
|
+
|
519
|
+
klass
|
443
520
|
end
|
444
521
|
|
445
522
|
attr_reader :type, :foreign_type
|
@@ -449,6 +526,21 @@ module ActiveRecord
|
|
449
526
|
super
|
450
527
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
451
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
|
452
544
|
|
453
545
|
ensure_option_not_given_as_class!(:class_name)
|
454
546
|
end
|
@@ -458,15 +550,33 @@ module ActiveRecord
|
|
458
550
|
if polymorphic?
|
459
551
|
key = [key, owner._read_attribute(@foreign_type)]
|
460
552
|
end
|
461
|
-
klass.
|
553
|
+
klass.with_connection do |connection|
|
554
|
+
klass.cached_find_by_statement(connection, key, &block)
|
555
|
+
end
|
462
556
|
end
|
463
557
|
|
464
558
|
def join_table
|
465
559
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
466
560
|
end
|
467
561
|
|
468
|
-
def foreign_key
|
469
|
-
@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
|
470
580
|
end
|
471
581
|
|
472
582
|
def association_foreign_key
|
@@ -478,7 +588,22 @@ module ActiveRecord
|
|
478
588
|
end
|
479
589
|
|
480
590
|
def active_record_primary_key
|
481
|
-
|
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
|
482
607
|
end
|
483
608
|
|
484
609
|
def join_primary_key(klass = nil)
|
@@ -495,6 +620,14 @@ module ActiveRecord
|
|
495
620
|
|
496
621
|
def check_validity!
|
497
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
|
498
631
|
end
|
499
632
|
|
500
633
|
def check_eager_loadable!
|
@@ -510,7 +643,7 @@ module ActiveRecord
|
|
510
643
|
end
|
511
644
|
|
512
645
|
def join_id_for(owner) # :nodoc:
|
513
|
-
owner
|
646
|
+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
|
514
647
|
end
|
515
648
|
|
516
649
|
def through_reflection
|
@@ -631,14 +764,20 @@ module ActiveRecord
|
|
631
764
|
|
632
765
|
begin
|
633
766
|
reflection = klass._reflect_on_association(inverse_name)
|
634
|
-
|
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
|
+
|
635
774
|
# Give up: we couldn't compute the klass type so we won't be able
|
636
775
|
# to find any associations either.
|
637
776
|
reflection = false
|
638
777
|
end
|
639
778
|
|
640
779
|
if valid_inverse_reflection?(reflection)
|
641
|
-
|
780
|
+
reflection.name
|
642
781
|
end
|
643
782
|
end
|
644
783
|
end
|
@@ -688,16 +827,58 @@ module ActiveRecord
|
|
688
827
|
class_name.camelize
|
689
828
|
end
|
690
829
|
|
691
|
-
def derive_foreign_key
|
830
|
+
def derive_foreign_key(infer_from_inverse_of: true)
|
692
831
|
if belongs_to?
|
693
832
|
"#{name}_id"
|
694
833
|
elsif options[:as]
|
695
834
|
"#{options[:as]}_id"
|
835
|
+
elsif options[:inverse_of] && infer_from_inverse_of
|
836
|
+
inverse_of.foreign_key(infer_from_inverse_of: false)
|
696
837
|
else
|
697
838
|
active_record.model_name.to_s.foreign_key
|
698
839
|
end
|
699
840
|
end
|
700
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
|
+
|
701
882
|
def derive_join_table
|
702
883
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
703
884
|
end
|
@@ -747,7 +928,17 @@ module ActiveRecord
|
|
747
928
|
# klass option is necessary to support loading polymorphic associations
|
748
929
|
def association_primary_key(klass = nil)
|
749
930
|
if primary_key = options[:primary_key]
|
750
|
-
@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
|
751
942
|
else
|
752
943
|
primary_key(klass || self.klass)
|
753
944
|
end
|
@@ -786,6 +977,7 @@ module ActiveRecord
|
|
786
977
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
787
978
|
|
788
979
|
def initialize(delegate_reflection)
|
980
|
+
super()
|
789
981
|
@delegate_reflection = delegate_reflection
|
790
982
|
@klass = delegate_reflection.options[:anonymous_class]
|
791
983
|
@source_reflection_name = delegate_reflection.options[:source]
|
@@ -798,7 +990,7 @@ module ActiveRecord
|
|
798
990
|
end
|
799
991
|
|
800
992
|
def klass
|
801
|
-
@klass ||= delegate_reflection.
|
993
|
+
@klass ||= delegate_reflection._klass(class_name)
|
802
994
|
end
|
803
995
|
|
804
996
|
# Returns the source of the through reflection. It checks both a singularized
|
@@ -819,6 +1011,8 @@ module ActiveRecord
|
|
819
1011
|
# # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
|
820
1012
|
#
|
821
1013
|
def source_reflection
|
1014
|
+
return unless source_reflection_name
|
1015
|
+
|
822
1016
|
through_reflection.klass._reflect_on_association(source_reflection_name)
|
823
1017
|
end
|
824
1018
|
|
@@ -919,24 +1113,23 @@ module ActiveRecord
|
|
919
1113
|
end
|
920
1114
|
|
921
1115
|
def source_reflection_name # :nodoc:
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
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
|
937
1132
|
end
|
938
|
-
|
939
|
-
@source_reflection_name = names.first
|
940
1133
|
end
|
941
1134
|
|
942
1135
|
def source_options
|
@@ -977,7 +1170,7 @@ module ActiveRecord
|
|
977
1170
|
end
|
978
1171
|
|
979
1172
|
if parent_reflection.nil?
|
980
|
-
reflections = active_record.
|
1173
|
+
reflections = active_record.normalized_reflections.keys
|
981
1174
|
|
982
1175
|
if reflections.index(through_reflection.name) > reflections.index(name)
|
983
1176
|
raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
|
@@ -1040,12 +1233,16 @@ module ActiveRecord
|
|
1040
1233
|
:name, :scope_for, to: :@reflection
|
1041
1234
|
|
1042
1235
|
def initialize(reflection, previous_reflection)
|
1236
|
+
super()
|
1043
1237
|
@reflection = reflection
|
1044
1238
|
@previous_reflection = previous_reflection
|
1045
1239
|
end
|
1046
1240
|
|
1047
1241
|
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
1048
|
-
scopes =
|
1242
|
+
scopes = super
|
1243
|
+
unless @previous_reflection.through_reflection?
|
1244
|
+
scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
|
1245
|
+
end
|
1049
1246
|
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
1050
1247
|
end
|
1051
1248
|
|
@@ -1065,6 +1262,7 @@ module ActiveRecord
|
|
1065
1262
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1066
1263
|
|
1067
1264
|
def initialize(reflection, association)
|
1265
|
+
super()
|
1068
1266
|
@reflection = reflection
|
1069
1267
|
@association = association
|
1070
1268
|
end
|