activerecord 6.1.7 → 7.1.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 +1516 -1019
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +50 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +35 -31
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -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 +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -16
- data/lib/active_record/associations/preloader/association.rb +207 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +423 -289
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +61 -14
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +25 -10
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +10 -13
- data/lib/active_record/attribute_methods.rb +121 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +61 -30
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- 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 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- 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 +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +107 -136
- data/lib/active_record/core.rb +194 -224
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +84 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +61 -15
- 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 +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +224 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +92 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +96 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +156 -62
- data/lib/active_record/errors.rb +171 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -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 +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +131 -86
- data/lib/active_record/future_result.rb +164 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +133 -20
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +117 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- 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 +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +108 -13
- data/lib/active_record/migration/compatibility.rb +221 -48
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +355 -171
- data/lib/active_record/model_schema.rb +116 -97
- data/lib/active_record/nested_attributes.rb +36 -15
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +405 -85
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +219 -43
- data/lib/active_record/railties/controller_runtime.rb +13 -9
- data/lib/active_record/railties/databases.rake +185 -249
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +229 -80
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +211 -90
- data/lib/active_record/relation/delegation.rb +27 -13
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +654 -127
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +262 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +18 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +225 -136
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +116 -96
- data/lib/active_record/timestamp.rb +28 -17
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- 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 +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +139 -19
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -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
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +92 -13
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/runtime_registry"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module Railties # :nodoc:
|
7
|
+
module JobRuntime # :nodoc:
|
8
|
+
private
|
9
|
+
def instrument(operation, payload = {}, &block)
|
10
|
+
if operation == :perform && block
|
11
|
+
super(operation, payload) do
|
12
|
+
db_runtime_before_perform = ActiveRecord::RuntimeRegistry.sql_runtime
|
13
|
+
result = block.call
|
14
|
+
payload[:db_runtime] = ActiveRecord::RuntimeRegistry.sql_runtime - db_runtime_before_perform
|
15
|
+
result
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -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,27 @@ 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.
|
17
|
+
#
|
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+.
|
20
|
+
#
|
21
|
+
# ==== Examples
|
22
|
+
#
|
23
|
+
# class Post < ActiveRecord::Base
|
24
|
+
# attr_readonly :title
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# post = Post.create!(title: "Introducing Ruby on Rails!")
|
28
|
+
# post.title = "a different title" # raises ActiveRecord::ReadonlyAttributeError
|
29
|
+
# post.update(title: "a different title") # raises ActiveRecord::ReadonlyAttributeError
|
14
30
|
def attr_readonly(*attributes)
|
15
|
-
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
|
16
36
|
end
|
17
37
|
|
18
38
|
# Returns an array of all the attributes that have been specified as readonly.
|
@@ -24,5 +44,23 @@ module ActiveRecord
|
|
24
44
|
_attr_readonly.include?(name)
|
25
45
|
end
|
26
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
|
27
65
|
end
|
28
66
|
end
|
@@ -10,6 +10,7 @@ module ActiveRecord
|
|
10
10
|
included do
|
11
11
|
class_attribute :_reflections, instance_writer: false, default: {}
|
12
12
|
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
13
|
+
class_attribute :automatic_scope_inversing, instance_writer: false, default: false
|
13
14
|
end
|
14
15
|
|
15
16
|
class << self
|
@@ -45,6 +46,8 @@ module ActiveRecord
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
49
|
+
# = Active Record Reflection
|
50
|
+
#
|
48
51
|
# \Reflection enables the ability to examine the associations and aggregations of
|
49
52
|
# Active Record classes and objects. This information, for example,
|
50
53
|
# can be used in a form builder that takes an Active Record object
|
@@ -115,7 +118,7 @@ module ActiveRecord
|
|
115
118
|
reflections[association.to_s]
|
116
119
|
end
|
117
120
|
|
118
|
-
def _reflect_on_association(association)
|
121
|
+
def _reflect_on_association(association) # :nodoc:
|
119
122
|
_reflections[association.to_s]
|
120
123
|
end
|
121
124
|
|
@@ -127,6 +130,14 @@ module ActiveRecord
|
|
127
130
|
def clear_reflections_cache # :nodoc:
|
128
131
|
@__reflections = nil
|
129
132
|
end
|
133
|
+
|
134
|
+
private
|
135
|
+
def inherited(subclass)
|
136
|
+
super
|
137
|
+
subclass.class_eval do
|
138
|
+
@__reflections = nil
|
139
|
+
end
|
140
|
+
end
|
130
141
|
end
|
131
142
|
|
132
143
|
# Holds all the methods that are shared between MacroReflection and ThroughReflection.
|
@@ -143,6 +154,14 @@ module ActiveRecord
|
|
143
154
|
# PolymorphicReflection
|
144
155
|
# RuntimeReflection
|
145
156
|
class AbstractReflection # :nodoc:
|
157
|
+
def initialize
|
158
|
+
@class_name = nil
|
159
|
+
@counter_cache_column = nil
|
160
|
+
@inverse_of = nil
|
161
|
+
@inverse_which_updates_counter_cache_defined = false
|
162
|
+
@inverse_which_updates_counter_cache = nil
|
163
|
+
end
|
164
|
+
|
146
165
|
def through_reflection?
|
147
166
|
false
|
148
167
|
end
|
@@ -182,10 +201,14 @@ module ActiveRecord
|
|
182
201
|
|
183
202
|
scope_chain_items.inject(klass_scope, &:merge!)
|
184
203
|
|
185
|
-
|
186
|
-
|
204
|
+
primary_key_column_names = Array(join_primary_key)
|
205
|
+
foreign_key_column_names = Array(join_foreign_key)
|
187
206
|
|
188
|
-
|
207
|
+
primary_foreign_key_pairs = primary_key_column_names.zip(foreign_key_column_names)
|
208
|
+
|
209
|
+
primary_foreign_key_pairs.each do |primary_key_column_name, foreign_key_column_name|
|
210
|
+
klass_scope.where!(table[primary_key_column_name].eq(foreign_table[foreign_key_column_name]))
|
211
|
+
end
|
189
212
|
|
190
213
|
if klass.finder_needs_type_condition?
|
191
214
|
klass_scope.where!(klass.send(:type_condition, table))
|
@@ -194,9 +217,9 @@ module ActiveRecord
|
|
194
217
|
klass_scope
|
195
218
|
end
|
196
219
|
|
197
|
-
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
220
|
+
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
198
221
|
if scope
|
199
|
-
[scope_for(build_scope(table, predicate_builder, klass))]
|
222
|
+
[scope_for(build_scope(table, predicate_builder, klass), record)]
|
200
223
|
else
|
201
224
|
[]
|
202
225
|
end
|
@@ -230,14 +253,17 @@ module ActiveRecord
|
|
230
253
|
end
|
231
254
|
|
232
255
|
def check_validity_of_inverse!
|
233
|
-
|
234
|
-
if
|
256
|
+
if !polymorphic? && has_inverse?
|
257
|
+
if inverse_of.nil?
|
235
258
|
raise InverseOfAssociationNotFoundError.new(self)
|
236
259
|
end
|
260
|
+
if inverse_of == self
|
261
|
+
raise InverseOfAssociationRecursiveError.new(self)
|
262
|
+
end
|
237
263
|
end
|
238
264
|
end
|
239
265
|
|
240
|
-
#
|
266
|
+
# We need to avoid the following situation:
|
241
267
|
#
|
242
268
|
# * An associated record is deleted via record.destroy
|
243
269
|
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
@@ -248,10 +274,16 @@ module ActiveRecord
|
|
248
274
|
#
|
249
275
|
# Hence this method.
|
250
276
|
def inverse_which_updates_counter_cache
|
251
|
-
|
252
|
-
|
253
|
-
|
277
|
+
unless @inverse_which_updates_counter_cache_defined
|
278
|
+
if counter_cache_column
|
279
|
+
inverse_candidates = inverse_of ? [inverse_of] : klass.reflect_on_all_associations(:belongs_to)
|
280
|
+
@inverse_which_updates_counter_cache = inverse_candidates.find do |inverse|
|
281
|
+
inverse.counter_cache_column == counter_cache_column && (inverse.polymorphic? || inverse.klass == active_record)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
@inverse_which_updates_counter_cache_defined = true
|
254
285
|
end
|
286
|
+
@inverse_which_updates_counter_cache
|
255
287
|
end
|
256
288
|
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
257
289
|
|
@@ -293,6 +325,12 @@ module ActiveRecord
|
|
293
325
|
options[:strict_loading]
|
294
326
|
end
|
295
327
|
|
328
|
+
def strict_loading_violation_message(owner)
|
329
|
+
message = +"`#{owner}` is marked for strict_loading."
|
330
|
+
message << " The #{polymorphic? ? "polymorphic association" : "#{klass} association"}"
|
331
|
+
message << " named `:#{name}` cannot be lazily loaded."
|
332
|
+
end
|
333
|
+
|
296
334
|
protected
|
297
335
|
def actual_source_reflection # FIXME: this is a horrible name
|
298
336
|
self
|
@@ -306,6 +344,12 @@ module ActiveRecord
|
|
306
344
|
def primary_key(klass)
|
307
345
|
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
308
346
|
end
|
347
|
+
|
348
|
+
def ensure_option_not_given_as_class!(option_name)
|
349
|
+
if options[option_name] && options[option_name].class == Class
|
350
|
+
raise ArgumentError, "A class was passed to `:#{option_name}` but we are expecting a string."
|
351
|
+
end
|
352
|
+
end
|
309
353
|
end
|
310
354
|
|
311
355
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
@@ -330,6 +374,7 @@ module ActiveRecord
|
|
330
374
|
attr_reader :plural_name # :nodoc:
|
331
375
|
|
332
376
|
def initialize(name, scope, options, active_record)
|
377
|
+
super()
|
333
378
|
@name = name
|
334
379
|
@scope = scope
|
335
380
|
@options = options
|
@@ -392,7 +437,7 @@ module ActiveRecord
|
|
392
437
|
|
393
438
|
# Holds all the metadata about an aggregation as it was specified in the
|
394
439
|
# Active Record class.
|
395
|
-
class AggregateReflection < MacroReflection
|
440
|
+
class AggregateReflection < MacroReflection # :nodoc:
|
396
441
|
def mapping
|
397
442
|
mapping = options[:mapping] || [name, name]
|
398
443
|
mapping.first.is_a?(Array) ? mapping : [mapping]
|
@@ -401,12 +446,29 @@ module ActiveRecord
|
|
401
446
|
|
402
447
|
# Holds all the metadata about an association as it was specified in the
|
403
448
|
# Active Record class.
|
404
|
-
class AssociationReflection < MacroReflection
|
449
|
+
class AssociationReflection < MacroReflection # :nodoc:
|
405
450
|
def compute_class(name)
|
406
451
|
if polymorphic?
|
407
452
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
408
453
|
end
|
409
|
-
|
454
|
+
|
455
|
+
begin
|
456
|
+
klass = active_record.send(:compute_type, name)
|
457
|
+
rescue NameError => error
|
458
|
+
if error.name.match?(/(?:\A|::)#{name}\z/)
|
459
|
+
message = "Missing model class #{name} for the #{active_record}##{self.name} association."
|
460
|
+
message += " You can specify a different model class with the :class_name option." unless options[:class_name]
|
461
|
+
raise NameError.new(message, name)
|
462
|
+
else
|
463
|
+
raise
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
unless klass < ActiveRecord::Base
|
468
|
+
raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass."
|
469
|
+
end
|
470
|
+
|
471
|
+
klass
|
410
472
|
end
|
411
473
|
|
412
474
|
attr_reader :type, :foreign_type
|
@@ -416,11 +478,12 @@ module ActiveRecord
|
|
416
478
|
super
|
417
479
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
418
480
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
419
|
-
@
|
481
|
+
@join_table = nil
|
482
|
+
@foreign_key = nil
|
483
|
+
@association_foreign_key = nil
|
484
|
+
@association_primary_key = nil
|
420
485
|
|
421
|
-
|
422
|
-
raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
|
423
|
-
end
|
486
|
+
ensure_option_not_given_as_class!(:class_name)
|
424
487
|
end
|
425
488
|
|
426
489
|
def association_scope_cache(klass, owner, &block)
|
@@ -431,16 +494,24 @@ module ActiveRecord
|
|
431
494
|
klass.cached_find_by_statement(key, &block)
|
432
495
|
end
|
433
496
|
|
434
|
-
def constructable? # :nodoc:
|
435
|
-
@constructable
|
436
|
-
end
|
437
|
-
|
438
497
|
def join_table
|
439
498
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
440
499
|
end
|
441
500
|
|
442
|
-
def foreign_key
|
443
|
-
@foreign_key ||=
|
501
|
+
def foreign_key(infer_from_inverse_of: true)
|
502
|
+
@foreign_key ||= if options[:query_constraints]
|
503
|
+
options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
|
504
|
+
elsif options[:foreign_key]
|
505
|
+
options[:foreign_key].to_s
|
506
|
+
else
|
507
|
+
derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
|
508
|
+
|
509
|
+
if active_record.has_query_constraints?
|
510
|
+
derived_fk = derive_fk_query_constraints(derived_fk)
|
511
|
+
end
|
512
|
+
|
513
|
+
derived_fk
|
514
|
+
end
|
444
515
|
end
|
445
516
|
|
446
517
|
def association_foreign_key
|
@@ -452,36 +523,62 @@ module ActiveRecord
|
|
452
523
|
end
|
453
524
|
|
454
525
|
def active_record_primary_key
|
455
|
-
|
526
|
+
custom_primary_key = options[:primary_key]
|
527
|
+
@active_record_primary_key ||= if custom_primary_key
|
528
|
+
if custom_primary_key.is_a?(Array)
|
529
|
+
custom_primary_key.map { |pk| pk.to_s.freeze }.freeze
|
530
|
+
else
|
531
|
+
custom_primary_key.to_s.freeze
|
532
|
+
end
|
533
|
+
elsif active_record.has_query_constraints? || options[:query_constraints]
|
534
|
+
active_record.query_constraints_list
|
535
|
+
elsif active_record.composite_primary_key?
|
536
|
+
# If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
537
|
+
primary_key = primary_key(active_record)
|
538
|
+
primary_key.include?("id") ? "id" : primary_key.freeze
|
539
|
+
else
|
540
|
+
primary_key(active_record).freeze
|
541
|
+
end
|
456
542
|
end
|
457
543
|
|
458
544
|
def join_primary_key(klass = nil)
|
459
545
|
foreign_key
|
460
546
|
end
|
461
547
|
|
548
|
+
def join_primary_type
|
549
|
+
type
|
550
|
+
end
|
551
|
+
|
462
552
|
def join_foreign_key
|
463
553
|
active_record_primary_key
|
464
554
|
end
|
465
555
|
|
466
556
|
def check_validity!
|
467
557
|
check_validity_of_inverse!
|
558
|
+
|
559
|
+
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
560
|
+
if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length
|
561
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
562
|
+
elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length
|
563
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
564
|
+
end
|
565
|
+
end
|
468
566
|
end
|
469
567
|
|
470
|
-
def
|
568
|
+
def check_eager_loadable!
|
471
569
|
return unless scope
|
472
570
|
|
473
571
|
unless scope.arity == 0
|
474
572
|
raise ArgumentError, <<-MSG.squish
|
475
573
|
The association scope '#{name}' is instance dependent (the scope
|
476
|
-
block takes an argument).
|
477
|
-
not supported.
|
574
|
+
block takes an argument). Eager loading instance dependent scopes
|
575
|
+
is not supported.
|
478
576
|
MSG
|
479
577
|
end
|
480
578
|
end
|
481
|
-
alias :check_eager_loadable! :check_preloadable!
|
482
579
|
|
483
580
|
def join_id_for(owner) # :nodoc:
|
484
|
-
owner
|
581
|
+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
|
485
582
|
end
|
486
583
|
|
487
584
|
def through_reflection
|
@@ -563,8 +660,9 @@ module ActiveRecord
|
|
563
660
|
options[:polymorphic]
|
564
661
|
end
|
565
662
|
|
566
|
-
|
567
|
-
|
663
|
+
def polymorphic_name
|
664
|
+
active_record.polymorphic_name
|
665
|
+
end
|
568
666
|
|
569
667
|
def add_as_source(seed)
|
570
668
|
seed
|
@@ -583,10 +681,6 @@ module ActiveRecord
|
|
583
681
|
end
|
584
682
|
|
585
683
|
private
|
586
|
-
def calculate_constructable(macro, options)
|
587
|
-
true
|
588
|
-
end
|
589
|
-
|
590
684
|
# Attempts to find the inverse association name automatically.
|
591
685
|
# If it cannot find a suitable inverse association name, it returns
|
592
686
|
# +nil+.
|
@@ -605,7 +699,9 @@ module ActiveRecord
|
|
605
699
|
|
606
700
|
begin
|
607
701
|
reflection = klass._reflect_on_association(inverse_name)
|
608
|
-
rescue NameError
|
702
|
+
rescue NameError => error
|
703
|
+
raise unless error.name.to_s == class_name
|
704
|
+
|
609
705
|
# Give up: we couldn't compute the klass type so we won't be able
|
610
706
|
# to find any associations either.
|
611
707
|
reflection = false
|
@@ -623,9 +719,10 @@ module ActiveRecord
|
|
623
719
|
# with the current reflection's klass name.
|
624
720
|
def valid_inverse_reflection?(reflection)
|
625
721
|
reflection &&
|
722
|
+
reflection != self &&
|
626
723
|
foreign_key == reflection.foreign_key &&
|
627
724
|
klass <= reflection.active_record &&
|
628
|
-
can_find_inverse_of_automatically?(reflection)
|
725
|
+
can_find_inverse_of_automatically?(reflection, true)
|
629
726
|
end
|
630
727
|
|
631
728
|
# Checks to see if the reflection doesn't have any options that prevent
|
@@ -634,14 +731,25 @@ module ActiveRecord
|
|
634
731
|
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
|
635
732
|
# Third, we must not have options such as <tt>:foreign_key</tt>
|
636
733
|
# which prevent us from correctly guessing the inverse association.
|
637
|
-
|
638
|
-
# Anything with a scope can additionally ruin our attempt at finding an
|
639
|
-
# inverse, so we exclude reflections with scopes.
|
640
|
-
def can_find_inverse_of_automatically?(reflection)
|
734
|
+
def can_find_inverse_of_automatically?(reflection, inverse_reflection = false)
|
641
735
|
reflection.options[:inverse_of] != false &&
|
642
|
-
|
643
|
-
!
|
736
|
+
!reflection.options[:through] &&
|
737
|
+
!reflection.options[:foreign_key] &&
|
738
|
+
scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
|
739
|
+
end
|
740
|
+
|
741
|
+
# Scopes on the potential inverse reflection prevent automatic
|
742
|
+
# <tt>inverse_of</tt>, since the scope could exclude the owner record
|
743
|
+
# we would inverse from. Scopes on the reflection itself allow for
|
744
|
+
# automatic <tt>inverse_of</tt> as long as
|
745
|
+
# <tt>config.active_record.automatic_scope_inversing<tt> is set to
|
746
|
+
# +true+ (the default for new applications).
|
747
|
+
def scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
|
748
|
+
if inverse_reflection
|
644
749
|
!reflection.scope
|
750
|
+
else
|
751
|
+
!reflection.scope || reflection.klass.automatic_scope_inversing
|
752
|
+
end
|
645
753
|
end
|
646
754
|
|
647
755
|
def derive_class_name
|
@@ -650,13 +758,53 @@ module ActiveRecord
|
|
650
758
|
class_name.camelize
|
651
759
|
end
|
652
760
|
|
653
|
-
def derive_foreign_key
|
761
|
+
def derive_foreign_key(infer_from_inverse_of: true)
|
654
762
|
if belongs_to?
|
655
763
|
"#{name}_id"
|
656
764
|
elsif options[:as]
|
657
765
|
"#{options[:as]}_id"
|
766
|
+
elsif options[:inverse_of] && infer_from_inverse_of
|
767
|
+
inverse_of.foreign_key(infer_from_inverse_of: false)
|
658
768
|
else
|
659
|
-
active_record.
|
769
|
+
active_record.model_name.to_s.foreign_key
|
770
|
+
end
|
771
|
+
end
|
772
|
+
|
773
|
+
def derive_fk_query_constraints(foreign_key)
|
774
|
+
primary_query_constraints = active_record.query_constraints_list
|
775
|
+
owner_pk = active_record.primary_key
|
776
|
+
|
777
|
+
if primary_query_constraints.size != 2
|
778
|
+
raise ArgumentError, <<~MSG.squish
|
779
|
+
The query constraints list on the `#{active_record}` model has more than 2
|
780
|
+
attributes. Active Record is unable to derive the query constraints
|
781
|
+
for the association. You need to explicitly define the query constraints
|
782
|
+
for this association.
|
783
|
+
MSG
|
784
|
+
end
|
785
|
+
|
786
|
+
if !primary_query_constraints.include?(owner_pk)
|
787
|
+
raise ArgumentError, <<~MSG.squish
|
788
|
+
The query constraints on the `#{active_record}` model does not include the primary
|
789
|
+
key so Active Record is unable to derive the foreign key constraints for
|
790
|
+
the association. You need to explicitly define the query constraints for this
|
791
|
+
association.
|
792
|
+
MSG
|
793
|
+
end
|
794
|
+
|
795
|
+
first_key, last_key = primary_query_constraints
|
796
|
+
|
797
|
+
if first_key == owner_pk
|
798
|
+
[foreign_key, last_key.to_s]
|
799
|
+
elsif last_key == owner_pk
|
800
|
+
[first_key.to_s, foreign_key]
|
801
|
+
else
|
802
|
+
raise ArgumentError, <<~MSG.squish
|
803
|
+
Active Record couldn't correctly interpret the query constraints
|
804
|
+
for the `#{active_record}` model. The query constraints on `#{active_record}` are
|
805
|
+
`#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
|
806
|
+
You need to explicitly set the query constraints for this association.
|
807
|
+
MSG
|
660
808
|
end
|
661
809
|
end
|
662
810
|
|
@@ -691,11 +839,6 @@ module ActiveRecord
|
|
691
839
|
Associations::HasOneAssociation
|
692
840
|
end
|
693
841
|
end
|
694
|
-
|
695
|
-
private
|
696
|
-
def calculate_constructable(macro, options)
|
697
|
-
!options[:through]
|
698
|
-
end
|
699
842
|
end
|
700
843
|
|
701
844
|
class BelongsToReflection < AssociationReflection # :nodoc:
|
@@ -715,6 +858,12 @@ module ActiveRecord
|
|
715
858
|
def association_primary_key(klass = nil)
|
716
859
|
if primary_key = options[:primary_key]
|
717
860
|
@association_primary_key ||= -primary_key.to_s
|
861
|
+
elsif !polymorphic? && ((klass || self.klass).has_query_constraints? || options[:query_constraints])
|
862
|
+
(klass || self.klass).composite_query_constraints_list
|
863
|
+
elsif (klass || self.klass).composite_primary_key?
|
864
|
+
# If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
865
|
+
primary_key = (klass || self.klass).primary_key
|
866
|
+
primary_key.include?("id") ? "id" : primary_key
|
718
867
|
else
|
719
868
|
primary_key(klass || self.klass)
|
720
869
|
end
|
@@ -733,13 +882,9 @@ module ActiveRecord
|
|
733
882
|
end
|
734
883
|
|
735
884
|
private
|
736
|
-
def can_find_inverse_of_automatically?(
|
885
|
+
def can_find_inverse_of_automatically?(*)
|
737
886
|
!polymorphic? && super
|
738
887
|
end
|
739
|
-
|
740
|
-
def calculate_constructable(macro, options)
|
741
|
-
!polymorphic?
|
742
|
-
end
|
743
888
|
end
|
744
889
|
|
745
890
|
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
@@ -752,14 +897,17 @@ module ActiveRecord
|
|
752
897
|
|
753
898
|
# Holds all the metadata about a :through association as it was specified
|
754
899
|
# in the Active Record class.
|
755
|
-
class ThroughReflection < AbstractReflection
|
900
|
+
class ThroughReflection < AbstractReflection # :nodoc:
|
756
901
|
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
|
757
902
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
758
903
|
|
759
904
|
def initialize(delegate_reflection)
|
905
|
+
super()
|
760
906
|
@delegate_reflection = delegate_reflection
|
761
907
|
@klass = delegate_reflection.options[:anonymous_class]
|
762
908
|
@source_reflection_name = delegate_reflection.options[:source]
|
909
|
+
|
910
|
+
ensure_option_not_given_as_class!(:source_type)
|
763
911
|
end
|
764
912
|
|
765
913
|
def through_reflection?
|
@@ -840,8 +988,8 @@ module ActiveRecord
|
|
840
988
|
source_reflection.scopes + super
|
841
989
|
end
|
842
990
|
|
843
|
-
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
844
|
-
source_reflection.join_scopes(table, predicate_builder, klass) + super
|
991
|
+
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
992
|
+
source_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
845
993
|
end
|
846
994
|
|
847
995
|
def has_scope?
|
@@ -888,24 +1036,23 @@ module ActiveRecord
|
|
888
1036
|
end
|
889
1037
|
|
890
1038
|
def source_reflection_name # :nodoc:
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
1039
|
+
@source_reflection_name ||= begin
|
1040
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
1041
|
+
names = names.find_all { |n|
|
1042
|
+
through_reflection.klass._reflect_on_association(n)
|
1043
|
+
}
|
1044
|
+
|
1045
|
+
if names.length > 1
|
1046
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
1047
|
+
active_record.name,
|
1048
|
+
macro,
|
1049
|
+
name,
|
1050
|
+
options,
|
1051
|
+
source_reflection_names
|
1052
|
+
)
|
1053
|
+
end
|
1054
|
+
names.first
|
906
1055
|
end
|
907
|
-
|
908
|
-
@source_reflection_name = names.first
|
909
1056
|
end
|
910
1057
|
|
911
1058
|
def source_options
|
@@ -1009,13 +1156,14 @@ module ActiveRecord
|
|
1009
1156
|
:name, :scope_for, to: :@reflection
|
1010
1157
|
|
1011
1158
|
def initialize(reflection, previous_reflection)
|
1159
|
+
super()
|
1012
1160
|
@reflection = reflection
|
1013
1161
|
@previous_reflection = previous_reflection
|
1014
1162
|
end
|
1015
1163
|
|
1016
|
-
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
1017
|
-
scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
|
1018
|
-
scopes << build_scope(table, predicate_builder, klass).instance_exec(
|
1164
|
+
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
1165
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
1166
|
+
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
1019
1167
|
end
|
1020
1168
|
|
1021
1169
|
def constraints
|
@@ -1034,6 +1182,7 @@ module ActiveRecord
|
|
1034
1182
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1035
1183
|
|
1036
1184
|
def initialize(reflection, association)
|
1185
|
+
super()
|
1037
1186
|
@reflection = reflection
|
1038
1187
|
@association = association
|
1039
1188
|
end
|