<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>this oughta be interesting... &#187; rails</title>
	<atom:link href="http://joshsharpe.com/archives/category/rails/feed" rel="self" type="application/rss+xml" />
	<link>http://joshsharpe.com</link>
	<description></description>
	<lastBuildDate>Tue, 06 Dec 2011 17:52:11 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>error and success messages and the like&#8230;</title>
		<link>http://joshsharpe.com/archives/error-and-success-messages-and-the-like</link>
		<comments>http://joshsharpe.com/archives/error-and-success-messages-and-the-like#comments</comments>
		<pubDate>Mon, 25 Jan 2010 21:58:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://joshsharpe.com/?p=42</guid>
		<description><![CDATA[For one of my apps I&#8217;ve developed what I think is a very easy to use and consistent way of handling error and success messages throughout my app. It would be a lie if I called the following list a set of goals since they evolved over time, but there&#8217;s definitely a bit of philosophy [...]]]></description>
			<content:encoded><![CDATA[<p>For one of my apps I&#8217;ve developed what I think is a very easy to use and consistent way of handling error and success messages throughout my app.  It would be a lie if I called the following list a set of goals since they evolved over time, but there&#8217;s definitely a bit of philosophy driving any future changes that I make to this aspect of the system.</p>
<p>1) Error/Success messages should look and feel the same throughout the application.  This means that the HTML and CSS that displays them should be located in one place and not scattered across several different views and stylesheets.  It also shouldn&#8217;t matter if there is one error or a list of errors.</p>
<p>2) I should be able to generate and display these errors whether this is a synchronous or an asynchronous request.</p>
<p>3) Everything should quack.</p>
<h3>The Setup</h3>
<p>The relevant code from ApplicationController:</p>
<pre>
# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.

class ApplicationController < ActionController::Base

  helper_method :errors_for, :success_for

private

  def success_for(message)
    unless message.blank?
      render_to_string :partial => 'shared/success', :locals => {:message => message}
    end
  end

  def errors_for(object)
    unless object.respond_to?(:errors) &#038;&#038; object.errors.empty?
      render_to_string :partial => 'shared/error', :locals => {:object => object}
    end
  end
end
</pre>
<p>Making them helper methods is a necessary evil so that they can be called from both the controller and the view.  Calling them from the view should be obvious.  Say you have an Active Record object with errors on it:</p>
<pre>
= errors_for @user_with_errors
</pre>
<p>&#8230;and calling it from inside a controller is extremely useful when you need to pass errors back to an AJAX request:</p>
<pre>
render :json => {:errors => errors_for(user_with_errors)}
</pre>
<p>This is where people start to get feisty.  &#8220;You shouldn&#8217;t be passing HTML back with JSON.  JSON is for passing raw data.&#8221;  I say rules are meant to be broken.  If I passed the raw data (the errors) back in JSON I would have to create the associated markup in javascript.  So not only am I duplicating logic, I&#8217;m doing it in two different languages (javascript, ERB/HAML).  That sucks.</p>
<p>Here&#8217;s my error partial, in HAML:</p>
<pre>
-if object.is_a?(String) || object.is_a?(Array)
  .errorWrapper{:style => "width:#{display_width(object)}px;"}
    -if object.is_a? String
      %p= object
    -elsif object.is_a? Array
      %ul
        -object.each do |message|
          %li= message
-elsif object.kind_of?(ActiveRecord::Base) || object.kind_of?(Authlogic::Session::Base)
  .errorWrapper{:style => "width:#{display_width(object)}px;"}
    =error_messages_for(:object => object, :id => nil, :header_message => nil, :message => nil)
</pre>
<p>I know that kind of looks a mess, and some slight duplication, but it&#8217;s all for the greater good.  First off, the partial renders nothing if it gets passed the wrong Thing.  Secondly, it&#8217;s wrapped up nicely by errorWrapper, which gives us a consistent place to start writing styles from.  We do this since you cannot (currently) change the class that error_messages_for gives you.  Thirdly, we&#8217;re using error_message_for so that we can seamlessly pass ActiveRecord and AuthLogic objects and it won&#8217;t blink.</p>
<p>The success partial is a bit simpler since we don&#8217;t expect objects to have &#8220;success&#8221; messages attached to them.  Instead it accepts single strings or an array of strings:</p>
<pre>
.success{:style => "width:#{display_width(object)}px;"}
  -if object.is_a? String
    %p= object
  -elsif object.is_a? Array
    %ul
      -object.each do |message|
        %li= message
</pre>
<p>And finally, a bit of &#8220;making things look nicer&#8221;:</p>
<pre>
module ApplicationHelper
  ERROR_COEF = 8.0
  def display_width(object)
    (if object.is_a?(String)
      object.length
    elsif object.is_a?(ActiveRecord::Base) || object.is_a?(UserSession)
      object.errors.full_messages.map(&#038;:length).max
    elsif object.is_a?(Array)
      object.map(&#038;:length).max
    end * ERROR_COEF).to_i
  end
end
</pre>
<p>A bit of a mess again, but what we&#8217;re doing here is defining a width for the wrapping container based on the length of the error messages.  This is so that we don&#8217;t just arbitrarily set our container to be 600px wide for errors that are only three words.  (This looks pretty awful)  You might need to play around with that coefficient a bit to make it work with the rest of your styles &#8212; and even that may not be very precise.  That said, I&#8217;ve yet to run into any error or set of errors that this didn&#8217;t handle very nicely.</p>
<h3>The Rub</h3>
<p>So how do we use all of this?  Pretty much any way you want!  Throw an errors_for(@object) in your edit template and forget it.  If @object has errors on it, they get displayed, if not, then no markup is generated.  Not even a wrapping container.</p>
<p>Want to create an array of errors for an AJAX request and display them?  No problem.</p>
<pre>
error_messages = []
error_messages << "Err"
error_messages << "Foo"
render :json => {:errors => errors_for(error_messages)}
</pre>
<p>Then, in your javascript (jQuery)</p>
<pre>
$('form#login').prepend(response.errors);
</pre>
<p>What about success messages?  They&#8217;re probably passed in some kind of Flash.</p>
<pre>
Controller:
flash[:success] = "The post was successful"

View
=success_for flash[:success]
</pre>
<p>This is what I&#8217;m doing, how are you handling this problem?</p>
]]></content:encoded>
			<wfw:commentRss>http://joshsharpe.com/archives/error-and-success-messages-and-the-like/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

