activerecord 2.3.2 → 2.3.3

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 (55) hide show
  1. data/CHANGELOG +11 -0
  2. data/Rakefile +10 -10
  3. data/lib/active_record/associations.rb +67 -30
  4. data/lib/active_record/associations/association_collection.rb +9 -5
  5. data/lib/active_record/associations/association_proxy.rb +2 -2
  6. data/lib/active_record/associations/belongs_to_association.rb +22 -4
  7. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -1
  8. data/lib/active_record/associations/has_one_through_association.rb +8 -8
  9. data/lib/active_record/autosave_association.rb +11 -6
  10. data/lib/active_record/base.rb +16 -21
  11. data/lib/active_record/batches.rb +23 -15
  12. data/lib/active_record/calculations.rb +5 -13
  13. data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -22
  14. data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -0
  15. data/lib/active_record/fixtures.rb +5 -4
  16. data/lib/active_record/named_scope.rb +2 -2
  17. data/lib/active_record/schema_dumper.rb +5 -1
  18. data/lib/active_record/serialization.rb +3 -2
  19. data/lib/active_record/serializers/json_serializer.rb +8 -18
  20. data/lib/active_record/session_store.rb +9 -1
  21. data/lib/active_record/timestamp.rb +39 -9
  22. data/lib/active_record/validations.rb +1 -1
  23. data/lib/active_record/version.rb +1 -1
  24. data/test/cases/associations/belongs_to_associations_test.rb +102 -4
  25. data/test/cases/associations/eager_test.rb +12 -0
  26. data/test/cases/associations/has_many_associations_test.rb +6 -0
  27. data/test/cases/associations/has_one_through_associations_test.rb +8 -1
  28. data/test/cases/associations/inner_join_association_test.rb +5 -0
  29. data/test/cases/autosave_association_test.rb +22 -0
  30. data/test/cases/base_test.rb +2 -2
  31. data/test/cases/calculations_test.rb +8 -14
  32. data/test/cases/copy_table_test_sqlite.rb +5 -5
  33. data/test/cases/finder_test.rb +6 -0
  34. data/test/cases/fixtures_test.rb +5 -0
  35. data/test/cases/helper.rb +1 -2
  36. data/test/cases/json_serialization_test.rb +57 -57
  37. data/test/cases/method_scoping_test.rb +13 -3
  38. data/test/cases/reflection_test.rb +5 -5
  39. data/test/cases/schema_dumper_test.rb +17 -7
  40. data/test/cases/schema_test_postgresql.rb +76 -0
  41. data/test/cases/timestamp_test.rb +75 -0
  42. data/test/debug.log +415 -0
  43. data/test/fixtures/fixture_database.sqlite3 +0 -0
  44. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  45. data/test/models/author.rb +4 -0
  46. data/test/models/company.rb +7 -7
  47. data/test/models/developer.rb +10 -0
  48. data/test/models/essay.rb +3 -0
  49. data/test/models/pet.rb +1 -1
  50. data/test/models/project.rb +1 -1
  51. data/test/models/reply.rb +2 -1
  52. data/test/models/topic.rb +1 -0
  53. data/test/models/toy.rb +2 -0
  54. data/test/schema/schema.rb +15 -0
  55. metadata +6 -3
data/CHANGELOG CHANGED
@@ -1,3 +1,14 @@
1
+ *2.3.3 (July 20, 2009)*
2
+
3
+ * Added :primary_key option to belongs_to associations. #765 [Szymon Nowak, Philip Hallstrom, Noel Rocha]
4
+ # employees.company_name references companies.name
5
+ Employee.belongs_to :company, :primary_key => 'name', :foreign_key => 'company_name'
6
+
7
+ * Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH]
8
+
9
+ * Added ActiveRecord::Base#touch to update the updated_at/on attributes (or another specified timestamp) with the current time [DHH]
10
+
11
+
1
12
  *2.3.2 [Final] (March 15, 2009)*
2
13
 
