activerecord 4.1.15 → 4.2.0.beta1

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 (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +634 -2176
  3. data/README.rdoc +15 -10
  4. data/lib/active_record/aggregations.rb +12 -8
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/association_scope.rb +53 -21
  7. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  8. data/lib/active_record/associations/builder/association.rb +16 -5
  9. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  10. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
  11. data/lib/active_record/associations/builder/has_one.rb +2 -2
  12. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  13. data/lib/active_record/associations/collection_association.rb +32 -44
  14. data/lib/active_record/associations/collection_proxy.rb +1 -10
  15. data/lib/active_record/associations/has_many_association.rb +60 -14
  16. data/lib/active_record/associations/has_many_through_association.rb +34 -23
  17. data/lib/active_record/associations/has_one_association.rb +0 -1
  18. data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
  19. data/lib/active_record/associations/join_dependency.rb +7 -9
  20. data/lib/active_record/associations/preloader/association.rb +9 -5
  21. data/lib/active_record/associations/preloader/through_association.rb +3 -3
  22. data/lib/active_record/associations/preloader.rb +2 -2
  23. data/lib/active_record/associations/singular_association.rb +16 -1
  24. data/lib/active_record/associations/through_association.rb +6 -22
  25. data/lib/active_record/associations.rb +58 -33
  26. data/lib/active_record/attribute.rb +131 -0
  27. data/lib/active_record/attribute_assignment.rb +19 -11
  28. data/lib/active_record/attribute_decorators.rb +66 -0
  29. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
  30. data/lib/active_record/attribute_methods/dirty.rb +85 -42
  31. data/lib/active_record/attribute_methods/primary_key.rb +6 -8
  32. data/lib/active_record/attribute_methods/read.rb +14 -57
  33. data/lib/active_record/attribute_methods/serialization.rb +12 -146
  34. data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
  35. data/lib/active_record/attribute_methods/write.rb +8 -23
  36. data/lib/active_record/attribute_methods.rb +53 -90
  37. data/lib/active_record/attribute_set/builder.rb +32 -0
  38. data/lib/active_record/attribute_set.rb +77 -0
  39. data/lib/active_record/attributes.rb +122 -0
  40. data/lib/active_record/autosave_association.rb +11 -21
  41. data/lib/active_record/base.rb +9 -19
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
  52. data/lib/active_record/connection_adapters/column.rb +13 -244
  53. data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
  55. data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
  56. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  57. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  58. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -20
  59. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -0
  60. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  62. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  63. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  64. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  67. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  68. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  69. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +76 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +85 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  85. data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
  86. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  87. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
  88. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
  89. data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
  90. data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
  91. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
  93. data/lib/active_record/connection_handling.rb +1 -1
  94. data/lib/active_record/core.rb +119 -22
  95. data/lib/active_record/counter_cache.rb +60 -6
  96. data/lib/active_record/enum.rb +9 -10
  97. data/lib/active_record/errors.rb +27 -26
  98. data/lib/active_record/explain.rb +1 -1
  99. data/lib/active_record/fixtures.rb +52 -45
  100. data/lib/active_record/gem_version.rb +3 -3
  101. data/lib/active_record/inheritance.rb +33 -8
  102. data/lib/active_record/integration.rb +4 -4
  103. data/lib/active_record/locking/optimistic.rb +34 -16
  104. data/lib/active_record/migration/command_recorder.rb +19 -2
  105. data/lib/active_record/migration/join_table.rb +1 -1
  106. data/lib/active_record/migration.rb +22 -32
  107. data/lib/active_record/model_schema.rb +39 -48
  108. data/lib/active_record/nested_attributes.rb +8 -18
  109. data/lib/active_record/persistence.rb +39 -22
  110. data/lib/active_record/query_cache.rb +3 -3
  111. data/lib/active_record/querying.rb +1 -8
  112. data/lib/active_record/railtie.rb +17 -10
  113. data/lib/active_record/railties/databases.rake +47 -42
  114. data/lib/active_record/readonly_attributes.rb +0 -1
  115. data/lib/active_record/reflection.rb +225 -92
  116. data/lib/active_record/relation/batches.rb +0 -2
  117. data/lib/active_record/relation/calculations.rb +28 -32
  118. data/lib/active_record/relation/delegation.rb +1 -1
  119. data/lib/active_record/relation/finder_methods.rb +42 -20
  120. data/lib/active_record/relation/merger.rb +0 -1
  121. data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
  122. data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
  123. data/lib/active_record/relation/predicate_builder.rb +1 -22
  124. data/lib/active_record/relation/query_methods.rb +98 -62
  125. data/lib/active_record/relation/spawn_methods.rb +6 -7
  126. data/lib/active_record/relation.rb +35 -11
  127. data/lib/active_record/result.rb +16 -9
  128. data/lib/active_record/sanitization.rb +8 -1
  129. data/lib/active_record/schema.rb +0 -1
  130. data/lib/active_record/schema_dumper.rb +51 -9
  131. data/lib/active_record/schema_migration.rb +4 -0
  132. data/lib/active_record/scoping/default.rb +5 -4
  133. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  134. data/lib/active_record/statement_cache.rb +79 -5
  135. data/lib/active_record/store.rb +5 -5
  136. data/lib/active_record/tasks/database_tasks.rb +37 -5
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  138. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
  139. data/lib/active_record/timestamp.rb +9 -7
  140. data/lib/active_record/transactions.rb +35 -21
  141. data/lib/active_record/type/binary.rb +40 -0
  142. data/lib/active_record/type/boolean.rb +19 -0
  143. data/lib/active_record/type/date.rb +46 -0
  144. data/lib/active_record/type/date_time.rb +43 -0
  145. data/lib/active_record/type/decimal.rb +40 -0
  146. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  147. data/lib/active_record/type/float.rb +19 -0
  148. data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
  149. data/lib/active_record/type/integer.rb +23 -0
  150. data/lib/active_record/type/mutable.rb +16 -0
  151. data/lib/active_record/type/numeric.rb +36 -0
  152. data/lib/active_record/type/serialized.rb +51 -0
  153. data/lib/active_record/type/string.rb +36 -0
  154. data/lib/active_record/type/text.rb +11 -0
  155. data/lib/active_record/type/time.rb +26 -0
  156. data/lib/active_record/type/time_value.rb +38 -0
  157. data/lib/active_record/type/type_map.rb +48 -0
  158. data/lib/active_record/type/value.rb +101 -0
  159. data/lib/active_record/type.rb +20 -0
  160. data/lib/active_record/validations/uniqueness.rb +9 -23
  161. data/lib/active_record/validations.rb +21 -16
  162. data/lib/active_record.rb +2 -1
  163. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  164. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  165. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  166. metadata +71 -14
  167. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,35 +1,44 @@
1
+ require 'thread'
2
+
1
3
  module ActiveRecord
2
4
  # = Active Record Reflection
3
5
  module Reflection # :nodoc:
4
6
  extend ActiveSupport::Concern
5
7
 
6
8
  included do
7
- class_attribute :_reflections, instance_writer: false
8
- class_attribute :aggregate_reflections, instance_writer: false
9
+ class_attribute :_reflections
10
+ class_attribute :aggregate_reflections
9
11
  self._reflections = {}
10
12
  self.aggregate_reflections = {}
11
13
  end
12
14
 
13
15
  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)
