activerecord 4.1.0 → 4.2.0

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.

Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +776 -1330
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/association_relation.rb +4 -0
  6. data/lib/active_record/associations/alias_tracker.rb +14 -13
  7. data/lib/active_record/associations/association.rb +2 -2
  8. data/lib/active_record/associations/association_scope.rb +83 -43
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  10. data/lib/active_record/associations/builder/association.rb +15 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
  13. data/lib/active_record/associations/builder/has_many.rb +1 -1
  14. data/lib/active_record/associations/builder/has_one.rb +2 -2
  15. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  16. data/lib/active_record/associations/collection_association.rb +66 -29
  17. data/lib/active_record/associations/collection_proxy.rb +22 -26
  18. data/lib/active_record/associations/has_many_association.rb +65 -18
  19. data/lib/active_record/associations/has_many_through_association.rb +55 -27
  20. data/lib/active_record/associations/has_one_association.rb +0 -1
  21. data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
  22. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  23. data/lib/active_record/associations/join_dependency.rb +20 -12
  24. data/lib/active_record/associations/preloader/association.rb +34 -11
  25. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  26. data/lib/active_record/associations/preloader.rb +49 -59
  27. data/lib/active_record/associations/singular_association.rb +25 -4
  28. data/lib/active_record/associations/through_association.rb +23 -14
  29. data/lib/active_record/associations.rb +171 -42
  30. data/lib/active_record/attribute.rb +149 -0
  31. data/lib/active_record/attribute_assignment.rb +18 -10
  32. data/lib/active_record/attribute_decorators.rb +66 -0
  33. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  34. data/lib/active_record/attribute_methods/dirty.rb +98 -44
  35. data/lib/active_record/attribute_methods/primary_key.rb +14 -8
  36. data/lib/active_record/attribute_methods/query.rb +1 -1
  37. data/lib/active_record/attribute_methods/read.rb +22 -59
  38. data/lib/active_record/attribute_methods/serialization.rb +37 -147
  39. data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
  40. data/lib/active_record/attribute_methods/write.rb +14 -21
  41. data/lib/active_record/attribute_methods.rb +67 -94
  42. data/lib/active_record/attribute_set/builder.rb +86 -0
  43. data/lib/active_record/attribute_set.rb +77 -0
  44. data/lib/active_record/attributes.rb +139 -0
  45. data/lib/active_record/autosave_association.rb +45 -38
  46. data/lib/active_record/base.rb +10 -20
  47. data/lib/active_record/callbacks.rb +7 -7
  48. data/lib/active_record/coders/json.rb +13 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
  53. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
  54. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
  55. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  56. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
  57. data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
  58. data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
  59. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
  60. data/lib/active_record/connection_adapters/column.rb +28 -239
  61. data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
  62. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
  63. data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
  64. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  65. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  66. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
  67. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
  93. data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
  94. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  96. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
  97. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  98. data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
  99. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  100. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
  101. data/lib/active_record/connection_handling.rb +3 -3
  102. data/lib/active_record/core.rb +143 -32
  103. data/lib/active_record/counter_cache.rb +60 -7
  104. data/lib/active_record/enum.rb +10 -11
  105. data/lib/active_record/errors.rb +49 -27
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixtures.rb +56 -70
  108. data/lib/active_record/gem_version.rb +2 -2
  109. data/lib/active_record/inheritance.rb +35 -10
  110. data/lib/active_record/integration.rb +4 -4
  111. data/lib/active_record/locking/optimistic.rb +35 -17
  112. data/lib/active_record/log_subscriber.rb +1 -1
  113. data/lib/active_record/migration/command_recorder.rb +19 -2
  114. data/lib/active_record/migration/join_table.rb +1 -1
  115. data/lib/active_record/migration.rb +52 -49
  116. data/lib/active_record/model_schema.rb +49 -57
  117. data/lib/active_record/nested_attributes.rb +7 -7
  118. data/lib/active_record/null_relation.rb +19 -5
  119. data/lib/active_record/persistence.rb +50 -31
  120. data/lib/active_record/query_cache.rb +3 -3
  121. data/lib/active_record/querying.rb +10 -7
  122. data/lib/active_record/railtie.rb +14 -11
  123. data/lib/active_record/railties/databases.rake +56 -54
  124. data/lib/active_record/readonly_attributes.rb +0 -1
  125. data/lib/active_record/reflection.rb +286 -102
  126. data/lib/active_record/relation/batches.rb +0 -1
  127. data/lib/active_record/relation/calculations.rb +39 -31
  128. data/lib/active_record/relation/delegation.rb +2 -2
  129. data/lib/active_record/relation/finder_methods.rb +80 -36
  130. data/lib/active_record/relation/merger.rb +25 -30
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
  132. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  133. data/lib/active_record/relation/predicate_builder.rb +11 -10
  134. data/lib/active_record/relation/query_methods.rb +141 -55
  135. data/lib/active_record/relation/spawn_methods.rb +3 -0
  136. data/lib/active_record/relation.rb +69 -30
  137. data/lib/active_record/result.rb +18 -7
  138. data/lib/active_record/sanitization.rb +12 -2
  139. data/lib/active_record/schema.rb +0 -1
  140. data/lib/active_record/schema_dumper.rb +58 -26
  141. data/lib/active_record/schema_migration.rb +11 -0
  142. data/lib/active_record/scoping/default.rb +8 -7
  143. data/lib/active_record/scoping/named.rb +4 -0
  144. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  145. data/lib/active_record/statement_cache.rb +95 -10
  146. data/lib/active_record/store.rb +19 -10
  147. data/lib/active_record/tasks/database_tasks.rb +73 -7
  148. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
  149. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  151. data/lib/active_record/timestamp.rb +11 -9
  152. data/lib/active_record/transactions.rb +37 -21
  153. data/lib/active_record/type/big_integer.rb +13 -0
  154. data/lib/active_record/type/binary.rb +50 -0
  155. data/lib/active_record/type/boolean.rb +30 -0
  156. data/lib/active_record/type/date.rb +46 -0
  157. data/lib/active_record/type/date_time.rb +43 -0
  158. data/lib/active_record/type/decimal.rb +40 -0
  159. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  160. data/lib/active_record/type/decorator.rb +14 -0
  161. data/lib/active_record/type/float.rb +19 -0
  162. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  163. data/lib/active_record/type/integer.rb +55 -0
  164. data/lib/active_record/type/mutable.rb +16 -0
  165. data/lib/active_record/type/numeric.rb +36 -0
  166. data/lib/active_record/type/serialized.rb +56 -0
  167. data/lib/active_record/type/string.rb +36 -0
  168. data/lib/active_record/type/text.rb +11 -0
  169. data/lib/active_record/type/time.rb +26 -0
  170. data/lib/active_record/type/time_value.rb +38 -0
  171. data/lib/active_record/type/type_map.rb +64 -0
  172. data/lib/active_record/type/unsigned_integer.rb +15 -0
  173. data/lib/active_record/type/value.rb +101 -0
  174. data/lib/active_record/type.rb +23 -0
  175. data/lib/active_record/validations/associated.rb +5 -3
  176. data/lib/active_record/validations/presence.rb +6 -4
  177. data/lib/active_record/validations/uniqueness.rb +11 -17
  178. data/lib/active_record/validations.rb +25 -19
  179. data/lib/active_record.rb +3 -0
  180. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  181. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
  182. data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +65 -10
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,35 +1,45 @@
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
10
+ class_attribute :_reflections
8
11
  class_attribute :aggregate_reflections
