activerecord 3.1.12 → 3.2.0.rc1

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 (99) hide show
  1. data/CHANGELOG.md +6263 -103
  2. data/README.rdoc +2 -2
  3. data/examples/performance.rb +55 -31
  4. data/lib/active_record.rb +28 -2
  5. data/lib/active_record/aggregations.rb +2 -2
  6. data/lib/active_record/associations.rb +82 -69
  7. data/lib/active_record/associations/association.rb +2 -37
  8. data/lib/active_record/associations/association_scope.rb +3 -30
  9. data/lib/active_record/associations/builder/association.rb +6 -4
  10. data/lib/active_record/associations/builder/belongs_to.rb +3 -3
  11. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +4 -4
  13. data/lib/active_record/associations/builder/has_one.rb +5 -6
  14. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  15. data/lib/active_record/associations/collection_association.rb +55 -28
  16. data/lib/active_record/associations/collection_proxy.rb +1 -35
  17. data/lib/active_record/associations/has_many_association.rb +5 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +11 -8
  19. data/lib/active_record/associations/join_dependency.rb +1 -1
  20. data/lib/active_record/associations/preloader/association.rb +3 -1
  21. data/lib/active_record/attribute_assignment.rb +221 -0
  22. data/lib/active_record/attribute_methods.rb +212 -32
  23. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  24. data/lib/active_record/attribute_methods/dirty.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  26. data/lib/active_record/attribute_methods/read.rb +69 -80
  27. data/lib/active_record/attribute_methods/serialization.rb +89 -0
  28. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
  29. data/lib/active_record/attribute_methods/write.rb +27 -5
  30. data/lib/active_record/autosave_association.rb +23 -8
  31. data/lib/active_record/base.rb +223 -1712
  32. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
  33. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
  40. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  41. data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
  42. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
  43. data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
  44. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  45. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  46. data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
  47. data/lib/active_record/counter_cache.rb +1 -1
  48. data/lib/active_record/dynamic_matchers.rb +79 -0
  49. data/lib/active_record/errors.rb +11 -1
  50. data/lib/active_record/explain.rb +83 -0
  51. data/lib/active_record/explain_subscriber.rb +21 -0
  52. data/lib/active_record/fixtures.rb +31 -76
  53. data/lib/active_record/fixtures/file.rb +65 -0
  54. data/lib/active_record/identity_map.rb +1 -7
  55. data/lib/active_record/inheritance.rb +167 -0
  56. data/lib/active_record/integration.rb +49 -0
  57. data/lib/active_record/locking/optimistic.rb +19 -11
  58. data/lib/active_record/locking/pessimistic.rb +1 -1
  59. data/lib/active_record/log_subscriber.rb +3 -3
  60. data/lib/active_record/migration.rb +38 -29
  61. data/lib/active_record/migration/command_recorder.rb +7 -7
  62. data/lib/active_record/model_schema.rb +362 -0
  63. data/lib/active_record/nested_attributes.rb +3 -2
  64. data/lib/active_record/persistence.rb +51 -1
  65. data/lib/active_record/querying.rb +58 -0
  66. data/lib/active_record/railtie.rb +24 -28
  67. data/lib/active_record/railties/controller_runtime.rb +3 -1
  68. data/lib/active_record/railties/databases.rake +133 -77
  69. data/lib/active_record/readonly_attributes.rb +26 -0
  70. data/lib/active_record/reflection.rb +7 -15
  71. data/lib/active_record/relation.rb +78 -35
  72. data/lib/active_record/relation/batches.rb +5 -2
  73. data/lib/active_record/relation/calculations.rb +27 -6
  74. data/lib/active_record/relation/delegation.rb +49 -0
  75. data/lib/active_record/relation/finder_methods.rb +5 -4
  76. data/lib/active_record/relation/predicate_builder.rb +13 -16
  77. data/lib/active_record/relation/query_methods.rb +59 -4
  78. data/lib/active_record/result.rb +1 -1
  79. data/lib/active_record/sanitization.rb +194 -0
  80. data/lib/active_record/schema_dumper.rb +5 -2
  81. data/lib/active_record/scoping.rb +152 -0
  82. data/lib/active_record/scoping/default.rb +140 -0
  83. data/lib/active_record/scoping/named.rb +202 -0
  84. data/lib/active_record/serialization.rb +1 -43
  85. data/lib/active_record/serializers/xml_serializer.rb +2 -44
  86. data/lib/active_record/session_store.rb +11 -11
  87. data/lib/active_record/store.rb +50 -0
  88. data/lib/active_record/test_case.rb +11 -7
  89. data/lib/active_record/timestamp.rb +16 -3
  90. data/lib/active_record/transactions.rb +5 -5
  91. data/lib/active_record/translation.rb +22 -0
  92. data/lib/active_record/validations.rb +1 -1
  93. data/lib/active_record/validations/associated.rb +5 -4
  94. data/lib/active_record/validations/uniqueness.rb +4 -4
  95. data/lib/active_record/version.rb +3 -3
  96. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  97. metadata +48 -38
  98. checksums.yaml +0 -7
  99. data/lib/active_record/named_scope.rb +0 -200
