activerecord 3.0.3 → 3.0.4.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.
- data/CHANGELOG +6 -0
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_preload.rb +22 -7
- data/lib/active_record/associations.rb +11 -6
- data/lib/active_record/associations/association_collection.rb +14 -15
- data/lib/active_record/associations/association_proxy.rb +3 -3
- data/lib/active_record/associations/belongs_to_association.rb +3 -3
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +10 -1
- data/lib/active_record/associations/has_many_through_association.rb +1 -1
- data/lib/active_record/associations/has_one_association.rb +6 -6
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +3 -4
- data/lib/active_record/attribute_methods/read.rb +2 -1
- data/lib/active_record/autosave_association.rb +8 -8
- data/lib/active_record/base.rb +9 -11
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +2 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +7 -6
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/nested_attributes.rb +2 -5
- data/lib/active_record/observer.rb +4 -26
- data/lib/active_record/persistence.rb +5 -5
- data/lib/active_record/reflection.rb +9 -1
- data/lib/active_record/relation.rb +1 -1
- data/lib/active_record/relation/calculations.rb +24 -24
- data/lib/active_record/relation/finder_methods.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +2 -2
- data/lib/active_record/relation/query_methods.rb +24 -6
- data/lib/active_record/session_store.rb +4 -4
- data/lib/active_record/timestamp.rb +1 -1
- data/lib/active_record/transactions.rb +17 -14
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record/version.rb +4 -3
- metadata +20 -15
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
*Rails 3.0.4 (unreleased)*
|
2
|
+
|
3
|
+
* Added deprecation warning for has_and_belongs_to_many associations where the join table has
|
4
|
+
additional attributes other than the keys. Access to these attributes is removed in 3.1.
|
5
|
+
Please use has_many :through instead. [Jon Leighton]
|
6
|
+
|
1
7
|
*Rails 3.0.3 (November 16, 2010)*
|
2
8
|
|
3
9
|
* Support find by class like this: Post.where(:name => Post)
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
def clear_aggregation_cache #:nodoc:
|
7
7
|
self.class.reflect_on_all_aggregations.to_a.each do |assoc|
|
8
8
|
instance_variable_set "@#{assoc.name}", nil
|
9
|
-
end
|
9
|
+
end unless self.new_record?
|
10
10
|
end
|
11
11
|
|
12
12
|
# Active Record implements aggregation through a macro-like class method called +composed_of+
|
@@ -193,13 +193,17 @@ module ActiveRecord
|
|
193
193
|
conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
|
194
194
|
conditions << append_conditions(reflection, preload_options)
|
195
195
|
|
196
|
-
|
196
|
+
associated_records_proxy = reflection.klass.unscoped.
|
197
197
|
includes(options[:include]).
|
198
198
|
joins("INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}").
|
199
199
|
select("#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id").
|
200
|
-
order(options[:order])
|
200
|
+
order(options[:order])
|
201
201
|
|
202
|
-
|
202
|
+
all_associated_records = associated_records(ids) do |some_ids|
|
203
|
+
associated_records_proxy.where([conditions, ids]).to_a
|
204
|
+
end
|
205
|
+
|
206
|
+
set_association_collection_records(id_to_record_map, reflection.name, all_associated_records, 'the_parent_record_id')
|
203
207
|
end
|
204
208
|
|
205
209
|
def preload_has_one_association(records, reflection, preload_options={})
|
@@ -336,9 +340,8 @@ module ActiveRecord
|
|
336
340
|
klass = klass_name.constantize
|
337
341
|
|
338
342
|
table_name = klass.quoted_table_name
|
339
|
-
primary_key = reflection.options[:primary_key] || klass.primary_key
|
343
|
+
primary_key = (reflection.options[:primary_key] || klass.primary_key).to_s
|
340
344
|
column_type = klass.columns.detect{|c| c.name == primary_key}.type
|
341
|
-
|
342
345
|
ids = id_map.keys.map do |id|
|
343
346
|
if column_type == :integer
|
344
347
|
id.to_i
|
@@ -374,13 +377,14 @@ module ActiveRecord
|
|
374
377
|
find_options = {
|
375
378
|
:select => preload_options[:select] || options[:select] || Arel::SqlLiteral.new("#{table_name}.*"),
|
376
379
|
:include => preload_options[:include] || options[:include],
|
377
|
-
:conditions => [conditions, ids],
|
378
380
|
:joins => options[:joins],
|
379
381
|
:group => preload_options[:group] || options[:group],
|
380
382
|
:order => preload_options[:order] || options[:order]
|
381
383
|
}
|
382
384
|
|
383
|
-
|
385
|
+
associated_records(ids) do |some_ids|
|
386
|
+
reflection.klass.scoped.apply_finder_options(find_options.merge(:conditions => [conditions, some_ids])).to_a
|
387
|
+
end
|
384
388
|
end
|
385
389
|
|
386
390
|
|
@@ -398,6 +402,17 @@ module ActiveRecord
|
|
398
402
|
def in_or_equals_for_ids(ids)
|
399
403
|
ids.size > 1 ? "IN (?)" : "= ?"
|
400
404
|
end
|
405
|
+
|
406
|
+
# Some databases impose a limit on the number of ids in a list (in Oracle its 1000)
|
407
|
+
# Make several smaller queries if necessary or make one query if the adapter supports it
|
408
|
+
def associated_records(ids)
|
409
|
+
max_ids_in_a_list = connection.ids_in_list_limit || ids.size
|
410
|
+
records = []
|
411
|
+
ids.each_slice(max_ids_in_a_list) do |some_ids|
|
412
|
+
records += yield(some_ids)
|
413
|
+
end
|
414
|
+
records
|
415
|
+
end
|
401
416
|
end
|
402
417
|
end
|
403
418
|
end
|
@@ -118,7 +118,7 @@ module ActiveRecord
|
|
118
118
|
def clear_association_cache #:nodoc:
|
119
119
|
self.class.reflect_on_all_associations.to_a.each do |assoc|
|
120
120
|
instance_variable_set "@#{assoc.name}", nil
|
121
|
-
end
|
121
|
+
end unless self.new_record?
|
122
122
|
end
|
123
123
|
|
124
124
|
private
|
@@ -2156,14 +2156,19 @@ module ActiveRecord
|
|
2156
2156
|
when :has_many, :has_one
|
2157
2157
|
if reflection.options[:through]
|
2158
2158
|
join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine)
|
2159
|
-
|
2159
|
+
jt_as_extra = jt_source_extra = jt_sti_extra = nil
|
2160
2160
|
first_key = second_key = as_extra = nil
|
2161
2161
|
|
2162
|
-
if through_reflection.
|
2163
|
-
|
2164
|
-
|
2162
|
+
if through_reflection.macro == :belongs_to
|
2163
|
+
jt_primary_key = through_reflection.primary_key_name
|
2164
|
+
jt_foreign_key = through_reflection.association_primary_key
|
2165
2165
|
else
|
2166
|
+
jt_primary_key = through_reflection.active_record_primary_key
|
2166
2167
|
jt_foreign_key = through_reflection.primary_key_name
|
2168
|
+
|
2169
|
+
if through_reflection.options[:as] # has_many :through against a polymorphic join
|
2170
|
+
jt_as_extra = join_table[through_reflection.options[:as].to_s + '_type'].eq(parent.active_record.base_class.name)
|
2171
|
+
end
|
2167
2172
|
end
|
2168
2173
|
|
2169
2174
|
case source_reflection.macro
|
@@ -2191,7 +2196,7 @@ module ActiveRecord
|
|
2191
2196
|
end
|
2192
2197
|
|
2193
2198
|
[
|
2194
|
-
[parent_table[
|
2199
|
+
[parent_table[jt_primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].reject{|x| x.blank? },
|
2195
2200
|
aliased_table[first_key].eq(join_table[second_key])
|
2196
2201
|
]
|
2197
2202
|
elsif reflection.options[:as]
|
@@ -127,13 +127,13 @@ module ActiveRecord
|
|
127
127
|
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
|
128
128
|
def <<(*records)
|
129
129
|
result = true
|
130
|
-
load_target
|
130
|
+
load_target if @owner.new_record?
|
131
131
|
|
132
132
|
transaction do
|
133
133
|
flatten_deeper(records).each do |record|
|
134
134
|
raise_on_type_mismatch(record)
|
135
135
|
add_record_to_target_with_callbacks(record) do |r|
|
136
|
-
result &&= insert_record(record)
|
136
|
+
result &&= insert_record(record) unless @owner.new_record?
|
137
137
|
end
|
138
138
|
end
|
139
139
|
end
|
@@ -291,12 +291,12 @@ module ActiveRecord
|
|
291
291
|
# This method is abstract in the sense that it relies on
|
292
292
|
# +count_records+, which is a method descendants have to provide.
|
293
293
|
def size
|
294
|
-
if
|
294
|
+
if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
|
295
295
|
@target.size
|
296
296
|
elsif !loaded? && @reflection.options[:group]
|
297
297
|
load_target.size
|
298
298
|
elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
|
299
|
-
unsaved_records = @target.
|
299
|
+
unsaved_records = @target.select { |r| r.new_record? }
|
300
300
|
unsaved_records.size + count_records
|
301
301
|
else
|
302
302
|
count_records
|
@@ -363,7 +363,7 @@ module ActiveRecord
|
|
363
363
|
|
364
364
|
def include?(record)
|
365
365
|
return false unless record.is_a?(@reflection.klass)
|
366
|
-
return include_in_memory?(record)
|
366
|
+
return include_in_memory?(record) if record.new_record?
|
367
367
|
load_target if @reflection.options[:finder_sql] && !loaded?
|
368
368
|
return @target.include?(record) if loaded?
|
369
369
|
exists?(record)
|
@@ -390,7 +390,7 @@ module ActiveRecord
|
|
390
390
|
end
|
391
391
|
|
392
392
|
def load_target
|
393
|
-
if
|
393
|
+
if !@owner.new_record? || foreign_key_present
|
394
394
|
begin
|
395
395
|
if !loaded?
|
396
396
|
if @target.is_a?(Array) && @target.any?
|
@@ -521,7 +521,7 @@ module ActiveRecord
|
|
521
521
|
|
522
522
|
transaction do
|
523
523
|
records.each { |record| callback(:before_remove, record) }
|
524
|
-
old_records = records.
|
524
|
+
old_records = records.reject { |r| r.new_record? }
|
525
525
|
yield(records, old_records)
|
526
526
|
records.each { |record| callback(:after_remove, record) }
|
527
527
|
end
|
@@ -546,23 +546,22 @@ module ActiveRecord
|
|
546
546
|
end
|
547
547
|
|
548
548
|
def ensure_owner_is_not_new
|
549
|
-
|
549
|
+
if @owner.new_record?
|
550
550
|
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
551
551
|
end
|
552
552
|
end
|
553
553
|
|
554
554
|
def fetch_first_or_last_using_find?(args)
|
555
|
-
args.first.kind_of?(Hash) || !(loaded? ||
|
556
|
-
|
555
|
+
args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
|
556
|
+
@target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
|
557
557
|
end
|
558
558
|
|
559
559
|
def include_in_memory?(record)
|
560
560
|
if @reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
561
|
-
@owner.send(proxy_reflection.through_reflection.name
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
false
|
561
|
+
@owner.send(proxy_reflection.through_reflection.name).any? { |source|
|
562
|
+
target = source.send(proxy_reflection.source_reflection.name)
|
563
|
+
target.respond_to?(:include?) ? target.include?(record) : target == record
|
564
|
+
} || @target.include?(record)
|
566
565
|
else
|
567
566
|
@target.include?(record)
|
568
567
|
end
|
@@ -174,10 +174,10 @@ module ActiveRecord
|
|
174
174
|
# If the association is polymorphic the type of the owner is also set.
|
175
175
|
def set_belongs_to_association_for(record)
|
176
176
|
if @reflection.options[:as]
|
177
|
-
record["#{@reflection.options[:as]}_id"] = @owner.id
|
177
|
+
record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
|
178
178
|
record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
|
179
179
|
else
|
180
|
-
|
180
|
+
unless @owner.new_record?
|
181
181
|
primary_key = @reflection.options[:primary_key] || :id
|
182
182
|
record[@reflection.primary_key_name] = @owner.send(primary_key)
|
183
183
|
end
|
@@ -233,7 +233,7 @@ module ActiveRecord
|
|
233
233
|
def load_target
|
234
234
|
return nil unless defined?(@loaded)
|
235
235
|
|
236
|
-
if !loaded? and (
|
236
|
+
if !loaded? and (!@owner.new_record? || foreign_key_present)
|
237
237
|
@target = find_target
|
238
238
|
end
|
239
239
|
|
@@ -14,7 +14,7 @@ module ActiveRecord
|
|
14
14
|
counter_cache_name = @reflection.counter_cache_column
|
15
15
|
|
16
16
|
if record.nil?
|
17
|
-
if counter_cache_name &&
|
17
|
+
if counter_cache_name && !@owner.new_record?
|
18
18
|
@reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
|
19
19
|
end
|
20
20
|
|
@@ -22,13 +22,13 @@ module ActiveRecord
|
|
22
22
|
else
|
23
23
|
raise_on_type_mismatch(record)
|
24
24
|
|
25
|
-
if counter_cache_name &&
|
25
|
+
if counter_cache_name && !@owner.new_record? && record.id != @owner[@reflection.primary_key_name]
|
26
26
|
@reflection.klass.increment_counter(counter_cache_name, record.id)
|
27
27
|
@reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
|
28
28
|
end
|
29
29
|
|
30
30
|
@target = (AssociationProxy === record ? record.target : record)
|
31
|
-
@owner[@reflection.primary_key_name] = record_id(record)
|
31
|
+
@owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
|
32
32
|
@updated = true
|
33
33
|
end
|
34
34
|
|
@@ -23,6 +23,10 @@ module ActiveRecord
|
|
23
23
|
@updated
|
24
24
|
end
|
25
25
|
|
26
|
+
def conditions
|
27
|
+
@conditions ||= interpolate_sql(association_class.send(:sanitize_sql, @reflection.options[:conditions])) if @reflection.options[:conditions]
|
28
|
+
end
|
29
|
+
|
26
30
|
private
|
27
31
|
|
28
32
|
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
@@ -1,7 +1,16 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record Has And Belongs To Many Association
|
3
5
|
module Associations
|
4
6
|
class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
|
7
|
+
def initialize(owner, reflection)
|
8
|
+
super
|
9
|
+
if columns.size > 2
|
10
|
+
ActiveSupport::Deprecation.warn "Having additional attributes on the join table of a has_and_belongs_to_many association is deprecated and will be removed in Rails 3.1. Please use a has_many :through association instead."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
def create(attributes = {})
|
6
15
|
create_record(attributes) { |record| insert_record(record) }
|
7
16
|
end
|
@@ -34,7 +43,7 @@ module ActiveRecord
|
|
34
43
|
end
|
35
44
|
|
36
45
|
def insert_record(record, force = true, validate = true)
|
37
|
-
|
46
|
+
if record.new_record?
|
38
47
|
if force
|
39
48
|
record.save!
|
40
49
|
else
|
@@ -35,18 +35,18 @@ module ActiveRecord
|
|
35
35
|
if dependent? && !dont_save
|
36
36
|
case @reflection.options[:dependent]
|
37
37
|
when :delete
|
38
|
-
@target.delete
|
38
|
+
@target.delete unless @target.new_record?
|
39
39
|
@owner.clear_association_cache
|
40
40
|
when :destroy
|
41
|
-
@target.destroy
|
41
|
+
@target.destroy unless @target.new_record?
|
42
42
|
@owner.clear_association_cache
|
43
43
|
when :nullify
|
44
44
|
@target[@reflection.primary_key_name] = nil
|
45
|
-
@target.save
|
45
|
+
@target.save unless @owner.new_record? || @target.new_record?
|
46
46
|
end
|
47
47
|
else
|
48
48
|
@target[@reflection.primary_key_name] = nil
|
49
|
-
@target.save
|
49
|
+
@target.save unless @owner.new_record? || @target.new_record?
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -61,7 +61,7 @@ module ActiveRecord
|
|
61
61
|
set_inverse_instance(obj, @owner)
|
62
62
|
@loaded = true
|
63
63
|
|
64
|
-
unless
|
64
|
+
unless @owner.new_record? or obj.nil? or dont_save
|
65
65
|
return (obj.save ? self : false)
|
66
66
|
else
|
67
67
|
return (obj.nil? ? nil : self)
|
@@ -120,7 +120,7 @@ module ActiveRecord
|
|
120
120
|
if replace_existing
|
121
121
|
replace(record, true)
|
122
122
|
else
|
123
|
-
record[@reflection.primary_key_name] = @owner.id
|
123
|
+
record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
|
124
124
|
self.target = record
|
125
125
|
set_inverse_instance(record, @owner)
|
126
126
|
end
|
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
21
21
|
if current_object
|
22
22
|
new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
|
23
23
|
elsif new_value
|
24
|
-
|
24
|
+
if @owner.new_record?
|
25
25
|
self.target = new_value
|
26
26
|
through_association = @owner.send(:association_instance_get, @reflection.through_reflection.name)
|
27
27
|
through_association.build(construct_join_attributes(new_value))
|
@@ -3,11 +3,10 @@ module ActiveRecord
|
|
3
3
|
module PrimaryKey
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
# Returns this record's primary key value wrapped in an Array
|
7
|
-
# the record is
|
6
|
+
# Returns this record's primary key value wrapped in an Array
|
7
|
+
# or nil if the record is a new_record?
|
8
8
|
def to_key
|
9
|
-
|
10
|
-
[key] if key
|
9
|
+
new_record? ? nil : [ id ]
|
11
10
|
end
|
12
11
|
|
13
12
|
module ClassMethods
|
@@ -75,7 +75,8 @@ module ActiveRecord
|
|
75
75
|
def read_attribute(attr_name)
|
76
76
|
attr_name = attr_name.to_s
|
77
77
|
attr_name = self.class.primary_key if attr_name == 'id'
|
78
|
-
|
78
|
+
value = @attributes[attr_name]
|
79
|
+
unless value.nil?
|
79
80
|
if column = column_for_attribute(attr_name)
|
80
81
|
if unserializable_attribute?(attr_name, column)
|
81
82
|
unserialize_attribute(attr_name)
|
@@ -89,7 +89,7 @@ module ActiveRecord
|
|
89
89
|
# post = Post.create(:title => 'ruby rocks')
|
90
90
|
# post.comments.create(:body => 'hello world')
|
91
91
|
# post.comments[0].body = 'hi everyone'
|
92
|
-
# post.save # => saves both post and comment, with 'hi everyone' as
|
92
|
+
# post.save # => saves both post and comment, with 'hi everyone' as body
|
93
93
|
#
|
94
94
|
# Destroying one of the associated models as part of the parent's save action
|
95
95
|
# is as simple as marking it for destruction:
|
@@ -208,7 +208,7 @@ module ActiveRecord
|
|
208
208
|
# Returns whether or not this record has been changed in any way (including whether
|
209
209
|
# any of its nested autosave associations are likewise changed)
|
210
210
|
def changed_for_autosave?
|
211
|
-
|
211
|
+
new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
|
212
212
|
end
|
213
213
|
|
214
214
|
private
|
@@ -222,7 +222,7 @@ module ActiveRecord
|
|
222
222
|
elsif autosave
|
223
223
|
association.target.find_all { |record| record.changed_for_autosave? }
|
224
224
|
else
|
225
|
-
association.target.find_all { |record|
|
225
|
+
association.target.find_all { |record| record.new_record? }
|
226
226
|
end
|
227
227
|
end
|
228
228
|
|
@@ -248,7 +248,7 @@ module ActiveRecord
|
|
248
248
|
# +reflection+.
|
249
249
|
def validate_collection_association(reflection)
|
250
250
|
if association = association_instance_get(reflection.name)
|
251
|
-
if records = associated_records_to_validate_or_save(association,
|
251
|
+
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
|
252
252
|
records.each { |record| association_valid?(reflection, record) }
|
253
253
|
end
|
254
254
|
end
|
@@ -277,7 +277,7 @@ module ActiveRecord
|
|
277
277
|
# Is used as a before_save callback to check while saving a collection
|
278
278
|
# association whether or not the parent was a new record before saving.
|
279
279
|
def before_save_collection_association
|
280
|
-
@new_record_before_save =
|
280
|
+
@new_record_before_save = new_record?
|
281
281
|
true
|
282
282
|
end
|
283
283
|
|
@@ -299,7 +299,7 @@ module ActiveRecord
|
|
299
299
|
|
300
300
|
if autosave && record.marked_for_destruction?
|
301
301
|
association.destroy(record)
|
302
|
-
elsif autosave != false && (@new_record_before_save ||
|
302
|
+
elsif autosave != false && (@new_record_before_save || record.new_record?)
|
303
303
|
if autosave
|
304
304
|
saved = association.send(:insert_record, record, false, false)
|
305
305
|
else
|
@@ -334,7 +334,7 @@ module ActiveRecord
|
|
334
334
|
association.destroy
|
335
335
|
else
|
336
336
|
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
|
337
|
-
if autosave != false && (
|
337
|
+
if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
|
338
338
|
association[reflection.primary_key_name] = key
|
339
339
|
saved = association.save(:validate => !autosave)
|
340
340
|
raise ActiveRecord::Rollback if !saved && autosave
|
@@ -354,7 +354,7 @@ module ActiveRecord
|
|
354
354
|
if autosave && association.marked_for_destruction?
|
355
355
|
association.destroy
|
356
356
|
elsif autosave != false
|
357
|
-
saved = association.save(:validate => !autosave) if
|
357
|
+
saved = association.save(:validate => !autosave) if association.new_record? || autosave
|
358
358
|
|
359
359
|
if association.updated?
|
360
360
|
association_id = association.send(reflection.options[:primary_key] || :id)
|
data/lib/active_record/base.rb
CHANGED
@@ -205,7 +205,7 @@ module ActiveRecord #:nodoc:
|
|
205
205
|
#
|
206
206
|
# # No 'Winter' tag exists
|
207
207
|
# winter = Tag.find_or_initialize_by_name("Winter")
|
208
|
-
# winter.
|
208
|
+
# winter.new_record? # true
|
209
209
|
#
|
210
210
|
# To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
|
211
211
|
# a list of parameters.
|
@@ -1393,7 +1393,7 @@ MSG
|
|
1393
1393
|
def initialize(attributes = nil)
|
1394
1394
|
@attributes = attributes_from_column_definition
|
1395
1395
|
@attributes_cache = {}
|
1396
|
-
@
|
1396
|
+
@new_record = true
|
1397
1397
|
@readonly = false
|
1398
1398
|
@destroyed = false
|
1399
1399
|
@marked_for_destruction = false
|
@@ -1428,7 +1428,7 @@ MSG
|
|
1428
1428
|
clear_aggregation_cache
|
1429
1429
|
clear_association_cache
|
1430
1430
|
@attributes_cache = {}
|
1431
|
-
@
|
1431
|
+
@new_record = true
|
1432
1432
|
ensure_proper_type
|
1433
1433
|
|
1434
1434
|
populate_with_current_scope_attributes
|
@@ -1447,8 +1447,7 @@ MSG
|
|
1447
1447
|
def init_with(coder)
|
1448
1448
|
@attributes = coder['attributes']
|
1449
1449
|
@attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
|
1450
|
-
@readonly = @destroyed = @marked_for_destruction = false
|
1451
|
-
@persisted = true
|
1450
|
+
@new_record = @readonly = @destroyed = @marked_for_destruction = false
|
1452
1451
|
_run_find_callbacks
|
1453
1452
|
_run_initialize_callbacks
|
1454
1453
|
end
|
@@ -1489,7 +1488,7 @@ MSG
|
|
1489
1488
|
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
|
1490
1489
|
def cache_key
|
1491
1490
|
case
|
1492
|
-
when
|
1491
|
+
when new_record?
|
1493
1492
|
"#{self.class.model_name.cache_key}/new"
|
1494
1493
|
when timestamp = self[:updated_at]
|
1495
1494
|
"#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}"
|
@@ -1618,9 +1617,8 @@ MSG
|
|
1618
1617
|
# models are still comparable.
|
1619
1618
|
def ==(comparison_object)
|
1620
1619
|
comparison_object.equal?(self) ||
|
1621
|
-
comparison_object.instance_of?(self.class) &&
|
1622
|
-
|
1623
|
-
comparison_object.id == id
|
1620
|
+
(comparison_object.instance_of?(self.class) &&
|
1621
|
+
comparison_object.id == id && !comparison_object.new_record?)
|
1624
1622
|
end
|
1625
1623
|
|
1626
1624
|
# Delegates to ==
|
@@ -1665,7 +1663,7 @@ MSG
|
|
1665
1663
|
# Returns the contents of the record as a nicely formatted string.
|
1666
1664
|
def inspect
|
1667
1665
|
attributes_as_nice_string = self.class.column_names.collect { |name|
|
1668
|
-
if has_attribute?(name) ||
|
1666
|
+
if has_attribute?(name) || new_record?
|
1669
1667
|
"#{name}: #{attribute_for_inspect(name)}"
|
1670
1668
|
end
|
1671
1669
|
}.compact.join(", ")
|
@@ -1717,7 +1715,7 @@ MSG
|
|
1717
1715
|
if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
|
1718
1716
|
value = read_attribute(name)
|
1719
1717
|
|
1720
|
-
if value && self.class.serialized_attributes.key?(name)
|
1718
|
+
if !value.nil? && self.class.serialized_attributes.key?(name)
|
1721
1719
|
value = YAML.dump value
|
1722
1720
|
end
|
1723
1721
|
attrs[self.class.arel_table[name]] = value
|
@@ -26,11 +26,12 @@ module ActiveRecord
|
|
26
26
|
when Float, Fixnum, Bignum then value.to_s
|
27
27
|
# BigDecimals need to be output in a non-normalized form and quoted.
|
28
28
|
when BigDecimal then value.to_s('F')
|
29
|
+
when Symbol then "'#{quote_string(value.to_s)}'"
|
29
30
|
else
|
30
31
|
if value.acts_like?(:date) || value.acts_like?(:time)
|
31
32
|
"'#{quoted_date(value)}'"
|
32
33
|
else
|
33
|
-
"'#{quote_string(value.
|
34
|
+
"'#{quote_string(value.to_yaml)}'"
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
@@ -90,6 +90,11 @@ module ActiveRecord
|
|
90
90
|
false
|
91
91
|
end
|
92
92
|
|
93
|
+
# Does this adapter restrict the number of ids you can use in a list. Oracle has a limit of 1000.
|
94
|
+
def ids_in_list_limit
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
93
98
|
# QUOTING ==================================================
|
94
99
|
|
95
100
|
# Override to return the quoted table name. Defaults to column quoting.
|
@@ -378,10 +378,7 @@ module ActiveRecord
|
|
378
378
|
# REFERENTIAL INTEGRITY ====================================
|
379
379
|
|
380
380
|
def supports_disable_referential_integrity?() #:nodoc:
|
381
|
-
|
382
|
-
version[0].to_i >= 8 && version[1].to_i >= 1
|
383
|
-
rescue
|
384
|
-
return false
|
381
|
+
postgresql_version >= 80100
|
385
382
|
end
|
386
383
|
|
387
384
|
def disable_referential_integrity #:nodoc:
|
@@ -897,8 +894,12 @@ module ActiveRecord
|
|
897
894
|
else
|
898
895
|
# Mimic PGconn.server_version behavior
|
899
896
|
begin
|
900
|
-
query('SELECT version()')[0][0] =~ /PostgreSQL (
|
901
|
-
|
897
|
+
if query('SELECT version()')[0][0] =~ /PostgreSQL ([0-9.]+)/
|
898
|
+
major, minor, tiny = $1.split(".")
|
899
|
+
(major.to_i * 10000) + (minor.to_i * 100) + tiny.to_i
|
900
|
+
else
|
901
|
+
0
|
902
|
+
end
|
902
903
|
rescue
|
903
904
|
0
|
904
905
|
end
|
@@ -385,11 +385,8 @@ module ActiveRecord
|
|
385
385
|
# Updates a record with the +attributes+ or marks it for destruction if
|
386
386
|
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
|
387
387
|
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
|
388
|
-
|
389
|
-
|
390
|
-
else
|
391
|
-
record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
|
392
|
-
end
|
388
|
+
record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
|
389
|
+
record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
|
393
390
|
end
|
394
391
|
|
395
392
|
# Determines if a hash contains a truthy _destroy key.
|
@@ -89,51 +89,29 @@ module ActiveRecord
|
|
89
89
|
# singletons and that call instantiates and registers them.
|
90
90
|
#
|
91
91
|
class Observer < ActiveModel::Observer
|
92
|
-
class_attribute :observed_methods
|
93
|
-
self.observed_methods = [].freeze
|
94
92
|
|
95
93
|
def initialize
|
96
94
|
super
|
97
95
|
observed_descendants.each { |klass| add_observer!(klass) }
|
98
96
|
end
|
99
97
|
|
100
|
-
def self.method_added(method)
|
101
|
-
method = method.to_sym
|
102
|
-
|
103
|
-
if ActiveRecord::Callbacks::CALLBACKS.include?(method)
|
104
|
-
self.observed_methods += [method]
|
105
|
-
self.observed_methods.freeze
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
98
|
protected
|
110
99
|
|
111
100
|
def observed_descendants
|
112
101
|
observed_classes.sum([]) { |klass| klass.descendants }
|
113
102
|
end
|
114
103
|
|
115
|
-
def observe_callbacks?
|
116
|
-
self.class.observed_methods.any?
|
117
|
-
end
|
118
|
-
|
119
104
|
def add_observer!(klass)
|
120
105
|
super
|
121
|
-
define_callbacks klass
|
106
|
+
define_callbacks klass
|
122
107
|
end
|
123
108
|
|
124
109
|
def define_callbacks(klass)
|
125
|
-
existing_methods = klass.instance_methods.map { |m| m.to_sym }
|
126
110
|
observer = self
|
127
|
-
observer_name = observer.class.name.underscore.gsub('/', '__')
|
128
111
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
klass.send(:define_method, callback) do # def _notify_user_observer_for_before_save
|
133
|
-
observer.update(method, self) # observer.update(:before_save, self)
|
134
|
-
end # end
|
135
|
-
klass.send(method, callback) # before_save :_notify_user_observer_for_before_save
|
136
|
-
end
|
112
|
+
ActiveRecord::Callbacks::CALLBACKS.each do |callback|
|
113
|
+
next unless respond_to?(callback)
|
114
|
+
klass.send(callback){|record| observer.send(callback, record)}
|
137
115
|
end
|
138
116
|
end
|
139
117
|
end
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
# Returns true if this object hasn't been saved yet -- that is, a record
|
5
5
|
# for the object doesn't exist in the data store yet; otherwise, returns false.
|
6
6
|
def new_record?
|
7
|
-
|
7
|
+
@new_record
|
8
8
|
end
|
9
9
|
|
10
10
|
# Returns true if this object has been destroyed, otherwise returns false.
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
# Returns if the record is persisted, i.e. it's not a new record and it was
|
16
16
|
# not destroyed.
|
17
17
|
def persisted?
|
18
|
-
|
18
|
+
!(new_record? || destroyed?)
|
19
19
|
end
|
20
20
|
|
21
21
|
# :call-seq:
|
@@ -97,7 +97,7 @@ module ActiveRecord
|
|
97
97
|
became = klass.new
|
98
98
|
became.instance_variable_set("@attributes", @attributes)
|
99
99
|
became.instance_variable_set("@attributes_cache", @attributes_cache)
|
100
|
-
became.instance_variable_set("@
|
100
|
+
became.instance_variable_set("@new_record", new_record?)
|
101
101
|
became.instance_variable_set("@destroyed", destroyed?)
|
102
102
|
became
|
103
103
|
end
|
@@ -243,7 +243,7 @@ module ActiveRecord
|
|
243
243
|
private
|
244
244
|
def create_or_update
|
245
245
|
raise ReadOnlyRecord if readonly?
|
246
|
-
result =
|
246
|
+
result = new_record? ? create : update
|
247
247
|
result != false
|
248
248
|
end
|
249
249
|
|
@@ -272,7 +272,7 @@ module ActiveRecord
|
|
272
272
|
|
273
273
|
self.id ||= new_id
|
274
274
|
|
275
|
-
@
|
275
|
+
@new_record = false
|
276
276
|
id
|
277
277
|
end
|
278
278
|
|
@@ -207,7 +207,15 @@ module ActiveRecord
|
|
207
207
|
end
|
208
208
|
|
209
209
|
def association_foreign_key
|
210
|
-
@association_foreign_key ||=
|
210
|
+
@association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
|
211
|
+
end
|
212
|
+
|
213
|
+
def association_primary_key
|
214
|
+
@association_primary_key ||= options[:primary_key] || klass.primary_key
|
215
|
+
end
|
216
|
+
|
217
|
+
def active_record_primary_key
|
218
|
+
@active_record_primary_key ||= options[:primary_key] || active_record.primary_key
|
211
219
|
end
|
212
220
|
|
213
221
|
def counter_cache_column
|
@@ -376,7 +376,7 @@ module ActiveRecord
|
|
376
376
|
|
377
377
|
def references_eager_loaded_tables?
|
378
378
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
379
|
-
joined_tables = (tables_in_string(arel.
|
379
|
+
joined_tables = (tables_in_string(arel.join_sql) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq
|
380
380
|
(tables_in_string(to_sql) - joined_tables).any?
|
381
381
|
end
|
382
382
|
|
@@ -166,7 +166,7 @@ module ActiveRecord
|
|
166
166
|
if operation == "count"
|
167
167
|
column_name ||= (select_for_count || :all)
|
168
168
|
|
169
|
-
if arel.
|
169
|
+
if arel.join_sql =~ /LEFT OUTER/i
|
170
170
|
distinct = true
|
171
171
|
column_name = @klass.primary_key if column_name == :all
|
172
172
|
end
|
@@ -208,14 +208,19 @@ module ActiveRecord
|
|
208
208
|
end
|
209
209
|
|
210
210
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
211
|
-
group_attr = @group_values
|
212
|
-
association = @klass.reflect_on_association(group_attr.to_sym)
|
213
|
-
associated = association && association.macro == :belongs_to # only count belongs_to associations
|
214
|
-
|
215
|
-
|
216
|
-
|
211
|
+
group_attr = @group_values
|
212
|
+
association = @klass.reflect_on_association(group_attr.first.to_sym)
|
213
|
+
associated = group_attr.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
|
214
|
+
group_fields = Array(associated ? association.primary_key_name : group_attr)
|
215
|
+
group_aliases = []
|
216
|
+
group_columns = {}
|
217
|
+
|
218
|
+
group_fields.each do |field|
|
219
|
+
group_aliases << column_alias_for(field)
|
220
|
+
group_columns[column_alias_for(field)] = column_for(field)
|
221
|
+
end
|
217
222
|
|
218
|
-
group = @klass.connection.adapter_name == 'FrontBase' ?
|
223
|
+
group = @klass.connection.adapter_name == 'FrontBase' ? group_aliases : group_fields
|
219
224
|
|
220
225
|
if operation == 'count' && column_name == :all
|
221
226
|
aggregate_alias = 'count_all'
|
@@ -223,22 +228,21 @@ module ActiveRecord
|
|
223
228
|
aggregate_alias = column_alias_for(operation, column_name)
|
224
229
|
end
|
225
230
|
|
226
|
-
relation = except(:group).group(group)
|
227
|
-
relation.select_values = [
|
228
|
-
|
229
|
-
"#{group_field} AS #{group_alias}"
|
230
|
-
]
|
231
|
+
relation = except(:group).group(group.join(','))
|
232
|
+
relation.select_values = [ operation_over_aggregate_column(aggregate_column(column_name), operation, distinct).as(aggregate_alias) ]
|
233
|
+
group_fields.each_index{ |i| relation.select_values << "#{group_fields[i]} AS #{group_aliases[i]}" }
|
231
234
|
|
232
235
|
calculated_data = @klass.connection.select_all(relation.to_sql)
|
233
236
|
|
234
237
|
if association
|
235
|
-
key_ids = calculated_data.collect { |row| row[
|
238
|
+
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
236
239
|
key_records = association.klass.base_class.find(key_ids)
|
237
240
|
key_records = Hash[key_records.map { |r| [r.id, r] }]
|
238
241
|
end
|
239
242
|
|
240
243
|
ActiveSupport::OrderedHash[calculated_data.map do |row|
|
241
|
-
key
|
244
|
+
key = group_aliases.map{|group_alias| type_cast_calculated_value(row[group_alias], group_columns[group_alias])}
|
245
|
+
key = key.first if key.size == 1
|
242
246
|
key = key_records[key] if associated
|
243
247
|
[key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
|
244
248
|
end]
|
@@ -269,15 +273,11 @@ module ActiveRecord
|
|
269
273
|
end
|
270
274
|
|
271
275
|
def type_cast_calculated_value(value, column, operation = nil)
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
else type_cast_using_column(value, column)
|
278
|
-
end
|
279
|
-
else
|
280
|
-
type_cast_using_column(value, column)
|
276
|
+
case operation
|
277
|
+
when 'count' then value.to_i
|
278
|
+
when 'sum' then type_cast_using_column(value || '0', column)
|
279
|
+
when 'average' then value.try(:to_d)
|
280
|
+
else type_cast_using_column(value, column)
|
281
281
|
end
|
282
282
|
end
|
283
283
|
|
@@ -194,7 +194,7 @@ module ActiveRecord
|
|
194
194
|
|
195
195
|
def construct_relation_for_association_calculations
|
196
196
|
including = (@eager_load_values + @includes_values).uniq
|
197
|
-
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.
|
197
|
+
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.join_sql)
|
198
198
|
relation = except(:includes, :eager_load, :preload)
|
199
199
|
apply_join_dependency(relation, join_dependency)
|
200
200
|
end
|
@@ -25,13 +25,13 @@ module ActiveRecord
|
|
25
25
|
case value
|
26
26
|
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation
|
27
27
|
values = value.to_a.map { |x|
|
28
|
-
x.
|
28
|
+
x.is_a?(ActiveRecord::Base) ? x.id : x
|
29
29
|
}
|
30
30
|
attribute.in(values)
|
31
31
|
when Range, Arel::Relation
|
32
32
|
attribute.in(value)
|
33
33
|
when ActiveRecord::Base
|
34
|
-
attribute.eq(value.
|
34
|
+
attribute.eq(value.id)
|
35
35
|
when Class
|
36
36
|
# FIXME: I think we need to deprecate this behavior
|
37
37
|
attribute.eq(value.name)
|
@@ -143,7 +143,7 @@ module ActiveRecord
|
|
143
143
|
"#{@klass.table_name}.#{@klass.primary_key} DESC" :
|
144
144
|
reverse_sql_order(order_clause)
|
145
145
|
|
146
|
-
relation.order(Arel
|
146
|
+
relation.order(Arel.sql(order))
|
147
147
|
end
|
148
148
|
|
149
149
|
def arel
|
@@ -168,7 +168,7 @@ module ActiveRecord
|
|
168
168
|
arel.join(join)
|
169
169
|
end
|
170
170
|
|
171
|
-
arel.
|
171
|
+
arel.join_sql
|
172
172
|
end
|
173
173
|
|
174
174
|
def build_arel
|
@@ -176,10 +176,7 @@ module ActiveRecord
|
|
176
176
|
|
177
177
|
arel = build_joins(arel, @joins_values) unless @joins_values.empty?
|
178
178
|
|
179
|
-
(@where_values - ['']).uniq
|
180
|
-
where = Arel.sql(where) if String === where
|
181
|
-
arel = arel.where(Arel::Nodes::Grouping.new(where))
|
182
|
-
end
|
179
|
+
arel = collapse_wheres(arel, (@where_values - ['']).uniq)
|
183
180
|
|
184
181
|
arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
|
185
182
|
|
@@ -200,6 +197,27 @@ module ActiveRecord
|
|
200
197
|
|
201
198
|
private
|
202
199
|
|
200
|
+
def collapse_wheres(arel, wheres)
|
201
|
+
equalities = wheres.grep(Arel::Nodes::Equality)
|
202
|
+
|
203
|
+
groups = equalities.group_by do |equality|
|
204
|
+
equality.left
|
205
|
+
end
|
206
|
+
|
207
|
+
groups.each do |_, eqls|
|
208
|
+
test = eqls.inject(eqls.shift) do |memo, expr|
|
209
|
+
memo.or(expr)
|
210
|
+
end
|
211
|
+
arel = arel.where(test)
|
212
|
+
end
|
213
|
+
|
214
|
+
(wheres - equalities).each do |where|
|
215
|
+
where = Arel.sql(where) if String === where
|
216
|
+
arel = arel.where(Arel::Nodes::Grouping.new(where))
|
217
|
+
end
|
218
|
+
arel
|
219
|
+
end
|
220
|
+
|
203
221
|
def build_where(opts, other = [])
|
204
222
|
case opts
|
205
223
|
when String, Array
|
@@ -228,7 +228,7 @@ module ActiveRecord
|
|
228
228
|
@session_id = attributes[:session_id]
|
229
229
|
@data = attributes[:data]
|
230
230
|
@marshaled_data = attributes[:marshaled_data]
|
231
|
-
@
|
231
|
+
@new_record = @marshaled_data.nil?
|
232
232
|
end
|
233
233
|
|
234
234
|
# Lazy-unmarshal session state.
|
@@ -252,8 +252,8 @@ module ActiveRecord
|
|
252
252
|
marshaled_data = self.class.marshal(data)
|
253
253
|
connect = connection
|
254
254
|
|
255
|
-
|
256
|
-
@
|
255
|
+
if @new_record
|
256
|
+
@new_record = false
|
257
257
|
connect.update <<-end_sql, 'Create session'
|
258
258
|
INSERT INTO #{table_name} (
|
259
259
|
#{connect.quote_column_name(session_id_column)},
|
@@ -272,7 +272,7 @@ module ActiveRecord
|
|
272
272
|
end
|
273
273
|
|
274
274
|
def destroy
|
275
|
-
return
|
275
|
+
return if @new_record
|
276
276
|
|
277
277
|
connect = connection
|
278
278
|
connect.delete <<-end_sql, 'Destroy session'
|
@@ -65,7 +65,7 @@ module ActiveRecord
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def timestamp_attributes_for_update_in_model
|
68
|
-
timestamp_attributes_for_update.select { |c|
|
68
|
+
timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c.to_s) }
|
69
69
|
end
|
70
70
|
|
71
71
|
def timestamp_attributes_for_update #:nodoc:
|
@@ -130,7 +130,7 @@ module ActiveRecord
|
|
130
130
|
#
|
131
131
|
# +transaction+ calls can be nested. By default, this makes all database
|
132
132
|
# statements in the nested transaction block become part of the parent
|
133
|
-
# transaction. For example:
|
133
|
+
# transaction. For example, the following behavior may be surprising:
|
134
134
|
#
|
135
135
|
# User.transaction do
|
136
136
|
# User.create(:username => 'Kotori')
|
@@ -140,12 +140,15 @@ module ActiveRecord
|
|
140
140
|
# end
|
141
141
|
# end
|
142
142
|
#
|
143
|
-
#
|
143
|
+
# creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
|
144
|
+
# exception in the nested block does not issue a ROLLBACK. Since these exceptions
|
145
|
+
# are captured in transaction blocks, the parent block does not see it and the
|
146
|
+
# real transaction is committed.
|
144
147
|
#
|
145
|
-
#
|
146
|
-
# <tt>:requires_new => true</tt>. If anything goes wrong,
|
147
|
-
# database rolls back to the beginning of the sub-transaction
|
148
|
-
#
|
148
|
+
# In order to get a ROLLBACK for the nested transaction you may ask for a real
|
149
|
+
# sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong,
|
150
|
+
# the database rolls back to the beginning of the sub-transaction without rolling
|
151
|
+
# back the parent transaction. If we add it to the previous example:
|
149
152
|
#
|
150
153
|
# User.transaction do
|
151
154
|
# User.create(:username => 'Kotori')
|
@@ -155,12 +158,12 @@ module ActiveRecord
|
|
155
158
|
# end
|
156
159
|
# end
|
157
160
|
#
|
158
|
-
#
|
161
|
+
# only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.)
|
159
162
|
#
|
160
163
|
# Most databases don't support true nested transactions. At the time of
|
161
164
|
# writing, the only database that we're aware of that supports true nested
|
162
165
|
# transactions, is MS-SQL. Because of this, Active Record emulates nested
|
163
|
-
# transactions by using savepoints. See
|
166
|
+
# transactions by using savepoints on MySQL and PostgreSQL. See
|
164
167
|
# http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
|
165
168
|
# for more information about savepoints.
|
166
169
|
#
|
@@ -242,7 +245,7 @@ module ActiveRecord
|
|
242
245
|
with_transaction_returning_status { super }
|
243
246
|
end
|
244
247
|
|
245
|
-
# Reset id and @
|
248
|
+
# Reset id and @new_record if the transaction rolls back.
|
246
249
|
def rollback_active_record_state!
|
247
250
|
remember_transaction_record_state
|
248
251
|
yield
|
@@ -297,9 +300,9 @@ module ActiveRecord
|
|
297
300
|
# Save the new record state and id of a record so it can be restored later if a transaction fails.
|
298
301
|
def remember_transaction_record_state #:nodoc
|
299
302
|
@_start_transaction_state ||= {}
|
300
|
-
unless @_start_transaction_state.include?(:
|
303
|
+
unless @_start_transaction_state.include?(:new_record)
|
301
304
|
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
|
302
|
-
@_start_transaction_state[:
|
305
|
+
@_start_transaction_state[:new_record] = @new_record
|
303
306
|
end
|
304
307
|
unless @_start_transaction_state.include?(:destroyed)
|
305
308
|
@_start_transaction_state[:destroyed] = @destroyed
|
@@ -323,7 +326,7 @@ module ActiveRecord
|
|
323
326
|
restore_state = remove_instance_variable(:@_start_transaction_state)
|
324
327
|
if restore_state
|
325
328
|
@attributes = @attributes.dup if @attributes.frozen?
|
326
|
-
@
|
329
|
+
@new_record = restore_state[:new_record]
|
327
330
|
@destroyed = restore_state[:destroyed]
|
328
331
|
if restore_state[:id]
|
329
332
|
self.id = restore_state[:id]
|
@@ -345,11 +348,11 @@ module ActiveRecord
|
|
345
348
|
def transaction_include_action?(action) #:nodoc
|
346
349
|
case action
|
347
350
|
when :create
|
348
|
-
transaction_record_state(:new_record)
|
351
|
+
transaction_record_state(:new_record)
|
349
352
|
when :destroy
|
350
353
|
destroyed?
|
351
354
|
when :update
|
352
|
-
!(transaction_record_state(:new_record) ||
|
355
|
+
!(transaction_record_state(:new_record) || destroyed?)
|
353
356
|
end
|
354
357
|
end
|
355
358
|
end
|
@@ -51,7 +51,7 @@ module ActiveRecord
|
|
51
51
|
|
52
52
|
# Runs all the specified validations and returns true if no errors were added otherwise false.
|
53
53
|
def valid?(context = nil)
|
54
|
-
context ||= (
|
54
|
+
context ||= (new_record? ? :create : :update)
|
55
55
|
output = super(context)
|
56
56
|
|
57
57
|
deprecated_callback_method(:validate)
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
relation = relation.where(scope_item => scope_value)
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
unless record.new_record?
|
35
35
|
# TODO : This should be in Arel
|
36
36
|
relation = relation.where("#{record.class.quoted_table_name}.#{record.class.primary_key} <> ?", record.send(:id))
|
37
37
|
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 977940590
|
5
|
+
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
|
9
|
+
- 4
|
10
|
+
- rc1
|
11
|
+
version: 3.0.4.rc1
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- David Heinemeier Hansson
|
@@ -15,7 +16,7 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date:
|
19
|
+
date: 2011-01-31 00:00:00 +13:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
@@ -26,12 +27,13 @@ dependencies:
|
|
26
27
|
requirements:
|
27
28
|
- - "="
|
28
29
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
30
|
+
hash: 977940590
|
30
31
|
segments:
|
31
32
|
- 3
|
32
33
|
- 0
|
33
|
-
-
|
34
|
-
|
34
|
+
- 4
|
35
|
+
- rc1
|
36
|
+
version: 3.0.4.rc1
|
35
37
|
type: :runtime
|
36
38
|
version_requirements: *id001
|
37
39
|
- !ruby/object:Gem::Dependency
|
@@ -42,12 +44,13 @@ dependencies:
|
|
42
44
|
requirements:
|
43
45
|
- - "="
|
44
46
|
- !ruby/object:Gem::Version
|
45
|
-
hash:
|
47
|
+
hash: 977940590
|
46
48
|
segments:
|
47
49
|
- 3
|
48
50
|
- 0
|
49
|
-
-
|
50
|
-
|
51
|
+
- 4
|
52
|
+
- rc1
|
53
|
+
version: 3.0.4.rc1
|
51
54
|
type: :runtime
|
52
55
|
version_requirements: *id002
|
53
56
|
- !ruby/object:Gem::Dependency
|
@@ -208,12 +211,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
208
211
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
209
212
|
none: false
|
210
213
|
requirements:
|
211
|
-
- - "
|
214
|
+
- - ">"
|
212
215
|
- !ruby/object:Gem::Version
|
213
|
-
hash:
|
216
|
+
hash: 25
|
214
217
|
segments:
|
215
|
-
-
|
216
|
-
|
218
|
+
- 1
|
219
|
+
- 3
|
220
|
+
- 1
|
221
|
+
version: 1.3.1
|
217
222
|
requirements: []
|
218
223
|
|
219
224
|
rubyforge_project: activerecord
|