9
- self.reflections = {}
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
- when :has_many, :belongs_to, :has_one
16
- klass = options[:through] ? ThroughReflection : AssociationReflection
17
- when :composed_of
18
- klass = AggregateReflection
19
- end
20
-
21
- klass.new(macro, name, scope, options, ar)
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.reflections = ar.reflections.merge(name => reflection)
35
+ ar._reflections = ar._reflections.merge(name.to_s => reflection)
26
36
  end
27
37
 
28
38
  def self.add_aggregate_reflection(ar, name, reflection)
29
- ar.aggregate_reflections = ar.aggregate_reflections.merge(name => reflection)
39
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
30
40
  end
31
41
 
32
- # \Reflection enables to interrogate Active Record classes and objects
42
+ # \Reflection enables interrogating of Active Record classes and objects
33
43
  # about their associations and aggregations. This information can,
34
44
  # for example, be used in a form builder that takes an Active Record object
35
45
  # and creates input fields for all of the attributes depending on their type
@@ -48,7 +58,25 @@ module ActiveRecord
48
58
  # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
49
59
  #
50
60
  def reflect_on_aggregation(aggregation)
51
- aggregate_reflections[aggregation]
61
+ aggregate_reflections[aggregation.to_s]
62
+ end
63
+
64
+ # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
65
+ #
66
+ # Account.reflections # => {"balance" => AggregateReflection}
67
+ #
68
+ # @api public
69
+ def reflections
70
+ ref = {}
71
+ _reflections.each do |name, reflection|
72
+ parent_name, parent_reflection = reflection.parent_reflection
73
+ if parent_name
74
+ ref[parent_name] = parent_reflection
75
+ else
76
+ ref[name] = reflection
77
+ end
78
+ end
79
+ ref
52
80
  end