@@ -0,0 +1,26 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext/class/attribute'
3
+
4
+ module ActiveRecord
5
+ module ReadonlyAttributes
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_attr_readonly, :instance_writer => false
10
+ self._attr_readonly = []
11
+ end
12
+
13
+ module ClassMethods
14
+ # Attributes listed as readonly will be used to create a new record but update operations will
15
+ # ignore these fields.
16
+ def attr_readonly(*attributes)
17
+ self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
18
+ end
19
+
20
+ # Returns an array of all the attributes that have been specified as readonly.
21
+ def readonly_attributes
22
+ self._attr_readonly
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,4 @@
1
1
  require 'active_support/core_ext/class/attribute'
2
- require 'active_support/core_ext/module/deprecation'
3
2
  require 'active_support/core_ext/object/inclusion'
4
3
 
5
4
  module ActiveRecord
@@ -125,7 +124,7 @@ module ActiveRecord
125
124
  # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
126
125
  # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
127
126
  def class_name
128
- @class_name ||= options[:class_name] || derive_class_name
127
+ @class_name ||= (options[:class_name] || derive_class_name).to_s
129
128
  end
130
129
 
131
130
  # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
@@ -178,15 +177,9 @@ module ActiveRecord
178
177
  @collection = macro.in?([:has_many, :has_and_belongs_to_many])
179
178
  end
180
179
 
181
- # This is a hack so that we can tell if build_association was overridden, in order to
182
- # provide an appropriate deprecation if the overridden method ignored the &block. Please
183
- # see Association#build_record for details.
184
- attr_accessor :original_build_association_called # :nodoc
185
-
186
180
  # Returns a new, unsaved instance of the associated class. +options+ will
187
181
  # be passed to the class's constructor.
188
182
  def build_association(*options, &block)
189
- @original_build_association_called = true
190
183
  klass.new(*options, &block)
191
184
  end
192
185
 
@@ -202,11 +195,6 @@ module ActiveRecord
202
195
  @foreign_key ||= options[:foreign_key] || derive_foreign_key
203
196
  end
204
197
 
205
- def primary_key_name
206
- foreign_key
207
- end
208
- deprecate :primary_key_name => :foreign_key
209
-
210
198
  def foreign_type
211
199
  @foreign_type ||= options[:foreign_type] || "#{name}_type"
212
200
  end
@@ -274,6 +262,10 @@ module ActiveRecord
274
262
  [self]
275
263
  end
276
264
 
265
+ def nested?
266
+ false
267
+ end
268
+
277
269
  # An array of arrays of conditions. Each item in the outside array corresponds to a reflection
278
270
  # in the #chain. The inside arrays are simply conditions (and each condition may itself be
279
271
  # a hash, array, arel predicate, etc...)
@@ -381,7 +373,7 @@ module ActiveRecord
381
373
  delegate :foreign_key, :foreign_type, :association_foreign_key,
382
374
  :active_record_primary_key, :type, :to => :source_reflection
383
375
 
384
- # Gets the source of the through reflection. It checks both a singularized
376
+ # Gets the source of the through reflection. It checks both a singularized
385
377
  # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
386
378
  #
387
379
  # class Post < ActiveRecord::Base
@@ -469,7 +461,7 @@ module ActiveRecord
469
461
  source_reflection.source_macro
470
462
  end
471
463
 
472
- # A through association is nested iff there would be more than one join table
464
+ # A through association is nested if there would be more than one join table
473
465
  def nested?
474
466
  chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
475
467
  end
@@ -1,5 +1,5 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'active_support/core_ext/object/blank'
2
- require 'active_support/core_ext/module/delegation'
3
3
 
4
4
  module ActiveRecord
5
5
  # = Active Record Relation
@@ -7,13 +7,9 @@ module ActiveRecord
7
7
  JoinOperation = Struct.new(:relation, :join_class, :on)
8
8
  ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
9
9
  MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
10
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order]
10
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order, :uniq]
11
11
 
12
- include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
13
-
14
- # These are explicitly delegated to improve performance (avoids method_missing)
15
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a
16
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass
12
+ include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
17
13
 