3
14
  * Added ActiveRecord::Base.find_each and ActiveRecord::Base.find_in_batches for batch processing [DHH/Jamis Buck]
data/Rakefile CHANGED
@@ -4,7 +4,6 @@ require 'rake/testtask'
4
4
  require 'rake/rdoctask'
5
5
  require 'rake/packagetask'
6
6
  require 'rake/gempackagetask'
7
- require 'rake/contrib/sshpublisher'
8
7
 
9
8
  require File.join(File.dirname(__FILE__), 'lib', 'active_record', 'version')
10
9
  require File.expand_path(File.dirname(__FILE__)) + "/test/config"
@@ -177,7 +176,7 @@ spec = Gem::Specification.new do |s|
177
176
  s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
178
177
  end
179
178
 
180
- s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD)
179
+ s.add_dependency('activesupport', '= 2.3.3' + PKG_BUILD)
181
180
 
182
181
  s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
183
182
  s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
@@ -231,23 +230,24 @@ end
231
230
 
232
231
  desc "Publish the beta gem"
233
232
  task :pgem => [:package] do
233
+ require 'rake/contrib/sshpublisher'
234
234
  Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
235
235
  `ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
236
236
  end
237
237
 
238
238
  desc "Publish the API documentation"
239
239
  task :pdoc => [:rdoc] do
240
+ require 'rake/contrib/sshpublisher'
240
241
  Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ar", "doc").upload
241
242
  end
242
243
 
243
244
  desc "Publish the release files to RubyForge."
244
245
  task :release => [ :package ] do
245
- require 'rubyforge'
246
- require 'rake/contrib/rubyforgepublisher'
246
+ `rubyforge login`
247
247
 
248
- packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
249
-
250
- rubyforge = RubyForge.new
251
- rubyforge.login
252
- rubyforge.add_release(PKG_NAME, PKG_NAME, "REL #{PKG_VERSION}", *packages)
253
- end
248
+ for ext in %w( gem tgz zip )
249
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
250
+ puts release_command
251
+ system(release_command)
252
+ end
253
+ end
@@ -957,6 +957,8 @@ module ActiveRecord
957
957
  # of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
958
958
  # "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
959
959
  # will use a foreign key of "favorite_person_id".
960
+ # [:primary_key]
961
+ # Specify the method that returns the primary key of associated object used for the association. By default this is id.
960
962
  # [:dependent]
961
963
  # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
962
964
  # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
@@ -981,15 +983,21 @@ module ActiveRecord
981
983
  # If false, don't validate the associated objects when saving the parent object. +false+ by default.
982
984
  # [:autosave]
983
985
  # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
986
+ # [:touch]
987
+ # If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or
988
+ # destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute.
984
989
  #
985
990
  # Option examples:
986
991
  # belongs_to :firm, :foreign_key => "client_of"
992
+ # belongs_to :person, :primary_key => "name", :foreign_key => "person_name"
987
993
  # belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
988
994
  # belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
989
995
  # :conditions => 'discounts > #{payments_count}'
990
996
  # belongs_to :attachable, :polymorphic => true
991
997
  # belongs_to :project, :readonly => true
992
998
  # belongs_to :post, :counter_cache => true
999
+ # belongs_to :company, :touch => true
1000
+ # belongs_to :company, :touch => :employees_last_updated_at
993
1001
  def belongs_to(association_id, options = {})
994
1002
  reflection = create_belongs_to_reflection(association_id, options)
995
1003
 
@@ -1001,28 +1009,8 @@ module ActiveRecord
1001
1009
  association_constructor_method(:create, reflection, BelongsToAssociation)
1002
1010
  end
1003
1011
 
1004
- # Create the callbacks to update counter cache
1005
- if options[:counter_cache]
1006
- cache_column = reflection.counter_cache_column
1007
-
1008
- method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
1009
- define_method(method_name) do
1010
- association = send(reflection.name)
1011
- association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
1012
- end
1013
- after_create method_name
1014
-
1015
- method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
1016
- define_method(method_name) do
1017
- association = send(reflection.name)
1018
- association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
1019
- end
1020
- before_destroy method_name
1021
-
1022
- module_eval(
1023
- "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
1024
- )
1025
- end
1012
+ add_counter_cache_callbacks(reflection) if options[:counter_cache]
1013
+ add_touch_callbacks(reflection, options[:touch]) if options[:touch]
1026
1014
 
1027
1015
  configure_dependency_for_belongs_to(reflection)
1028
1016
  end
@@ -1329,6 +1317,43 @@ module ActiveRecord
1329
1317
  end
1330
1318
  end
1331
1319
 
1320
+ def add_counter_cache_callbacks(reflection)
1321
+ cache_column = reflection.counter_cache_column
1322
+
1323
+ method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
1324
+ define_method(method_name) do
1325
+ association = send(reflection.name)
1326
+ association.class.increment_counter(cache_column, association.id) unless association.nil?
1327
+ end
1328
+ after_create(method_name)
1329
+
1330
+ method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
1331
+ define_method(method_name) do
1332
+ association = send(reflection.name)
1333
+ association.class.decrement_counter(cache_column, association.id) unless association.nil?
1334
+ end
1335
+ before_destroy(method_name)
1336
+
1337
+ module_eval(
1338
+ "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
1339
+ )
1340
+ end
1341
+
1342
+ def add_touch_callbacks(reflection, touch_attribute)
1343
+ method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
1344
+ define_method(method_name) do
1345
+ association = send(reflection.name)
1346
+
1347
+ if touch_attribute == true
1348
+ association.touch unless association.nil?
1349
+ else
1350
+ association.touch(touch_attribute) unless association.nil?
1351
+ end
1352
+ end
1353
+ after_save(method_name)
1354
+ after_destroy(method_name)
1355
+ end
1356
+
1332
1357
  def find_with_associations(options = {})
1333
1358
  catch :invalid_query do
1334
1359
  join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
@@ -1353,7 +1378,7 @@ module ActiveRecord
1353
1378
  dependent_conditions = []
1354
1379
  dependent_conditions << "#{reflection.primary_key_name} = \#{record.quoted_id}"
1355
1380
  dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as]
1356
- dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions]
1381
+ dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.quoted_table_name) if reflection.options[:conditions]
1357
1382
  dependent_conditions << extra_conditions if extra_conditions
1358
1383
  dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
1359
1384
  dependent_conditions = dependent_conditions.gsub('@', '\@')
@@ -1497,9 +1522,9 @@ module ActiveRecord
1497
1522
 
1498
1523
  mattr_accessor :valid_keys_for_belongs_to_association
1499
1524
  @@valid_keys_for_belongs_to_association = [
1500
- :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
1525
+ :class_name, :primary_key, :foreign_key, :foreign_type, :remote, :select, :conditions,
1501
1526
  :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
1502
- :validate
1527
+ :validate, :touch
1503
1528
  ]
1504
1529
 
1505
1530
  def create_belongs_to_reflection(association_id, options)
@@ -1643,17 +1668,29 @@ module ActiveRecord
1643
1668
  string.scan(/([\.a-zA-Z_]+).?\./).flatten
1644
1669
  end
1645
1670
 
1671
+ def tables_in_hash(hash)
1672
+ return [] if hash.blank?
1673
+ tables = hash.map do |key, value|
1674
+ if value.is_a?(Hash)
1675
+ key.to_s
1676
+ else
1677
+ tables_in_string(key) if key.is_a?(String)
1678
+ end
1679
+ end
1680
+ tables.flatten.compact
1681
+ end
1682
+
1646
1683
  def conditions_tables(options)
1647
1684
  # look in both sets of conditions
1648
1685
  conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond|
1649
1686
  case cond
1650
1687
  when nil then all
1651
- when Array then all << cond.first
1652
- when Hash then all << cond.keys
1653
- else all << cond
1688
+ when Array then all << tables_in_string(cond.first)
1689
+ when Hash then all << tables_in_hash(cond)
1690
+ else all << tables_in_string(cond)
1654
1691
  end
1655
1692
  end
1656
- tables_in_string(conditions.join(' '))
1693
+ conditions.flatten
1657
1694
  end
1658
1695
 
1659
1696
  def order_tables(options)
@@ -2101,7 +2138,7 @@ module ActiveRecord
2101
2138
  klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record?
2102
2139
 
2103
2140
  [through_reflection, reflection].each do |ref|
2104
- join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions]
2141
+ join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions]
2105
2142
  end
2106
2143
 
2107
2144
  join
@@ -143,6 +143,8 @@ module ActiveRecord
143
143
  end
144
144
 
145
145
  # Remove all records from this association
146
+ #
147
+ # See delete for more info.
146
148
  def delete_all
147
149
  load_target
148
150
  delete(@target)
@@ -200,11 +202,11 @@ module ActiveRecord
200
202
  end
201
203
  end
202
204
 
203
- # Destroy +records+ and remove from this association calling +before_remove+
204
- # and +after_remove+ callbacks.
205
+ # Destroy +records+ and remove them from this association calling
206
+ # +before_remove+ and +after_remove+ callbacks.
205
207
  #
206
- # Note this method will always remove records from database ignoring the
207
- # +:dependent+ option.
208
+ # Note that this method will _always_ remove records from the database
209
+ # ignoring the +:dependent+ option.
208
210
  def destroy(*records)
209
211
  remove_records(records) do |records, old_records|
210
212
  old_records.each { |record| record.destroy }
@@ -226,7 +228,9 @@ module ActiveRecord
226
228
  self
227
229
  end
228
230
 
229
- # Destory all the records from this association
231
+ # Destory all the records from this association.
232
+ #
233
+ # See destroy for more info.
230
234
  def destroy_all
231
235
  load_target
232
236
  destroy(@target)
@@ -169,8 +169,8 @@ module ActiveRecord
169
169
  end
170
170
 
171
171
  # Forwards the call to the reflection class.
172
- def sanitize_sql(sql)
173
- @reflection.klass.send(:sanitize_sql, sql)
172
+ def sanitize_sql(sql, table_name = @reflection.klass.quoted_table_name)
173
+ @reflection.klass.send(:sanitize_sql, sql, table_name)
174
174
  end
175
175
 
176
176
  # Assigns the ID of the owner to the corresponding foreign key in +record+.
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
 
15
15
  if record.nil?
16
16
  if counter_cache_name && !@owner.new_record?
17
- @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
17
+ @reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
18
18
  end
19
19
 
20
20
  @target = @owner[@reflection.primary_key_name] = nil
@@ -27,7 +27,7 @@ module ActiveRecord
27
27
  end
28
28
 
29
29
  @target = (AssociationProxy === record ? record.target : record)
30
- @owner[@reflection.primary_key_name] = record.id unless record.new_record?
30
+ @owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
31
31
  @updated = true
32
32
  end
33
33
 
@@ -41,18 +41,36 @@ module ActiveRecord
41
41
 
42
42
  private
43
43
  def find_target
44
- @reflection.klass.find(
44
+ find_method = if @reflection.options[:primary_key]
45
+ "find_by_#{@reflection.options[:primary_key]}"
46
+ else
47
+ "find"
48
+ end
49
+ @reflection.klass.send(find_method,
45
50
  @owner[@reflection.primary_key_name],
46
51
  :select => @reflection.options[:select],
47
52
  :conditions => conditions,
48
53
  :include => @reflection.options[:include],
49
54
  :readonly => @reflection.options[:readonly]
50
- )
55
+ ) if @owner[@reflection.primary_key_name]
51
56
  end
52
57
 
53
58
  def foreign_key_present
54
59
  !@owner[@reflection.primary_key_name].nil?
55
60
  end
61
+
62
+ def record_id(record)
63
+ record.send(@reflection.options[:primary_key] || :id)
64
+ end
65
+
66
+ def previous_record_id
67
+ @previous_record_id ||= if @reflection.options[:primary_key]
68
+ previous_record = @owner.send(@reflection.name)
69
+ previous_record.nil? ? nil : previous_record.id
70
+ else
71
+ @owner[@reflection.primary_key_name]
72
+ end
73
+ end
56
74
  end
57
75
  end
58
76
  end
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  else
8
8
  @target = (AssociationProxy === record ? record.target : record)
9
9
 
10
- @owner[@reflection.primary_key_name] = record.id
10
+ @owner[@reflection.primary_key_name] = record_id(record)
11
11
  @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
12
12
 
13
13
  @updated = true
@@ -41,6 +41,10 @@ module ActiveRecord
41
41
  !@owner[@reflection.primary_key_name].nil?
42
42
  end
43
43
 
44
+ def record_id(record)
45
+ record.send(@reflection.options[:primary_key] || :id)
46
+ end
47
+
44
48
  def association_class
45
49
  @owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
46
50
  end
@@ -1,31 +1,31 @@
1
1
  module ActiveRecord
2
2
  module Associations
3
3
  class HasOneThroughAssociation < HasManyThroughAssociation
4
-
4
+
5
5
  def create_through_record(new_value) #nodoc:
6
6
  klass = @reflection.through_reflection.klass
7
7
 
8
8
  current_object = @owner.send(@reflection.through_reflection.name)
9
-
9
+
10
10
  if current_object
11
- current_object.update_attributes(construct_join_attributes(new_value))
11
+ new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
12
12
  else
13
- @owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value)))
13
+ @owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value))) if new_value
14
14
  end
15
15
  end
16
-
16
+
17
17
  private
18
18
  def find(*args)
19
19
  super(args.merge(:limit => 1))
20
20
  end
21
-
21
+
22
22
  def find_target
23
23
  super.first
24
24
  end
25
25
 
26
26
  def reset_target!
27
27
  @target = nil
28
- end
29
- end
28
+ end
29
+ end
30
30
  end
31
31
  end
@@ -311,11 +311,13 @@ module ActiveRecord
311
311
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
312
312
  def save_has_one_association(reflection)
313
313
  if (association = association_instance_get(reflection.name)) && !association.target.nil?
314
- if reflection.options[:autosave] && association.marked_for_destruction?
314
+ autosave = reflection.options[:autosave]
315
+
316
+ if autosave && association.marked_for_destruction?
315
317
  association.destroy
316
- elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || reflection.options[:autosave]
318
+ elsif new_record? || association.new_record? || association[reflection.primary_key_name] != id || autosave
317
319
  association[reflection.primary_key_name] = id
318
- association.save(false)
320
+ association.save(!autosave)
319
321
  end
320
322
  end
321
323
  end
@@ -330,13 +332,16 @@ module ActiveRecord
330
332
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
331
333
  def save_belongs_to_association(reflection)
332
334
  if association = association_instance_get(reflection.name)
333
- if reflection.options[:autosave] && association.marked_for_destruction?
335
+ autosave = reflection.options[:autosave]
336
+
337
+ if autosave && association.marked_for_destruction?
334
338
  association.destroy
335
339
  else
336
- association.save(false) if association.new_record? || reflection.options[:autosave]
340
+ association.save(!autosave) if association.new_record? || autosave
337
341
 
338
342
  if association.updated?
339
- self[reflection.primary_key_name] = association.id
343
+ association_id = association.send(reflection.options[:primary_key] || :id)
344
+ self[reflection.primary_key_name] = association_id
340
345
  # TODO: Removing this code doesn't seem to matter…
341
346
  if reflection.options[:polymorphic]
342
347
  self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
@@ -687,14 +687,9 @@ module ActiveRecord #:nodoc:
687
687
  # Person.exists?(['name LIKE ?', "%#{query}%"])
688
688
  # Person.exists?
689
689
  def exists?(id_or_conditions = {})
690
- connection.select_all(
691
- construct_finder_sql(
692
- :select => "#{quoted_table_name}.#{primary_key}",
693
- :conditions => expand_id_conditions(id_or_conditions),
694
- :limit => 1
695
- ),
696
- "#{name} Exists"
697
- ).size > 0
690
+ find_initial(
691
+ :select => "#{quoted_table_name}.#{primary_key}",
692
+ :conditions => expand_id_conditions(id_or_conditions)) ? true : false
698
693
  end
699
694
 
700
695
  # Creates an object (or multiple objects) and saves it to the database, if validations pass.
@@ -736,12 +731,12 @@ module ActiveRecord #:nodoc:
736
731
  # ==== Parameters
737
732
  #
738
733
  # * +id+ - This should be the id or an array of ids to be updated.
739
- # * +attributes+ - This should be a Hash of attributes to be set on the object, or an array of Hashes.
734
+ # * +attributes+ - This should be a hash of attributes to be set on the object, or an array of hashes.
740
735
  #
741
736
  # ==== Examples
742
737
  #
743
738
  # # Updating one record:
744
- # Person.update(15, { :user_name => 'Samuel', :group => 'expert' })
739
+ # Person.update(15, :user_name => 'Samuel', :group => 'expert')
745
740
  #
746
741
  # # Updating multiple records:
747
742
  # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
@@ -2168,7 +2163,7 @@ module ActiveRecord #:nodoc:
2168
2163
  # default_scope :order => 'last_name, first_name'
2169
2164
  # end
2170
2165
  def default_scope(options = {})
2171
- self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} }
2166
+ self.default_scoping << { :find => options, :create => options[:conditions].is_a?(Hash) ? options[:conditions] : {} }
2172
2167
  end
2173
2168
 
2174
2169
  # Test whether the given method and optional key are scoped.
@@ -2228,12 +2223,12 @@ module ActiveRecord #:nodoc:
2228
2223
  # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
2229
2224
  # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'"
2230
2225
  # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
2231
- def sanitize_sql_for_conditions(condition)
2226
+ def sanitize_sql_for_conditions(condition, table_name = quoted_table_name)
2232
2227
  return nil if condition.blank?
2233
2228
 
2234
2229
  case condition
2235
2230
  when Array; sanitize_sql_array(condition)
2236
- when Hash; sanitize_sql_hash_for_conditions(condition)
2231
+ when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
2237
2232
  else condition
2238
2233
  end
2239
2234
  end
@@ -3034,11 +3029,11 @@ module ActiveRecord #:nodoc:
3034
3029
  def execute_callstack_for_multiparameter_attributes(callstack)
3035
3030
  errors = []
3036
3031
  callstack.each do |name, values|
3037
- klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
3038
- if values.empty?
3039
- send(name + "=", nil)
3040
- else
3041
- begin
3032
+ begin
3033
+ klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
3034
+ if values.empty?
3035
+ send(name + "=", nil)
3036
+ else
3042
3037
  value = if Time == klass
3043
3038
  instantiate_time_object(name, values)
3044
3039
  elsif Date == klass
@@ -3052,9 +3047,9 @@ module ActiveRecord #:nodoc:
3052
3047
  end
3053
3048
 
3054
3049
  send(name + "=", value)
3055
- rescue => ex
3056
- errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
3057
3050
  end
3051
+ rescue => ex
3052
+ errors << AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
3058
3053
  end
3059
3054
  end
3060
3055
  unless errors.empty?
@@ -3080,7 +3075,7 @@ module ActiveRecord #:nodoc:
3080
3075
  end
3081
3076
 
3082
3077
  def type_cast_attribute_value(multiparameter_name, value)
3083
- multiparameter_name =~ /\([0-9]*([a-z])\)/ ? value.send("to_" + $1) : value
3078
+ multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
3084
3079
  end
3085
3080
 
3086
3081
  def find_parameter_position(multiparameter_name)