53
81
 
54
82
  # Returns an array of AssociationReflection objects for all the
@@ -61,6 +89,7 @@ module ActiveRecord
61
89
  # Account.reflect_on_all_associations # returns an array of all associations
62
90
  # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
63
91
  #
92
+ # @api public
64
93
  def reflect_on_all_associations(macro = nil)
65
94
  association_reflections = reflections.values
66
95
  macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
@@ -71,36 +100,85 @@ module ActiveRecord
71
100
  # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
72
101
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
73
102
  #
103
+ # @api public
74
104
  def reflect_on_association(association)
75
- reflections[association]
105
+ reflections[association.to_s]
106
+ end
107
+
108
+ # @api private
109
+ def _reflect_on_association(association) #:nodoc:
110
+ _reflections[association.to_s]
76
111
  end
77
112
 
78
113
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
114
+ #
115
+ # @api public
79
116
  def reflect_on_all_autosave_associations
80
117
  reflections.values.select { |reflection| reflection.options[:autosave] }
81
118
  end
82
119
  end
83
120
 
121
+ # Holds all the methods that are shared between MacroReflection, AssociationReflection
122
+ # and ThroughReflection
123
+ class AbstractReflection # :nodoc:
124
+ def table_name
125
+ klass.table_name
126
+ end
127
+
128
+ # Returns a new, unsaved instance of the associated class. +attributes+ will
129
+ # be passed to the class's constructor.
130
+ def build_association(attributes, &block)
131
+ klass.new(attributes, &block)
132
+ end
133
+
134
+ def quoted_table_name
135
+ klass.quoted_table_name
136
+ end
137
+
138
+ def primary_key_type
139
+ klass.type_for_attribute(klass.primary_key)
140
+ end
141
+
142
+ # Returns the class name for the macro.
143
+ #
144
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
145
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
146
+ def class_name
147
+ @class_name ||= (options[:class_name] || derive_class_name).to_s
148
+ end
149
+
150
+ JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
151
+
152
+ def join_keys(assoc_klass)
153
+ JoinKeys.new(foreign_key, active_record_primary_key)
154
+ end
155
+
156
+ def source_macro
157
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
158
+ ActiveRecord::Base.source_macro is deprecated and will be removed
159
+ without replacement.
160
+ MSG
161
+
162
+ macro
163
+ end
164
+ end
84
165
  # Base class for AggregateReflection and AssociationReflection. Objects of
85
166
  # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
86
167
  #
87
168
  # MacroReflection
88
- # AggregateReflection
89
169
  # AssociationReflection
90
- # ThroughReflection
91
- class MacroReflection
170
+ # AggregateReflection
171
+ # HasManyReflection
172
+ # HasOneReflection
173
+ # BelongsToReflection
174
+ # ThroughReflection
175
+ class MacroReflection < AbstractReflection
92
176
  # Returns the name of the macro.
93
177
  #
94
178
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
95
179
  # <tt>has_many :clients</tt> returns <tt>:clients</tt>
96
180
  attr_reader :name
97
181
 
98
- # Returns the macro type.
99
- #
100
- # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
101
- # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
102
- attr_reader :macro
103
-
104
182
  attr_reader :scope
105
183
 
106
184
  # Returns the hash of options used for the macro.
@@ -113,8 +191,7 @@ module ActiveRecord
113
191
 
114
192
  attr_reader :plural_name # :nodoc:
115
193
 
116
- def initialize(macro, name, scope, options, active_record)
117
- @macro = macro
194
+ def initialize(name, scope, options, active_record)
118
195
  @name = name
119
196
  @scope = scope
120
197
  @options = options
@@ -127,6 +204,10 @@ module ActiveRecord
127
204
  def autosave=(autosave)
128
205
  @automatic_inverse_of = false
129
206
  @options[:autosave] = autosave
