aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG14
-rw-r--r--activerecord/lib/active_record/association_preload.rb41
-rw-r--r--activerecord/lib/active_record/associations.rb214
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb7
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb21
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb5
-rw-r--r--activerecord/lib/active_record/base.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb14
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb7
-rw-r--r--activerecord/lib/active_record/railties/databases.rake11
-rw-r--r--activerecord/lib/active_record/relation/batches.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb12
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb6
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb10
-rw-r--r--activerecord/test/cases/base_test.rb19
-rw-r--r--activerecord/test/cases/date_time_test.rb22
-rw-r--r--activerecord/test/cases/helper.rb26
-rw-r--r--activerecord/test/cases/migration_test.rb7
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb32
-rw-r--r--activerecord/test/models/car.rb11
-rw-r--r--activerecord/test/models/engine.rb1
26 files changed, 286 insertions, 237 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 62f1470287..8f283e1117 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,7 +1,19 @@
*Rails 3.1.0 (unreleased)*
-* No changes
+* The following code:
+ Model.limit(10).scoping { Model.count }
+
+ now generates the following SQL:
+
+ SELECT COUNT(*) FROM models LIMIT 10
+
+ This may not return what you want. Instead, you may with to do something
+ like this:
+
+ Model.limit(10).scoping { Model.all.size }
+
+ [Aaron Patterson]
*Rails 3.0.0 (August 29, 2010)*
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index 63224377a3..715c868598 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -209,23 +209,20 @@ module ActiveRecord
records.each {|record| record.send("set_#{reflection.name}_target", nil)}
if options[:through]
through_records = preload_through_records(records, reflection, options[:through])
- through_reflection = reflections[options[:through]]
- through_primary_key = through_reflection.primary_key_name
+
unless through_records.empty?
+ through_reflection = reflections[options[:through]]
+ through_primary_key = through_reflection.primary_key_name
source = reflection.source_reflection.name
through_records.first.class.preload_associations(through_records, source)
if through_reflection.macro == :belongs_to
- rev_id_to_record_map = construct_id_map(records, through_primary_key).first
- rev_primary_key = through_reflection.klass.primary_key
- through_records.each do |through_record|
- add_preloaded_record_to_collection(rev_id_to_record_map[through_record[rev_primary_key].to_s],
- reflection.name, through_record.send(source))
- end
- else
- through_records.each do |through_record|
- add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
- reflection.name, through_record.send(source))
- end
+ id_to_record_map = construct_id_map(records, through_primary_key).first
+ through_primary_key = through_reflection.klass.primary_key
+ end
+
+ through_records.each do |through_record|
+ add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
+ reflection.name, through_record.send(source))
end
end
else
@@ -299,9 +296,10 @@ module ActiveRecord
options = reflection.options
primary_key_name = reflection.primary_key_name
+ klasses_and_ids = {}
+
if options[:polymorphic]
polymorph_type = options[:foreign_type]
- klasses_and_ids = {}
# Construct a mapping from klass to a list of ids to load and a mapping of those ids back
# to their parent_records
@@ -310,27 +308,20 @@ module ActiveRecord
klass_id = record.send(primary_key_name)
if klass_id
id_map = klasses_and_ids[klass] ||= {}
- id_list_for_klass_id = (id_map[klass_id.to_s] ||= [])
- id_list_for_klass_id << record
+ (id_map[klass_id.to_s] ||= []) << record
end
end
end
- klasses_and_ids = klasses_and_ids.to_a
else
id_map = {}
records.each do |record|
key = record.send(primary_key_name)
- if key
- mapped_records = (id_map[key.to_s] ||= [])
- mapped_records << record
- end
+ (id_map[key.to_s] ||= []) << record if key
end
- klasses_and_ids = [[reflection.klass.name, id_map]]
+ klasses_and_ids[reflection.klass.name] = id_map unless id_map.empty?
end
- klasses_and_ids.each do |klass_and_id|
- klass_name, id_map = *klass_and_id
- next if id_map.empty?
+ klasses_and_ids.each do |klass_name, id_map|
klass = klass_name.constantize
table_name = klass.quoted_table_name
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 4bf206d589..565ebf8197 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1416,8 +1416,8 @@ module ActiveRecord
include Module.new {
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def destroy # def destroy
- super # super
#{reflection.name}.clear # posts.clear
+ super # super
end # end
RUBY
}
@@ -1841,7 +1841,7 @@ module ActiveRecord
@reflections = []
@base_records_hash = {}
@base_records_in_order = []
- @table_aliases = Hash.new { |aliases, table| aliases[table] = 0 }
+ @table_aliases = Hash.new(0)
@table_aliases[base.table_name] = 1
build(associations)
end
@@ -1855,7 +1855,7 @@ module ActiveRecord
end
def join_associations
- @joins[1..-1].to_a
+ @joins.last(@joins.length - 1)
end
def join_base
@@ -1951,53 +1951,54 @@ module ActiveRecord
def construct(parent, associations, joins, row)
case associations
- when Symbol, String
- join = joins.detect{|j| j.reflection.name.to_s == associations.to_s && j.parent_table_name == parent.class.table_name }
+ when Symbol, String
+ join = joins.detect{|j| j.reflection.name.to_s == associations.to_s && j.parent_table_name == parent.class.table_name }
+ raise(ConfigurationError, "No such association") if join.nil?
+
+ joins.delete(join)
+ construct_association(parent, join, row)
+ when Array
+ associations.each do |association|
+ construct(parent, association, joins, row)
+ end
+ when Hash
+ associations.sort_by { |k,_| k.to_s }.each do |name, assoc|
+ join = joins.detect{|j| j.reflection.name.to_s == name.to_s && j.parent_table_name == parent.class.table_name }
raise(ConfigurationError, "No such association") if join.nil?
+ association = construct_association(parent, join, row)
joins.delete(join)
- construct_association(parent, join, row)
- when Array
- associations.each do |association|
- construct(parent, association, joins, row)
- end
- when Hash
- associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
- join = joins.detect{|j| j.reflection.name.to_s == name.to_s && j.parent_table_name == parent.class.table_name }
- raise(ConfigurationError, "No such association") if join.nil?
-
- association = construct_association(parent, join, row)
- joins.delete(join)
- construct(association, associations[name], joins, row) if association
- end
- else
- raise ConfigurationError, associations.inspect
+ construct(association, assoc, joins, row) if association
+ end
+ else
+ raise ConfigurationError, associations.inspect
end
end
def construct_association(record, join, row)
- case join.reflection.macro
+ return if record.id.to_s != join.parent.record_id(row).to_s
+
+ macro = join.reflection.macro
+ if macro == :has_one
+ return if record.instance_variable_defined?("@#{join.reflection.name}")
+ association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
+ set_target_and_inverse(join, association, record)
+ else
+ return if row[join.aliased_primary_key].nil?
+ association = join.instantiate(row)
+ case macro
when :has_many, :has_and_belongs_to_many
collection = record.send(join.reflection.name)
collection.loaded
-
- return nil if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
- association = join.instantiate(row)
collection.target.push(association)
collection.__send__(:set_inverse_instance, association, record)
- when :has_one
- return if record.id.to_s != join.parent.record_id(row).to_s
- return if record.instance_variable_defined?("@#{join.reflection.name}")
- association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
- set_target_and_inverse(join, association, record)
when :belongs_to
- return if record.id.to_s != join.parent.record_id(row).to_s or row[join.aliased_primary_key].nil?
- association = join.instantiate(row)
set_target_and_inverse(join, association, record)
else
raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
+ end
end
- return association
+ association
end
def set_target_and_inverse(join, association, record)
@@ -2046,7 +2047,7 @@ module ActiveRecord
end
def extract_record(row)
- column_names_with_alias.inject({}){|record, (cn, an)| record[cn] = row[an]; record}
+ Hash[column_names_with_alias.map{|cn, an| [cn, row[an]]}]
end
def record_id(row)
@@ -2107,71 +2108,15 @@ module ActiveRecord
def association_join
return @join if @join
- aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
- parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine)
-
- @join = case reflection.macro
- when :has_and_belongs_to_many
- join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine)
- fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
- klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key
+ aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name,
+ :engine => arel_engine,
+ :columns => klass.columns)
- [
- join_table[fk].eq(parent_table[reflection.active_record.primary_key]),
- aliased_table[klass.primary_key].eq(join_table[klass_fk])
- ]
- when :has_many, :has_one
- if reflection.options[:through]
- join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine)
- jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
- first_key = second_key = as_extra = nil
-
- if through_reflection.options[:as] # has_many :through against a polymorphic join
- jt_foreign_key = through_reflection.options[:as].to_s + '_id'
- jt_as_extra = join_table[through_reflection.options[:as].to_s + '_type'].eq(parent.active_record.base_class.name)
- else
- jt_foreign_key = through_reflection.primary_key_name
- end
+ parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name,
+ :engine => arel_engine,
+ :columns => parent.active_record.columns)
- case source_reflection.macro
- when :has_many
- if source_reflection.options[:as]
- first_key = "#{source_reflection.options[:as]}_id"
- second_key = options[:foreign_key] || primary_key
- as_extra = aliased_table["#{source_reflection.options[:as]}_type"].eq(source_reflection.active_record.base_class.name)
- else
- first_key = through_reflection.klass.base_class.to_s.foreign_key
- second_key = options[:foreign_key] || primary_key
- end
-
- unless through_reflection.klass.descends_from_active_record?
- jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name)
- end
- when :belongs_to
- first_key = primary_key
- if reflection.options[:source_type]
- second_key = source_reflection.association_foreign_key
- jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type])
- else
- second_key = source_reflection.primary_key_name
- end
- end
-
- [
- [parent_table[parent.primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].reject{|x| x.blank? },
- aliased_table[first_key].eq(join_table[second_key])
- ]
- elsif reflection.options[:as]
- id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key])
- type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name)
- [id_rel, type_rel]
- else
- foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
- [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
- end
- when :belongs_to
- [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
- end
+ @join = send("build_#{reflection.macro}", aliased_table, parent_table)
unless klass.descends_from_active_record?
sti_column = aliased_table[klass.inheritance_column]
@@ -2191,7 +2136,9 @@ module ActiveRecord
end
def relation
- aliased = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
+ aliased = Arel::Table.new(table_name, :as => @aliased_table_name,
+ :engine => arel_engine,
+ :columns => klass.columns)
if reflection.macro == :has_and_belongs_to_many
[Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine), aliased]
@@ -2244,6 +2191,77 @@ module ActiveRecord
def interpolate_sql(sql)
instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
end
+
+ private
+
+ def build_has_and_belongs_to_many(aliased_table, parent_table)
+ join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine)
+ fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
+ klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key
+
+ [
+ join_table[fk].eq(parent_table[reflection.active_record.primary_key]),
+ aliased_table[klass.primary_key].eq(join_table[klass_fk])
+ ]
+ end
+
+ def build_has_many(aliased_table, parent_table)
+ if reflection.options[:through]
+ join_table = Arel::Table.new(through_reflection.klass.table_name,
+ :as => aliased_join_table_name,
+ :engine => arel_engine)
+ jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
+ first_key = second_key = nil
+
+ if through_reflection.options[:as] # has_many :through against a polymorphic join
+ as_key = through_reflection.options[:as].to_s
+ jt_foreign_key = as_key + '_id'
+ jt_as_extra = join_table[as_key + '_type'].eq(parent.active_record.base_class.name)
+ else
+ jt_foreign_key = through_reflection.primary_key_name
+ end
+
+ case source_reflection.macro
+ when :has_many
+ second_key = options[:foreign_key] || primary_key
+
+ if source_reflection.options[:as]
+ first_key = "#{source_reflection.options[:as]}_id"
+ else
+ first_key = through_reflection.klass.base_class.to_s.foreign_key
+ end
+
+ unless through_reflection.klass.descends_from_active_record?
+ jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name)
+ end
+ when :belongs_to
+ first_key = primary_key
+ if reflection.options[:source_type]
+ second_key = source_reflection.association_foreign_key
+ jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type])
+ else
+ second_key = source_reflection.primary_key_name
+ end
+ end
+
+ [
+ [parent_table[parent.primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].compact,
+ aliased_table[first_key].eq(join_table[second_key])
+ ]
+ elsif reflection.options[:as]
+ id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key])
+ type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name)
+ [id_rel, type_rel]
+ else
+ foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
+ [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
+ end
+ end
+ alias :build_has_one :build_has_many
+
+ def build_belongs_to(aliased_table, parent_table)
+ [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 1221a56bbc..91e0a9f2f8 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -338,13 +338,12 @@ module ActiveRecord
def uniq(collection = self)
seen = Set.new
- collection.inject([]) do |kept, record|
+ collection.map do |record|
unless seen.include?(record.id)
- kept << record
seen << record.id
+ record
end
- kept
- end
+ end.compact
end
# Replace this collection with +other_array+
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 c0ec65bd40..eb65234dfb 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
@@ -49,23 +49,20 @@ module ActiveRecord
timestamps = record_timestamp_columns(record)
timezone = record.send(:current_time_from_proper_timezone) if timestamps.any?
- attributes = columns.inject({}) do |attrs, column|
+ attributes = Hash[columns.map do |column|
name = column.name
- case name.to_s
+ value = case name.to_s
when @reflection.primary_key_name.to_s
- attrs[relation[name]] = @owner.id
+ @owner.id
when @reflection.association_foreign_key.to_s
- attrs[relation[name]] = record.id
+ record.id
when *timestamps
- attrs[relation[name]] = timezone
+ timezone
else
- if record.has_attribute?(name)
- value = @owner.send(:quote_value, record[name], column)
- attrs[relation[name]] = value unless value.nil?
- end
+ @owner.send(:quote_value, record[name], column) if record.has_attribute?(name)
end
- attrs
- end
+ [relation[name], value] unless value.nil?
+ end]
relation.insert(attributes)
end
@@ -79,7 +76,7 @@ module ActiveRecord
else
relation = Arel::Table.new(@reflection.options[:join_table])
relation.where(relation[@reflection.primary_key_name].eq(@owner.id).
- and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }))
+ and(relation[@reflection.association_foreign_key].in(records.map { |x| x.id }.compact))
).delete
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 a4e144f233..23195b02f7 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -13,10 +13,7 @@ module ActiveRecord
# Returns a hash of attributes before typecasting and deserialization.
def attributes_before_type_cast
- self.attribute_names.inject({}) do |attrs, name|
- attrs[name] = read_attribute_before_type_cast(name)
- attrs
- end
+ Hash[attribute_names.map { |name| [name, read_attribute_before_type_cast(name)] }]
end
private
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 5da4eb169b..e86d4984a6 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -884,21 +884,13 @@ module ActiveRecord #:nodoc:
# single-table inheritance model that makes it possible to create
# objects of different types from the same table.
def instantiate(record)
- object = find_sti_class(record[inheritance_column]).allocate
-
- object.instance_variable_set(:@attributes, record)
- object.instance_variable_set(:@attributes_cache, {})
- object.instance_variable_set(:@new_record, false)
- object.instance_variable_set(:@readonly, false)
- object.instance_variable_set(:@destroyed, false)
- object.instance_variable_set(:@marked_for_destruction, false)
- object.instance_variable_set(:@previously_changed, {})
- object.instance_variable_set(:@changed_attributes, {})
-
- object.send(:_run_find_callbacks)
- object.send(:_run_initialize_callbacks)
-
- object
+ find_sti_class(record[inheritance_column]).allocate.instance_eval do
+ @attributes, @attributes_cache, @previously_changed, @changed_attributes = record, {}, {}, {}
+ @new_record = @readonly = @destroyed = @marked_for_destruction = false
+ _run_find_callbacks
+ _run_initialize_callbacks
+ self
+ end
end
def find_sti_class(type_name)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
index 8e74eff0ab..ec7035e540 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -67,8 +67,8 @@ module ActiveRecord
begin
require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
- rescue LoadError
- raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})"
+ rescue LoadError => e
+ raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e})"
end
adapter_method = "#{spec[:adapter]}_connection"
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 0245e63294..310423bb20 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -340,12 +340,10 @@ module ActiveRecord
end
if index_name.length > index_name_length
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.")
- return
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
end
if index_name_exists?(table_name, index_name, false)
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.")
- return
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
end
quoted_column_names = quoted_columns_for_index(column_names, options).join(", ")
@@ -365,8 +363,7 @@ module ActiveRecord
def remove_index(table_name, options = {})
index_name = index_name(table_name, options)
unless index_name_exists?(table_name, index_name, true)
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.")
- return
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
end
remove_index!(table_name, index_name)
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index d797f9c6c3..e7f7b37b27 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -19,11 +19,11 @@ module ActiveRecord
begin
require 'mysql'
rescue LoadError
- raise "!!! Missing the mysql gem. Add it to your Gemfile: gem 'mysql', '2.8.1'"
+ raise "!!! Missing the mysql2 gem. Add it to your Gemfile: gem 'mysql2'"
end
unless defined?(Mysql::Result) && Mysql::Result.method_defined?(:each_hash)
- raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'"
+ raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'. Or use gem 'mysql2'"
end
end
@@ -276,7 +276,7 @@ module ActiveRecord
rows = []
result.each { |row| rows << row }
result.free
- @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
+ @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end
@@ -358,10 +358,10 @@ module ActiveRecord
sql = "SHOW TABLES"
end
- select_all(sql).inject("") do |structure, table|
+ select_all(sql).map do |table|
table.delete('Table_type')
- structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
- end
+ select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
+ end.join("")
end
def recreate_database(name, options = {}) #:nodoc:
@@ -620,7 +620,7 @@ module ActiveRecord
rows = []
result.each_hash { |row| rows << row }
result.free
- @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
+ @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 33611b410c..18519a712b 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -386,7 +386,7 @@ module ActiveRecord
association.to_a
else
attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
- attribute_ids.present? ? association.all(:conditions => {association.primary_key => attribute_ids}) : []
+ attribute_ids.empty? ? [] : association.all(:conditions => {association.primary_key => attribute_ids})
end
attributes_collection.each do |attributes|
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 1b016f0895..f80e304c5d 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -279,10 +279,9 @@ module ActiveRecord
# that a new instance, or one populated from a passed-in Hash, still has all the attributes
# that instances loaded from the database would.
def attributes_from_column_definition
- self.class.columns.inject({}) do |attributes, column|
- attributes[column.name] = column.default unless column.name == self.class.primary_key
- attributes
- end
+ Hash[self.class.columns.map do |column|
+ [column.name, column.default] unless column.name == self.class.primary_key
+ end]
end
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 8e5332214c..ccaa1f01f6 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -67,8 +67,8 @@ namespace :db do
# Create the SQLite database
ActiveRecord::Base.establish_connection(config)
ActiveRecord::Base.connection
- rescue
- $stderr.puts $!, *($!.backtrace)
+ rescue Exception => e
+ $stderr.puts e, *(e.backtrace)
$stderr.puts "Couldn't create database for #{config.inspect}"
end
end
@@ -113,8 +113,8 @@ namespace :db do
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
ActiveRecord::Base.establish_connection(config)
- rescue
- $stderr.puts $!, *($!.backtrace)
+ rescue Exception => e
+ $stderr.puts e, *(e.backtrace)
$stderr.puts "Couldn't create database for #{config.inspect}"
end
end
@@ -299,8 +299,7 @@ namespace :db do
desc 'Load the seed data from db/seeds.rb'
task :seed => 'db:abort_if_pending_migrations' do
- seed_file = File.join(Rails.root, 'db', 'seeds.rb')
- load(seed_file) if File.exist?(seed_file)
+ Rails.application.load_seed
end
namespace :fixtures do
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index 5584397439..b41e935ed5 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -1,7 +1,7 @@
require 'active_support/core_ext/object/blank'
module ActiveRecord
- module Batches # :nodoc:
+ module Batches
# Yields each record that was found by the find +options+. The find is
# performed by find_in_batches with a batch size of 1000 (or as
# specified by the <tt>:batch_size</tt> option).
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 398ab75b69..12a2c6aec3 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -225,13 +225,11 @@ module ActiveRecord
key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) }
end
- calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row|
- key = type_cast_calculated_value(row[group_alias], group_column)
- key = key_records[key] if associated
- value = row[aggregate_alias]
- all[key] = type_cast_calculated_value(value, column_for(column_name), operation)
- all
- end
+ ActiveSupport::OrderedHash[calculated_data.map do |row|
+ key = type_cast_calculated_value(row[group_alias], group_column)
+ key = key_records[key] if associated
+ [key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
+ end]
end
# Converts the given keys to the value that the database adapter returns as
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index de57725af2..6a33edeb97 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -222,7 +222,7 @@ module ActiveRecord
def build_joins(relation, joins)
association_joins = []
- joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
+ 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)
@@ -260,8 +260,8 @@ module ActiveRecord
def build_select(arel, selects)
unless selects.empty?
@implicit_readonly = false
- # TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array.
- # Before this change we were passing to ARel the last element only, and ARel is capable of handling an array
+ # TODO: fix this ugly hack, we should refactor the callers to get an Arel compatible array.
+ # Before this change we were passing to Arel the last element only, and Arel is capable of handling an array
case select = selects.last
when Arel::Expression, Arel::SqlLiteral
arel.project(select)
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 8bb8edde0e..2bdf9d8971 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -425,7 +425,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_removing_associations_on_destroy
david = DeveloperWithBeforeDestroyRaise.find(1)
assert !david.projects.empty?
- assert_nothing_raised { david.destroy }
+ assert_raise(RuntimeError) { david.destroy }
assert david.projects.empty?
assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index b83966e91c..f131dc01f6 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -44,6 +44,16 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert !authors(:mary).unique_categorized_posts.loaded?
end
+ def test_column_caching
+ # pre-heat our cache
+ Post.arel_table.columns
+ Comment.columns
+
+ Post.connection.column_calls = 0
+ 2.times { Post.joins(:comments).to_a }
+ assert_equal 0, Post.connection.column_calls
+ end
+
def test_has_many_uniq_through_find
assert_equal 1, authors(:mary).unique_categorized_posts.find(:all).size
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index dcb1da7d91..d87f259f4b 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -659,6 +659,10 @@ class BasicsTest < ActiveRecord::TestCase
cloned_topic.save
assert !cloned_topic.new_record?
assert_not_equal cloned_topic.id, topic.id
+
+ cloned_topic.reload
+ # FIXME: I think this is poor behavior, and will fix it with #5686
+ assert_equal({'a' => 'c'}.to_s, cloned_topic.title)
end
def test_clone_with_aggregate_of_same_name_as_attribute
@@ -1433,19 +1437,4 @@ class BasicsTest < ActiveRecord::TestCase
ensure
Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
end
-
- protected
- def with_env_tz(new_tz = 'US/Eastern')
- old_tz, ENV['TZ'] = ENV['TZ'], new_tz
- yield
- ensure
- old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
- end
-
- def with_active_record_default_timezone(zone)
- old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
- yield
- ensure
- ActiveRecord::Base.default_timezone = old_zone
- end
end
diff --git a/activerecord/test/cases/date_time_test.rb b/activerecord/test/cases/date_time_test.rb
index a8b4b7a096..3deb0dac99 100644
--- a/activerecord/test/cases/date_time_test.rb
+++ b/activerecord/test/cases/date_time_test.rb
@@ -4,17 +4,21 @@ require 'models/task'
class DateTimeTest < ActiveRecord::TestCase
def test_saves_both_date_and_time
- time_values = [1807, 2, 10, 15, 30, 45]
- # create DateTime value with local time zone offset
- local_offset = Rational(Time.local_time(*time_values).utc_offset, 86400)
- now = DateTime.civil(*(time_values + [local_offset]))
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :utc do
+ time_values = [1807, 2, 10, 15, 30, 45]
+ # create DateTime value with local time zone offset
+ local_offset = Rational(Time.local_time(*time_values).utc_offset, 86400)
+ now = DateTime.civil(*(time_values + [local_offset]))
- task = Task.new
- task.starting = now
- task.save!
+ task = Task.new
+ task.starting = now
+ task.save!
- # check against Time.local_time, since some platforms will return a Time instead of a DateTime
- assert_equal Time.local_time(*time_values), Task.find(task.id).starting
+ # check against Time.local_time, since some platforms will return a Time instead of a DateTime
+ assert_equal Time.local_time(*time_values), Task.find(task.id).starting
+ end
+ end
end
def test_assign_empty_date_time
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 4bf3c25d28..2d3047c875 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -31,6 +31,20 @@ def current_adapter?(*types)
end
end
+def with_env_tz(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
+ensure
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
+end
+
+def with_active_record_default_timezone(zone)
+ old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
+ yield
+ensure
+ ActiveRecord::Base.default_timezone = old_zone
+end
+
ActiveRecord::Base.connection.class.class_eval do
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /SHOW FIELDS/]
@@ -43,6 +57,18 @@ ActiveRecord::Base.connection.class.class_eval do
alias_method_chain :execute, :query_record
end
+ActiveRecord::Base.connection.class.class_eval {
+ attr_accessor :column_calls
+
+ def columns_with_calls(*args)
+ @column_calls ||= 0
+ @column_calls += 1
+ columns_without_calls(*args)
+ end
+
+ alias_method_chain :columns, :calls
+}
+
unless ENV['FIXTURE_DEBUG']
module ActiveRecord::TestFixtures::ClassMethods
def try_to_load_dependency_with_silence(*args)
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 03a8cc90f6..bcae46c7e8 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -127,16 +127,17 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_add_index_length_limit
good_index_name = 'x' * Person.connection.index_name_length
too_long_index_name = good_index_name + 'x'
- assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
+ assert_raise(ArgumentError) { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
assert !Person.connection.index_name_exists?("people", too_long_index_name, false)
assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) }
assert Person.connection.index_name_exists?("people", good_index_name, false)
+ Person.connection.remove_index("people", :name => good_index_name)
end
def test_remove_nonexistent_index
# we do this by name, so OpenBase is a wash as noted above
unless current_adapter?(:OpenBaseAdapter)
- assert_nothing_raised { Person.connection.remove_index("people", "no_such_index") }
+ assert_raise(ArgumentError) { Person.connection.remove_index("people", "no_such_index") }
end
end
@@ -154,7 +155,7 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_double_add_index
unless current_adapter?(:OpenBaseAdapter)
Person.connection.add_index('people', [:first_name], :name => 'some_idx')
- assert_nothing_raised { Person.connection.add_index('people', [:first_name], :name => 'some_idx') }
+ assert_raise(ArgumentError) { Person.connection.add_index('people', [:first_name], :name => 'some_idx') }
end
end
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index f538d2a326..b571e9a8b8 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -161,7 +161,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
def test_merge_inner_scope_has_priority
Developer.limit(5).scoping do
Developer.limit(10).scoping do
- assert_equal 10, Developer.count
+ assert_equal 10, Developer.all.size
end
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 649fe79fd3..3bc3671b77 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -679,24 +679,36 @@ class RelationTest < ActiveRecord::TestCase
assert_equal Post.order(Post.arel_table[:title]).all, Post.order("title").all
end
- def test_relations_limit_with_conditions_or_limit
- assert_equal Post.limit(2).size, Post.limit(2).all.size
- end
-
def test_order_with_find_with_order
- assert_equal 'zyke', Car.order('name desc').find(:first, :order => 'id').name
+ assert_equal 'zyke', CoolCar.order('name desc').find(:first, :order => 'id').name
+ assert_equal 'zyke', FastCar.order('name desc').find(:first, :order => 'id').name
end
def test_default_scope_order_with_named_scope_order
- assert_equal 'zyke', Car.order_using_new_style.limit(1).first.name
- assert_equal 'zyke', Car.order_using_old_style.limit(1).first.name
+ assert_equal 'zyke', CoolCar.order_using_new_style.limit(1).first.name
+ assert_equal 'zyke', CoolCar.order_using_old_style.limit(1).first.name
+ assert_equal 'zyke', FastCar.order_using_new_style.limit(1).first.name
+ assert_equal 'zyke', FastCar.order_using_old_style.limit(1).first.name
end
def test_order_using_scoping
- car = Car.order('id DESC').scoping do
- Car.find(:first, :order => 'id asc')
+ car1 = CoolCar.order('id DESC').scoping do
+ CoolCar.find(:first, :order => 'id asc')
+ end
+ assert_equal 'zyke', car1.name
+
+ car2 = FastCar.order('id DESC').scoping do
+ FastCar.find(:first, :order => 'id asc')
end
- assert_equal 'zyke', car.name
+ assert_equal 'zyke', car2.name
+ end
+
+ def test_unscoped_block_style
+ assert_equal 'honda', CoolCar.unscoped { CoolCar.order_using_new_style.limit(1).first.name}
+ assert_equal 'honda', CoolCar.unscoped { CoolCar.order_using_old_style.limit(1).first.name}
+
+ assert_equal 'honda', FastCar.unscoped { FastCar.order_using_new_style.limit(1).first.name}
+ assert_equal 'honda', FastCar.unscoped { FastCar.order_using_old_style.limit(1).first.name}
end
def test_intersection_with_array
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index a1e351b54e..e7db3d3423 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -1,4 +1,5 @@
class Car < ActiveRecord::Base
+
has_many :bulbs
has_many :tyres
has_many :engines
@@ -7,9 +8,15 @@ class Car < ActiveRecord::Base
scope :incl_tyres, includes(:tyres)
scope :incl_engines, includes(:engines)
- default_scope :order => 'name desc'
-
scope :order_using_new_style, order('name asc')
scope :order_using_old_style, :order => 'name asc'
end
+
+class CoolCar < Car
+ default_scope :order => 'name desc'
+end
+
+class FastCar < Car
+ default_scope order('name desc')
+end
diff --git a/activerecord/test/models/engine.rb b/activerecord/test/models/engine.rb
index 751c3f02d1..851ff8c22b 100644
--- a/activerecord/test/models/engine.rb
+++ b/activerecord/test/models/engine.rb
@@ -1,3 +1,4 @@
class Engine < ActiveRecord::Base
belongs_to :my_car, :class_name => 'Car', :foreign_key => 'car_id', :counter_cache => :engines_count
end
+