16
+ klass = case macro
17
+ when :composed_of
18
+ AggregateReflection
19
+ when :has_many
20
+ HasManyReflection
21
+ when :has_one
22
+ HasOneReflection
23
+ when :belongs_to
24
+ BelongsToReflection
25
+ else
26
+ raise "Unsupported Macro: #{macro}"
27
+ end
28
+
29
+ reflection = klass.new(name, scope, options, ar)
30
+ options[:through] ? ThroughReflection.new(reflection) : reflection
22
31
  end
23
32
 
24
33
  def self.add_reflection(ar, name, reflection)
25
- ar._reflections = ar._reflections.merge(name.to_sym => reflection)
34
+ ar._reflections = ar._reflections.merge(name.to_s => reflection)
26
35
  end
27
36
 
28
37
  def self.add_aggregate_reflection(ar, name, reflection)
29
- ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_sym => reflection)
38
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
30
39
  end
31
40
 
32
- # \Reflection enables to interrogate Active Record classes and objects
41
+ # \Reflection enables interrogating Active Record classes and objects
33
42
  # about their associations and aggregations. This information can,
34
43
  # for example, be used in a form builder that takes an Active Record object
35
44
  # and creates input fields for all of the attributes depending on their type
@@ -48,7 +57,7 @@ module ActiveRecord
48
57
  # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