207
+ _, parent_reflection = self.parent_reflection
208
+ if parent_reflection
209
+ parent_reflection.autosave = autosave
210
+ end
130
211
  end
131
212
 
132
213
  # Returns the class for the macro.
@@ -134,15 +215,11 @@ module ActiveRecord
134
215
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
135
216
  # <tt>has_many :clients</tt> returns the Client class
136
217
  def klass
137
- @klass ||= class_name.constantize
218
+ @klass ||= compute_class(class_name)
138
219
  end
139
220
 
140
- # Returns the class name for the macro.
141
- #
142
- # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
143
- # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
144
- def class_name
145
- @class_name ||= (options[:class_name] || derive_class_name).to_s
221
+ def compute_class(name)
222
+ name.constantize
146
223
  end
147
224
 
148
225
  # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
@@ -187,38 +264,40 @@ module ActiveRecord
187
264
  # a new association object. Use +build_association+ or +create_association+
188
265
  # instead. This allows plugins to hook into association object creation.
189
266
  def klass
190
- @klass ||= active_record.send(:compute_type, class_name)
267
+ @klass ||= compute_class(class_name)
268
+ end
269
+
270
+ def compute_class(name)
271
+ active_record.send(:compute_type, name)
191
272
  end
192
273
 
193
274
  attr_reader :type, :foreign_type
275
+ attr_accessor :parent_reflection # [:name, Reflection]
194
276
 
195
- def initialize(macro, name, scope, options, active_record)
277
+ def initialize(name, scope, options, active_record)
196
278
  super
197
- @collection = :has_many == macro
198
279
  @automatic_inverse_of = nil
199
- @type = options[:as] && "#{options[:as]}_type"
280
+ @type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
200
281
  @foreign_type = options[:foreign_type] || "#{name}_type"
201
282
  @constructable = calculate_constructable(macro, options)
283
+ @association_scope_cache = {}
284
+ @scope_lock = Mutex.new
202
285
  end
203
286
 
204
- # Returns a new, unsaved instance of the associated class. +attributes+ will
205
- # be passed to the class's constructor.
206
- def build_association(attributes, &block)
207
- klass.new(attributes, &block)
287
+ def association_scope_cache(conn, owner)
288
+ key = conn.prepared_statements
289
+ if polymorphic?
290
+ key = [key, owner._read_attribute(@foreign_type)]
291
+ end
292
+ @association_scope_cache[key] ||= @scope_lock.synchronize {
293
+ @association_scope_cache[key] ||= yield
294
+ }
208
295
  end
209
296
 
210
297
  def constructable? # :nodoc:
211
298
  @constructable
212
299
  end
213
300
 
214
- def table_name
215
- klass.table_name
216
- end
217
-
218
- def quoted_table_name
219
- klass.quoted_table_name
220
- end
221
-
222
301
  def join_table
223
302
  @join_table ||= options[:join_table] || derive_join_table
224
303
  end
@@ -227,10 +306,6 @@ module ActiveRecord
227
306
  @foreign_key ||= options[:foreign_key] || derive_foreign_key
228
307
  end
229
308
 
230
- def primary_key_column
231
- klass.columns_hash[klass.primary_key]
232
- end
233
-
234
309
  def association_foreign_key
235
310
  @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
236
311
  end
@@ -257,13 +332,33 @@ module ActiveRecord
257
332
  end
258
333
 
259
334
  def check_validity_of_inverse!
260
- unless options[:polymorphic]
335
+ unless polymorphic?
261
336
  if has_inverse? && inverse_of.nil?
262
337
  raise InverseOfAssociationNotFoundError.new(self)
263
338
  end
264
339
  end
265
340
  end
266
341
 
342
+ def check_preloadable!
343
+ return unless scope
344
+
345
+ if scope.arity > 0
346
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
347
+ The association scope '#{name}' is instance dependent (the scope
348
+ block takes an argument). Preloading happens before the individual
349
+ instances are created. This means that there is no instance being
350
+ passed to the association scope. This will most likely result in
351
+ broken or incorrect behavior. Joining, Preloading and eager loading
352
+ of these associations is deprecated and will be removed in the future.
353
+ MSG
354
+ end
355
+ end
356
+ alias :check_eager_loadable! :check_preloadable!
357
+
358
+ def join_id_for(owner) # :nodoc:
359
+ owner[active_record_primary_key]
360
+ end
361
+
267
362
  def through_reflection
268
363
  nil
269
364
  end
