Thursday 4 October 2012

Evil Twin Controllers

Evil Twin Controllers


As an application grows and matures, the number of things that occur in its controllers increases. Even if you remain diligent and move as much functionality from the controllers into the models as possible, there will inevitably be functionality that remains in the controllers, such as alternative formats available for APIs (JSON, XML, and so on).

For Example, examine the following controller for Songs, which exposes an XML API:

class SongsController < ApplicationController
  before_filter :grab_album_from_album_id

  def index
    @songs = songs.all
    respond_to do |format|
      format.html
      format.xml { render :xml => @songs }
    end
 end

  def show
    @song = songs.find(params[:id])
    respond_to do |format|
      format.html
      format.xml { render :xml => @song }
    end
  end  

  def new
    @song = songs.new
    respond_to do |format|
      format.html
      format.xml { render :xml => @song }
    end
  end 

  def edit
    @song = songs.find(params[:id])
  end 

  def create
    @song = songs.new(params[:song])
    respond_to do |format|
      if @song.save
         format.html do
            redirect_to(@song, :notice => 'Song was successfully created.')
         end
         format.xml do 
           render :xml => @song, :status => :created, :location => @song
         end
       else
         format.html { render :action => "new"}
         format.xml do
           render :xml => @song.errors, :status => :unprocessable_entity
         end
       end
    end
  end

  def update
    @song = songs.find(params[:id])
    respond_to do |format|
      if @song.update_attributes(params[:song])
        format.html do
           redirect_to(@song, :notice => "Song was successfully updated.")
        end
       format.xml { head :ok }
      else
         format.html {render :action => "edit" }
         format.xml do
           render :xml => @song.errors, :status => :unprocessable_entity
         end
      end
    end
  end

  def destroy
    Song.find(params[:id]).destroy
    respond_to do |format|
     format.html { redirect_to(songs_url)}
     format.xml { head :ok}
    end
  end

  private

  def songs
   @album? @album.songs : Song
  end

  def grab_album_from_album_id
    @album = Album.find(params[:album_id]) if params[:album_id]
  end
end


Rails 3 introduced a new set of methods called responders that abstract the boilerplate responding code so that the controller becomes much simpler. In the following example, the preceding Songs controller is rewritten using responders:

class SongsController < ApplicationController
  respond_to :html, :xml
  before_filter :grab_album_from_album_id

  def index
    @songs = songs.all
    respond_with(@song)
  end

  def show
    @song = songs.find(params[:id])
    respond_with(@song)
  end

  def new
    @song = songs.new
     respond_with(@song)
  end

  def edit
    @song = songs.find(params[:id])
    respond_with(@song)
  end  

  def create
    @song = songs.new(params[:song])
     if @song.save
        flash[:notice] = "Song was successfully created."
     end
     respond_with(@song)
  end

  def update
    @song  = songs.find(params[:id])
     if @song.update_attributes(params[:song])
      flash[:notice] = "Song was successfully updated."
     end
     respond_with(@song)
  end

  def destroy
    @song = Song.find(params[:id])
    @song.destroy
    respond_with(@song)
  end

  private

   def songs
     @album ? @album.songs : Song
   end

   def grab_album_from_album_id
     @album = Album.find(params[:album_id]) if params[:album_id]
   end
end


No comments:

Post a Comment