49
58
  #
50
59
  def reflect_on_aggregation(aggregation)
51
- aggregate_reflections[aggregation.to_sym]
60
+ aggregate_reflections[aggregation.to_s]
52
61
  end
53
62
 
54
63
  # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
@@ -92,12 +101,12 @@ module ActiveRecord
92
101
  #
93
102
  # @api public
94
103
  def reflect_on_association(association)
95
- reflections[association.to_sym]
104
+ reflections[association.to_s]
96
105
  end
97
106
 
98
107
  # @api private
99
108
  def _reflect_on_association(association) #:nodoc:
100
- _reflections[association.to_sym]
109
+ _reflections[association.to_s]
101
110
  end
102
111
 
103
112
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
@@ -108,26 +117,64 @@ module ActiveRecord
108
117
  end
109
118
  end
110
119
 
120
+ # Holds all the methods that are shared between MacroReflection, AssociationReflection
121
+ # and ThroughReflection
122
+ class AbstractReflection # :nodoc:
123
+ def table_name
124
+ klass.table_name
125
+ end
126
+
127
+ # Returns a new, unsaved instance of the associated class. +attributes+ will
128
+ # be passed to the class's constructor.
129
+ def build_association(attributes, &block)
130
+ klass.new(attributes, &block)
131
+ end
132
+
133
+ def quoted_table_name
134
+ klass.quoted_table_name
135
+ end
136
+
137
+ def primary_key_type
138
+ klass.type_for_attribute(klass.primary_key)
139
+ end
140
+
141
+ # Returns the class name for the macro.
142
+ #
143
+ # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
144
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
145
+ def class_name
146
+ @class_name ||= (options[:class_name] || derive_class_name).to_s
147
+ end
148
+
149
+ JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
150
+
151
+ def join_keys(assoc_klass)
152
+ JoinKeys.new(foreign_key, active_record_primary_key)
153
+ end
154
+
155
+ def source_macro
156
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.source_macro is deprecated and " \
157
+ "will be removed without replacement.")
158
+ macro
159
+ end
160
+ end
111
161
  # Base class for AggregateReflection and AssociationReflection. Objects of
112
162
  # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
113
163
  #
114
164
  # MacroReflection
115
- # AggregateReflection
116
165
  # AssociationReflection
117
- # ThroughReflection
118
- class MacroReflection
166
+ # AggregateReflection
167
+ # HasManyReflection
168
+ # HasOneReflection
169
+ # BelongsToReflection
170
+ # ThroughReflection
171
+ class MacroReflection < AbstractReflection
119
172
  # Returns the name of the macro.
120
173
  #
121
174
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
122
175
  # <tt>has_many :clients</tt> returns <tt>:clients</tt>
123
176
  attr_reader :name
124
177
 
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
178
  attr_reader :scope
132
179
 
133
180
  # Returns the hash of options used for the macro.
@@ -140,13 +187,12 @@ module ActiveRecord
140
187
 
141
188
  attr_reader :plural_name # :nodoc:
142
189
 
