diff options
-rw-r--r-- | actionpack/CHANGELOG | 4 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/active_record_helper.rb | 46 | ||||
-rw-r--r-- | actionpack/test/template/active_record_helper_test.rb | 52 |
3 files changed, 85 insertions, 17 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 225d0c0319..c4615c2cc1 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,9 @@ *SVN* +* Allow error_messages_for to report errors for multiple objects, as well as support for customizing the name of the object in the error summary header. Closes #4186. [andrew@redlinesoftware.com, Marcel Molina Jr.] + + error_messages_for :account, :user, :subscription, :object_name => :account + * Enhance documentation for setting headers in integration tests. Skip auto HTTP prepending when its already there. Closes #4079. [Rick Olson] * Documentation for AbstractRequest. Closes #4895. [kevin.clark@gmail.com] diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 828aebf790..c9d2c095bf 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -90,31 +90,45 @@ module ActionView end end - # Returns a string with a div containing all the error messages for the object located as an instance variable by the name - # of <tt>object_name</tt>. This div can be tailored by the following options: + # Returns a string with a div containing all of the error messages for the objects located as instance variables by the names + # given. If more than one object is specified, the errors for the objects are displayed in the order that the object names are + # provided. + # + # This div can be tailored by the following options: # # * <tt>header_tag</tt> - Used for the header of the error div (default: h2) # * <tt>id</tt> - The id of the error div (default: errorExplanation) # * <tt>class</tt> - The class of the error div (default: errorExplanation) + # * <tt>object_name</tt> - The object name to use in the header, or + # any text that you prefer. If <tt>object_name</tt> is not set, the name of + # the first object will be used. + # + # Specifying one object: + # + # error_messages_for 'user' + # + # Specifying more than one object (and using the name 'user' in the + # header as the <tt>object_name</tt> instead of 'user_common'): + # + # error_messages_for 'user_common', 'user', :object_name => 'user' # # NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what # you need is significantly different from the default presentation, it makes plenty of sense to access the object.errors # instance yourself and set it up. View the source of this method to see how easy it is. - def error_messages_for(object_name, options = {}) - options = options.symbolize_keys - object = instance_variable_get("@#{object_name}") - if object && !object.errors.empty? - content_tag("div", - content_tag( - options[:header_tag] || "h2", - "#{pluralize(object.errors.count, "error")} prohibited this #{object_name.to_s.gsub("_", " ")} from being saved" - ) + - content_tag("p", "There were problems with the following fields:") + - content_tag("ul", object.errors.full_messages.collect { |msg| content_tag("li", msg) }), - "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation" - ) + def error_messages_for(*params) + options = params.last.is_a?(Hash) ? params.pop.symbolize_keys : {} + objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact + count = objects.inject(0) {|sum, object| sum + object.errors.count } + unless count.zero? + header_message = "#{pluralize(count, 'error')} prohibited this #{(options[:object_name] || params.first).to_s.gsub('_', ' ')} from being saved" + error_messages = objects.map {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } } + content_tag(:div, + content_tag(options[:header_tag] || :h2, header_message) << + content_tag(:p, 'There were problems with the following fields:') << + content_tag(:ul, error_messages), + :id => options[:id] || 'errorExplanation', :class => options[:class] || 'errorExplanation') else - "" + '' end end diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index e153e630ab..ffaae99e28 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -22,10 +22,16 @@ class ActiveRecordHelperTest < Test::Unit::TestCase alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast) alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast) end + + User = Struct.new("User", :email) + User.class_eval do + alias_method :email_before_type_cast, :email unless respond_to?(:email_before_type_cast) + end + Column = Struct.new("Column", :type, :name, :human_name) end - def setup + def setup_post @post = Post.new def @post.errors Class.new { @@ -50,6 +56,34 @@ class ActiveRecordHelperTest < Test::Unit::TestCase @post.body = "Back to the hill and over it again!" @post.secret = 1 @post.written_on = Date.new(2004, 6, 15) + end + + def setup_user + @user = User.new + def @user.errors + Class.new { + def on(field) field == "email" end + def empty?() false end + def count() 1 end + def full_messages() [ "User email can't be empty" ] end + }.new + end + + def @user.new_record?() true end + def @user.to_param() nil end + + def @user.column_for_attribute(attr_name) + User.content_columns.select { |column| column.name == attr_name }.first + end + + def User.content_columns() [ Column.new(:string, "email", "Email") ] end + + @user.email = "" + end + + def setup + setup_post + setup_user @controller = Object.new def @controller.url_for(options, *parameters_for_method_reference) @@ -124,6 +158,22 @@ class ActiveRecordHelperTest < Test::Unit::TestCase assert_equal "", error_messages_for("notthere") end + def test_error_messages_for_many_objects + assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li><li>User email can't be empty</li></ul></div>), error_messages_for("post", "user") + + # reverse the order, error order changes and so does the title + assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post") + + # add the default to put post back in the title + assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post", :object_name => "post") + + # symbols work as well + assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => :post) + + # any default works too + assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this monkey from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => "monkey") + end + def test_form_with_string_multipart assert_dom_equal( %(<form action="create" enctype="multipart/form-data" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>), |