activerecord 4.1.8 → 4.2.11.3
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.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1165 -1591
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +84 -43
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -14
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +87 -30
- data/lib/active_record/associations/collection_proxy.rb +33 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -12
- data/lib/active_record/associations/preloader/association.rb +14 -10
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +37 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +16 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +20 -12
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -28
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +57 -95
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +30 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +85 -53
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +139 -57
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +271 -74
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -60
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +295 -141
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +17 -33
- data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -145
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -385
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +134 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -40
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +10 -12
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +62 -74
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +79 -47
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +18 -8
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +48 -27
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +19 -14
- data/lib/active_record/railties/databases.rake +55 -56
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +281 -117
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +71 -48
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +42 -12
- data/lib/active_record/relation/query_methods.rb +130 -73
- data/lib/active_record/relation/spawn_methods.rb +10 -3
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -8
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +54 -28
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +24 -20
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,35 +1,46 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
3
|
+
|
1
4
|
module ActiveRecord
|
2
5
|
# = Active Record Reflection
|
3
6
|
module Reflection # :nodoc:
|
4
7
|
extend ActiveSupport::Concern
|
5
8
|
|
6
9
|
included do
|
7
|
-
class_attribute :_reflections
|
8
|
-
class_attribute :aggregate_reflections
|
10
|
+
class_attribute :_reflections, instance_writer: false
|
11
|
+
class_attribute :aggregate_reflections, instance_writer: false
|
9
12
|
self._reflections = {}
|
10
13
|
self.aggregate_reflections = {}
|
11
14
|
end
|
12
15
|
|
13
16
|
def self.create(macro, name, scope, options, ar)
|
14
|
-
case macro
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
klass = case macro
|
18
|
+
when :composed_of
|
19
|
+
AggregateReflection
|
20
|
+
when :has_many
|
21
|
+
HasManyReflection
|
22
|
+
when :has_one
|
23
|
+
HasOneReflection
|
24
|
+
when :belongs_to
|
25
|
+
BelongsToReflection
|
26
|
+
else
|
27
|
+
raise "Unsupported Macro: #{macro}"
|
28
|
+
end
|
29
|
+
|
30
|
+
reflection = klass.new(name, scope, options, ar)
|
31
|
+
options[:through] ? ThroughReflection.new(reflection) : reflection
|
22
32
|
end
|
23
33
|
|
24
34
|
def self.add_reflection(ar, name, reflection)
|
25
|
-
ar.
|
35
|
+
ar.clear_reflections_cache
|
36
|
+
ar._reflections = ar._reflections.merge(name.to_s => reflection)
|
26
37
|
end
|
27
38
|
|
28
39
|
def self.add_aggregate_reflection(ar, name, reflection)
|
29
|
-
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.
|
40
|
+
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
|
30
41
|
end
|
31
42
|
|
32
|
-
# \Reflection enables
|
43
|
+
# \Reflection enables interrogating of Active Record classes and objects
|
33
44
|
# about their associations and aggregations. This information can,
|
34
45
|
# for example, be used in a form builder that takes an Active Record object
|
35
46
|
# and creates input fields for all of the attributes depending on their type
|
@@ -48,25 +59,30 @@ module ActiveRecord
|
|
48
59
|
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
49
60
|
#
|
50
61
|
def reflect_on_aggregation(aggregation)
|
51
|
-
aggregate_reflections[aggregation.
|
62
|
+
aggregate_reflections[aggregation.to_s]
|
52
63
|
end
|
53
64
|
|
54
65
|
# Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
|
55
66
|
#
|
56
|
-
# Account.reflections # => {balance
|
67
|
+
# Account.reflections # => {"balance" => AggregateReflection}
|
57
68
|
#
|
58
69
|
# @api public
|
59
70
|
def reflections
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
71
|
+
@__reflections ||= begin
|
72
|
+
ref = {}
|
73
|
+
|
74
|
+
_reflections.each do |name, reflection|
|
75
|
+
parent_name, parent_reflection = reflection.parent_reflection
|
76
|
+
|
77
|
+
if parent_name
|
78
|
+
ref[parent_name] = parent_reflection
|
79
|
+
else
|
80
|
+
ref[name] = reflection
|
81
|
+
end
|
67
82
|
end
|
83
|
+
|
84
|
+
ref
|
68
85
|
end
|
69
|
-
ref
|
70
86
|
end
|
71
87
|
|
72
88
|
# Returns an array of AssociationReflection objects for all the
|
@@ -92,12 +108,12 @@ module ActiveRecord
|
|
92
108
|
#
|
93
109
|
# @api public
|
94
110
|
def reflect_on_association(association)
|
95
|
-
reflections[association.
|
111
|
+
reflections[association.to_s]
|
96
112
|
end
|
97
113
|
|
98
114
|
# @api private
|
99
115
|
def _reflect_on_association(association) #:nodoc:
|
100
|
-
_reflections[association.
|
116
|
+
_reflections[association.to_s]
|
101
117
|
end
|
102
118
|
|
103
119
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
@@ -106,28 +122,87 @@ module ActiveRecord
|
|
106
122
|
def reflect_on_all_autosave_associations
|
107
123
|
reflections.values.select { |reflection| reflection.options[:autosave] }
|
108
124
|
end
|
125
|
+
|
126
|
+
def clear_reflections_cache #:nodoc:
|
127
|
+
@__reflections = nil
|
128
|
+
end
|
109
129
|
end
|
110
130
|
|
131
|
+
# Holds all the methods that are shared between MacroReflection, AssociationReflection
|
132
|
+
# and ThroughReflection
|
133
|
+
class AbstractReflection # :nodoc:
|
134
|
+
def table_name
|
135
|
+
klass.table_name
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns a new, unsaved instance of the associated class. +attributes+ will
|
139
|
+
# be passed to the class's constructor.
|
140
|
+
def build_association(attributes, &block)
|
141
|
+
klass.new(attributes, &block)
|
142
|
+
end
|
143
|
+
|
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
|
+
# Returns the class name for the macro.
|
153
|
+
#
|
154
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
155
|
+
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
156
|
+
def class_name
|
157
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
158
|
+
end
|
159
|
+
|
160
|
+
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
|
161
|
+
|
162
|
+
def join_keys(assoc_klass)
|
163
|
+
JoinKeys.new(foreign_key, active_record_primary_key)
|
164
|
+
end
|
165
|
+
|
166
|
+
def source_macro
|
167
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
168
|
+
ActiveRecord::Base.source_macro is deprecated and will be removed
|
169
|
+
without replacement.
|
170
|
+
MSG
|
171
|
+
|
172
|
+
macro
|
173
|
+
end
|
174
|
+
|
175
|
+
def inverse_of
|
176
|
+
return unless inverse_name
|
177
|
+
|
178
|
+
@inverse_of ||= klass._reflect_on_association inverse_name
|
179
|
+
end
|
180
|
+
|
181
|
+
def check_validity_of_inverse!
|
182
|
+
unless polymorphic?
|
183
|
+
if has_inverse? && inverse_of.nil?
|
184
|
+
raise InverseOfAssociationNotFoundError.new(self)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
111
189
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
112
190
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
113
191
|
#
|
114
192
|
# MacroReflection
|
115
|
-
# AggregateReflection
|
116
193
|
# AssociationReflection
|
117
|
-
#
|
118
|
-
|
194
|
+
# AggregateReflection
|
195
|
+
# HasManyReflection
|
196
|
+
# HasOneReflection
|
197
|
+
# BelongsToReflection
|
198
|
+
# ThroughReflection
|
199
|
+
class MacroReflection < AbstractReflection
|
119
200
|
# Returns the name of the macro.
|
120
201
|
#
|
121
202
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
|
122
203
|
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
123
204
|
attr_reader :name
|
124
205
|
|
125
|
-
# Returns the macro type.
|
126
|
-
#
|
127
|
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
|
128
|
-
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
129
|
-
attr_reader :macro
|
130
|
-
|
131
206
|
attr_reader :scope
|
132
207
|
|
133
208
|
# Returns the hash of options used for the macro.
|
@@ -140,13 +215,12 @@ module ActiveRecord
|
|
140
215
|
|
141
216
|
attr_reader :plural_name # :nodoc:
|
142
217
|
|
143
|
-
def initialize(
|
144
|
-
@macro = macro
|
218
|
+
def initialize(name, scope, options, active_record)
|
145
219
|
@name = name
|
146
220
|
@scope = scope
|
147
221
|
@options = options
|
148
222
|
@active_record = active_record
|
149
|
-
@klass = options[:
|
223
|
+
@klass = options[:anonymous_class]
|
150
224
|
@plural_name = active_record.pluralize_table_names ?
|
151
225
|
name.to_s.pluralize : name.to_s
|
152
226
|
end
|
@@ -165,15 +239,11 @@ module ActiveRecord
|
|
165
239
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
166
240
|
# <tt>has_many :clients</tt> returns the Client class
|
167
241
|
def klass
|
168
|
-
@klass ||= class_name
|
242
|
+
@klass ||= compute_class(class_name)
|
169
243
|
end
|
170
244
|
|
171
|
-
|
172
|
-
|
173
|
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
174
|
-
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
175
|
-
def class_name
|
176
|
-
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
245
|
+
def compute_class(name)
|
246
|
+
name.constantize
|
177
247
|
end
|
178
248
|
|
179
249
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
@@ -218,49 +288,46 @@ module ActiveRecord
|
|
218
288
|
# a new association object. Use +build_association+ or +create_association+
|
219
289
|
# instead. This allows plugins to hook into association object creation.
|
220
290
|
def klass
|
221
|
-
@klass ||=
|
291
|
+
@klass ||= compute_class(class_name)
|
292
|
+
end
|
293
|
+
|
294
|
+
def compute_class(name)
|
295
|
+
active_record.send(:compute_type, name)
|
222
296
|
end
|
223
297
|
|
224
298
|
attr_reader :type, :foreign_type
|
225
299
|
attr_accessor :parent_reflection # [:name, Reflection]
|
226
300
|
|
227
|
-
def initialize(
|
301
|
+
def initialize(name, scope, options, active_record)
|
228
302
|
super
|
229
|
-
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
|
230
303
|
@automatic_inverse_of = nil
|
231
|
-
@type = options[:as] && "#{options[:as]}_type"
|
304
|
+
@type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
|
232
305
|
@foreign_type = options[:foreign_type] || "#{name}_type"
|
233
306
|
@constructable = calculate_constructable(macro, options)
|
307
|
+
@association_scope_cache = {}
|
308
|
+
@scope_lock = Mutex.new
|
234
309
|
end
|
235
310
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
311
|
+
def association_scope_cache(conn, owner)
|
312
|
+
key = conn.prepared_statements
|
313
|
+
if polymorphic?
|
314
|
+
key = [key, owner._read_attribute(@foreign_type)]
|
315
|
+
end
|
316
|
+
@association_scope_cache[key] ||= @scope_lock.synchronize {
|
317
|
+
@association_scope_cache[key] ||= yield
|
318
|
+
}
|
240
319
|
end
|
241
320
|
|
242
321
|
def constructable? # :nodoc:
|
243
322
|
@constructable
|
244
323
|
end
|
245
324
|
|
246
|
-
def table_name
|
247
|
-
klass.table_name
|
248
|
-
end
|
249
|
-
|
250
|
-
def quoted_table_name
|
251
|
-
klass.quoted_table_name
|
252
|
-
end
|
253
|
-
|
254
325
|
def join_table
|
255
326
|
@join_table ||= options[:join_table] || derive_join_table
|
256
327
|
end
|
257
328
|
|
258
329
|
def foreign_key
|
259
|
-
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
260
|
-
end
|
261
|
-
|
262
|
-
def primary_key_column
|
263
|
-
klass.columns_hash[klass.primary_key]
|
330
|
+
@foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
|
264
331
|
end
|
265
332
|
|
266
333
|
def association_foreign_key
|
@@ -288,13 +355,25 @@ module ActiveRecord
|
|
288
355
|
check_validity_of_inverse!
|
289
356
|
end
|
290
357
|
|
291
|
-
def
|
292
|
-
unless
|
293
|
-
|
294
|
-
|
295
|
-
|
358
|
+
def check_preloadable!
|
359
|
+
return unless scope
|
360
|
+
|
361
|
+
if scope.arity > 0
|
362
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
363
|
+
The association scope '#{name}' is instance dependent (the scope
|
364
|
+
block takes an argument). Preloading happens before the individual
|
365
|
+
instances are created. This means that there is no instance being
|
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.
|
369
|
+
MSG
|
296
370
|
end
|
297
371
|
end
|
372
|
+
alias :check_eager_loadable! :check_preloadable!
|
373
|
+
|
374
|
+
def join_id_for(owner) # :nodoc:
|
375
|
+
owner[active_record_primary_key]
|
376
|
+
end
|
298
377
|
|
299
378
|
def through_reflection
|
300
379
|
nil
|
@@ -320,18 +399,10 @@ module ActiveRecord
|
|
320
399
|
scope ? [[scope]] : [[]]
|
321
400
|
end
|
322
401
|
|
323
|
-
alias :source_macro :macro
|
324
|
-
|
325
402
|
def has_inverse?
|
326
403
|
inverse_name
|
327
404
|
end
|
328
405
|
|
329
|
-
def inverse_of
|
330
|
-
return unless inverse_name
|
331
|
-
|
332
|
-
@inverse_of ||= klass._reflect_on_association inverse_name
|
333
|
-
end
|
334
|
-
|
335
406
|
def polymorphic_inverse_of(associated_class)
|
336
407
|
if has_inverse?
|
337
408
|
if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
|
@@ -342,11 +413,16 @@ module ActiveRecord
|
|
342
413
|
end
|
343
414
|
end
|
344
415
|
|
416
|
+
# Returns the macro type.
|
417
|
+
#
|
418
|
+
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
419
|
+
def macro; raise NotImplementedError; end
|
420
|
+
|
345
421
|
# Returns whether or not this association reflection is for a collection
|
346
422
|
# association. Returns +true+ if the +macro+ is either +has_many+ or
|
347
423
|
# +has_and_belongs_to_many+, +false+ otherwise.
|
348
424
|
def collection?
|
349
|
-
|
425
|
+
false
|
350
426
|
end
|
351
427
|
|
352
428
|
# Returns whether or not the association should be validated as part of
|
@@ -359,18 +435,19 @@ module ActiveRecord
|
|
359
435
|
# * you use autosave; <tt>autosave: true</tt>
|
360
436
|
# * the association is a +has_many+ association
|
361
437
|
def validate?
|
362
|
-
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true ||
|
438
|
+
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
|
363
439
|
end
|
364
440
|
|
365
441
|
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
366
|
-
def belongs_to
|
367
|
-
|
368
|
-
|
442
|
+
def belongs_to?; false; end
|
443
|
+
|
444
|
+
# Returns +true+ if +self+ is a +has_one+ reflection.
|
445
|
+
def has_one?; false; end
|
369
446
|
|
370
447
|
def association_class
|
371
448
|
case macro
|
372
449
|
when :belongs_to
|
373
|
-
if
|
450
|
+
if polymorphic?
|
374
451
|
Associations::BelongsToPolymorphicAssociation
|
375
452
|
else
|
376
453
|
Associations::BelongsToAssociation
|
@@ -391,7 +468,7 @@ module ActiveRecord
|
|
391
468
|
end
|
392
469
|
|
393
470
|
def polymorphic?
|
394
|
-
options
|
471
|
+
options[:polymorphic]
|
395
472
|
end
|
396
473
|
|
397
474
|
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
|
@@ -408,7 +485,7 @@ module ActiveRecord
|
|
408
485
|
def calculate_constructable(macro, options)
|
409
486
|
case macro
|
410
487
|
when :belongs_to
|
411
|
-
!
|
488
|
+
!polymorphic?
|
412
489
|
when :has_one
|
413
490
|
!options[:through]
|
414
491
|
else
|
@@ -432,7 +509,7 @@ module ActiveRecord
|
|
432
509
|
# returns either nil or the inverse association name that it finds.
|
433
510
|
def automatic_inverse_of
|
434
511
|
if can_find_inverse_of_automatically?(self)
|
435
|
-
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name).to_sym
|
512
|
+
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
|
436
513
|
|
437
514
|
begin
|
438
515
|
reflection = klass._reflect_on_association(inverse_name)
|
@@ -497,7 +574,7 @@ module ActiveRecord
|
|
497
574
|
end
|
498
575
|
|
499
576
|
def derive_join_table
|
500
|
-
|
577
|
+
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
501
578
|
end
|
502
579
|
|
503
580
|
def primary_key(klass)
|
@@ -505,15 +582,72 @@ module ActiveRecord
|
|
505
582
|
end
|
506
583
|
end
|
507
584
|
|
585
|
+
class HasManyReflection < AssociationReflection # :nodoc:
|
586
|
+
def initialize(name, scope, options, active_record)
|
587
|
+
super(name, scope, options, active_record)
|
588
|
+
end
|
589
|
+
|
590
|
+
def macro; :has_many; end
|
591
|
+
|
592
|
+
def collection?; true; end
|
593
|
+
end
|
594
|
+
|
595
|
+
class HasOneReflection < AssociationReflection # :nodoc:
|
596
|
+
def initialize(name, scope, options, active_record)
|
597
|
+
super(name, scope, options, active_record)
|
598
|
+
end
|
599
|
+
|
600
|
+
def macro; :has_one; end
|
601
|
+
|
602
|
+
def has_one?; true; end
|
603
|
+
end
|
604
|
+
|
605
|
+
class BelongsToReflection < AssociationReflection # :nodoc:
|
606
|
+
def initialize(name, scope, options, active_record)
|
607
|
+
super(name, scope, options, active_record)
|
608
|
+
end
|
609
|
+
|
610
|
+
def macro; :belongs_to; end
|
611
|
+
|
612
|
+
def belongs_to?; true; end
|
613
|
+
|
614
|
+
def join_keys(assoc_klass)
|
615
|
+
key = polymorphic? ? association_primary_key(assoc_klass) : association_primary_key
|
616
|
+
JoinKeys.new(key, foreign_key)
|
617
|
+
end
|
618
|
+
|
619
|
+
def join_id_for(owner) # :nodoc:
|
620
|
+
owner[foreign_key]
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
625
|
+
def initialize(name, scope, options, active_record)
|
626
|
+
super
|
627
|
+
end
|
628
|
+
|
629
|
+
def macro; :has_and_belongs_to_many; end
|
630
|
+
|
631
|
+
def collection?
|
632
|
+
true
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
508
636
|
# Holds all the meta-data about a :through association as it was specified
|
509
637
|
# in the Active Record class.
|
510
|
-
class ThroughReflection <
|
638
|
+
class ThroughReflection < AbstractReflection #:nodoc:
|
639
|
+
attr_reader :delegate_reflection
|
511
640
|
delegate :foreign_key, :foreign_type, :association_foreign_key,
|
512
641
|
:active_record_primary_key, :type, :to => :source_reflection
|
513
642
|
|
514
|
-
def initialize(
|
515
|
-
|
516
|
-
@
|
643
|
+
def initialize(delegate_reflection)
|
644
|
+
@delegate_reflection = delegate_reflection
|
645
|
+
@klass = delegate_reflection.options[:anonymous_class]
|
646
|
+
@source_reflection_name = delegate_reflection.options[:source]
|
647
|
+
end
|
648
|
+
|
649
|
+
def klass
|
650
|
+
@klass ||= delegate_reflection.compute_class(class_name)
|
517
651
|
end
|
518
652
|
|
519
653
|
# Returns the source of the through reflection. It checks both a singularized
|
@@ -531,7 +665,7 @@ module ActiveRecord
|
|
531
665
|
#
|
532
666
|
# tags_reflection = Post.reflect_on_association(:tags)
|
533
667
|
# tags_reflection.source_reflection
|
534
|
-
# # => <ActiveRecord::Reflection::
|
668
|
+
# # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
|
535
669
|
#
|
536
670
|
def source_reflection
|
537
671
|
through_reflection.klass._reflect_on_association(source_reflection_name)
|
@@ -547,7 +681,7 @@ module ActiveRecord
|
|
547
681
|
#
|
548
682
|
# tags_reflection = Post.reflect_on_association(:tags)
|
549
683
|
# tags_reflection.through_reflection
|
550
|
-
# # => <ActiveRecord::Reflection::
|
684
|
+
# # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
|
551
685
|
#
|
552
686
|
def through_reflection
|
553
687
|
active_record._reflect_on_association(options[:through])
|
@@ -567,8 +701,8 @@ module ActiveRecord
|
|
567
701
|
#
|
568
702
|
# tags_reflection = Post.reflect_on_association(:tags)
|
569
703
|
# tags_reflection.chain
|
570
|
-
# # => [<ActiveRecord::Reflection::ThroughReflection: @
|
571
|
-
# <ActiveRecord::Reflection::
|
704
|
+
# # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
|
705
|
+
# <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
|
572
706
|
#
|
573
707
|
def chain
|
574
708
|
@chain ||= begin
|
@@ -609,8 +743,11 @@ module ActiveRecord
|
|
609
743
|
through_scope_chain = through_reflection.scope_chain.map(&:dup)
|
610
744
|
|
611
745
|
if options[:source_type]
|
612
|
-
|
613
|
-
|
746
|
+
type = foreign_type
|
747
|
+
source_type = options[:source_type]
|
748
|
+
through_scope_chain.first << lambda { |object|
|
749
|
+
where(type => source_type)
|
750
|
+
}
|
614
751
|
end
|
615
752
|
|
616
753
|
# Recursively fill out the rest of the array from the through reflection
|
@@ -618,8 +755,17 @@ module ActiveRecord
|
|
618
755
|
end
|
619
756
|
end
|
620
757
|
|
758
|
+
def join_keys(assoc_klass)
|
759
|
+
source_reflection.join_keys(assoc_klass)
|
760
|
+
end
|
761
|
+
|
621
762
|
# The macro used by the source association
|
622
763
|
def source_macro
|
764
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
765
|
+
ActiveRecord::Base.source_macro is deprecated and will be removed
|
766
|
+
without replacement.
|
767
|
+
MSG
|
768
|
+
|
623
769
|
source_reflection.source_macro
|
624
770
|
end
|
625
771
|
|
@@ -649,11 +795,11 @@ module ActiveRecord
|
|
649
795
|
# # => [:tag, :tags]
|
650
796
|
#
|
651
797
|
def source_reflection_names
|
652
|
-
|
798
|
+
options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
|
653
799
|
end
|
654
800
|
|
655
801
|
def source_reflection_name # :nodoc:
|
656
|
-
return @source_reflection_name
|
802
|
+
return @source_reflection_name if @source_reflection_name
|
657
803
|
|
658
804
|
names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
|
659
805
|
names = names.find_all { |n|
|
@@ -663,15 +809,13 @@ module ActiveRecord
|
|
663
809
|
if names.length > 1
|
664
810
|
example_options = options.dup
|
665
811
|
example_options[:source] = source_reflection_names.first
|
666
|
-
ActiveSupport::Deprecation.warn
|
667
|
-
Ambiguous source reflection for through association. Please
|
668
|
-
directive on your declaration like
|
669
|
-
|
670
|
-
class #{active_record.name} < ActiveRecord::Base
|
671
|
-
#{macro} :#{name}, #{example_options}
|
672
|
-
end
|
673
|
-
|
674
|
-
eowarn
|
812
|
+
ActiveSupport::Deprecation.warn \
|
813
|
+
"Ambiguous source reflection for through association. Please " \
|
814
|
+
"specify a :source directive on your declaration like:\n" \
|
815
|
+
"\n" \
|
816
|
+
" class #{active_record.name} < ActiveRecord::Base\n" \
|
817
|
+
" #{macro} :#{name}, #{example_options}\n" \
|
818
|
+
" end"
|
675
819
|
end
|
676
820
|
|
677
821
|
@source_reflection_name = names.first
|
@@ -685,28 +829,36 @@ directive on your declaration like:
|
|
685
829
|
through_reflection.options
|
686
830
|
end
|
687
831
|
|
832
|
+
def join_id_for(owner) # :nodoc:
|
833
|
+
source_reflection.join_id_for(owner)
|
834
|
+
end
|
835
|
+
|
688
836
|
def check_validity!
|
689
837
|
if through_reflection.nil?
|
690
838
|
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
|
691
839
|
end
|
692
840
|
|
693
|
-
if through_reflection.
|
694
|
-
|
841
|
+
if through_reflection.polymorphic?
|
842
|
+
if has_one?
|
843
|
+
raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
|
844
|
+
else
|
845
|
+
raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
|
846
|
+
end
|
695
847
|
end
|
696
848
|
|
697
849
|
if source_reflection.nil?
|
698
850
|
raise HasManyThroughSourceAssociationNotFoundError.new(self)
|
699
851
|
end
|
700
852
|
|
701
|
-
if options[:source_type] && source_reflection.
|
853
|
+
if options[:source_type] && !source_reflection.polymorphic?
|
702
854
|
raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
|
703
855
|
end
|
704
856
|
|
705
|
-
if source_reflection.
|
857
|
+
if source_reflection.polymorphic? && options[:source_type].nil?
|
706
858
|
raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
|
707
859
|
end
|
708
860
|
|
709
|
-
if
|
861
|
+
if has_one? && through_reflection.collection?
|
710
862
|
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
|
711
863
|
end
|
712
864
|
|
@@ -715,15 +867,27 @@ directive on your declaration like:
|
|
715
867
|
|
716
868
|
protected
|
717
869
|
|
718
|
-
|
719
|
-
|
720
|
-
|
870
|
+
def actual_source_reflection # FIXME: this is a horrible name
|
871
|
+
source_reflection.send(:actual_source_reflection)
|
872
|
+
end
|
873
|
+
|
874
|
+
def primary_key(klass)
|
875
|
+
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
876
|
+
end
|
877
|
+
|
878
|
+
def inverse_name; delegate_reflection.send(:inverse_name); end
|
721
879
|
|
722
880
|
private
|
723
881
|
def derive_class_name
|
724
882
|
# get the class_name of the belongs_to association of the through reflection
|
725
883
|
options[:source_type] || source_reflection.class_name
|
726
884
|
end
|
885
|
+
|
886
|
+
delegate_methods = AssociationReflection.public_instance_methods -
|
887
|
+
public_instance_methods
|
888
|
+
|
889
|
+
delegate(*delegate_methods, to: :delegate_reflection)
|
890
|
+
|
727
891
|
end
|
728
892
|
end
|
729
893
|
end
|