18
14
  attr_reader :table, :klass, :loaded
19
15
  attr_accessor :extensions, :default_scoped
@@ -81,7 +77,6 @@ module ActiveRecord
81
77
  end
82
78
 
83
79
  def initialize_copy(other)
84
- @bind_values = @bind_values.dup
85
80
  reset
86
81
  end
87
82
 
@@ -95,14 +90,77 @@ module ActiveRecord
95
90
  scoping { @klass.create!(*args, &block) }
96
91
  end
97
92
 
98
- def respond_to?(method, include_private = false)
99
- arel.respond_to?(method, include_private) ||
100
- Array.method_defined?(method) ||
101
- @klass.respond_to?(method, include_private) ||
102
- super
93
+ # Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
94
+ #
95
+ # Expects arguments in the same format as <tt>Base.create</tt>.
96
+ #
97
+ # ==== Examples
98
+ # # Find the first user named Penélope or create a new one.
99
+ # User.where(:first_name => 'Penélope').first_or_create
100
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
101
+ #
102
+ # # Find the first user named Penélope or create a new one.
103
+ # # We already have one so the existing record will be returned.
104
+ # User.where(:first_name => 'Penélope').first_or_create
105
+ # # => <User id: 1, first_name: 'Penélope', last_name: nil>
106
+ #
107
+ # # Find the first user named Scarlett or create a new one with a particular last name.
108
+ # User.where(:first_name => 'Scarlett').first_or_create(:last_name => 'Johansson')
109
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
110
+ #
111
+ # # Find the first user named Scarlett or create a new one with a different last name.
112
+ # # We already have one so the existing record will be returned.
113
+ # User.where(:first_name => 'Scarlett').first_or_create do |user|
114
+ # user.last_name = "O'Hara"
115
+ # end
116
+ # # => <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
117
+ def first_or_create(attributes = nil, options = {}, &block)
118
+ first || create(attributes, options, &block)
119
+ end
120
+
121
+ # Like <tt>first_or_create</tt> but calls <tt>create!</tt> so an exception is raised if the created record is invalid.
122
+ #
123
+ # Expects arguments in the same format as <tt>Base.create!</tt>.
124
+ def first_or_create!(attributes = nil, options = {}, &block)
125
+ first || create!(attributes, options, &block)
126
+ end
127
+
128
+ # Like <tt>first_or_create</tt> but calls <tt>new</tt> instead of <tt>create</tt>.
129
+ #
130
+ # Expects arguments in the same format as <tt>Base.new</tt>.
131
+ def first_or_initialize(attributes = nil, options = {}, &block)
132
+ first || new(attributes, options, &block)
133
+ end
134
+
135
+ # Runs EXPLAIN on the query or queries triggered by this relation and
136
+ # returns the result as a string. The string is formatted imitating the
137
+ # ones printed by the database shell.
138
+ #
139
+ # Note that this method actually runs the queries, since the results of some
140
+ # are needed by the next ones when eager loading is going on.
141
+ #
142
+ # Please see further details in the
143
+ # {Active Record Query Interface guide}[http://edgeguides.rubyonrails.org/active_record_querying.html#running-explain].
144
+ def explain
145
+ _, queries = collecting_queries_for_explain { exec_queries }
146
+ exec_explain(queries)
103
147
  end
104
148
 
105
149
  def to_a
150
+ # We monitor here the entire execution rather than individual SELECTs
151
+ # because from the point of view of the user fetching the records of a
152
+ # relation is a single unit of work. You want to know if this call takes
153
+ # too long, not if the individual queries take too long.
154
+ #
155
+ # It could be the case that none of the queries involved surpass the
156
+ # threshold, and at the same time the sum of them all does. The user
157
+ # should get a query plan logged in that case.
158
+ logging_query_plan do
159
+ exec_queries
160
+ end
161
+ end
162
+
163
+ def exec_queries
106
164
  return @records if loaded?
107
165
 
108
166
  default_scoped = with_default_scope
@@ -133,6 +191,7 @@ module ActiveRecord
133
191
  @loaded = true
134
192
  @records
135
193
  end
194
+ private :exec_queries
136
195
 
137
196
  def as_json(options = nil) #:nodoc:
138
197
  to_a.as_json(options)
@@ -178,7 +237,7 @@ module ActiveRecord
178
237
  # Please check unscoped if you want to remove all previous scopes (including
179
238
  # the default_scope) during the execution of a block.
180
239
  def scoping
181
- @klass.send(:with_scope, self, :overwrite) { yield }
240
+ @klass.with_scope(self, :overwrite) { yield }
182
241
  end
