aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2010-12-12 09:55:32 +0000
committerJon Leighton <j@jonathanleighton.com>2010-12-12 09:55:32 +0000
commit9a98c766e045aebc2ef6d5b716936b73407f095d (patch)
tree899834482c828f31a89ebc7bb6e19cbe0b5f18d3 /activerecord/lib
parent3a7f43ca6ecf1735e1a82d4a68ac8f62b5cf2fcf (diff)
parent307443972c5f6de959a5401eec76ca327484b10c (diff)
downloadrails-9a98c766e045aebc2ef6d5b716936b73407f095d.tar.gz
rails-9a98c766e045aebc2ef6d5b716936b73407f095d.tar.bz2
rails-9a98c766e045aebc2ef6d5b716936b73407f095d.zip
Merge branch 'master' into nested_has_many_through
Conflicts: activerecord/CHANGELOG activerecord/lib/active_record/associations/class_methods/join_dependency.rb activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb activerecord/lib/active_record/associations/has_many_through_association.rb
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations.rb41
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb34
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb22
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb2
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency.rb18
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb51
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb10
-rw-r--r--activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb16
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb5
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb4
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb17
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb3
-rw-r--r--activerecord/lib/active_record/autosave_association.rb14
-rw-r--r--activerecord/lib/active_record/base.rb48
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb13
-rw-r--r--activerecord/lib/active_record/counter_cache.rb3
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb6
-rw-r--r--activerecord/lib/active_record/migration.rb69
-rw-r--r--activerecord/lib/active_record/observer.rb40
-rw-r--r--activerecord/lib/active_record/persistence.rb14
-rw-r--r--activerecord/lib/active_record/railties/databases.rake64
-rw-r--r--activerecord/lib/active_record/reflection.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb41
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb91
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb7
-rw-r--r--activerecord/lib/active_record/schema.rb6
-rw-r--r--activerecord/lib/active_record/session_store.rb8
-rw-r--r--activerecord/lib/active_record/transactions.rb34
-rw-r--r--activerecord/lib/active_record/validations.rb2
37 files changed, 387 insertions, 324 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index fe22fa7ca2..9544fdcb39 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1266,12 +1266,14 @@ module ActiveRecord
if reflection.options[:polymorphic]
association_accessor_methods(reflection, BelongsToPolymorphicAssociation)
+ association_foreign_type_setter_method(reflection)
else
association_accessor_methods(reflection, BelongsToAssociation)
association_constructor_method(:build, reflection, BelongsToAssociation)
association_constructor_method(:create, reflection, BelongsToAssociation)
end
+ association_foreign_key_setter_method(reflection)
add_counter_cache_callbacks(reflection) if options[:counter_cache]
add_touch_callbacks(reflection, options[:touch]) if options[:touch]
@@ -1598,6 +1600,45 @@ module ActiveRecord
end
end
+ def association_foreign_key_setter_method(reflection)
+ setters = reflect_on_all_associations(:belongs_to).map do |belongs_to_reflection|
+ if belongs_to_reflection.primary_key_name == reflection.primary_key_name
+ "association_instance_set(:#{belongs_to_reflection.name}, nil);"
+ end
+ end.compact.join
+
+ if method_defined?(:"#{reflection.primary_key_name}=")
+ undef_method :"#{reflection.primary_key_name}="
+ end
+
+ class_eval <<-FILE, __FILE__, __LINE__ + 1
+ def #{reflection.primary_key_name}=(new_id)
+ write_attribute :#{reflection.primary_key_name}, new_id
+ if #{reflection.primary_key_name}_changed?
+ #{ setters }
+ end
+ end
+ FILE
+ end
+
+ def association_foreign_type_setter_method(reflection)
+ setters = reflect_on_all_associations(:belongs_to).map do |belongs_to_reflection|
+ if belongs_to_reflection.options[:foreign_type] == reflection.options[:foreign_type]
+ "association_instance_set(:#{belongs_to_reflection.name}, nil);"
+ end
+ end.compact.join
+
+ field = reflection.options[:foreign_type]
+ class_eval <<-FILE, __FILE__, __LINE__ + 1
+ def #{field}=(new_id)
+ write_attribute :#{field}, new_id
+ if #{field}_changed?
+ #{ setters }
+ end
+ end
+ FILE
+ end
+
def add_counter_cache_callbacks(reflection)
cache_column = reflection.counter_cache_column
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 64582188b6..6428fcec0a 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -5,11 +5,10 @@ module ActiveRecord
# Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
# ActiveRecord::Associations::ThroughAssociationScope
class AliasTracker # :nodoc:
- # other_sql is some other sql which might conflict with the aliases we assign here. Therefore
- # we store other_sql so that we can scan it before assigning a specific name.
- def initialize(other_sql = nil)
- @aliases = Hash.new
- @other_sql = other_sql.to_s.downcase
+ # table_joins is an array of arel joins which might conflict with the aliases we assign here
+ def initialize(table_joins = nil)
+ @aliases = Hash.new
+ @table_joins = table_joins
end
def aliased_name_for(table_name, aliased_name = nil)
@@ -47,15 +46,24 @@ module ActiveRecord
def initialize_count_for(name)
@aliases[name] = 0
- unless @other_sql.blank?
+ unless @table_joins.nil? || Arel::Table === @table_joins
# quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
- quoted_name = connection.quote_table_name(name.downcase).downcase
-
- # Table names
- @aliases[name] += @other_sql.scan(/join(?:\s+\w+)?\s+#{quoted_name}\son/).size
-
- # Table aliases
- @aliases[name] += @other_sql.scan(/join(?:\s+\w+)?\s+\S+\s+#{quoted_name}\son/).size
+ quoted_name = connection.quote_table_name(name).downcase
+
+ @aliases[name] += @table_joins.grep(Arel::Nodes::Join).map { |join|
+ right = join.right
+ case right
+ when Arel::Table
+ right.name.downcase == name ? 1 : 0
+ when String
+ # Table names + table aliases
+ right.downcase.scan(
+ /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
+ ).size
+ else
+ 0
+ end
+ }.sum
end
@aliases[name]
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index ba9373ba6a..abb17a11c6 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -121,13 +121,13 @@ module ActiveRecord
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
def <<(*records)
result = true
- load_target unless @owner.persisted?
+ load_target if @owner.new_record?
transaction do
flatten_deeper(records).each do |record|
raise_on_type_mismatch(record)
add_record_to_target_with_callbacks(record) do |r|
- result &&= insert_record(record) if @owner.persisted?
+ result &&= insert_record(record) unless @owner.new_record?
end
end
end
@@ -286,12 +286,12 @@ module ActiveRecord
# This method is abstract in the sense that it relies on
# +count_records+, which is a method descendants have to provide.
def size
- if !@owner.persisted? || (loaded? && !@reflection.options[:uniq])
+ if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
@target.size
elsif !loaded? && @reflection.options[:group]
load_target.size
elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
- unsaved_records = @target.reject { |r| r.persisted? }
+ unsaved_records = @target.select { |r| r.new_record? }
unsaved_records.size + count_records
else
count_records
@@ -355,7 +355,7 @@ module ActiveRecord
def include?(record)
return false unless record.is_a?(@reflection.klass)
- return include_in_memory?(record) unless record.persisted?
+ return include_in_memory?(record) if record.new_record?
load_target if @reflection.options[:finder_sql] && !loaded?
loaded? ? @target.include?(record) : exists?(record)
end
@@ -369,7 +369,7 @@ module ActiveRecord
end
def load_target
- if @owner.persisted? || foreign_key_present
+ if !@owner.new_record? || foreign_key_present
begin
unless loaded?
if @target.is_a?(Array) && @target.any?
@@ -478,7 +478,7 @@ module ActiveRecord
private
def create_record(attrs)
attrs.update(@reflection.options[:conditions]) if @reflection.options[:conditions].is_a?(Hash)
- ensure_owner_is_not_new
+ ensure_owner_is_persisted!
scoped_where = scoped.where_values_hash
create_scope = scoped_where ? @scope[:create].merge(scoped_where) : @scope[:create]
@@ -508,7 +508,7 @@ module ActiveRecord
transaction do
records.each { |record| callback(:before_remove, record) }
- old_records = records.select { |r| r.persisted? }
+ old_records = records.reject { |r| r.new_record? }
yield(records, old_records)
records.each { |record| callback(:after_remove, record) }
end
@@ -532,15 +532,15 @@ module ActiveRecord
@owner.class.send(full_callback_name.to_sym) || []
end
- def ensure_owner_is_not_new
+ def ensure_owner_is_persisted!
unless @owner.persisted?
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
end
end
def fetch_first_or_last_using_find?(args)
- (args.first.kind_of?(Hash) && !args.first.empty?) || !(loaded? || !@owner.persisted? || @reflection.options[:finder_sql] ||
- !@target.all? { |record| record.persisted? } || args.first.kind_of?(Integer))
+ (args.first.kind_of?(Hash) && !args.first.empty?) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
+ @target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
end
def include_in_memory?(record)
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 7cd04a1ad5..252ff7e7ea 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -252,7 +252,7 @@ module ActiveRecord
def load_target
return nil unless defined?(@loaded)
- if !loaded? and (@owner.persisted? || foreign_key_present)
+ if !loaded? && (!@owner.new_record? || foreign_key_present)
@target = find_target
end
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
index ce20420aad..78b634a26c 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency.rb
@@ -6,13 +6,15 @@ module ActiveRecord
module Associations
module ClassMethods
class JoinDependency # :nodoc:
- attr_reader :join_parts, :reflections, :alias_tracker
+ attr_reader :join_parts, :reflections, :alias_tracker, :active_record
def initialize(base, associations, joins)
- @join_parts = [JoinBase.new(base, joins)]
- @associations = {}
- @reflections = []
- @alias_tracker = AliasTracker.new(joins)
+ @active_record = base
+ @table_joins = joins
+ @join_parts = [JoinBase.new(base)]
+ @associations = {}
+ @reflections = []
+ @alias_tracker = AliasTracker.new(joins)
@alias_tracker.aliased_name_for(base.table_name) # Updates the count for base.table_name to 1
build(associations)
end
@@ -49,11 +51,11 @@ module ActiveRecord
records = rows.map { |model|
primary_id = model[primary_key]
parent = parents[primary_id] ||= join_base.instantiate(model)
- construct(parent, @associations, join_associations.dup, model)
+ construct(parent, @associations, join_associations, model)
parent
}.uniq
- remove_duplicate_results!(join_base.active_record, records, @associations)
+ remove_duplicate_results!(active_record, records, @associations)
records
end
@@ -122,7 +124,7 @@ module ActiveRecord
build(association, parent, join_type)
end
when Hash
- associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
+ associations.keys.sort_by { |a| a.to_s }.each do |name|
join_association = build(name, parent, join_type)
build(associations[name], join_association, join_type)
end
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
index ed2053d3df..5cc96a7aef 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_association.rb
@@ -21,7 +21,7 @@ module ActiveRecord
attr_reader :aliased_prefix
delegate :options, :through_reflection, :source_reflection, :through_reflection_chain, :to => :reflection
- delegate :table, :table_name, :to => :parent, :prefix => true
+ delegate :table, :table_name, :to => :parent, :prefix => :parent
delegate :alias_tracker, :to => :join_dependency
def initialize(reflection, join_dependency, parent = nil)
@@ -50,7 +50,7 @@ module ActiveRecord
def find_parent_in(other_join_dependency)
other_join_dependency.join_parts.detect do |join_part|
- self.parent == join_part
+ parent == join_part
end
end
@@ -129,7 +129,15 @@ module ActiveRecord
conditions << reflection_conditions(index, table)
conditions << sti_conditions(reflection, table)
- relation = relation.join(table, join_type).on(*conditions.flatten.compact)
+ ands = relation.create_and(conditions.flatten.compact)
+
+ join = relation.create_join(
+ relation.froms.first,
+ table,
+ relation.create_on(ands),
+ join_type)
+
+ relation = relation.from(join)
# The current table in this iteration becomes the foreign table in the next
foreign_table = table
@@ -165,10 +173,6 @@ module ActiveRecord
name
end
- def interpolate_sql(sql)
- instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
- end
-
private
# Generate aliases and Arel::Table instances for each of the tables which we will
@@ -181,10 +185,7 @@ module ActiveRecord
table_alias_for(reflection, reflection != self.reflection)
)
- table = Arel::Table.new(
- reflection.table_name, :engine => arel_engine,
- :as => aliased_table_name, :columns => reflection.klass.columns
- )
+ table = Arel::Table.new(reflection.table_name, :as => aliased_table_name)
# For habtm, we have two Arel::Table instances related to a single reflection, so
# we just store them as a pair in the array.
@@ -199,10 +200,7 @@ module ActiveRecord
table_alias_for(reflection, true)
)
- join_table = Arel::Table.new(
- join_table_name, :engine => arel_engine,
- :as => aliased_join_table_name
- )
+ join_table = Arel::Table.new(join_table_name, :as => aliased_join_table_name)
[table, join_table]
else
@@ -220,24 +218,23 @@ module ActiveRecord
def reflection_conditions(index, table)
@reflection.through_conditions.reverse[index].map do |condition|
- Arel.sql(interpolate_sql(sanitize_sql(
- condition,
- table.table_alias || table.name
- )))
+ Arel.sql(sanitize_sql(condition, table.table_alias || table.name))
end
end
+ def sanitize_sql(condition, table_name)
+ active_record.send(:sanitize_sql, condition, table_name)
+ end
+
def sti_conditions(reflection, table)
unless reflection.klass.descends_from_active_record?
- sti_column = table[reflection.klass.inheritance_column]
-
- condition = sti_column.eq(reflection.klass.sti_name)
-
- reflection.klass.descendants.each do |subclass|
- condition = condition.or(sti_column.eq(subclass.sti_name))
- end
+ sti_column = table[reflection.klass.inheritance_column]
+ sti_condition = sti_column.eq(reflection.klass.sti_name)
+ subclasses = reflection.klass.descendants
- condition
+ subclasses.inject(sti_condition) { |attr,subclass|
+ attr.or(sti_column.eq(subclass.sti_name))
+ }
end
end
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb
index ed05003f66..67567f06df 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_base.rb
@@ -3,14 +3,6 @@ module ActiveRecord
module ClassMethods
class JoinDependency # :nodoc:
class JoinBase < JoinPart # :nodoc:
- # Extra joins provided when the JoinDependency was created
- attr_reader :table_joins
-
- def initialize(active_record, joins = nil)
- super(active_record)
- @table_joins = joins
- end
-
def ==(other)
other.class == self.class &&
other.active_record == active_record
@@ -21,7 +13,7 @@ module ActiveRecord
end
def table
- Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns)
+ Arel::Table.new(table_name, arel_engine)
end
def aliased_table_name
diff --git a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb
index 0b093b65e9..cd16ae5a8b 100644
--- a/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb
+++ b/activerecord/lib/active_record/associations/class_methods/join_dependency/join_part.rb
@@ -14,7 +14,7 @@ module ActiveRecord
# association.
attr_reader :active_record
- delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record
+ delegate :table_name, :column_names, :primary_key, :reflections, :arel_engine, :to => :active_record
def initialize(active_record)
@active_record = active_record
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index 2c72fd0004..e2ce9aefcf 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -34,7 +34,7 @@ module ActiveRecord
end
def insert_record(record, force = true, validate = true)
- unless record.persisted?
+ if record.new_record?
if force
record.save!
else
@@ -49,7 +49,7 @@ module ActiveRecord
timestamps = record_timestamp_columns(record)
timezone = record.send(:current_time_from_proper_timezone) if timestamps.any?
- attributes = Hash[columns.map do |column|
+ attributes = columns.map do |column|
name = column.name
value = case name.to_s
when @reflection.primary_key_name.to_s
@@ -62,9 +62,10 @@ module ActiveRecord
@owner.send(:quote_value, record[name], column) if record.has_attribute?(name)
end
[relation[name], value] unless value.nil?
- end]
+ end
- relation.insert(attributes)
+ stmt = relation.compile_insert Hash[attributes]
+ @owner.connection.insert stmt.to_sql
end
true
@@ -75,9 +76,10 @@ module ActiveRecord
records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) }
else
relation = Arel::Table.new(@reflection.options[:join_table])
- relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
+ stmt = relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
- ).delete
+ ).compile_delete
+ @owner.connection.delete stmt.to_sql
end
end
@@ -113,7 +115,7 @@ module ActiveRecord
private
def create_record(attributes, &block)
# Can't use Base.create because the foreign key may be a protected attribute.
- ensure_owner_is_not_new
+ ensure_owner_is_persisted!
if attributes.is_a?(Array)
attributes.collect { |attr| create(attr) }
else
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index bbf62796bb..851b10e300 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -67,9 +67,10 @@ module ActiveRecord
@reflection.klass.delete(records.map { |record| record.id })
else
relation = Arel::Table.new(@reflection.table_name)
- relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
+ stmt = relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
and(relation[@reflection.klass.primary_key].in(records.map { |r| r.id }))
- ).update(relation[@reflection.primary_key_name] => nil)
+ ).compile_update(relation[@reflection.primary_key_name] => nil)
+ @owner.connection.update stmt.to_sql
@owner.class.update_counters(@owner.id, cached_counter_attribute_name => -records.size) if has_cached_counter?
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index c1fc16b0ed..0b00132ad9 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -42,7 +42,7 @@ module ActiveRecord
protected
def create_record(attrs, force = true)
ensure_not_nested
- ensure_owner_is_not_new
+ ensure_owner_is_persisted!
transaction do
object = @reflection.klass.new(attrs)
@@ -67,7 +67,7 @@ module ActiveRecord
def insert_record(record, force = true, validate = true)
ensure_not_nested
- unless record.persisted?
+ if record.new_record?
if force
record.save!
else
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index 0ccf07f15e..d581939f04 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -56,7 +56,7 @@ module ActiveRecord
set_inverse_instance(obj, @owner)
@loaded = true
- unless !@owner.persisted? or obj.nil? or dont_save
+ unless !@owner.persisted? || obj.nil? || dont_save
return (obj.save ? self : false)
else
return (obj.nil? ? nil : self)
diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb
index cfd4637e8e..e9dc32efd3 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -23,7 +23,7 @@ module ActiveRecord
if current_object
new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
elsif new_value
- unless @owner.persisted?
+ if @owner.new_record?
self.target = new_value
through_association = @owner.send(:association_instance_get, @reflection.through_reflection.name)
through_association.build(construct_join_attributes(new_value))
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 67f70c434e..4f4a0a5fee 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -54,7 +54,7 @@ module ActiveRecord
protected
def attribute_method?(attr_name)
- attr_name == 'id' || @attributes.include?(attr_name)
+ attr_name == 'id' || (defined?(@attributes) && @attributes.include?(attr_name))
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
index 23195b02f7..bde11d0494 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -13,18 +13,19 @@ module ActiveRecord
# Returns a hash of attributes before typecasting and deserialization.
def attributes_before_type_cast
- Hash[attribute_names.map { |name| [name, read_attribute_before_type_cast(name)] }]
+ @attributes
end
private
- # Handle *_before_type_cast for method_missing.
- def attribute_before_type_cast(attribute_name)
- if attribute_name == 'id'
- read_attribute_before_type_cast(self.class.primary_key)
- else
- read_attribute_before_type_cast(attribute_name)
- end
+
+ # Handle *_before_type_cast for method_missing.
+ def attribute_before_type_cast(attribute_name)
+ if attribute_name == 'id'
+ read_attribute_before_type_cast(self.class.primary_key)
+ else
+ read_attribute_before_type_cast(attribute_name)
end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index ad5a3e7562..506f6e878f 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -85,7 +85,8 @@ module ActiveRecord
def _read_attribute(attr_name)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id'
- if value = @attributes[attr_name]
+ value = @attributes[attr_name]
+ unless value.nil?
if column = column_for_attribute(attr_name)
if unserializable_attribute?(attr_name, column)
unserialize_attribute(attr_name)
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 73ac8e82c6..c3dda29d03 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -217,7 +217,7 @@ module ActiveRecord
# Returns whether or not this record has been changed in any way (including whether
# any of its nested autosave associations are likewise changed)
def changed_for_autosave?
- !persisted? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
+ new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
end
private
@@ -231,7 +231,7 @@ module ActiveRecord
elsif autosave
association.target.find_all { |record| record.changed_for_autosave? }
else
- association.target.find_all { |record| !record.persisted? }
+ association.target.find_all { |record| record.new_record? }
end
end
@@ -257,7 +257,7 @@ module ActiveRecord
# +reflection+.
def validate_collection_association(reflection)
if association = association_instance_get(reflection.name)
- if records = associated_records_to_validate_or_save(association, !persisted?, reflection.options[:autosave])
+ if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
records.each { |record| association_valid?(reflection, record) }
end
end
@@ -286,7 +286,7 @@ module ActiveRecord
# Is used as a before_save callback to check while saving a collection
# association whether or not the parent was a new record before saving.
def before_save_collection_association
- @new_record_before_save = !persisted?
+ @new_record_before_save = new_record?
true
end
@@ -308,7 +308,7 @@ module ActiveRecord
if autosave && record.marked_for_destruction?
association.destroy(record)
- elsif autosave != false && (@new_record_before_save || !record.persisted?)
+ elsif autosave != false && (@new_record_before_save || record.new_record?)
if autosave
saved = association.send(:insert_record, record, false, false)
else
@@ -343,7 +343,7 @@ module ActiveRecord
association.destroy
else
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
- if autosave != false && (!persisted? || !association.persisted? || association[reflection.primary_key_name] != key || autosave)
+ if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
association[reflection.primary_key_name] = key
saved = association.save(:validate => !autosave)
raise ActiveRecord::Rollback if !saved && autosave
@@ -363,7 +363,7 @@ module ActiveRecord
if autosave && association.marked_for_destruction?
association.destroy
elsif autosave != false
- saved = association.save(:validate => !autosave) if !association.persisted? || autosave
+ saved = association.save(:validate => !autosave) if association.new_record? || autosave
if association.updated?
association_id = association.send(reflection.options[:primary_key] || :id)
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 9b09b14c87..7b9ce21ceb 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1365,7 +1365,7 @@ MSG
def initialize(attributes = nil)
@attributes = attributes_from_column_definition
@attributes_cache = {}
- @persisted = false
+ @new_record = true
@readonly = false
@destroyed = false
@marked_for_destruction = false
@@ -1396,7 +1396,7 @@ MSG
@attributes = coder['attributes']
@attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
@readonly = @destroyed = @marked_for_destruction = false
- @persisted = true
+ @new_record = false
_run_find_callbacks
_run_initialize_callbacks
end
@@ -1437,7 +1437,7 @@ MSG
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
def cache_key
case
- when !persisted?
+ when new_record?
"#{self.class.model_name.cache_key}/new"
when timestamp = self[:updated_at]
"#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}"
@@ -1455,22 +1455,9 @@ MSG
@attributes.has_key?(attr_name.to_s)
end
- # Returns an array of names for the attributes available on this object sorted alphabetically.
+ # Returns an array of names for the attributes available on this object.
def attribute_names
- @attributes.keys.sort
- end
-
- # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
- # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
- # (Alias for the protected read_attribute method).
- def [](attr_name)
- read_attribute(attr_name)
- end
-
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
- # (Alias for the protected write_attribute method).
- def []=(attr_name, value)
- write_attribute(attr_name, value)
+ @attributes.keys
end
# Allows you to set all the attributes at once by passing in a hash with keys
@@ -1513,9 +1500,7 @@ MSG
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
def attributes
- attrs = {}
- attribute_names.each { |name| attrs[name] = read_attribute(name) }
- attrs
+ Hash[@attributes.map { |name, _| [name, read_attribute(name)] }]
end
# Returns an <tt>#inspect</tt>-like string for the value of the
@@ -1622,7 +1607,7 @@ MSG
clear_aggregation_cache
clear_association_cache
@attributes_cache = {}
- @persisted = false
+ @new_record = true
ensure_proper_type
populate_with_current_scope_attributes
@@ -1643,7 +1628,7 @@ MSG
# Returns the contents of the record as a nicely formatted string.
def inspect
attributes_as_nice_string = self.class.column_names.collect { |name|
- if has_attribute?(name) || !persisted?
+ if has_attribute?(name) || new_record?
"#{name}: #{attribute_for_inspect(name)}"
end
}.compact.join(", ")
@@ -1695,7 +1680,7 @@ MSG
if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
value = read_attribute(name)
- if value && self.class.serialized_attributes.key?(name)
+ if !value.nil? && self.class.serialized_attributes.key?(name)
value = YAML.dump value
end
attrs[self.class.arel_table[name]] = value
@@ -1830,7 +1815,9 @@ MSG
def populate_with_current_scope_attributes
if scope = self.class.send(:current_scoped_methods)
create_with = scope.scope_for_create
- create_with.each { |att,value| self.respond_to?(:"#{att}=") && self.send("#{att}=", value) } if create_with
+ create_with.each { |att,value|
+ respond_to?(:"#{att}=") && send("#{att}=", value)
+ }
end
end
@@ -1871,6 +1858,17 @@ MSG
include Aggregations, Transactions, Reflection, Serialization
NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
+
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
+ # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
+ # (Alias for the protected read_attribute method).
+ alias [] read_attribute
+
+ # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
+ # (Alias for the protected write_attribute method).
+ alias []= write_attribute
+
+ public :[], :[]=
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index a130c330dd..29ac9341ec 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -37,9 +37,9 @@ module ActiveRecord
16
end
- # the maximum number of elements in an IN (x,y,z) clause
+ # the maximum number of elements in an IN (x,y,z) clause. nil means no limit
def in_clause_length
- 65535
+ nil
end
# the maximum length of an SQL query
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 4e770c37da..5b9c48bafa 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -12,7 +12,7 @@ module ActiveRecord
# Truncates a table alias according to the limits of the current adapter.
def table_alias_for(table_name)
- table_name[0..table_alias_length-1].gsub(/\./, '_')
+ table_name[0...table_alias_length].gsub(/\./, '_')
end
# def tables(name = nil) end
@@ -442,12 +442,14 @@ module ActiveRecord
end
end
- def assume_migrated_upto_version(version, migrations_path = ActiveRecord::Migrator.migrations_path)
+ def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
+ migrations_paths = Array.wrap(migrations_paths)
version = version.to_i
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
- versions = Dir["#{migrations_path}/[0-9]*_*.rb"].map do |filename|
+ paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
+ versions = Dir[*paths].map do |filename|
filename.split('/').last.split('_').first.to_i
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index ccc5085b84..a4b1aa7154 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -404,10 +404,7 @@ module ActiveRecord
# REFERENTIAL INTEGRITY ====================================
def supports_disable_referential_integrity?() #:nodoc:
- version = query("SHOW server_version")[0][0].split('.')
- version[0].to_i >= 8 && version[1].to_i >= 1
- rescue
- return false
+ postgresql_version >= 80100
end
def disable_referential_integrity #:nodoc:
@@ -956,8 +953,12 @@ module ActiveRecord
else
# Mimic PGconn.server_version behavior
begin
- query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
- ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
+ if query('SELECT version()')[0][0] =~ /PostgreSQL ([0-9.]+)/
+ major, minor, tiny = $1.split(".")
+ (major.to_i * 10000) + (minor.to_i * 100) + tiny.to_i
+ else
+ 0
+ end
rescue
0
end
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index ed0d4aef7f..8180bf0987 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -30,9 +30,10 @@ module ActiveRecord
reflection = belongs_to.find { |e| e.class_name == expected_name }
counter_name = reflection.counter_cache_column
- self.unscoped.where(arel_table[self.primary_key].eq(object.id)).arel.update({
+ stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
arel_table[counter_name] => object.send(association).count
})
+ connection.update stmt.to_sql
end
return true
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index c0e1dda2bd..e5065de7fb 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -87,11 +87,13 @@ module ActiveRecord
begin
relation = self.class.unscoped
- affected_rows = relation.where(
+ stmt = relation.where(
relation.table[self.class.primary_key].eq(quoted_id).and(
relation.table[lock_col].eq(quote_value(previous_value))
)
- ).arel.update(arel_attributes_values(false, false, attribute_names))
+ ).arel.compile_update(arel_attributes_values(false, false, attribute_names))
+
+ affected_rows = connection.update stmt.to_sql
unless affected_rows == 1
raise ActiveRecord::StaleObjectError, "Attempted to update a stale object: #{self.class.name}"
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index f6321f1499..640111096d 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/array/wrap"
+
module ActiveRecord
# Exception that can be raised to stop migrations from going backwards.
class IrreversibleMigration < ActiveRecordError
@@ -511,38 +513,40 @@ module ActiveRecord
class Migrator#:nodoc:
class << self
- attr_writer :migrations_path
+ attr_writer :migrations_paths
+ alias :migrations_path= :migrations_paths=
- def migrate(migrations_path, target_version = nil)
+ def migrate(migrations_paths, target_version = nil)
case
when target_version.nil?
- up(migrations_path, target_version)
+ up(migrations_paths, target_version)
when current_version == 0 && target_version == 0
+ []
when current_version > target_version
- down(migrations_path, target_version)
+ down(migrations_paths, target_version)
else
- up(migrations_path, target_version)
+ up(migrations_paths, target_version)
end
end
- def rollback(migrations_path, steps=1)
- move(:down, migrations_path, steps)
+ def rollback(migrations_paths, steps=1)
+ move(:down, migrations_paths, steps)
end
- def forward(migrations_path, steps=1)
- move(:up, migrations_path, steps)
+ def forward(migrations_paths, steps=1)
+ move(:up, migrations_paths, steps)
end
- def up(migrations_path, target_version = nil)
- self.new(:up, migrations_path, target_version).migrate
+ def up(migrations_paths, target_version = nil)
+ self.new(:up, migrations_paths, target_version).migrate
end
- def down(migrations_path, target_version = nil)
- self.new(:down, migrations_path, target_version).migrate
+ def down(migrations_paths, target_version = nil)
+ self.new(:down, migrations_paths, target_version).migrate
end
- def run(direction, migrations_path, target_version)
- self.new(direction, migrations_path, target_version).run
+ def run(direction, migrations_paths, target_version)
+ self.new(direction, migrations_paths, target_version).run
end
def schema_migrations_table_name
@@ -568,12 +572,20 @@ module ActiveRecord
name.table_name rescue "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
end
+ def migrations_paths
+ @migrations_paths ||= ['db/migrate']
+ # just to not break things if someone uses: migration_path = some_string
+ Array.wrap(@migrations_paths)
+ end
+
def migrations_path
- @migrations_path ||= 'db/migrate'
+ migrations_paths.first
end
- def migrations(path)
- files = Dir["#{path}/[0-9]*_*.rb"]
+ def migrations(paths)
+ paths = Array.wrap(paths)
+
+ files = Dir[*paths.map { |p| "#{p}/[0-9]*_*.rb" }]
seen = Hash.new false
@@ -597,22 +609,22 @@ module ActiveRecord
private
- def move(direction, migrations_path, steps)
- migrator = self.new(direction, migrations_path)
+ def move(direction, migrations_paths, steps)
+ migrator = self.new(direction, migrations_paths)
start_index = migrator.migrations.index(migrator.current_migration)
if start_index
finish = migrator.migrations[start_index + steps]
version = finish ? finish.version : 0
- send(direction, migrations_path, version)
+ send(direction, migrations_paths, version)
end
end
end
- def initialize(direction, migrations_path, target_version = nil)
+ def initialize(direction, migrations_paths, target_version = nil)
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
Base.connection.initialize_schema_migrations_table
- @direction, @migrations_path, @target_version = direction, migrations_path, target_version
+ @direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
end
def current_version
@@ -647,6 +659,7 @@ module ActiveRecord
# skip the last migration if we're headed down, but not ALL the way down
runnable.pop if down? && target
+ ran = []
runnable.each do |migration|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
@@ -666,16 +679,18 @@ module ActiveRecord
migration.migrate(@direction)
record_version_state_after_migrating(migration.version)
end
+ ran << migration
rescue => e
canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : ""
raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
end
end
+ ran
end
def migrations
@migrations ||= begin
- migrations = self.class.migrations(@migrations_path)
+ migrations = self.class.migrations(@migrations_paths)
down? ? migrations.reverse : migrations
end
end
@@ -696,10 +711,12 @@ module ActiveRecord
@migrated_versions ||= []
if down?
@migrated_versions.delete(version)
- table.where(table["version"].eq(version.to_s)).delete
+ stmt = table.where(table["version"].eq(version.to_s)).compile_delete
+ Base.connection.delete stmt.to_sql
else
@migrated_versions.push(version).sort!
- table.insert table["version"] => version.to_s
+ stmt = table.compile_insert table["version"] => version.to_s
+ Base.connection.insert stmt.to_sql
end
end
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
index 022cf109af..8b011ad9af 100644
--- a/activerecord/lib/active_record/observer.rb
+++ b/activerecord/lib/active_record/observer.rb
@@ -89,51 +89,25 @@ module ActiveRecord
# singletons and that call instantiates and registers them.
#
class Observer < ActiveModel::Observer
- class_attribute :observed_methods
- self.observed_methods = [].freeze
-
- def initialize
- super
- observed_descendants.each { |klass| add_observer!(klass) }
- end
-
- def self.method_added(method)
- method = method.to_sym
-
- if ActiveRecord::Callbacks::CALLBACKS.include?(method)
- self.observed_methods += [method]
- self.observed_methods.freeze
- end
- end
protected
- def observed_descendants
- observed_classes.sum([]) { |klass| klass.descendants }
- end
-
- def observe_callbacks?
- self.class.observed_methods.any?
+ def observed_classes
+ klasses = super
+ klasses + klasses.map { |klass| klass.descendants }.flatten
end
def add_observer!(klass)
super
- define_callbacks klass if observe_callbacks?
+ define_callbacks klass
end
def define_callbacks(klass)
- existing_methods = klass.instance_methods.map { |m| m.to_sym }
observer = self
- observer_name = observer.class.name.underscore.gsub('/', '__')
- self.class.observed_methods.each do |method|
- callback = :"_notify_#{observer_name}_for_#{method}"
- unless existing_methods.include? callback
- klass.send(:define_method, callback) do # def _notify_user_observer_for_before_save
- observer.update(method, self) # observer.update(:before_save, self)
- end # end
- klass.send(method, callback) # before_save :_notify_user_observer_for_before_save
- end
+ ActiveRecord::Callbacks::CALLBACKS.each do |callback|
+ next unless respond_to?(callback)
+ klass.send(callback){|record| observer.send(callback, record)}
end
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 594a2214bb..9ac8fcb176 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -4,7 +4,7 @@ module ActiveRecord
# Returns true if this object hasn't been saved yet -- that is, a record
# for the object doesn't exist in the data store yet; otherwise, returns false.
def new_record?
- !@persisted
+ @new_record
end
# Returns true if this object has been destroyed, otherwise returns false.
@@ -15,7 +15,7 @@ module ActiveRecord
# Returns if the record is persisted, i.e. it's not a new record and it was
# not destroyed.
def persisted?
- @persisted && !destroyed?
+ !(new_record? || destroyed?)
end
# Saves the model.
@@ -94,7 +94,7 @@ module ActiveRecord
became = klass.new
became.instance_variable_set("@attributes", @attributes)
became.instance_variable_set("@attributes_cache", @attributes_cache)
- became.instance_variable_set("@persisted", persisted?)
+ became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
became.type = klass.name unless self.class.descends_from_active_record?
became
@@ -241,7 +241,7 @@ module ActiveRecord
private
def create_or_update
raise ReadOnlyRecord if readonly?
- result = persisted? ? update : create
+ result = new_record? ? create : update
result != false
end
@@ -250,7 +250,9 @@ module ActiveRecord
def update(attribute_names = @attributes.keys)
attributes_with_values = arel_attributes_values(false, false, attribute_names)
return 0 if attributes_with_values.empty?
- self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
+ klass = self.class
+ stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
+ klass.connection.update stmt.to_sql
end
# Creates a record with values matching those of the instance attributes
@@ -270,7 +272,7 @@ module ActiveRecord
self.id ||= new_id
- @persisted = true
+ @new_record = false
id
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 1fbc8a1d32..a4fc18148e 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -1,8 +1,14 @@
-namespace :db do
+db_namespace = namespace :db do
task :load_config => :rails_env do
require 'active_record'
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
- ActiveRecord::Migrator.migrations_path = Rails.application.paths["db/migrate"].first
+ ActiveRecord::Migrator.migrations_paths = Rails.application.paths["db/migrate"].to_a
+
+ if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
+ if engine.paths["db/migrate"].existent
+ ActiveRecord::Migrator.migrations_paths += engine.paths["db/migrate"].to_a
+ end
+ end
end
namespace :create do
@@ -138,21 +144,21 @@ namespace :db do
desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
- task :migrate => :environment do
+ task :migrate => [:environment, :load_config] do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
- ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+ ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
+ db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
namespace :migrate do
# desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
- task :redo => :environment do
+ task :redo => [:environment, :load_config] do
if ENV["VERSION"]
- Rake::Task["db:migrate:down"].invoke
- Rake::Task["db:migrate:up"].invoke
+ db_namespace["migrate:down"].invoke
+ db_namespace["migrate:up"].invoke
else
- Rake::Task["db:rollback"].invoke
- Rake::Task["db:migrate"].invoke
+ db_namespace["rollback"].invoke
+ db_namespace["migrate"].invoke
end
end
@@ -160,23 +166,23 @@ namespace :db do
task :reset => ["db:drop", "db:create", "db:migrate"]
# desc 'Runs the "up" for a given migration VERSION.'
- task :up => :environment do
+ task :up => [:environment, :load_config] do
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
raise "VERSION is required" unless version
- ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_path, version)
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+ ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version)
+ db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
# desc 'Runs the "down" for a given migration VERSION.'
- task :down => :environment do
+ task :down => [:environment, :load_config] do
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
raise "VERSION is required" unless version
- ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_path, version)
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+ ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version)
+ db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
desc "Display status of migrations"
- task :status => :environment do
+ task :status => [:environment, :load_config] do
config = ActiveRecord::Base.configurations[Rails.env || 'development']
ActiveRecord::Base.establish_connection(config)
unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
@@ -207,17 +213,17 @@ namespace :db do
end
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
- task :rollback => :environment do
+ task :rollback => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
- ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_path, step)
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+ ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
+ db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
# desc 'Pushes the schema to the next version (specify steps w/ STEP=n).'
- task :forward => :environment do
+ task :forward => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
- ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_path, step)
- Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
+ ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step)
+ db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
# desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.'
@@ -261,7 +267,7 @@ namespace :db do
# desc "Raises an error if there are pending migrations"
task :abort_if_pending_migrations => :environment do
if defined? ActiveRecord
- pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_path).pending_migrations
+ pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations
if pending_migrations.any?
puts "You have #{pending_migrations.size} pending migrations:"
@@ -321,12 +327,12 @@ namespace :db do
namespace :schema do
desc "Create a db/schema.rb file that can be portably used against any DB supported by AR"
- task :dump => :environment do
+ task :dump => :load_config do
require 'active_record/schema_dumper'
File.open(ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb", "w") do |file|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
end
- Rake::Task["db:schema:dump"].reenable
+ db_namespace["schema:dump"].reenable
end
desc "Load a schema.rb file into the database"
@@ -383,7 +389,7 @@ namespace :db do
task :load => 'db:test:purge' do
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
ActiveRecord::Schema.verbose = false
- Rake::Task["db:schema:load"].invoke
+ db_namespace["schema:load"].invoke
end
# desc "Recreate the test database from the current environment's database schema"
@@ -457,7 +463,7 @@ namespace :db do
# desc 'Check for pending migrations and load the test schema'
task :prepare => 'db:abort_if_pending_migrations' do
if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
- Rake::Task[{ :sql => "db:test:clone_structure", :ruby => "db:test:load" }[ActiveRecord::Base.schema_format]].invoke
+ db_namespace[{ :sql => "test:clone_structure", :ruby => "test:load" }[ActiveRecord::Base.schema_format]].invoke
end
end
end
@@ -501,7 +507,7 @@ namespace :railties do
puts "Copied migration #{migration.basename} from #{name}"
end
- ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_path, railties,
+ ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_paths.first, railties,
:on_skip => on_skip, :on_copy => on_copy)
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index d1de8cefb9..0ec8da0088 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -261,7 +261,7 @@ module ActiveRecord
end
def has_inverse?
- !@options[:inverse_of].nil?
+ @options[:inverse_of]
end
def inverse_of
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 3b22be78cb..3009bb70c1 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -156,7 +156,8 @@ module ActiveRecord
else
# Apply limit and order only if they're both present
if @limit_value.present? == @order_values.present?
- arel.update(Arel::SqlLiteral.new(@klass.send(:sanitize_sql_for_assignment, updates)))
+ stmt = arel.compile_update(Arel::SqlLiteral.new(@klass.send(:sanitize_sql_for_assignment, updates)))
+ @klass.connection.update stmt.to_sql
else
except(:limit, :order).update_all(updates)
end
@@ -270,7 +271,14 @@ module ActiveRecord
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
def delete_all(conditions = nil)
- conditions ? where(conditions).delete_all : arel.delete.tap { reset }
+ if conditions
+ where(conditions).delete_all
+ else
+ statement = arel.compile_delete
+ affected = @klass.connection.delete statement.to_sql
+ reset
+ affected
+ end
end
# Deletes the row with a primary key matching the +id+ argument, using a
@@ -319,24 +327,15 @@ module ActiveRecord
end
def where_values_hash
- Hash[@where_values.find_all { |w|
- w.respond_to?(:operator) && w.operator == :== && w.left.relation.name == table_name
- }.map { |where|
- [
- where.left.name,
- where.right.respond_to?(:value) ? where.right.value : where.right
- ]
- }]
+ equalities = @where_values.grep(Arel::Nodes::Equality).find_all { |node|
+ node.left.relation.name == table_name
+ }
+
+ Hash[equalities.map { |where| [where.left.name, where.right] }]
end
def scope_for_create
- @scope_for_create ||= begin
- if @create_with_value
- @create_with_value.reverse_merge(where_values_hash)
- else
- where_values_hash
- end
- end
+ @scope_for_create ||= where_values_hash.merge(@create_with_value || {})
end
def eager_loading?
@@ -348,7 +347,7 @@ module ActiveRecord
when Relation
other.to_sql == to_sql
when Array
- to_a == other.to_a
+ to_a == other
end
end
@@ -356,6 +355,10 @@ module ActiveRecord
to_a.inspect
end
+ def table_name
+ @klass.table_name
+ end
+
protected
def method_missing(method, *args, &block)
@@ -376,7 +379,7 @@ module ActiveRecord
def references_eager_loaded_tables?
# always convert table names to downcase as in Oracle quoted table names are in uppercase
- joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq
+ joined_tables = (tables_in_string(arel.join_sql) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq
(tables_in_string(to_sql) - joined_tables).any?
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index c8adaddfca..fd45bb24dd 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -166,7 +166,7 @@ module ActiveRecord
if operation == "count"
column_name ||= (select_for_count || :all)
- if arel.joins(arel) =~ /LEFT OUTER/i
+ unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
distinct = true
column_name = @klass.primary_key if column_name == :all
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 23ae0b4325..906ad7699c 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -196,7 +196,7 @@ module ActiveRecord
def construct_relation_for_association_calculations
including = (@eager_load_values + @includes_values).uniq
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.joins(arel))
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.froms.first)
relation = except(:includes, :eager_load, :preload)
apply_join_dependency(relation, join_dependency)
end
@@ -290,7 +290,7 @@ module ActiveRecord
def find_one(id)
id = id.id if ActiveRecord::Base === id
- column = primary_key.column
+ column = columns_hash[primary_key.name.to_s]
substitute = connection.substitute_for(column, @bind_values)
relation = where(primary_key.eq(substitute))
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 0a4c119849..51a39be065 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -162,52 +162,57 @@ module ActiveRecord
@arel ||= build_arel
end
- def custom_join_sql(*joins)
- arel = table.select_manager
+ def build_arel
+ arel = table.from table
- joins.each do |join|
- next if join.blank?
+ build_joins(arel, @joins_values) unless @joins_values.empty?
- @implicit_readonly = true
+ collapse_wheres(arel, (@where_values - ['']).uniq)
- case join
- when Array
- join = Arel.sql(join.join(' ')) if array_of_strings?(join)
- when String
- join = Arel.sql(join)
- end
+ arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
- arel.join(join)
- end
+ arel.take(@limit_value) if @limit_value
+ arel.skip(@offset_value) if @offset_value
- arel.joins(arel)
- end
+ arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
- def build_arel
- arel = table
+ arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
- arel = build_joins(arel, @joins_values) unless @joins_values.empty?
+ build_select(arel, @select_values.uniq)
- arel = collapse_wheres(arel, (@where_values - ['']).uniq)
+ arel.from(@from_value) if @from_value
+ arel.lock(@lock_value) if @lock_value
- arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
+ arel
+ end
- arel = arel.take(@limit_value) if @limit_value
- arel = arel.skip(@offset_value) if @offset_value
+ private
- arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
+ def custom_join_ast(table, joins)
+ joins = joins.reject { |join| join.blank? }
- arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
+ return if joins.empty?
- arel = build_select(arel, @select_values.uniq)
+ @implicit_readonly = true
- arel = arel.from(@from_value) if @from_value
- arel = arel.lock(@lock_value) if @lock_value
+ joins.map! do |join|
+ case join
+ when Array
+ join = Arel.sql(join.join(' ')) if array_of_strings?(join)
+ when String
+ join = Arel.sql(join)
+ end
+ join
+ end
- arel
- end
+ head = table.create_string_join(table, joins.shift)
- private
+ joins.inject(head) do |ast, join|
+ ast.right = table.create_string_join(ast.right, join)
+ end
+
+ head
+ end
def collapse_wheres(arel, wheres)
equalities = wheres.grep(Arel::Nodes::Equality)
@@ -220,14 +225,13 @@ module ActiveRecord
test = eqls.inject(eqls.shift) do |memo, expr|
memo.or(expr)
end
- arel = arel.where(test)
+ arel.where(test)
end
(wheres - equalities).each do |where|
where = Arel.sql(where) if String === where
- arel = arel.where(Arel::Nodes::Grouping.new(where))
+ arel.where(Arel::Nodes::Grouping.new(where))
end
- arel
end
def build_where(opts, other = [])
@@ -242,31 +246,34 @@ module ActiveRecord
end
end
- def build_joins(relation, joins)
- association_joins = []
-
+ def build_joins(manager, joins)
joins = joins.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
- joins.each do |join|
- association_joins << join if [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join)
+ association_joins = joins.find_all do |join|
+ [Hash, Array, Symbol].include?(join.class) && !array_of_strings?(join)
end
stashed_association_joins = joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
non_association_joins = (joins - association_joins - stashed_association_joins)
- custom_joins = custom_join_sql(*non_association_joins)
+ join_ast = custom_join_ast(manager.froms.first, non_association_joins)
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins)
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, join_ast)
join_dependency.graft(*stashed_association_joins)
@implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
+ # FIXME: refactor this to build an AST
join_dependency.join_associations.each do |association|
- relation = association.join_to(relation)
+ association.join_to(manager)
end
- relation.join(custom_joins)
+ return manager unless join_ast
+
+ join_ast.left = manager.froms.first
+ manager.from join_ast
+ manager
end
def build_select(arel, selects)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index a61a3bd41c..5acf3ec83a 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -3,10 +3,11 @@ require 'active_support/core_ext/object/blank'
module ActiveRecord
module SpawnMethods
def merge(r)
- merged_relation = clone
- return merged_relation unless r
+ return self unless r
return to_a & r if r.is_a?(Array)
+ merged_relation = clone
+
Relation::ASSOCIATION_METHODS.each do |method|
value = r.send(:"#{method}_values")
@@ -24,7 +25,7 @@ module ActiveRecord
merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
end
- merged_relation = merged_relation.joins(r.joins_values)
+ merged_relation.joins_values += r.joins_values
merged_wheres = @where_values + r.where_values
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index c6bb5c1961..d815ab05ac 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -30,8 +30,8 @@ module ActiveRecord
# ActiveRecord::Schema is only supported by database adapters that also
# support migrations, the two features being very similar.
class Schema < Migration
- def migrations_path
- ActiveRecord::Migrator.migrations_path
+ def migrations_paths
+ ActiveRecord::Migrator.migrations_paths
end
# Eval the given block. All methods available to the current connection
@@ -51,7 +51,7 @@ module ActiveRecord
unless info[:version].blank?
initialize_schema_migrations_table
- assume_migrated_upto_version(info[:version], schema.migrations_path)
+ assume_migrated_upto_version(info[:version], schema.migrations_paths)
end
end
end
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index ba99800fb2..3400fd6ade 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -228,7 +228,7 @@ module ActiveRecord
@session_id = attributes[:session_id]
@data = attributes[:data]
@marshaled_data = attributes[:marshaled_data]
- @persisted = !@marshaled_data.nil?
+ @new_record = @marshaled_data.nil?
end
# Lazy-unmarshal session state.
@@ -252,8 +252,8 @@ module ActiveRecord
marshaled_data = self.class.marshal(data)
connect = connection
- unless @persisted
- @persisted = true
+ if @new_record
+ @new_record = false
connect.update <<-end_sql, 'Create session'
INSERT INTO #{table_name} (
#{connect.quote_column_name(session_id_column)},
@@ -272,7 +272,7 @@ module ActiveRecord
end
def destroy
- return unless @persisted
+ return if @new_record
connect = connection
connect.delete <<-end_sql, 'Destroy session'
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 8c94d1a2bc..443f318067 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -11,6 +11,7 @@ module ActiveRecord
included do
define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name]
end
+
# = Active Record Transactions
#
# Transactions are protective blocks where SQL statements are only permanent
@@ -130,7 +131,7 @@ module ActiveRecord
#
# +transaction+ calls can be nested. By default, this makes all database
# statements in the nested transaction block become part of the parent
- # transaction. For example:
+ # transaction. For example, the following behavior may be surprising:
#
# User.transaction do
# User.create(:username => 'Kotori')
@@ -140,12 +141,15 @@ module ActiveRecord
# end
# end
#
- # User.find(:all) # => empty
+ # creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
+ # exception in the nested block does not issue a ROLLBACK. Since these exceptions
+ # are captured in transaction blocks, the parent block does not see it and the
+ # real transaction is committed.
#
- # It is also possible to requires a sub-transaction by passing
- # <tt>:requires_new => true</tt>. If anything goes wrong, the
- # database rolls back to the beginning of the sub-transaction
- # without rolling back the parent transaction. For example:
+ # In order to get a ROLLBACK for the nested transaction you may ask for a real
+ # sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong,
+ # the database rolls back to the beginning of the sub-transaction without rolling
+ # back the parent transaction. If we add it to the previous example:
#
# User.transaction do
# User.create(:username => 'Kotori')
@@ -155,12 +159,12 @@ module ActiveRecord
# end
# end
#
- # User.find(:all) # => Returns only Kotori
+ # only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.)
#
# Most databases don't support true nested transactions. At the time of
# writing, the only database that we're aware of that supports true nested
# transactions, is MS-SQL. Because of this, Active Record emulates nested
- # transactions by using savepoints. See
+ # transactions by using savepoints on MySQL and PostgreSQL. See
# http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
# for more information about savepoints.
#
@@ -242,7 +246,7 @@ module ActiveRecord
with_transaction_returning_status { super }
end
- # Reset id and @persisted if the transaction rolls back.
+ # Reset id and @new_record if the transaction rolls back.
def rollback_active_record_state!
remember_transaction_record_state
yield
@@ -297,9 +301,9 @@ module ActiveRecord
# Save the new record state and id of a record so it can be restored later if a transaction fails.
def remember_transaction_record_state #:nodoc
@_start_transaction_state ||= {}
- unless @_start_transaction_state.include?(:persisted)
+ unless @_start_transaction_state.include?(:new_record)
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
- @_start_transaction_state[:persisted] = @persisted
+ @_start_transaction_state[:new_record] = @new_record
end
unless @_start_transaction_state.include?(:destroyed)
@_start_transaction_state[:destroyed] = @destroyed
@@ -323,8 +327,8 @@ module ActiveRecord
restore_state = remove_instance_variable(:@_start_transaction_state)
if restore_state
@attributes = @attributes.dup if @attributes.frozen?
- @persisted = restore_state[:persisted]
- @destroyed = restore_state[:destroyed]
+ @new_record = restore_state[:new_record]
+ @destroyed = restore_state[:destroyed]
if restore_state[:id]
self.id = restore_state[:id]
else
@@ -345,11 +349,11 @@ module ActiveRecord
def transaction_include_action?(action) #:nodoc
case action
when :create
- transaction_record_state(:new_record) || !transaction_record_state(:persisted)
+ transaction_record_state(:new_record)
when :destroy
destroyed?
when :update
- !(transaction_record_state(:new_record) || !transaction_record_state(:persisted) || destroyed?)
+ !(transaction_record_state(:new_record) || destroyed?)
end
end
end
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index ee45fcdf35..f367315b22 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -51,7 +51,7 @@ module ActiveRecord
# Runs all the specified validations and returns true if no errors were added otherwise false.
def valid?(context = nil)
- context ||= (persisted? ? :update : :create)
+ context ||= (new_record? ? :create : :update)
output = super(context)
errors.empty? && output
end