Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added validates_associated that enables validation of objects in an u…

…nsaved association #398 [Tim Bates]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@418 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 51390b8524a644aa8655691fc7bf56e2a174d140 1 parent 823554e
@dhh dhh authored
View
9 activerecord/CHANGELOG
@@ -1,5 +1,14 @@
*SVN*
+* Added validates_associated that enables validation of objects in an unsaved association #398 [Tim Bates]. Example:
+
+ class Book < ActiveRecord::Base
+ has_many :pages
+ belongs_to :library
+
+ validates_associated :pages, :library
+ end
+
* Added support for associating unsaved objects #402 [Tim Bates]. Rules that govern this addition:
== Unsaved objects and associations
View
33 activerecord/lib/active_record/validations.rb
@@ -271,6 +271,39 @@ def validates_inclusion_of(*attr_names)
end
end
end
+
+ # Validates whether the associated object or objects are all themselves valid. Works with any kind of assocation.
+ #
+ # class Book < ActiveRecord::Base
+ # has_many :pages
+ # belongs_to :library
+ #
+ # validates_associated :pages, :library
+ # end
+ #
+ # Warning: If, after the above definition, you then wrote:
+ #
+ # class Page < ActiveRecord::Base
+ # belongs_to :book
+ #
+ # validates_associated :book
+ # end
+ #
+ # this would specify a circular dependency and cause infinite recursion. The Rails team recommends against this practice.
+ #
+ # Configuration options:
+ # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
+ def validates_associated(*attr_names)
+ configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
+ configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+
+ for attr_name in attr_names
+ class_eval(%(#{validation_method(configuration[:on])} %{
+ errors.add("#{attr_name}", "#{configuration[:message]}") unless
+ (#{attr_name}.is_a?(Array) ? #{attr_name} : [#{attr_name}]).inject(true){ |memo, record| memo and (record.nil? or record.valid?) }
+ }))
+ end
+ end
private
View
21 activerecord/test/validations_test.rb
@@ -389,6 +389,27 @@ def test_validates_length_of_custom_errors_for_is_with_wrong_length
assert_equal "hoo 5", t.errors["title"]
end
+ def test_validates_associated_many
+ Topic.validates_associated( :replies )
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
+ t.replies << [r = Reply.create("title" => "A reply"), Reply.create("title" => "Another reply", "content" => "with content!")]
+ assert !t.valid?
+ assert t.errors.on(:replies)
+ r.content = "non-empty"
+ assert t.valid?
+ end
+
+ def test_validates_associated_one
+ Reply.validates_associated( :topic )
+ Topic.validates_presence_of( :content )
+ r = Reply.create("title" => "A reply", "content" => "with content!")
+ r.topic = Topic.create("title" => "uhohuhoh")
+ assert !r.valid?
+ assert r.errors.on(:topic)
+ r.topic.content = "non-empty"
+ assert r.valid?
+ end
+
def test_throw_away_typing
d = Developer.create "name" => "David", "salary" => "100,000"
assert !d.valid?
Please sign in to comment.
Something went wrong with that request. Please try again.