183
242
 
184
243
  # Updates all records with details given if they match a set of conditions supplied, limits and order can
@@ -253,8 +312,7 @@ module ActiveRecord
253
312
  # Person.update(people.keys, people.values)
254
313
  def update(id, attributes)
255
314
  if id.is_a?(Array)
256
- idx = -1
257
- id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
315
+ id.each.with_index.map {|one_id, idx| update(one_id, attributes[idx])}
258
316
  else
259
317
  object = find(id)
260
318
  object.update_attributes(attributes)
@@ -298,7 +356,7 @@ module ActiveRecord
298
356
  end
299
357
 
300
358
  # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
301
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
359
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
302
360
  # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
303
361
  #
304
362
  # This essentially finds the object (or multiple objects) with the given id, creates a new object
@@ -327,7 +385,7 @@ module ActiveRecord
327
385
  # Deletes the records matching +conditions+ without instantiating the records first, and hence not
328
386
  # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
329
387
  # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
330
- # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
388
+ # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
331
389
  # the number of rows affected.
332
390
  #
333
391
  # ==== Parameters
@@ -395,7 +453,7 @@ module ActiveRecord
395
453
  end
396
454
 
397
455
  def to_sql
398
- @to_sql ||= klass.connection.to_sql(arel, @bind_values.dup)
456
+ @to_sql ||= klass.connection.to_sql(arel)
399
457
  end
400
458
 
401
459
  def where_values_hash
@@ -403,7 +461,7 @@ module ActiveRecord
403
461
  node.left.relation.name == table_name
404
462
  }
405
463
 
406
- Hash[equalities.map { |where| [where.left.name, where.right] }].with_indifferent_access
464
+ Hash[equalities.map { |where| [where.left.name, where.right] }]
407
465
  end
408
466
 
409
467
  def scope_for_create
@@ -447,20 +505,6 @@ module ActiveRecord
447
505
  end
448
506
  end
449
507
 
450
- protected
451
-
452
- def method_missing(method, *args, &block)
453
- if Array.method_defined?(method)
454
- to_a.send(method, *args, &block)
455
- elsif @klass.respond_to?(method)
456
- scoping { @klass.send(method, *args, &block) }
457
- elsif arel.respond_to?(method)
458
- arel.send(method, *args, &block)
459
- else
460
- super
461
- end
462
- end
463
-
464
508
  private
465
509
 
466
510
  def references_eager_loaded_tables?
@@ -486,6 +530,5 @@ module ActiveRecord
486
530
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
487
531
  string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
488
532
  end
489
-
490
533
  end
491
534
  end
@@ -66,11 +66,14 @@ module ActiveRecord
66
66
  records = relation.where(table[primary_key].gteq(start)).all
67
67
 
68
68
  while records.any?
69
+ records_size = records.size
70
+ primary_key_offset = records.last.id
71
+
69
72
  yield records
70
73
 
71
- break if records.size < batch_size
74
+ break if records_size < batch_size
72
75
 
73
- if primary_key_offset = records.last.id
76
+ if primary_key_offset
74
77
  records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
75
78
  else
76
79
  raise "Primary key not included in the custom select clause"
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  calculate(:average, column_name, options)
67
67
  end
68
68
 
69
- # Calculates the minimum value on a given column. The value is returned
69
+ # Calculates the minimum value on a given column. The value is returned
70
70
  # with the same data type of the column, or +nil+ if there's no row. See
71
71
  # +calculate+ for examples with options.
72
72
  #
@@ -89,11 +89,15 @@ module ActiveRecord
89
89
  # +calculate+ for examples with options.
90
90
  #
91
91
  # Person.sum('age') # => 4562
92
- def sum(column_name, options = {})
93
- calculate(:sum, column_name, options)
92
+ def sum(*args)
93
+ if block_given?
94
+ self.to_a.sum(*args) {|*block_args| yield(*block_args)}
95
+ else
96
+ calculate(:sum, *args)
97
+ end
94
98
  end
95
99
 
96
- # This calculates aggregate values in the given column. Methods for count, sum, average,
100
+ # This calculates aggregate values in the given column. Methods for count, sum, average,
97
101
  # minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
98
102
  # <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
99
103
  #
@@ -101,7 +105,7 @@ module ActiveRecord
101
105
  # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
102
106
  # for AVG, and the given column's type for everything else.
103
107
  # * Grouped values: This returns an ordered hash of the values and groups them by the
104
- # <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
108
+ # <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
105
109
  #
106
110
  # values = Person.maximum(:age, :group => 'last_name')
