aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG8
-rwxr-xr-xactiverecord/Rakefile4
-rw-r--r--activerecord/activerecord.gemspec3
-rw-r--r--activerecord/lib/active_record/associations.rb17
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb4
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb20
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb24
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb10
-rw-r--r--activerecord/lib/active_record/associations/join_helper.rb2
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb2
-rw-r--r--activerecord/lib/active_record/base.rb53
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb3
-rw-r--r--activerecord/lib/active_record/fixtures.rb73
-rw-r--r--activerecord/lib/active_record/fixtures/file.rb55
-rw-r--r--activerecord/lib/active_record/observer.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb6
-rw-r--r--activerecord/lib/active_record/railtie.rb4
-rw-r--r--activerecord/lib/active_record/railties/controller_runtime.rb4
-rw-r--r--activerecord/lib/active_record/railties/databases.rake8
-rw-r--r--activerecord/lib/active_record/reflection.rb9
-rw-r--r--activerecord/lib/active_record/relation.rb41
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb6
-rw-r--r--activerecord/lib/active_record/serialization.rb2
-rw-r--r--activerecord/lib/active_record/version.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/migration.rb2
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb11
-rw-r--r--activerecord/test/cases/associations/eager_test.rb46
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb42
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb5
-rw-r--r--activerecord/test/cases/associations_test.rb12
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb8
-rw-r--r--activerecord/test/cases/base_test.rb9
-rw-r--r--activerecord/test/cases/fixtures/file_test.rb83
-rw-r--r--activerecord/test/cases/fixtures_test.rb6
-rw-r--r--activerecord/test/cases/mass_assignment_security_test.rb48
-rw-r--r--activerecord/test/cases/reflection_test.rb4
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb8
-rw-r--r--activerecord/test/fixtures/categories_ordered.yml2
-rw-r--r--activerecord/test/fixtures/parrots.yml2
-rw-r--r--activerecord/test/models/aircraft.rb1
-rw-r--r--activerecord/test/models/author.rb3
-rw-r--r--activerecord/test/models/developer.rb50
-rw-r--r--activerecord/test/models/post.rb6
51 files changed, 575 insertions, 158 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 502a7e43de..b05d3970c7 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,13 @@
*Rails 3.1.0 (unreleased)*
+* AR#pluralize_table_names can be used to singularize/pluralize table name of an individual model:
+
+ class User < ActiveRecord::Base
+ self.pluralize_table_names = false
+ end
+
+ Previously this could only be set globally for all models through ActiveRecord::Base.pluralize_table_names. [Guillermo Iguaran]
+
* Add block setting of attributes to singular associations:
class User < ActiveRecord::Base
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index e414c4fb1c..346c7e8142 100755
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -1,7 +1,7 @@
#!/usr/bin/env rake
require 'rake/testtask'
require 'rake/packagetask'
-require 'rake/gempackagetask'
+require 'rubygems/package_task'
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
@@ -171,7 +171,7 @@ task :rebuild_frontbase_databases => 'frontbase:rebuild_databases'
spec = eval(File.read('activerecord.gemspec'))
-Rake::GemPackageTask.new(spec) do |p|
+Gem::PackageTask.new(spec) do |p|
p.gem_spec = spec
end
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index 335127f38e..b7e23faa09 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -12,9 +12,8 @@ Gem::Specification.new do |s|
s.author = 'David Heinemeier Hansson'
s.email = 'david@loudthinking.com'
s.homepage = 'http://www.rubyonrails.org'
- s.rubyforge_project = 'activerecord'
- s.files = Dir['CHANGELOG', 'README.rdoc', 'examples/**/*', 'lib/**/*']
+ s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'examples/**/*', 'lib/**/*']
s.require_path = 'lib'
s.extra_rdoc_files = %w( README.rdoc )
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 2116a94980..1c7209e64e 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -33,7 +33,7 @@ module ActiveRecord
class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
def initialize(owner_class_name, reflection, source_reflection)
- super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
+ super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
end
end
@@ -48,7 +48,7 @@ module ActiveRecord
through_reflection = reflection.through_reflection
source_reflection_names = reflection.source_reflection_names
source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect }
- super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
+ super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
end
end
@@ -96,7 +96,7 @@ module ActiveRecord
class ReadOnlyAssociation < ActiveRecordError #:nodoc:
def initialize(reflection)
- super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
+ super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
end
end
@@ -457,12 +457,13 @@ module ActiveRecord
# has_many :people, :extend => [FindOrCreateByNameExtension, FindRecentExtension]
# end
#
- # Some extensions can only be made to work with knowledge of the association proxy's internals.
- # Extensions can access relevant state using accessors on the association proxy:
+ # Some extensions can only be made to work with knowledge of the association's internals.
+ # Extensions can access relevant state using the following methods (where 'items' is the
+ # name of the association):
#
- # * +proxy_owner+ - Returns the object the association is part of.
- # * +proxy_reflection+ - Returns the reflection object that describes the association.
- # * +proxy_target+ - Returns the associated object for +belongs_to+ and +has_one+, or
+ # * +record.association(:items).owner+ - Returns the object the association is part of.
+ # * +record.association(:items).reflection+ - Returns the reflection object that describes the association.
+ # * +record.association(:items).target+ - Returns the associated object for +belongs_to+ and +has_one+, or
# the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
#
# === Association Join Models
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 44e2ee141e..bd98cf2f46 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -49,8 +49,8 @@ module ActiveRecord
end
end
- def pluralize(table_name)
- ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name.to_s
+ def pluralize(table_name, base)
+ base.pluralize_table_names ? table_name.to_s.pluralize : table_name.to_s
end
private
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index ab102b2b8f..94847bc2ae 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -75,10 +75,16 @@ module ActiveRecord
foreign_key = reflection.active_record_primary_key
end
+ conditions = self.conditions[i]
+
if reflection == chain.last
scope = scope.where(table[key].eq(owner[foreign_key]))
- conditions[i].each do |condition|
+ if reflection.type
+ scope = scope.where(table[reflection.type].eq(owner.class.base_class.name))
+ end
+
+ conditions.each do |condition|
if options[:through] && condition.is_a?(Hash)
condition = { table.name => condition }
end
@@ -87,12 +93,16 @@ module ActiveRecord
end
else
constraint = table[key].eq(foreign_table[foreign_key])
- join = join(foreign_table, constraint)
- scope = scope.joins(join)
+ if reflection.type
+ type = chain[i + 1].klass.base_class.name
+ constraint = constraint.and(table[reflection.type].eq(type))
+ end
+
+ scope = scope.joins(join(foreign_table, constraint))
- unless conditions[i].empty?
- scope = scope.where(sanitize(conditions[i], table))
+ unless conditions.empty?
+ scope = scope.where(sanitize(conditions, table))
end
end
end
diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
index 1ca448236e..198ad06360 100644
--- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
@@ -19,7 +19,7 @@ module ActiveRecord
def klass
type = owner[reflection.foreign_type]
- type && type.constantize
+ type.presence && type.constantize
end
def raise_on_type_mismatch(record)
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index adfc71d435..8415942a1a 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -123,6 +123,30 @@ module ActiveRecord
method_missing(:new, *args, &block)
end
end
+
+ def proxy_owner
+ ActiveSupport::Deprecation.warn(
+ "Calling record.#{@association.reflection.name}.proxy_owner is deprecated. Please use " \
+ "record.association(:#{@association.reflection.name}).owner instead."
+ )
+ @association.owner
+ end
+
+ def proxy_target
+ ActiveSupport::Deprecation.warn(
+ "Calling record.#{@association.reflection.name}.proxy_target is deprecated. Please use " \
+ "record.association(:#{@association.reflection.name}).target instead."
+ )
+ @association.target
+ end
+
+ def proxy_reflection
+ ActiveSupport::Deprecation.warn(
+ "Calling record.#{@association.reflection.name}.proxy_reflection is deprecated. Please use " \
+ "record.association(:#{@association.reflection.name}).reflection instead."
+ )
+ @association.reflection
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index c32753782f..03963ab060 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -62,6 +62,7 @@ module ActiveRecord
def join_to(relation)
tables = @tables.dup
foreign_table = parent_table
+ foreign_klass = parent.active_record
# The chain starts with the target table, but we want to end with it here (makes
# more sense in this context), so we reverse
@@ -91,14 +92,17 @@ module ActiveRecord
constraint = build_constraint(reflection, table, key, foreign_table, foreign_key)
- unless conditions[i].empty?
- constraint = constraint.and(sanitize(conditions[i], table))
+ conditions = self.conditions[i].dup
+ conditions << { reflection.type => foreign_klass.base_class.name } if reflection.type
+
+ unless conditions.empty?
+ constraint = constraint.and(sanitize(conditions, table))
end
relation.from(join(table, constraint))
# The current table in this iteration becomes the foreign table in the next
- foreign_table = table
+ foreign_table, foreign_klass = table, reflection.klass
end
relation
diff --git a/activerecord/lib/active_record/associations/join_helper.rb b/activerecord/lib/active_record/associations/join_helper.rb
index eae546e76e..87e33891a5 100644
--- a/activerecord/lib/active_record/associations/join_helper.rb
+++ b/activerecord/lib/active_record/associations/join_helper.rb
@@ -32,7 +32,7 @@ module ActiveRecord
end
def table_alias_for(reflection, join = false)
- name = alias_tracker.pluralize(reflection.name)
+ name = alias_tracker.pluralize(reflection.name, reflection.active_record)
name << "_#{alias_suffix}"
name << "_join" if join
name
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index 53c5c3cedf..81172179e0 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -16,7 +16,7 @@ module ActiveRecord
chain[1..-1].each do |reflection|
scope = scope.merge(
reflection.klass.scoped.with_default_scope.
- except(:select, :create_with)
+ except(:select, :create_with, :includes)
)
end
scope
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index cd16b8d3ca..cb2c621c79 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -393,8 +393,8 @@ module ActiveRecord #:nodoc:
# Indicates whether table names should be the pluralized versions of the corresponding class names.
# If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
# See table_name for the full rules on table/class naming. This is true, by default.
- cattr_accessor :pluralize_table_names, :instance_writer => false
- @@pluralize_table_names = true
+ class_attribute :pluralize_table_names, :instance_writer => false
+ self.pluralize_table_names = true
##
# :singleton-method:
@@ -428,6 +428,10 @@ module ActiveRecord #:nodoc:
class_attribute :default_scopes, :instance_writer => false
self.default_scopes = []
+ # Boolean flag to prevent infinite recursion when evaluating default scopes
+ class_attribute :apply_default_scope, :instance_writer => false
+ self.apply_default_scope = true
+
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
class_attribute :serialized_attributes
@@ -577,15 +581,25 @@ module ActiveRecord #:nodoc:
#
# ==== Examples
#
- # class Invoice < ActiveRecord::Base; end;
+ # class Invoice < ActiveRecord::Base
+ # end
+ #
# file class table_name
# invoice.rb Invoice invoices
#
- # class Invoice < ActiveRecord::Base; class Lineitem < ActiveRecord::Base; end; end;
+ # class Invoice < ActiveRecord::Base
+ # class Lineitem < ActiveRecord::Base
+ # end
+ # end
+ #
# file class table_name
# invoice.rb Invoice::Lineitem invoice_lineitems
#
- # module Invoice; class Lineitem < ActiveRecord::Base; end; end;
+ # module Invoice
+ # class Lineitem < ActiveRecord::Base
+ # end
+ # end
+ #
# file class table_name
# invoice/lineitem.rb Invoice::Lineitem lineitems
#
@@ -1251,11 +1265,14 @@ MSG
self.default_scopes = default_scopes + [scope]
end
+ # The apply_default_scope flag is used to prevent an infinite recursion situation where
+ # a default scope references a scope which has a default scope which references a scope...
def build_default_scope #:nodoc:
+ return unless apply_default_scope
+ self.apply_default_scope = false
+
if method(:default_scope).owner != Base.singleton_class
- # Use relation.scoping to ensure we ignore whatever the current value of
- # self.current_scope may be.
- relation.scoping { default_scope }
+ default_scope
elsif default_scopes.any?
default_scopes.inject(relation) do |default_scope, scope|
if scope.is_a?(Hash)
@@ -1267,6 +1284,8 @@ MSG
end
end
end
+ ensure
+ self.apply_default_scope = true
end
# Returns the class type of the record using the current module as a prefix. So descendants of
@@ -1851,12 +1870,16 @@ 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)
- "#{name}: #{attribute_for_inspect(name)}"
- end
- }.compact.join(", ")
- "#<#{self.class} #{attributes_as_nice_string}>"
+ inspection = if @attributes
+ self.class.column_names.collect { |name|
+ if has_attribute?(name)
+ "#{name}: #{attribute_for_inspect(name)}"
+ end
+ }.compact.join(", ")
+ else
+ "not initialized"
+ end
+ "#<#{self.class} #{inspection}>"
end
protected
@@ -2018,7 +2041,7 @@ MSG
def extract_callstack_for_multiparameter_attributes(pairs)
attributes = { }
- for pair in pairs
+ pairs.each do |pair|
multiparameter_name, value = pair
attribute_name = multiparameter_name.split("(").first
attributes[attribute_name] = {} unless attributes.include?(attribute_name)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index c2f051c33a..8ffd40f7e5 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -262,7 +262,7 @@ module ActiveRecord
else
clear_stale_cached_connections!
if @size == @checked_out.size
- raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
+ raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
end
end
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 2e6416412e..a754f46af0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -564,7 +564,7 @@ module ActiveRecord
def columns_for_remove(table_name, *column_names)
column_names = column_names.flatten
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.blank?
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.blank?
column_names.map {|column_name| quote_column_name(column_name) }
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 4e3d8a096f..3eddb69e73 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -189,7 +189,7 @@ module ActiveRecord
def new_time(year, mon, mday, hour, min, sec, microsec)
# Treat 0000-00-00 00:00:00 as nil.
- return nil if year.nil? || year == 0
+ return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index ac2da73a84..bbaac29e5a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -280,7 +280,7 @@ module ActiveRecord
end
rescue ActiveRecord::StatementInvalid => exception
if exception.message.split(":").first =~ /Packets out of order/
- raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
+ raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
else
raise
end
@@ -662,7 +662,7 @@ module ActiveRecord
result = @connection.query(sql)
rescue ActiveRecord::StatementInvalid => exception
if exception.message.split(":").first =~ /Packets out of order/
- raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
+ raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
else
raise
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index a7ad85e477..8bd9099a36 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -1,5 +1,4 @@
require 'active_record/connection_adapters/abstract_adapter'
-require 'active_support/core_ext/kernel/requires'
require 'active_support/core_ext/object/blank'
require 'set'
@@ -432,7 +431,7 @@ module ActiveRecord
end
rescue ActiveRecord::StatementInvalid => exception
if exception.message.split(":").first =~ /Packets out of order/
- raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
+ raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
else
raise
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 9e7b3d5449..8b48c055ac 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1,5 +1,4 @@
require 'active_record/connection_adapters/abstract_adapter'
-require 'active_support/core_ext/kernel/requires'
require 'active_support/core_ext/object/blank'
# Make sure we're using pg high enough for PGResult#values
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index d2785b234a..3c6f52e0fa 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -1,5 +1,4 @@
require 'active_record/connection_adapters/abstract_adapter'
-require 'active_support/core_ext/kernel/requires'
module ActiveRecord
module ConnectionAdapters #:nodoc:
@@ -320,7 +319,7 @@ module ActiveRecord
end
def remove_column(table_name, *column_names) #:nodoc:
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
column_names.flatten.each do |column_name|
alter_table(table_name) do |definition|
definition.columns.delete(definition[column_name])
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index ba9dcf6936..612a904031 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -6,7 +6,6 @@ rescue LoadError
end
require 'yaml'
-require 'csv'
require 'zlib'
require 'active_support/dependencies'
require 'active_support/core_ext/array/wrap'
@@ -14,6 +13,7 @@ require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/logger'
require 'active_support/ordered_hash'
require 'active_support/core_ext/module/deprecation'
+require 'active_record/fixtures/file'
if defined? ActiveRecord
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
@@ -385,11 +385,11 @@ class FixturesFileNotFound < StandardError; end
#
# first:
# name: Smurf
-# <<: *DEFAULTS
+# *DEFAULTS
#
# second:
# name: Fraggle
-# <<: *DEFAULTS
+# *DEFAULTS
#
# Any fixture labeled "DEFAULTS" is safely ignored.
@@ -477,7 +477,7 @@ module ActiveRecord
connection,
table_name,
class_names[table_name.to_sym] || table_name.classify,
- File.join(fixtures_directory, path))
+ ::File.join(fixtures_directory, path))
end
all_loaded_fixtures.update(fixtures_map)
@@ -655,74 +655,33 @@ module ActiveRecord
end
def read_fixture_files
- if File.file?(yaml_file_path)
+ if ::File.file?(yaml_file_path)
read_yaml_fixture_files
- elsif File.file?(csv_file_path)
- read_csv_fixture_files
else
- raise FixturesFileNotFound, "Could not find #{yaml_file_path} or #{csv_file_path}"
+ raise FixturesFileNotFound, "Could not find #{yaml_file_path}"
end
end
def read_yaml_fixture_files
- yaml_string = (Dir["#{@fixture_path}/**/*.yml"].select { |f|
- File.file?(f)
- } + [yaml_file_path]).map { |file_path| IO.read(file_path) }.join
-
- if yaml = parse_yaml_string(yaml_string)
- # If the file is an ordered map, extract its children.
- yaml_value =
- if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
- yaml.value
- else
- [yaml]
- end
-
- yaml_value.each do |fixture|
- raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
- fixture.each do |name, data|
- unless data
- raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
- end
-
- fixtures[name] = ActiveRecord::Fixture.new(data, model_class)
+ yaml_files = Dir["#{@fixture_path}/**/*.yml"].select { |f|
+ ::File.file?(f)
+ } + [yaml_file_path]
+
+ yaml_files.each do |file|
+ Fixtures::File.open(file) do |fh|
+ fh.each do |name, row|
+ fixtures[name] = ActiveRecord::Fixture.new(row, model_class)
end
end
end
end
- def read_csv_fixture_files
- reader = CSV.parse(erb_render(IO.read(csv_file_path)))
- header = reader.shift
- i = 0
- reader.each do |row|
- data = {}
- row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
- fixtures["#{@class_name.to_s.underscore}_#{i+=1}"] = ActiveRecord::Fixture.new(data, model_class)
- end
- end
- deprecate :read_csv_fixture_files
-
def yaml_file_path
"#{@fixture_path}.yml"
end
- def csv_file_path
- @fixture_path + ".csv"
- end
-
def yaml_fixtures_key(path)
- File.basename(@fixture_path).split(".").first
- end
-
- def parse_yaml_string(fixture_content)
- YAML::load(erb_render(fixture_content))
- rescue => error
- raise Fixture::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}"
- end
-
- def erb_render(fixture_content)
- ERB.new(fixture_content).result
+ ::File.basename(@fixture_path).split(".").first
end
end
@@ -798,7 +757,7 @@ module ActiveRecord
def fixtures(*fixture_names)
if fixture_names.first == :all
- fixture_names = Dir["#{fixture_path}/**/*.{yml,csv}"]
+ fixture_names = Dir["#{fixture_path}/**/*.{yml}"]
fixture_names.map! { |f| f[(fixture_path.size + 1)..-5] }
else
fixture_names = fixture_names.flatten.map { |n| n.to_s }
diff --git a/activerecord/lib/active_record/fixtures/file.rb b/activerecord/lib/active_record/fixtures/file.rb
new file mode 100644
index 0000000000..04f494db2c
--- /dev/null
+++ b/activerecord/lib/active_record/fixtures/file.rb
@@ -0,0 +1,55 @@
+begin
+ require 'psych'
+rescue LoadError
+end
+
+require 'erb'
+require 'yaml'
+
+module ActiveRecord
+ class Fixtures
+ class File
+ include Enumerable
+
+ ##
+ # Open a fixture file named +file+. When called with a block, the block
+ # is called with the filehandle and the filehandle is automatically closed
+ # when the block finishes.
+ def self.open(file)
+ x = new file
+ block_given? ? yield(x) : x
+ end
+
+ def initialize(file)
+ @file = file
+ @rows = nil
+ end
+
+ def each(&block)
+ rows.each(&block)
+ end
+
+ private
+ def rows
+ return @rows if @rows
+
+ data = YAML.load(render(IO.read(@file)))
+ @rows = data ? validate(data).to_a : []
+ end
+
+ def render(content)
+ ERB.new(content).result
+ end
+
+ # Validate our unmarshalled data.
+ def validate(data)
+ unless Hash === data || YAML::Omap === data
+ raise Fixture::FormatError, 'fixture is not a hash'
+ end
+
+ raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
+ data
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
index c723436330..5a5351b517 100644
--- a/activerecord/lib/active_record/observer.rb
+++ b/activerecord/lib/active_record/observer.rb
@@ -11,7 +11,7 @@ module ActiveRecord
#
# class CommentObserver < ActiveRecord::Observer
# def after_save(comment)
- # Notifications.deliver_comment("admin@do.com", "New comment was posted", comment)
+ # Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
# end
# end
#
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 221823364c..4ec0431b8c 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -33,7 +33,11 @@ module ActiveRecord
# +save+ returns +false+. See ActiveRecord::Callbacks for further
# details.
def save(*)
- create_or_update
+ begin
+ create_or_update
+ rescue ActiveRecord::RecordInvalid
+ false
+ end
end
# Saves the model.
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index bae2ded244..47133e77e8 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -29,8 +29,8 @@ module ActiveRecord
# When loading console, force ActiveRecord::Base to be loaded
# to avoid cross references when loading a constant for the
# first time. Also, make it output to STDERR.
- console do |sandbox|
- require "active_record/railties/console_sandbox" if sandbox
+ console do |app|
+ require "active_record/railties/console_sandbox" if app.sandbox?
ActiveRecord::Base.logger = Logger.new(STDERR)
end
diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb
index fb3fd34665..c5db9b4625 100644
--- a/activerecord/lib/active_record/railties/controller_runtime.rb
+++ b/activerecord/lib/active_record/railties/controller_runtime.rb
@@ -32,7 +32,9 @@ module ActiveRecord
def append_info_to_payload(payload)
super
- payload[:db_runtime] = db_runtime
+ if ActiveRecord::Base.connected?
+ payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
+ end
end
module ClassMethods
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 85ad43b35f..6f8f84d50b 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -27,7 +27,7 @@ db_namespace = namespace :db do
#
# development:
# database: blog_development
- # <<: *defaults
+ # *defaults
next unless config['database']
# Only connect to local databases
local_database?(config) { create_database(config) }
@@ -296,7 +296,7 @@ db_namespace = namespace :db do
end
namespace :fixtures do
- desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
+ desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
task :load => :environment do
require 'active_record/fixtures'
@@ -338,6 +338,7 @@ db_namespace = namespace :db do
task :dump => :load_config do
require 'active_record/schema_dumper'
File.open(ENV['SCHEMA'] || "#{Rails.root}/db/schema.rb", "w") do |file|
+ ActiveRecord::Base.establish_connection(Rails.env)
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
end
db_namespace['schema:dump'].reenable
@@ -480,8 +481,7 @@ db_namespace = namespace :db do
# desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
task :create => :environment do
raise 'Task unavailable to this database (no migration support)' unless ActiveRecord::Base.connection.supports_migrations?
- require 'rails/generators'
- Rails::Generators.configure!
+ Rails.application.load_generators
require 'rails/generators/rails/session_migration/session_migration_generator'
Rails::Generators::SessionMigrationGenerator.start [ ENV['MIGRATION'] || 'add_sessions_table' ]
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index bbff7c015e..a058e880ca 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -212,7 +212,7 @@ module ActiveRecord
end
def type
- @type ||= "#{options[:as]}_type"
+ @type ||= options[:as] && "#{options[:as]}_type"
end
def primary_key_column
@@ -280,9 +280,7 @@ module ActiveRecord
# in the #chain. The inside arrays are simply conditions (and each condition may itself be
# a hash, array, arel predicate, etc...)
def conditions
- conditions = [options[:conditions]].compact
- conditions << { type => active_record.base_class.name } if options[:as]
- [conditions]
+ [[options[:conditions]].compact]
end
alias :source_macro :macro
@@ -378,7 +376,8 @@ module ActiveRecord
# Holds all the meta-data about a :through association as it was specified
# in the Active Record class.
class ThroughReflection < AssociationReflection #:nodoc:
- delegate :foreign_key, :foreign_type, :association_foreign_key, :active_record_primary_key, :to => :source_reflection
+ delegate :foreign_key, :foreign_type, :association_foreign_key,
+ :active_record_primary_key, :type, :to => :source_reflection
# Gets the source of the through reflection. It checks both a singularized
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 8f801e6dad..d88e2693b6 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -102,24 +102,30 @@ module ActiveRecord
def to_a
return @records if loaded?
- @records = if @readonly_value.nil? && !@klass.locking_enabled?
- eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values)
- else
- IdentityMap.without do
+ default_scoped = with_default_scope
+
+ if default_scoped.equal?(self)
+ @records = if @readonly_value.nil? && !@klass.locking_enabled?
eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values)
+ else
+ IdentityMap.without do
+ eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values)
+ end
end
- end
- preload = @preload_values
- preload += @includes_values unless eager_loading?
- preload.each do |associations|
- ActiveRecord::Associations::Preloader.new(@records, associations).run
- end
+ preload = @preload_values
+ preload += @includes_values unless eager_loading?
+ preload.each do |associations|
+ ActiveRecord::Associations::Preloader.new(@records, associations).run
+ end
- # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
- # are JOINS and no explicit SELECT.
- readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
- @records.each { |record| record.readonly! } if readonly
+ # @readonly_value is true only if set explicitly. @implicit_readonly is true if there
+ # are JOINS and no explicit SELECT.
+ readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value
+ @records.each { |record| record.readonly! } if readonly
+ else
+ @records = default_scoped.to_a
+ end
@loaded = true
@records
@@ -418,9 +424,10 @@ module ActiveRecord
end
def with_default_scope #:nodoc:
- if default_scoped?
- default_scope = @klass.send(:build_default_scope)
- default_scope ? default_scope.merge(self) : self
+ if default_scoped? && default_scope = klass.send(:build_default_scope)
+ default_scope = default_scope.merge(self)
+ default_scope.default_scoped = false
+ default_scope
else
self
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 35c72f9536..c6e8762b4a 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -226,7 +226,7 @@ module ActiveRecord
end
def apply_join_dependency(relation, join_dependency)
- for association in join_dependency.join_associations
+ join_dependency.join_associations.each do |association|
relation = association.join_relation(relation)
end
@@ -264,6 +264,7 @@ module ActiveRecord
end
def find_or_instantiator_by_attributes(match, attributes, *args)
+ options = args.size > 1 && args.last(2).all?{ |a| a.is_a?(Hash) } ? args.extract_options! : {}
protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
args.each_with_index do |arg, i|
if arg.is_a?(Hash)
@@ -278,8 +279,7 @@ module ActiveRecord
record = where(conditions).first
unless record
- record = @klass.new do |r|
- r.assign_attributes(protected_attributes_for_create)
+ record = @klass.new(protected_attributes_for_create, options) do |r|
r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
end
yield(record) if block_given?
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index 2bde06f562..be4354ce6a 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -37,7 +37,7 @@ module ActiveRecord #:nodoc:
include_has_options = include_associations.is_a?(Hash)
associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
- for association in associations
+ associations.each do |association|
records = case self.class.reflect_on_association(association).macro
when :has_many, :has_and_belongs_to_many
send(association).to_a
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index 2c20dd997f..43fe1cdee5 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -3,7 +3,7 @@ module ActiveRecord
MAJOR = 3
MINOR = 1
TINY = 0
- PRE = "beta1"
+ PRE = "rc1"
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
diff --git a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb b/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
index 4f81a52fd0..851930344a 100644
--- a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
@@ -1,7 +1,7 @@
class <%= migration_class_name %> < ActiveRecord::Migration
def change
create_table :<%= table_name %> do |t|
-<% for attribute in attributes -%>
+<% attributes.each do |attribute| -%>
t.<%= attribute.type %> :<%= attribute.name %>
<% end -%>
<% if options[:timestamps] %>
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index b993bf6e90..5a900e0605 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -158,6 +158,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_not_nil Company.find(3).firm_with_condition, "Microsoft should have a firm"
end
+ def test_polymorphic_association_class
+ sponsor = Sponsor.new
+ assert_nil sponsor.association(:sponsorable).send(:klass)
+
+ sponsor.sponsorable_type = '' # the column doesn't have to be declared NOT NULL
+ assert_nil sponsor.association(:sponsorable).send(:klass)
+
+ sponsor.sponsorable = Member.new :name => "Bert"
+ assert_equal Member, sponsor.association(:sponsorable).send(:klass)
+ end
+
def test_with_polymorphic_and_condition
sponsor = Sponsor.create
member = Member.create :name => "Bert"
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 3e92a77830..5028cee32d 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -448,6 +448,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal post_tags, eager_post_tags
end
+ def test_eager_with_has_many_through_join_model_ignores_default_includes
+ assert_nothing_raised do
+ authors(:david).comments_on_posts_with_default_include.to_a
+ end
+ end
+
def test_eager_with_has_many_and_limit
posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
assert_equal 2, posts.size
@@ -675,6 +681,46 @@ class EagerAssociationTest < ActiveRecord::TestCase
}
end
+ def test_eager_with_default_scope
+ developer = EagerDeveloperWithDefaultScope.where(:name => 'David').first
+ projects = Project.order(:id).all
+ assert_no_queries do
+ assert_equal(projects, developer.projects)
+ end
+ end
+
+ def test_eager_with_default_scope_as_class_method
+ developer = EagerDeveloperWithClassMethodDefaultScope.where(:name => 'David').first
+ projects = Project.order(:id).all
+ assert_no_queries do
+ assert_equal(projects, developer.projects)
+ end
+ end
+
+ def test_eager_with_default_scope_as_lambda
+ developer = EagerDeveloperWithLambdaDefaultScope.where(:name => 'David').first
+ projects = Project.order(:id).all
+ assert_no_queries do
+ assert_equal(projects, developer.projects)
+ end
+ end
+
+ def test_eager_with_default_scope_as_block
+ developer = EagerDeveloperWithBlockDefaultScope.where(:name => 'David').first
+ projects = Project.order(:id).all
+ assert_no_queries do
+ assert_equal(projects, developer.projects)
+ end
+ end
+
+ def test_eager_with_default_scope_as_callable
+ developer = EagerDeveloperWithCallableDefaultScope.where(:name => 'David').first
+ projects = Project.order(:id).all
+ assert_no_queries do
+ assert_equal(projects, developer.projects)
+ end
+ end
+
def find_all_ordered(className, include=nil)
className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 522ac56d82..43974fd895 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -11,6 +11,7 @@ require 'models/comment'
require 'models/person'
require 'models/reader'
require 'models/tagging'
+require 'models/tag'
require 'models/invoice'
require 'models/line_item'
require 'models/car'
@@ -1468,4 +1469,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
bulb = car.bulbs.build({ :bulb_type => :custom }, :as => :admin)
assert_equal CustomBulb, bulb.class
end
+
+ def test_abstract_class_with_polymorphic_has_many
+ post = SubStiPost.create! :title => "fooo", :body => "baa"
+ tagging = Tagging.create! :taggable => post
+ assert_equal [tagging], post.taggings
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 89117593fd..877148bd5e 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -766,4 +766,46 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [category.name], post.named_category_ids # checks when target loaded
assert_equal [category.name], post.reload.named_category_ids # checks when target no loaded
end
+
+ def test_create_should_not_raise_exception_when_join_record_has_errors
+ repair_validations(Categorization) do
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
+ Category.create(:name => 'Fishing', :authors => [Author.first])
+ end
+ end
+
+ def test_save_should_not_raise_exception_when_join_record_has_errors
+ repair_validations(Categorization) do
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
+ c = Category.create(:name => 'Fishing', :authors => [Author.first])
+ c.save
+ end
+ end
+
+ def test_create_bang_should_raise_exception_when_join_record_has_errors
+ repair_validations(Categorization) do
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
+ assert_raises(ActiveRecord::RecordInvalid) do
+ Category.create!(:name => 'Fishing', :authors => [Author.first])
+ end
+ end
+ end
+
+ def test_save_bang_should_raise_exception_when_join_record_has_errors
+ repair_validations(Categorization) do
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
+ c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ assert_raises(ActiveRecord::RecordInvalid) do
+ c.save!
+ end
+ end
+ end
+
+ def test_create_bang_returns_falsy_when_join_record_has_errors
+ repair_validations(Categorization) do
+ Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
+ c = Category.new(:name => 'Fishing', :authors => [Author.first])
+ assert !c.save
+ end
+ end
end
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 8e23ab78be..b59ce4efeb 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -708,12 +708,9 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_with_pluralize_table_names_false
- engine = Engine.create(:car_id => 1)
- Aircraft.pluralize_table_names = false
+ engine = Engine.create!(:car_id => 1)
aircraft = Aircraft.create!(:name => "Airbus 380", :id => 1)
assert_equal aircraft.engines, [engine]
- ensure
- ActiveRecord::Base.pluralize_table_names = true
end
private
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 49d82ba2df..38d439d68a 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -203,6 +203,18 @@ class AssociationProxyTest < ActiveRecord::TestCase
assert_equal david.projects, david.projects.reload.reload
end
end
+
+ # Tests that proxy_owner, proxy_target and proxy_reflection are implement as deprecated methods
+ def test_proxy_deprecations
+ david = developers(:david)
+ david.projects.load_target
+
+ [:owner, :target, :reflection].each do |name|
+ assert_deprecated do
+ assert_equal david.association(:projects).send(name), david.projects.send("proxy_#{name}")
+ end
+ end
+ end
end
class OverridingAssociationsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 5074ae50ab..54c4d4ae90 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -109,6 +109,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_respond_to topic, :title
end
+ # IRB inspects the return value of "MyModel.allocate"
+ # by inspecting it.
+ def test_allocated_object_can_be_inspected
+ topic = Topic.allocate
+ assert_nothing_raised { topic.inspect }
+ assert topic.inspect, "#<Topic not initialized>"
+ end
+
def test_array_content
topic = Topic.new
topic.content = %w( one two three )
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index bfb66f07da..87ce537e0e 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -367,6 +367,15 @@ class BasicsTest < ActiveRecord::TestCase
GUESSED_CLASSES.each(&:reset_table_name)
end
+ def test_singular_table_name_guesses_for_individual_table
+ CreditCard.pluralize_table_names = false
+ CreditCard.reset_table_name
+ assert_equal "credit_card", CreditCard.table_name
+ assert_equal "categories", Category.table_name
+ ensure
+ CreditCard.pluralize_table_names = true
+ CreditCard.reset_table_name
+ end
if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
def test_update_all_with_order_and_limit
diff --git a/activerecord/test/cases/fixtures/file_test.rb b/activerecord/test/cases/fixtures/file_test.rb
new file mode 100644
index 0000000000..8dbf92ae9a
--- /dev/null
+++ b/activerecord/test/cases/fixtures/file_test.rb
@@ -0,0 +1,83 @@
+require "cases/helper"
+require 'tempfile'
+
+module ActiveRecord
+ class Fixtures
+ class FileTest < ActiveRecord::TestCase
+ def test_open
+ fh = File.open(::File.join(FIXTURES_ROOT, "accounts.yml"))
+ assert_equal 6, fh.to_a.length
+ end
+
+ def test_open_with_block
+ called = false
+ File.open(::File.join(FIXTURES_ROOT, "accounts.yml")) do |fh|
+ called = true
+ assert_equal 6, fh.to_a.length
+ end
+ assert called, 'block called'
+ end
+
+ def test_names
+ File.open(::File.join(FIXTURES_ROOT, "accounts.yml")) do |fh|
+ assert_equal ["signals37",
+ "unknown",
+ "rails_core_account",
+ "last_account",
+ "rails_core_account_2",
+ "odegy_account"].sort, fh.to_a.map(&:first).sort
+ end
+ end
+
+ def test_values
+ File.open(::File.join(FIXTURES_ROOT, "accounts.yml")) do |fh|
+ assert_equal [1,2,3,4,5,6].sort, fh.to_a.map(&:last).map { |x|
+ x['id']
+ }.sort
+ end
+ end
+
+ def test_erb_processing
+ File.open(::File.join(FIXTURES_ROOT, "developers.yml")) do |fh|
+ devs = Array.new(8) { |i| "dev_#{i + 3}" }
+ assert_equal [], devs - fh.to_a.map(&:first)
+ end
+ end
+
+ def test_empty_file
+ tmp_yaml ['empty', 'yml'], '' do |t|
+ assert_equal [], File.open(t.path) { |fh| fh.to_a }
+ end
+ end
+
+ # A valid YAML file is not necessarily a value Fixture file. Make sure
+ # an exception is raised if the format is not valid Fixture format.
+ def test_wrong_fixture_format_string
+ tmp_yaml ['empty', 'yml'], 'qwerty' do |t|
+ assert_raises(ActiveRecord::Fixture::FormatError) do
+ File.open(t.path) { |fh| fh.to_a }
+ end
+ end
+ end
+
+ def test_wrong_fixture_format_nested
+ tmp_yaml ['empty', 'yml'], 'one: two' do |t|
+ assert_raises(ActiveRecord::Fixture::FormatError) do
+ File.open(t.path) { |fh| fh.to_a }
+ end
+ end
+ end
+
+ private
+ def tmp_yaml(name, contents)
+ t = Tempfile.new name
+ t.binmode
+ t.write contents
+ t.close
+ yield t
+ ensure
+ t.close true
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index b0bd9c5763..306b437fb3 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -174,12 +174,6 @@ class FixturesTest < ActiveRecord::TestCase
end
end
- def test_empty_csv_fixtures
- assert_deprecated do
- assert_not_nil ActiveRecord::Fixtures.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/csv/accounts")
- end
- end
-
def test_omap_fixtures
assert_nothing_raised do
fixtures = ActiveRecord::Fixtures.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered")
diff --git a/activerecord/test/cases/mass_assignment_security_test.rb b/activerecord/test/cases/mass_assignment_security_test.rb
index 765033852d..33737e12a8 100644
--- a/activerecord/test/cases/mass_assignment_security_test.rb
+++ b/activerecord/test/cases/mass_assignment_security_test.rb
@@ -239,6 +239,54 @@ class MassAssignmentSecurityTest < ActiveRecord::TestCase
end
end
+ def test_find_or_initialize_by_with_attr_accessible_attributes
+ p = TightPerson.find_or_initialize_by_first_name('Josh', attributes_hash)
+
+ assert_default_attributes(p)
+ end
+
+ def test_find_or_initialize_by_with_admin_role_with_attr_accessible_attributes
+ p = TightPerson.find_or_initialize_by_first_name('Josh', attributes_hash, :as => :admin)
+
+ assert_admin_attributes(p)
+ end
+
+ def test_find_or_initialize_by_with_attr_protected_attributes
+ p = LoosePerson.find_or_initialize_by_first_name('Josh', attributes_hash)
+
+ assert_default_attributes(p)
+ end
+
+ def test_find_or_initialize_by_with_admin_role_with_attr_protected_attributes
+ p = LoosePerson.find_or_initialize_by_first_name('Josh', attributes_hash, :as => :admin)
+
+ assert_admin_attributes(p)
+ end
+
+ def test_find_or_create_by_with_attr_accessible_attributes
+ p = TightPerson.find_or_create_by_first_name('Josh', attributes_hash)
+
+ assert_default_attributes(p, true)
+ end
+
+ def test_find_or_create_by_with_admin_role_with_attr_accessible_attributes
+ p = TightPerson.find_or_create_by_first_name('Josh', attributes_hash, :as => :admin)
+
+ assert_admin_attributes(p, true)
+ end
+
+ def test_find_or_create_by_with_attr_protected_attributes
+ p = LoosePerson.find_or_create_by_first_name('Josh', attributes_hash)
+
+ assert_default_attributes(p, true)
+ end
+
+ def test_find_or_create_by_with_admin_role_with_attr_protected_attributes
+ p = LoosePerson.find_or_create_by_first_name('Josh', attributes_hash, :as => :admin)
+
+ assert_admin_attributes(p, true)
+ end
+
end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 97d9669483..7e4ae1ea8d 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -216,7 +216,7 @@ class ReflectionTest < ActiveRecord::TestCase
def test_conditions
expected = [
[{ :tags => { :name => 'Blue' } }],
- [{ :taggings => { :comment => 'first' } }, { "taggable_type" => "Post" }],
+ [{ :taggings => { :comment => 'first' } }],
[{ :posts => { :title => ['misc post by bob', 'misc post by mary'] } }]
]
actual = Author.reflect_on_association(:misc_post_first_blue_tags).conditions
@@ -224,7 +224,7 @@ class ReflectionTest < ActiveRecord::TestCase
expected = [
[{ :tags => { :name => 'Blue' } }, { :taggings => { :comment => 'first' } }, { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }],
- [{ "taggable_type" => "Post" }],
+ [],
[]
]
actual = Author.reflect_on_association(:misc_post_first_blue_tags_2).conditions
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index c215602567..8f8e72f052 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -312,6 +312,14 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all
end
+ def test_default_scope_as_class_method_referencing_scope
+ assert_equal [developers(:david).becomes(ClassMethodReferencingScopeDeveloperCalledDavid)], ClassMethodReferencingScopeDeveloperCalledDavid.all
+ end
+
+ def test_default_scope_as_block_referencing_scope
+ assert_equal [developers(:david).becomes(LazyBlockReferencingScopeDeveloperCalledDavid)], LazyBlockReferencingScopeDeveloperCalledDavid.all
+ end
+
def test_default_scope_with_lambda
assert_equal [developers(:david).becomes(LazyLambdaDeveloperCalledDavid)], LazyLambdaDeveloperCalledDavid.all
end
diff --git a/activerecord/test/fixtures/categories_ordered.yml b/activerecord/test/fixtures/categories_ordered.yml
index 2afc6cb5a9..294a6368d6 100644
--- a/activerecord/test/fixtures/categories_ordered.yml
+++ b/activerecord/test/fixtures/categories_ordered.yml
@@ -1,4 +1,4 @@
---- !!omap
+--- !omap
<% 100.times do |i| %>
- fixture_no_<%= i %>:
id: <%= i %>
diff --git a/activerecord/test/fixtures/parrots.yml b/activerecord/test/fixtures/parrots.yml
index 8b73b8cdf6..8425ef98e0 100644
--- a/activerecord/test/fixtures/parrots.yml
+++ b/activerecord/test/fixtures/parrots.yml
@@ -24,4 +24,4 @@ DEFAULTS: &DEFAULTS
parrot_sti_class: LiveParrot
davey:
- <<: *DEFAULTS
+ *DEFAULTS
diff --git a/activerecord/test/models/aircraft.rb b/activerecord/test/models/aircraft.rb
index 0c47aab539..1f35ef45da 100644
--- a/activerecord/test/models/aircraft.rb
+++ b/activerecord/test/models/aircraft.rb
@@ -1,3 +1,4 @@
class Aircraft < ActiveRecord::Base
+ self.pluralize_table_names = false
has_many :engines, :foreign_key => "car_id"
end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index e0cbc44265..23db5650d4 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -138,6 +138,9 @@ class Author < ActiveRecord::Base
has_many :misc_post_first_blue_tags_2, :through => :posts, :source => :first_blue_tags_2,
:conditions => { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }
+ has_many :posts_with_default_include, :class_name => 'PostWithDefaultInclude'
+ has_many :comments_on_posts_with_default_include, :through => :posts_with_default_include, :source => :comments
+
scope :relation_include_posts, includes(:posts)
scope :relation_include_tags, includes(:tags)
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 152f804e16..41c52f7df0 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -127,6 +127,21 @@ class ClassMethodDeveloperCalledDavid < ActiveRecord::Base
end
end
+class ClassMethodReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ scope :david, where(:name => 'David')
+
+ def self.default_scope
+ david
+ end
+end
+
+class LazyBlockReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ scope :david, where(:name => 'David')
+ default_scope { david }
+end
+
class DeveloperCalledJamis < ActiveRecord::Base
self.table_name = 'developers'
@@ -165,4 +180,39 @@ class ModuleIncludedPoorDeveloperCalledJamis < DeveloperCalledJamis
include SalaryDefaultScope
end
+class EagerDeveloperWithDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+
+ default_scope includes(:projects)
+end
+class EagerDeveloperWithClassMethodDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+
+ def self.default_scope
+ includes(:projects)
+ end
+end
+
+class EagerDeveloperWithLambdaDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+
+ default_scope lambda { includes(:projects) }
+end
+
+class EagerDeveloperWithBlockDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+
+ default_scope { includes(:projects) }
+end
+
+class EagerDeveloperWithCallableDefaultScope < ActiveRecord::Base
+ self.table_name = 'developers'
+ has_and_belongs_to_many :projects, :foreign_key => 'developer_id', :join_table => 'developers_projects', :order => 'projects.id'
+
+ default_scope OpenStruct.new(:call => includes(:projects))
+end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 80296032bb..f2ab7b053e 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -162,3 +162,9 @@ class FirstPost < ActiveRecord::Base
has_many :comments, :foreign_key => :post_id
has_one :comment, :foreign_key => :post_id
end
+
+class PostWithDefaultInclude < ActiveRecord::Base
+ self.table_name = 'posts'
+ default_scope includes(:comments)
+ has_many :comments, :foreign_key => :post_id
+end \ No newline at end of file