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