107
111
  # puts values["Drake"]
@@ -119,7 +123,7 @@ module ActiveRecord
119
123
  # Options:
120
124
  # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
121
125
  # See conditions in the intro to ActiveRecord::Base.
122
- # * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
126
+ # * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
123
127
  # the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
124
128
  # * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
125
129
  # (Rarely needed).
@@ -162,6 +166,23 @@ module ActiveRecord
162
166
  0
163
167
  end
164
168
 
169
+ # This method is designed to perform select by a single column as direct SQL query
170
+ # Returns <tt>Array</tt> with values of the specified column name
171
+ # The values has same data type as column.
172
+ #
173
+ # Examples:
174
+ #
175
+ # Person.pluck(:id) # SELECT people.id FROM people
176
+ # Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
177
+ # Person.where(:confirmed => true).limit(5).pluck(:id)
178
+ #
179
+ def pluck(column_name)
180
+ scope = self.select(column_name)
181
+ self.connection.select_values(scope.to_sql).map! do |value|
182
+ type_cast_using_column(value, column_for(column_name))
183
+ end
184
+ end
185
+
165
186
  private
166
187
 
167
188
  def perform_calculation(operation, column_name, options = {})
@@ -0,0 +1,49 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
3
+ module ActiveRecord
4
+ module Delegation
5
+ # Set up common delegations for performance (avoids method_missing)
6
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
7
+ delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
8
+ :connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
9
+
10
+ def self.delegate_to_scoped_klass(method)
11
+ if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
12
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
13
+ def #{method}(*args, &block)
14
+ scoping { @klass.#{method}(*args, &block) }
15
+ end
16
+ RUBY
17
+ else
18
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
19
+ def #{method}(*args, &block)
20
+ scoping { @klass.send(#{method.inspect}, *args, &block) }
21
+ end
22
+ RUBY
23
+ end
24
+ end
25
+
26
+ def respond_to?(method, include_private = false)
27
+ super || Array.method_defined?(method) ||
28
+ @klass.respond_to?(method, include_private) ||
29
+ arel.respond_to?(method, include_private)
30
+ end
31
+
32
+ protected
33
+
34
+ def method_missing(method, *args, &block)
35
+ if Array.method_defined?(method)
36
+ ::ActiveRecord::Delegation.delegate method, :to => :to_a
37
+ to_a.send(method, *args, &block)
38
+ elsif @klass.respond_to?(method)
39
+ ::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
40
+ scoping { @klass.send(method, *args, &block) }
41
+ elsif arel.respond_to?(method)
42
+ ::ActiveRecord::Delegation.delegate method, :to => :arel
43
+ arel.send(method, *args, &block)
44
+ else
45
+ super
46
+ end
47
+ end
48
+ end
49
+ end
@@ -83,7 +83,7 @@ module ActiveRecord
83
83
  #
84
84
  # Example for find with a lock: Imagine two concurrent transactions:
85
85
  # each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
86
- # in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
86
+ # in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
87
87
  # transaction has to wait until the first is finished; we get the
88
88
  # expected <tt>person.visits == 4</tt>.
89
89
  #
@@ -191,7 +191,7 @@ module ActiveRecord
191
191
 
192
192
  join_dependency = construct_join_dependency_for_association_find
193
193
  relation = construct_relation_for_association_find(join_dependency)
194
- relation = relation.except(:select).select("1").limit(1)
194
+ relation = relation.except(:select, :order).select("1").limit(1)
195
195
 
196
196
  case id
197
197
  when Array, Hash
@@ -200,7 +200,7 @@ module ActiveRecord
200
200
  relation = relation.where(table[primary_key].eq(id)) if id
201
201
  end
202
202
 
203
- connection.select_value(relation) ? true : false
203
+ connection.select_value(relation, "#{name} Exists") ? true : false
204
204
  end
205
205
 
206
206
  protected
@@ -208,7 +208,7 @@ module ActiveRecord
208
208
  def find_with_associations
209
209
  join_dependency = construct_join_dependency_for_association_find
210
210
  relation = construct_relation_for_association_find(join_dependency)
211
- rows = connection.select_all(relation, 'SQL', relation.bind_values.dup)
211
+ rows = connection.select_all(relation, 'SQL', relation.bind_values)
212
212
  join_dependency.instantiate(rows)
213
213
  rescue ThrowResult
214
214
  []
@@ -265,6 +265,7 @@ module ActiveRecord
265
265
  if match.bang? && result.blank?
266
266
  raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
267
267
  else
268
+ yield(result) if block_given?
268
269
  result
269
270
  end
270
271
  end