143
- def initialize(macro, name, scope, options, active_record)
144
- @macro = macro
190
+ def initialize(name, scope, options, active_record)
145
191
  @name = name
146
192
  @scope = scope
147
193
  @options = options
148
194
  @active_record = active_record
149
- @klass = options[:anonymous_class]
195
+ @klass = options[:class]
150
196
  @plural_name = active_record.pluralize_table_names ?
151
197
  name.to_s.pluralize : name.to_s
152
198
  end
@@ -165,15 +211,11 @@ module ActiveRecord
165
211
  # <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
166
212
  # <tt>has_many :clients</tt> returns the Client class
167
213
  def klass
168
- @klass ||= class_name.constantize
214
+ @klass ||= compute_class(class_name)
169
215
  end
170
216
 
171
- # Returns the class name for the macro.
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
217
+ def compute_class(name)
218
+ name.constantize
177
219
  end
178
220
 
179
221
  # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
@@ -218,39 +260,40 @@ module ActiveRecord
218
260
  # a new association object. Use +build_association+ or +create_association+
219
261
  # instead. This allows plugins to hook into association object creation.
220
262
  def klass
221
- @klass ||= active_record.send(:compute_type, class_name)
263
+ @klass ||= compute_class(class_name)
264
+ end
265
+
266
+ def compute_class(name)
267
+ active_record.send(:compute_type, name)
222
268
  end
223
269
 
224
270
  attr_reader :type, :foreign_type
225
271
  attr_accessor :parent_reflection # [:name, Reflection]
226
272
 
227
- def initialize(macro, name, scope, options, active_record)
273
+ def initialize(name, scope, options, active_record)
228
274
  super
229
- @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
230
275
  @automatic_inverse_of = nil
231
276
  @type = options[:as] && "#{options[:as]}_type"
232
277
  @foreign_type = options[:foreign_type] || "#{name}_type"
233
278
  @constructable = calculate_constructable(macro, options)
279
+ @association_scope_cache = {}
280
+ @scope_lock = Mutex.new
234
281
  end
235
282
 
236
- # Returns a new, unsaved instance of the associated class. +attributes+ will
237
- # be passed to the class's constructor.
238
- def build_association(attributes, &block)
239
- klass.new(attributes, &block)
283
+ def association_scope_cache(conn, owner)
284
+ key = conn.prepared_statements
285
+ if polymorphic?
286
+ key = [key, owner.read_attribute(@foreign_type)]
287
+ end
288
+ @association_scope_cache[key] ||= @scope_lock.synchronize {
289
+ @association_scope_cache[key] ||= yield
290
+ }
240
291
  end
241
292
 
242
293
  def constructable? # :nodoc:
243
294
  @constructable
244
295
  end
245
296
 
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
297
  def join_table
255
298
  @join_table ||= options[:join_table] || derive_join_table
256
299
  end
@@ -259,10 +302,6 @@ module ActiveRecord
259
302
  @foreign_key ||= options[:foreign_key] || derive_foreign_key
260
303
  end
261
304
 
262
- def primary_key_column
263
- klass.columns_hash[klass.primary_key]
264
- end
265
-
266
305
  def association_foreign_key
267
306
  @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
268
307
  end
@@ -289,13 +328,31 @@ module ActiveRecord
289
328
  end
290
329
 
291
330
  def check_validity_of_inverse!
292
- unless options[:polymorphic]
331
+ unless polymorphic?
293
332
  if has_inverse? && inverse_of.nil?
294
333
  raise InverseOfAssociationNotFoundError.new(self)
295
334
  end
296
335
  end
297
336
  end
298
337
 
