Rails

business logic

models

REST

class StandardsController < ApplicationController

# GET /standards

# GET /standards.xml

def index

@standards = Standard.find(:all)



respond_to do | format |

format.html # index.html.erb

format.xml { render :xml => @standards }

end

end



# GET /standards/1

# GET /standards/1.xml

def show

@standard = Standard.find(params[:id])



respond_to do | format |

format.html # show.html.erb

format.xml { render :xml => @standard }

end

end



# GET /standards/new

# GET /standards/new.xml

def new

@standard = Standard.new



respond_to do | format |

format.html # new.html.erb

format.xml { render :xml => @standard }

end

end



# GET /standards/1/edit

def edit

@standard = Standard.find(params[:id])

end



# POST /standards

# POST /standards.xml

def create

@standard = Standard.new(params[:standard])



respond_to do | format |

if @standard.save

flash[:notice] = 'Standard was successfully created.'

format.html { redirect_to(@standard) }

format.xml { render :xml => @standard, :status => :created, :location => @standard }

else

format.html { render :action => "new" }

format.xml { render :xml => @standard.errors, :status => :unprocessable_entity }

end

end

end



# PUT /standards/1

# PUT /standards/1.xml

def update

@standard = Standard.find(params[:id])



respond_to do | format |

if @standard.update_attributes(params[:standard])

flash[:notice] = 'Standard was successfully updated.'

format.html { redirect_to(@standard) }

format.xml { head :ok }

else

format.html { render :action => "edit" }

format.xml { render :xml => @standard.errors, :status => :unprocessable_entity }

end

end

end



# DELETE /standards/1

# DELETE /standards/1.xml

def destroy

@standard = Standard.find(params[:id])

@standard.destroy



respond_to do | format |

format.html { redirect_to(standards_url) }

format.xml { head :ok }

end

end

end

One guiding principle advocated by manygurus is "fat models, and skinny controllers" . Rails controllers should only have enough code to mediate between models and views. Thebelongs in the, not in controllers and views. With the current state of support forin Rails 2.0, controllers become quite similar, with seven basic actions (index, get, create, update, destroy, new, and edit), which tend to have very similar implementations. In Rails 2.0 pro-forma controllers can easily be generated using, say script/generate scaffold standard, which generates a RESTful chunk of code including a controller which looks like this:

Other than the specific names, all generated controllers look very much like this.

Using generated controllers is quite easy, and in many cases, little or no change needs to be made to the generated code, particularly if you take the "skinny controllers" mantra to heart.

On the other hand, another Ruby/Rails mantra is "don't repeat yourself", and having all that almost duplicate code, even if you didn't write it yourself, is a violation of the DRY principle.

Enter: resource_controller. James Golick offered up a new plugin for rails called resource_controller which allows the same controller shown above to be written as:

class StandardsController < ApplicationController

resource_controller

end

Well, there is a little bit of a white lie here, this won't give you the standard xml response capability, but that's easy to get back with a few lines:

class StandardsController < ApplicationController

resource_controller



index.wants.xml { render :xml => @standards }

[new, show].each do | action |

action.wants.xml { render :xml => @standard })

end

create.wants.xml { render :xml => @standard, :status => :created, :location => @standard }

[update, destroy].each do | action |

action.wants.xml { head :ok }

end

[create_fails, update_fails].each do | action |

action.wants.xml { render :xml => @standard.errors, :status => :unprocessable_entity }

end

end

This plugin makes writing controllers look more like writing models, with declarative class methods like resource_controller, and "callbacks" like action.wants. The plugin automatically gives the controller the right instance variable for each action, in this case either @standards for the index action or @standard for the others.

There are some common patterns in Rails which force changing controller code. One of these is nesting resources. It's easy to set up the routes in the config/routes.rb file

map.resources :organization, :has_many => :standards

But once you've done this, you need to change the controller to fetch and use the parent resource and use it appropriately for each action. The resource_controller plugin simplifies this. After the above routing change all that's needed is to add a declarative call to our controller

class StandardsController < ApplicationController

resource_controller

belongs_to :organization

end

The belongs_to declaration enables the controller to be used with the nested resource. Now when a controller action is reached through a nested resource URL, like /organization/1234/standards, the controller will automatically create an instance variable named @organization and set it appropriately, and will use the standards association of the parent object to find, and build instances of the model Standard.

Note that the same controller also works if the URL is not nested, so we can have another mapping in routes.rb which allows access to standards outside of an organization:

map.resources :standard

map.resources :organization, :has_many :standards

And the resource controller will automatically work in either context.

The plugin also handles namespaced controllers, polymorphic nested resources (similar and related to polymorphic associations in ActiveRecord) and other magic. You also get URL and path helper functions which work in the context of the URL in the request.

Resource_controller looks like a useful plugin, and it will no doubt get even better as it matures. The details are on James Golicks blog . There's also a rapid-fire screencast by Fabio Akita, which really shows what the plugin can do in action.