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.
- data/CHANGELOG +11 -0
- data/Rakefile +10 -10
- data/lib/active_record/associations.rb +67 -30
- data/lib/active_record/associations/association_collection.rb +9 -5
- data/lib/active_record/associations/association_proxy.rb +2 -2
- data/lib/active_record/associations/belongs_to_association.rb +22 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -1
- data/lib/active_record/associations/has_one_through_association.rb +8 -8
- data/lib/active_record/autosave_association.rb +11 -6
- data/lib/active_record/base.rb +16 -21
- data/lib/active_record/batches.rb +23 -15
- data/lib/active_record/calculations.rb +5 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -0
- data/lib/active_record/fixtures.rb +5 -4
- data/lib/active_record/named_scope.rb +2 -2
- data/lib/active_record/schema_dumper.rb +5 -1
- data/lib/active_record/serialization.rb +3 -2
- data/lib/active_record/serializers/json_serializer.rb +8 -18
- data/lib/active_record/session_store.rb +9 -1
- data/lib/active_record/timestamp.rb +39 -9
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +1 -1
- data/test/cases/associations/belongs_to_associations_test.rb +102 -4
- data/test/cases/associations/eager_test.rb +12 -0
- data/test/cases/associations/has_many_associations_test.rb +6 -0
- data/test/cases/associations/has_one_through_associations_test.rb +8 -1
- data/test/cases/associations/inner_join_association_test.rb +5 -0
- data/test/cases/autosave_association_test.rb +22 -0
- data/test/cases/base_test.rb +2 -2
- data/test/cases/calculations_test.rb +8 -14
- data/test/cases/copy_table_test_sqlite.rb +5 -5
- data/test/cases/finder_test.rb +6 -0
- data/test/cases/fixtures_test.rb +5 -0
- data/test/cases/helper.rb +1 -2
- data/test/cases/json_serialization_test.rb +57 -57
- data/test/cases/method_scoping_test.rb +13 -3
- data/test/cases/reflection_test.rb +5 -5
- data/test/cases/schema_dumper_test.rb +17 -7
- data/test/cases/schema_test_postgresql.rb +76 -0
- data/test/cases/timestamp_test.rb +75 -0
- data/test/debug.log +415 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- data/test/models/author.rb +4 -0
- data/test/models/company.rb +7 -7
- data/test/models/developer.rb +10 -0
- data/test/models/essay.rb +3 -0
- data/test/models/pet.rb +1 -1
- data/test/models/project.rb +1 -1
- data/test/models/reply.rb +2 -1
- data/test/models/topic.rb +1 -0
- data/test/models/toy.rb +2 -0
- data/test/schema/schema.rb +15 -0
- 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.
|
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
|
-
|
246
|
-
require 'rake/contrib/rubyforgepublisher'
|
246
|
+
`rubyforge login`
|
247
247
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
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
|
-
|
1005
|
-
if options[:
|
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
|
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
|
-
|
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
|
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
|
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,
|
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
|
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.
|
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
|
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
|
-
|
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 ||
|
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(
|
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
|
-
|
335
|
+
autosave = reflection.options[:autosave]
|
336
|
+
|
337
|
+
if autosave && association.marked_for_destruction?
|
334
338
|
association.destroy
|
335
339
|
else
|
336
|
-
association.save(
|
340
|
+
association.save(!autosave) if association.new_record? || autosave
|
337
341
|
|
338
342
|
if association.updated?
|
339
|
-
|
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
|
data/lib/active_record/base.rb
CHANGED
@@ -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
|
-
|
691
|
-
|
692
|
-
|
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
|
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,
|
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 =>
|
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
|
-
|
3038
|
-
|
3039
|
-
|
3040
|
-
|
3041
|
-
|
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]*([
|
3078
|
+
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
|
3084
3079
|
end
|
3085
3080
|
|
3086
3081
|
def find_parameter_position(multiparameter_name)
|