338
+ def check_preloadable!
339
+ return unless scope
340
+
341
+ if scope.arity > 0
342
+ ActiveSupport::Deprecation.warn <<-WARNING
343
+ The association scope '#{name}' is instance dependent (the scope block takes an argument).
344
+ Preloading happens before the individual instances are created. This means that there is no instance
345
+ being passed to the association scope. This will most likely result in broken or incorrect behavior.
346
+ Joining, Preloading and eager loading of these associations is deprecated and will be removed in the future.
347
+ WARNING
348
+ end
349
+ end
350
+ alias :check_eager_loadable! :check_preloadable!
351
+
352
+ def join_id_for(owner) # :nodoc:
353
+ owner[active_record_primary_key]
354
+ end
355
+
299
356
  def through_reflection
300
357
  nil
301
358
  end
@@ -320,8 +377,6 @@ module ActiveRecord
320
377
  scope ? [[scope]] : [[]]
321
378
  end
322
379
 
323
- alias :source_macro :macro
324
-
325
380
  def has_inverse?
326
381
  inverse_name
327
382
  end
@@ -342,11 +397,16 @@ module ActiveRecord
342
397
  end
343
398
  end
344
399
 
400
+ # Returns the macro type.
401
+ #
402
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
403
+ def macro; raise NotImplementedError; end
404
+
345
405
  # Returns whether or not this association reflection is for a collection
346
406
  # association. Returns +true+ if the +macro+ is either +has_many+ or
347
407
  # +has_and_belongs_to_many+, +false+ otherwise.
348
408
  def collection?
349
- @collection
409
+ false
350
410
  end
351
411
 
352
412
  # Returns whether or not the association should be validated as part of
@@ -359,18 +419,19 @@ module ActiveRecord
359
419
  # * you use autosave; <tt>autosave: true</tt>
360
420
  # * the association is a +has_many+ association
361
421
  def validate?
362
- !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
422
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
363
423
  end
364
424
 
365
425
  # Returns +true+ if +self+ is a +belongs_to+ reflection.
366
- def belongs_to?
367
- macro == :belongs_to
368
- end
426
+ def belongs_to?; false; end
427
+
428
+ # Returns +true+ if +self+ is a +has_one+ reflection.
429
+ def has_one?; false; end
369
430
 
370
431
  def association_class
371
432
  case macro
372
433
  when :belongs_to
373
- if options[:polymorphic]
434
+ if polymorphic?
374
435
  Associations::BelongsToPolymorphicAssociation
375
436
  else
376
437
  Associations::BelongsToAssociation
@@ -391,7 +452,7 @@ module ActiveRecord
391
452
  end
392
453
 
393
454
  def polymorphic?
394
- options.key? :polymorphic
455
+ options[:polymorphic]
395
456
  end
396
457
 
397
458
  VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
@@ -408,7 +469,7 @@ module ActiveRecord
408
469
  def calculate_constructable(macro, options)
409
470
  case macro
410
471
  when :belongs_to
411
- !options[:polymorphic]
472
+ !polymorphic?
412
473
  when :has_one
413
474
  !options[:through]
414
475
  else
@@ -432,7 +493,7 @@ module ActiveRecord
432
493
  # returns either nil or the inverse association name that it finds.
433
494
  def automatic_inverse_of
434
495
  if can_find_inverse_of_automatically?(self)
435
- inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
496
+ inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name).to_sym
436
497
 
437
498
  begin
438
499
  reflection = klass._reflect_on_association(inverse_name)
@@ -497,7 +558,7 @@ module ActiveRecord
497
558
  end
498
559
 
499
560
  def derive_join_table