@@ -288,8 +383,6 @@ module ActiveRecord
288
383
  scope ? [[scope]] : [[]]
289
384
  end
290
385
 
291
- alias :source_macro :macro
292
-
293
386
  def has_inverse?
294
387
  inverse_name
295
388
  end
@@ -297,12 +390,12 @@ module ActiveRecord
297
390
  def inverse_of
298
391
  return unless inverse_name
299
392
 
300
- @inverse_of ||= klass.reflect_on_association inverse_name
393
+ @inverse_of ||= klass._reflect_on_association inverse_name
301
394
  end
302
395
 
303
396
  def polymorphic_inverse_of(associated_class)
304
397
  if has_inverse?
305
- if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
398
+ if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
306
399
  inverse_relationship
307
400
  else
308
401
  raise InverseOfAssociationNotFoundError.new(self, associated_class)
@@ -310,11 +403,16 @@ module ActiveRecord
310
403
  end
311
404
  end
312
405
 
406
+ # Returns the macro type.
407
+ #
408
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
409
+ def macro; raise NotImplementedError; end
410
+
313
411
  # Returns whether or not this association reflection is for a collection
314
412
  # association. Returns +true+ if the +macro+ is either +has_many+ or
315
413
  # +has_and_belongs_to_many+, +false+ otherwise.
316
414
  def collection?
317
- @collection
415
+ false
318
416
  end
319
417
 
320
418
  # Returns whether or not the association should be validated as part of
@@ -327,18 +425,19 @@ module ActiveRecord
327
425
  # * you use autosave; <tt>autosave: true</tt>
328
426
  # * the association is a +has_many+ association
329
427
  def validate?
330
- !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
428
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
331
429
  end
332
430
 
333
431
  # Returns +true+ if +self+ is a +belongs_to+ reflection.
334
- def belongs_to?
335
- macro == :belongs_to
336
- end
432
+ def belongs_to?; false; end
433
+
434
+ # Returns +true+ if +self+ is a +has_one+ reflection.
435
+ def has_one?; false; end
337
436
 
338
437
  def association_class
339
438
  case macro
340
439
  when :belongs_to
341
- if options[:polymorphic]
440
+ if polymorphic?
342
441
  Associations::BelongsToPolymorphicAssociation
343
442
  else
344
443
  Associations::BelongsToAssociation
@@ -359,7 +458,7 @@ module ActiveRecord
359
458
  end
360
459
 
361
460
  def polymorphic?
362
- options.key? :polymorphic
461
+ options[:polymorphic]
363
462
  end
364
463
 
365
464
  VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
@@ -376,7 +475,7 @@ module ActiveRecord
376
475
  def calculate_constructable(macro, options)
377
476
  case macro
378
477
  when :belongs_to
379
- !options[:polymorphic]
478
+ !polymorphic?
380
479
  when :has_one
381
480
  !options[:through]
382
481
  else
@@ -400,10 +499,10 @@ module ActiveRecord
400
499
  # returns either nil or the inverse association name that it finds.
401
500
  def automatic_inverse_of
402
501
  if can_find_inverse_of_automatically?(self)
403
- inverse_name = ActiveSupport::Inflector.underscore(active_record.name).to_sym
502
+ inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name).to_sym
404
503
 
405
504
  begin
406
- reflection = klass.reflect_on_association(inverse_name)
505
+ reflection = klass._reflect_on_association(inverse_name)
407
506
  rescue NameError
408
507
  # Give up: we couldn't compute the klass type so we won't be able
409
508
  # to find any associations either.
@@ -449,9 +548,9 @@ module ActiveRecord
449
548
  end
450
549
 
451
550
  def derive_class_name
452
- class_name = name.to_s.camelize
551
+ class_name = name.to_s
453
552
  class_name = class_name.singularize if collection?
454
- class_name
553
+ class_name.camelize
455
554
  end
456
555
 
457
556
  def derive_foreign_key
@@ -465,7 +564,7 @@ module ActiveRecord
465
564
  end
466
565
 
467
566
  def derive_join_table
