aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rwxr-xr-xactiverecord/Rakefile4
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/persistence.rb6
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/relation.rb41
-rw-r--r--activerecord/test/cases/associations/eager_test.rb40
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb42
-rw-r--r--activerecord/test/fixtures/parrots.yml2
-rw-r--r--activerecord/test/models/developer.rb35
9 files changed, 152 insertions, 24 deletions
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/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 49df63d585..1f31722a7c 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -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.
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index b9041f44d8..e84a479a13 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/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 089bbed08a..2c7298e220 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) }
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index ae9afad48a..a3f4a6fe94 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/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index ea186be9dd..5028cee32d 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -681,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_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/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/developer.rb b/activerecord/test/models/developer.rb
index 152f804e16..a2aa8ae664 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -165,4 +165,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