500
- [active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
561
+ ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
501
562
  end
502
563
 
503
564
  def primary_key(klass)
@@ -505,15 +566,72 @@ module ActiveRecord
505
566
  end
506
567
  end
507
568
 
569
+ class HasManyReflection < AssociationReflection # :nodoc:
570
+ def initialize(name, scope, options, active_record)
571
+ super(name, scope, options, active_record)
572
+ end
573
+
574
+ def macro; :has_many; end
575
+
576
+ def collection?; true; end
577
+ end
578
+
579
+ class HasOneReflection < AssociationReflection # :nodoc:
580
+ def initialize(name, scope, options, active_record)
581
+ super(name, scope, options, active_record)
582
+ end
583
+
584
+ def macro; :has_one; end
585
+
586
+ def has_one?; true; end
587
+ end
588
+
589
+ class BelongsToReflection < AssociationReflection # :nodoc:
590
+ def initialize(name, scope, options, active_record)
591
+ super(name, scope, options, active_record)
592
+ end
593
+
594
+ def macro; :belongs_to; end
595
+
596
+ def belongs_to?; true; end
597
+
598
+ def join_keys(assoc_klass)
599
+ key = polymorphic? ? association_primary_key(assoc_klass) : association_primary_key
600
+ JoinKeys.new(key, foreign_key)
601
+ end
602
+
603
+ def join_id_for(owner) # :nodoc:
604
+ owner[foreign_key]
605
+ end
606
+ end
607
+
608
+ class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
609
+ def initialize(name, scope, options, active_record)
610
+ super
611
+ end
612
+
613
+ def macro; :has_and_belongs_to_many; end
614
+
615
+ def collection?
616
+ true
617
+ end
618
+ end
619
+
508
620
  # Holds all the meta-data about a :through association as it was specified
509
621
  # in the Active Record class.
510
- class ThroughReflection < AssociationReflection #:nodoc:
622
+ class ThroughReflection < AbstractReflection #:nodoc:
623
+ attr_reader :delegate_reflection
511
624
  delegate :foreign_key, :foreign_type, :association_foreign_key,
512
625
  :active_record_primary_key, :type, :to => :source_reflection
513
626
 
514
- def initialize(macro, name, scope, options, active_record)
515
- super
516
- @source_reflection_name = options[:source]
627
+ def initialize(delegate_reflection)
628
+ @delegate_reflection = delegate_reflection
629
+ @klass = delegate_reflection.options[:class]
630
+ @source_reflection_name = delegate_reflection.options[:source]
631
+ end
632
+
633
+ def klass
634
+ @klass ||= delegate_reflection.compute_class(class_name)
517
635
  end
518
636
 
519
637
  # Returns the source of the through reflection. It checks both a singularized
@@ -531,12 +649,10 @@ module ActiveRecord
531
649
  #
532
650
  # tags_reflection = Post.reflect_on_association(:tags)
533
651
  # tags_reflection.source_reflection
534
- # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
652
+ # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
535
653
  #
536
654
  def source_reflection
537
- if source_reflection_name
538
- through_reflection.klass._reflect_on_association(source_reflection_name)
539
- end
655
+ through_reflection.klass._reflect_on_association(source_reflection_name)
540
656
  end
541
657
 
542
658
  # Returns the AssociationReflection object specified in the <tt>:through</tt> option
@@ -549,7 +665,7 @@ module ActiveRecord
549
665
  #
550
666
  # tags_reflection = Post.reflect_on_association(:tags)
551
667
  # tags_reflection.through_reflection
552
- # # => <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @active_record=Post, @plural_name="taggings">
668
+ # # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
553
669
  #
554
670
  def through_reflection
555
671
  active_record._reflect_on_association(options[:through])
@@ -569,8 +685,8 @@ module ActiveRecord
569
685
  #
570
686
  # tags_reflection = Post.reflect_on_association(:tags)
571
687
  # tags_reflection.chain
572
- # # => [<ActiveRecord::Reflection::ThroughReflection: @macro=:has_many, @name=:tags, @options={:through=>:taggings}, @active_record=Post>,
573
- # <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @options={}, @active_record=Post>]
688
+ # # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
689
+ # <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
574
690
  #
575
691
  def chain
576
692
  @chain ||= begin
@@ -611,11 +727,8 @@ module ActiveRecord
611
727
  through_scope_chain = through_reflection.scope_chain.map(&:dup)
612
728
 
613
729
  if options[:source_type]
614
- type = foreign_type
615
- source_type = options[:source_type]
616
- through_scope_chain.first << lambda { |object|
617
- where(type => source_type)
618
- }
730
+ through_scope_chain.first <<
731
+ through_reflection.klass.where(foreign_type => options[:source_type])
619
732
  end
620
733
 
621
734
  # Recursively fill out the rest of the array from the through reflection
@@ -623,8 +736,14 @@ module ActiveRecord
623
736
  end
624
737
  end
625
738
 
739
+ def join_keys(assoc_klass)
740
+ source_reflection.join_keys(assoc_klass)
741
+ end
742
+
626
743
  # The macro used by the source association
627
744
  def source_macro
745
+ ActiveSupport::Deprecation.warn("ActiveRecord::Base.source_macro is deprecated and " \
746
+ "will be removed without replacement.")
628
747
  source_reflection.source_macro
629
748
  end
630
749
 
@@ -654,11 +773,11 @@ module ActiveRecord
654
773
  # # => [:tag, :tags]
655
774
  #
656
775
  def source_reflection_names
657
- (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq
776
+ options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
658
777
  end
659
778
 
660
779
  def source_reflection_name # :nodoc:
661
- return @source_reflection_name.to_sym if @source_reflection_name
780
+ return @source_reflection_name if @source_reflection_name
662
781
 
663
782
  names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
664
783
  names = names.find_all { |n|
@@ -690,12 +809,16 @@ directive on your declaration like:
690
809
  through_reflection.options
691
810
  end
692
811
 
812
+ def join_id_for(owner) # :nodoc:
813
+ source_reflection.join_id_for(owner)
814
+ end
815
+
693
816
  def check_validity!
694
817
  if through_reflection.nil?
695
818
  raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
696
819
  end
697
820
 
698
- if through_reflection.options[:polymorphic]
821
+ if through_reflection.polymorphic?
699
822
  raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
700
823
  end
701
824
 
@@ -703,15 +826,15 @@ directive on your declaration like:
703
826
  raise HasManyThroughSourceAssociationNotFoundError.new(self)
704
827
  end
705
828
 
706
- if options[:source_type] && source_reflection.options[:polymorphic].nil?
829
+ if options[:source_type] && !source_reflection.polymorphic?
707
830
  raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
708
831
  end
709
832
 
710
- if source_reflection.options[:polymorphic] && options[:source_type].nil?
833
+ if source_reflection.polymorphic? && options[:source_type].nil?
711
834
  raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
712
835
  end
713
836
 
714
- if macro == :has_one && through_reflection.collection?
837
+ if has_one? && through_reflection.collection?
715
838
  raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
716
839
  end
717
840
 
@@ -720,15 +843,25 @@ directive on your declaration like:
720
843
 
721
844
  protected
722
845
 
723
- def actual_source_reflection # FIXME: this is a horrible name
724
- source_reflection.actual_source_reflection
725
- end
846
+ def actual_source_reflection # FIXME: this is a horrible name
847
+ source_reflection.send(:actual_source_reflection)
848
+ end
849
+
850
+ def primary_key(klass)
851
+ klass.primary_key || raise(UnknownPrimaryKey.new(klass))
852
+ end
726
853
 
727
854
  private
728
855
  def derive_class_name
729
856
  # get the class_name of the belongs_to association of the through reflection
730
857
  options[:source_type] || source_reflection.class_name
731
858
  end
859
+
860
+ delegate_methods = AssociationReflection.public_instance_methods -
861
+ public_instance_methods
862
+
863
+ delegate(*delegate_methods, to: :delegate_reflection)
864
+
732
865
  end
733
866
  end
734
867
  end
@@ -1,4 +1,3 @@
1
-
2
1
  module ActiveRecord
3
2
  module Batches
4
3
  # Looping through a collection of records from the database
@@ -115,7 +114,6 @@ module ActiveRecord
115
114
  end
116
115
 
117
116
  relation = relation.reorder(batch_order).limit(batch_size)
118
- relation.reverse_order_value = false
119
117
  records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
120
118
 
121
119
  while records.any?