468
- [active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
567
+ ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
469
568
  end
470
569
 
471
570
  def primary_key(klass)
@@ -473,15 +572,72 @@ module ActiveRecord
473
572
  end
474
573
  end
475
574
 
575
+ class HasManyReflection < AssociationReflection # :nodoc:
576
+ def initialize(name, scope, options, active_record)
577
+ super(name, scope, options, active_record)
578
+ end
579
+
580
+ def macro; :has_many; end
581
+
582
+ def collection?; true; end
583
+ end
584
+
585
+ class HasOneReflection < AssociationReflection # :nodoc:
586
+ def initialize(name, scope, options, active_record)
587
+ super(name, scope, options, active_record)
588
+ end
589
+
590
+ def macro; :has_one; end
591
+
592
+ def has_one?; true; end
593
+ end
594
+
595
+ class BelongsToReflection < AssociationReflection # :nodoc:
596
+ def initialize(name, scope, options, active_record)
597
+ super(name, scope, options, active_record)
598
+ end
599
+
600
+ def macro; :belongs_to; end
601
+
602
+ def belongs_to?; true; end
603
+
604
+ def join_keys(assoc_klass)
605
+ key = polymorphic? ? association_primary_key(assoc_klass) : association_primary_key
606
+ JoinKeys.new(key, foreign_key)
607
+ end
608
+
609
+ def join_id_for(owner) # :nodoc:
610
+ owner[foreign_key]
611
+ end
612
+ end
613
+
614
+ class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
615
+ def initialize(name, scope, options, active_record)
616
+ super
617
+ end
618
+
619
+ def macro; :has_and_belongs_to_many; end
620
+
621
+ def collection?
622
+ true
623
+ end
624
+ end
625
+
476
626
  # Holds all the meta-data about a :through association as it was specified
477
627
  # in the Active Record class.
478
- class ThroughReflection < AssociationReflection #:nodoc:
628
+ class ThroughReflection < AbstractReflection #:nodoc:
629
+ attr_reader :delegate_reflection
479
630
  delegate :foreign_key, :foreign_type, :association_foreign_key,
480
631
  :active_record_primary_key, :type, :to => :source_reflection
481
632
 
482
- def initialize(macro, name, scope, options, active_record)
483
- super
484
- @source_reflection_name = options[:source]
633
+ def initialize(delegate_reflection)
634
+ @delegate_reflection = delegate_reflection
635
+ @klass = delegate_reflection.options[:class]
636
+ @source_reflection_name = delegate_reflection.options[:source]
637
+ end
638
+
639
+ def klass
640
+ @klass ||= delegate_reflection.compute_class(class_name)
485
641
  end
486
642
 
487
643
  # Returns the source of the through reflection. It checks both a singularized
@@ -499,10 +655,10 @@ module ActiveRecord
499
655
  #
500
656
  # tags_reflection = Post.reflect_on_association(:tags)
501
657
  # tags_reflection.source_reflection
502
- # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
658
+ # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
503
659
  #
504
660
  def source_reflection
505
- through_reflection.klass.reflect_on_association(source_reflection_name)
661
+ through_reflection.klass._reflect_on_association(source_reflection_name)
506
662
  end
507
663
 
508
664
  # Returns the AssociationReflection object specified in the <tt>:through</tt> option
@@ -515,10 +671,10 @@ module ActiveRecord
515
671
  #
516
672
  # tags_reflection = Post.reflect_on_association(:tags)
517
673
  # tags_reflection.through_reflection
518
- # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @active_record=Post, @plural_name="taggings">
674
+ # # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
519
675
  #
520
676
  def through_reflection
521
- active_record.reflect_on_association(options[:through])
677
+ active_record._reflect_on_association(options[:through])
522
678
  end
523
679
 
524
680
  # Returns an array of reflections which are involved in this association. Each item in the
@@ -535,8 +691,8 @@ module ActiveRecord
535
691
  #
536
692
  # tags_reflection = Post.reflect_on_association(:tags)
537
693
  # tags_reflection.chain
538
- # # => [<ActiveRecord::Reflection::ThroughReflection: @macro=:has_many, @name=:tags, @options={:through=>:taggings}, @active_record=Post>,
539
- # <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @options={}, @active_record=Post>]
694
+ # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
695
+ # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
540
696
  #
541
697
  def chain
542
698
  @chain ||= begin
@@ -577,8 +733,11 @@ module ActiveRecord
577
733
  through_scope_chain = through_reflection.scope_chain.map(&:dup)
578
734
 
579
735
  if options[:source_type]
580
- through_scope_chain.first <<
581
- through_reflection.klass.where(foreign_type => options[:source_type])
736
+ type = foreign_type
737
+ source_type = options[:source_type]
738
+ through_scope_chain.first << lambda { |object|
739
+ where(type => source_type)
740
+ }
582
741
  end
583
742
 
584
743
  # Recursively fill out the rest of the array from the through reflection
@@ -586,8 +745,17 @@ module ActiveRecord
586
745
  end
587
746
  end
588
747
 
748
+ def join_keys(assoc_klass)
749
+ source_reflection.join_keys(assoc_klass)
750
+ end
751
+
589
752
  # The macro used by the source association
590
753
  def source_macro
754
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
755
+ ActiveRecord::Base.source_macro is deprecated and will be removed
756
+ without replacement.
757
+ MSG
758
+
591
759
  source_reflection.source_macro
592
760
  end
593
761
 
@@ -617,7 +785,7 @@ module ActiveRecord
617
785
  # # => [:tag, :tags]
618
786
  #
619
787
  def source_reflection_names
620
- (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq
788
+ options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
621
789
  end
622
790
 
623
791
  def source_reflection_name # :nodoc:
@@ -625,21 +793,19 @@ module ActiveRecord
625
793
 
626
794
  names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
627
795
  names = names.find_all { |n|
628
- through_reflection.klass.reflect_on_association(n)
796
+ through_reflection.klass._reflect_on_association(n)
629
797
  }
630
798
 
631
799
  if names.length > 1
632
800
  example_options = options.dup
633
801
  example_options[:source] = source_reflection_names.first
634
- ActiveSupport::Deprecation.warn <<-eowarn
635
- Ambiguous source reflection for through association. Please specify a :source
636
- directive on your declaration like:
637
-
638
- class #{active_record.name} < ActiveRecord::Base
639
- #{macro} :#{name}, #{example_options}
640
- end
641
-
642
- eowarn
802
+ ActiveSupport::Deprecation.warn \
803
+ "Ambiguous source reflection for through association. Please " \
804
+ "specify a :source directive on your declaration like:\n" \
805
+ "\n" \
806
+ " class #{active_record.name} < ActiveRecord::Base\n" \
807
+ " #{macro} :#{name}, #{example_options}\n" \
808
+ " end"
643
809
  end
644
810
 
645
811
  @source_reflection_name = names.first
@@ -653,28 +819,36 @@ directive on your declaration like:
653
819
  through_reflection.options
654
820
  end
655
821
 
822
+ def join_id_for(owner) # :nodoc:
823
+ source_reflection.join_id_for(owner)
824
+ end
825
+
656
826
  def check_validity!
657
827
  if through_reflection.nil?
658
828
  raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
659
829
  end
660
830
 
661
- if through_reflection.options[:polymorphic]
662
- raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
831
+ if through_reflection.polymorphic?
832
+ if has_one?
833
+ raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
834
+ else
835
+ raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
836
+ end
663
837
  end
664
838
 
665
839
  if source_reflection.nil?
666
840
  raise HasManyThroughSourceAssociationNotFoundError.new(self)
667
841
  end
668
842
 
669
- if options[:source_type] && source_reflection.options[:polymorphic].nil?
843
+ if options[:source_type] && !source_reflection.polymorphic?
670
844
  raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
671
845
  end
672
846
 
673
- if source_reflection.options[:polymorphic] && options[:source_type].nil?
847
+ if source_reflection.polymorphic? && options[:source_type].nil?
674
848
  raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
675
849
  end
676
850
 
677
- if macro == :has_one && through_reflection.collection?
851
+ if has_one? && through_reflection.collection?
678
852
  raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
679
853
  end
680
854
 
@@ -683,15 +857,25 @@ directive on your declaration like:
683
857
 
684
858
  protected
685
859
 
686
- def actual_source_reflection # FIXME: this is a horrible name
687
- source_reflection.actual_source_reflection
688
- end
860
+ def actual_source_reflection # FIXME: this is a horrible name
861
+ source_reflection.send(:actual_source_reflection)
862
+ end
863
+
864
+ def primary_key(klass)
865
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
866
+ end
689
867
 
690
868
  private
691
869
  def derive_class_name
692
870
  # get the class_name of the belongs_to association of the through reflection
693
871
  options[:source_type] || source_reflection.class_name
694
872
  end
873
+
874
+ delegate_methods = AssociationReflection.public_instance_methods -
875
+ public_instance_methods
876
+
877
+ delegate(*delegate_methods, to: :delegate_reflection)
878
+
695
879
  end
696
880
  end
697
881
  end