Permalink
Browse files

Prevent infinite loops when validating associated documents from both…

… sides of the relation
  • Loading branch information...
1 parent 47faa2e commit 47b7cf933e3e182e445f4c58a721dd2a355bba4d @durran durran committed Jan 10, 2011
View
@@ -12,6 +12,8 @@ module Validations
included do
include ActiveModel::Validations
+ attr_accessor :validated
+
# Overrides the default ActiveModel behaviour since we need to handle
# validations of relations slightly different than just calling the
# getter.
@@ -25,11 +27,25 @@ module Validations
# @param [ Symbol ] attr The name of the field or relation.
#
# @return [ Object ] The value of the field or the relation.
+ #
+ # @since 2.0.0.rc.1
def read_attribute_for_validation(attr)
relations[attr.to_s] ? send(attr, false, :continue => false) : send(attr)
end
end
+ # Used to prevent infinite loops in associated validations.
+ #
+ # @example Is the document validated?
+ # document.validated?
+ #
+ # @return [ true, false ] Has the document already been validated?
+ #
+ # @since 2.0.0.rc.2
+ def validated?
+ !!@validated
+ end
+
module ClassMethods #:nodoc:
# Validates whether or not an association is valid or not. Will correctly
@@ -27,8 +27,13 @@ class AssociatedValidator < ActiveModel::EachValidator
# @param [ Symbol ] attribute The relation to validate.
# @param [ Object ] value The value of the relation.
def validate_each(document, attribute, value)
- return if value.to_a.collect { |doc| doc.nil? || doc.valid? }.all?
- document.errors.add(attribute, :invalid, options.merge(:value => value))
+ unless document.validated?
+ document.validated = true
+ return if value.to_a.collect { |doc| doc.nil? || doc.valid? }.all?
+ document.errors.add(attribute, :invalid, options.merge(:value => value))
+ else
+ document.validated = false
+ end
end
end
end
@@ -0,0 +1,30 @@
+require "spec_helper"
+
+describe Mongoid::Validations::AssociatedValidator do
+
+ describe "#valid?" do
+
+ context "when validating associated on both sides" do
+
+ let(:user) do
+ User.new(:name => "test")
+ end
+
+ let(:description) do
+ Description.new(:details => "testing")
+ end
+
+ before do
+ user.descriptions << description
+ end
+
+ it "only validates the parent once" do
+ user.should be_valid
+ end
+
+ it "only validates the child once" do
+ description.should be_valid
+ end
+ end
+ end
+end
@@ -5,4 +5,6 @@ class Description
referenced_in :user
referenced_in :updater, :class_name => 'User'
+
+ validates :user, :associated => true
end

0 comments on commit 47b7cf9

Please sign in to comment.