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