render_either - the beginning

There is this helpful method I've been evolving in my apps for a bit, it's called render_either and it goes a little something like this: (in application.rb)

 
  def render_either(opts = {})
    # For MIME:Type Recognition
    # respond_to do |f|
    #   f.js {@wants = :js}
    #   f.xml {@wants = :xml}
    #   f.html {@wants = :html}
    # end
    # Then use @wants
    if request.xhr?
      if opts[:ajax]
        if opts[:ajax][:json]
          render :json => opts[:ajax][:json].to_json
        else
          render opts[:ajax]
        end
      end
    elsif params[:bookmarklet]
      if opts[:bookmarklet]
        if opts[:bookmarklet][:redirect_to]
          redirect_to opts[:bookmarklet][:redirect_to]
        else
          render opts[:bookmarklet]
        end
      end
    else
      if opts[:default]
        if opts[:default][:redirect]
          redirect_to opts[:default][:redirect]
        else
          render opts[:default]
        end
      end
    end
    return false
  end
 

This method is very much incomplete - I mean it works - but I want to take it further (and make it prettier and more condensed, but for now, sadly, that's actually more readable to me while I improve it). So why did I write it, what does it do, and why should you care? Let's say you want to be able to create a Post from a vanilla form, from a lightbox (or an ajax form), and from a bookmarklet. Obviously, you want this all occurring in a PostsController within the new and create methods. Here this is without render_either

 
PostsController < ApplicationController
 
  # For a vanilla form new.rhtml should be rendered with layout application (default)
  # For a bookmarklet, render new.rhtml but with layout bookmarklet
  # For a lightbox, send back new.rhtml without a layout (so that it back be loaded into a lightbox easily)
  def new
    @post ||= Post.new
    if request.xhr?
      render :layout => false
      return false # To prevent DoubleRenderError
    elsif params[:bookmarklet] #indicating that this is being loaded in a bookmarklet
      render :layout => "bookmarklet"
      return false
    end
  end
 
  # For a vanilla form, if post is valid, redirect_to post_path(@post),
  # else, render :action => "new"
  # For a lightbox, if post is valid, render :partial => "post",
  # else, render :action => "new" without layout
  # For a bookmarklet, if post is valid, redirect_to close_bookmarklet_path,
  # else, render :action => "new", :layout => "bookmarklet"
  def create
    @post = Post.new(params[:post])
    if @post.save
      if request.xhr?
        render :partial => "post"
        return false
      elsif params[:bookmarklet]
        redirect_to close_bookmarklet_path
        return false
      else
        redirect_to post_path
        return false
      end
    else
      if request.xhr?
        render :action => "new", :layout => false
        return false
      elsif params[:bookmarklet]
        render :action => "new", :layout => "bookmarklet"
        return false
      else
        render :action => "new"
        return false
      end
    end
  end
 
end
 

Ow, that's painful, right? And that doesn't even deal with some of the added complexities in a real application (returning json, returning xml for api calls, other branches and conditions). The render_either method.

 
PostsController < ApplicationController
 
  # For a vanilla form new.rhtml should be rendered with layout application (default)
  # For a bookmarklet, render new.rhtml but with layout bookmarklet
  # For a lightbox, send back new.rhtml without a layout (so that it back be loaded into a lightbox easily)
  def new
    @post ||= Post.new
    render_either :ajax => {:layout => false},
                  :bookmarklet => {:layout => "bookmarklet"},
  end
 
  # For a vanilla form, if post is valid, redirect_to post_path(@post),
  # else, render :action => "new"
  # For a lightbox, if post is valid, render :partial => "post",
  # else, render :action => "new" without layout
  # For a bookmarklet, if post is valid, redirect_to close_bookmarklet_path,
  # else, render :action => "new", :layout => "bookmarklet"
  def create
    @post = Post.new(params[:post])
    if @post.save
      render_either :ajax => {:partial => "post"},
                    :bookmarklet => {:redirect_to => close_bookmarklet_path},
                    :default => {:redirect_to => post_path(@post)}
    else
      render_either :ajax => {:action => "new", :layout => false},
                    :bookmarklet => {:action => "new", :layout => "bookmarklet"}
                    :default => {:action => "new"}
    end
  end
end
 

That's the point of render_either, in a nutshell, to abstract out display logic from a controller. This is one of the most basic arguments for render_either, and the reason I created it. I wanted to DRY the logic and remove less if / else branches in a controller action. It then began growing into a stronger package that allows me to do some awesome things with xml and json that I will touch on in later posts. Let's just say that set_flash loves render_either. Any questions?

Post a Comment

Your email is never published nor shared. Required fields are marked *