Skip to content
Browse files

Ensure uniqueness validation works with custom persistence:

- Previously the persistence options would be cleared out after the
  database query that the uniqueness validation performs, causing the
  document to be persisted in its normal location. This now keeps the
  options in the thread local unless validation fails.
- [ fix #2234 ]
  • Loading branch information...
1 parent fc2e7e4 commit b5218a80c98c65a2be1e1e09198b6a0f23a82de5 @durran durran committed Jul 26, 2012
View
4 CHANGELOG.md
@@ -37,6 +37,10 @@ For instructions on upgrading to newer versions, visit
* \#2236 Keep the instance of the document in the validations exception
accessible via `document` or `record`.
+* \#2234 Ensure validations when document is getting persisted with custom
+ options work properly with custom options, and do not clear them out if
+ validation passes.
+
* \#2224 `Model#inc` now accepts `BigDecimal` values.
* \#2216 Fixed assignment of metadata on embeds one relations when setting
View
2 lib/mongoid/sessions.rb
@@ -151,7 +151,7 @@ def clear_persistence_options
def collection
if opts = persistence_options
coll = mongo_session.with(opts)[opts[:collection] || collection_name]
- clear_persistence_options
+ clear_persistence_options unless validating_with_query?
coll
else
mongo_session[collection_name]
View
27 lib/mongoid/validations.rb
@@ -3,6 +3,7 @@
require "mongoid/validations/associated"
require "mongoid/validations/format"
require "mongoid/validations/length"
+require "mongoid/validations/queryable"
require "mongoid/validations/presence"
require "mongoid/validations/uniqueness"
@@ -89,6 +90,18 @@ def validated?
Threaded.validated?(self)
end
+ # Are we currently performing a validation that has a query?
+ #
+ # @example Are we validating with a query?
+ # document.validating_with_query?
+ #
+ # @return [ true, false ] If we are validating with a query.
+ #
+ # @since 3.0.2
+ def validating_with_query?
+ self.class.validating_with_query?
+ end
+
module ClassMethods
# Validates whether or not an association is valid or not. Will correctly
@@ -202,7 +215,7 @@ def validates_with(*args, &block)
super
end
- protected
+ private
# Adds an associated validator for the relation if the validate option
# was not provided or set to true.
@@ -218,6 +231,18 @@ def validates_relation(metadata)
validates_associated(metadata.name)
end
end
+
+ # Are we currently performing a validation that has a query?
+ #
+ # @example Are we validating with a query?
+ # Model.validating_with_query?
+ #
+ # @return [ true, false ] If we are validating with a query.
+ #
+ # @since 3.0.2
+ def validating_with_query?
+ Threaded.executing?("#{name}-validate-with-query")
+ end
end
end
end
View
30 lib/mongoid/validations/queryable.rb
@@ -0,0 +1,30 @@
+# encoding: utf-8
+module Mongoid
+ module Validations
+ module Queryable
+
+ # Wrap the validation inside the an execution block that alert's the
+ # session not to clear it's persistence options.
+ #
+ # @example Execute the validation with a query.
+ # with_query(document) do
+ # #...
+ # end
+ #
+ # @param [ Document ] document The document being validated.
+ #
+ # @return [ Object ] The result of the yield.
+ #
+ # @since 3.0.2
+ def with_query(document)
+ begin
+ Threaded.begin("#{klass.name}-validate-with-query")
+ yield
+ ensure
+ klass.clear_persistence_options unless document.errors.empty?
+ Threaded.exit("#{klass.name}-validate-with-query")
+ end
+ end
+ end
+ end
+end
View
16 lib/mongoid/validations/uniqueness.rb
@@ -14,6 +14,8 @@ module Validations
# validates_uniqueness_of :title
# end
class UniquenessValidator < ActiveModel::EachValidator
+ include Queryable
+
attr_reader :klass
# Unfortunately, we have to tie Uniqueness validators to a class.
@@ -41,12 +43,14 @@ def setup(klass)
#
# @since 1.0.0
def validate_each(document, attribute, value)
- attrib, val = to_validate(document, attribute, value)
- return unless validation_required?(document, attrib)
- if document.embedded?
- validate_embedded(document, attrib, val)
- else
- validate_root(document, attrib, val)
+ with_query(document) do
+ attrib, val = to_validate(document, attribute, value)
+ return unless validation_required?(document, attrib)
+ if document.embedded?
+ validate_embedded(document, attrib, val)
+ else
+ validate_root(document, attrib, val)
+ end
end
end
View
55 spec/mongoid/validations/uniqueness_spec.rb
@@ -6,6 +6,61 @@
context "when the document is a root document" do
+ context "when adding custom persistence options" do
+
+ before do
+ Dictionary.validates_uniqueness_of :name
+ end
+
+ after do
+ Dictionary.reset_callbacks(:validate)
+ end
+
+ context "when persisting to another collection" do
+
+ before do
+ Dictionary.with(collection: "dicts").create(name: "websters")
+ end
+
+ context "when the document is not valid" do
+
+ let(:websters) do
+ Dictionary.with(collection: "dicts").new(name: "websters")
+ end
+
+ it "performs the validation on the correct collection" do
+ websters.should_not be_valid
+ end
+
+ it "adds the uniqueness error" do
+ websters.valid?
+ websters.errors[:name].should_not be_nil
+ end
+
+ it "clears the persistence options in the thread local" do
+ websters.valid?
+ Dictionary.persistence_options.should be_nil
+ end
+ end
+
+ context "when the document is valid" do
+
+ let(:oxford) do
+ Dictionary.with(collection: "dicts").new(name: "oxford")
+ end
+
+ it "performs the validation on the correct collection" do
+ oxford.should be_valid
+ end
+
+ it "does not clear the persistence options in the thread local" do
+ oxford.valid?
+ Dictionary.persistence_options.should_not be_nil
+ end
+ end
+ end
+ end
+
context "when the document is paranoid" do
before do

0 comments on commit b5218a8

Please sign in to comment.
Something went wrong with that request. Please try again.