The Rails Way

A big part of using Rails is understanding that if you conform to "The Rails Way" things are much easier. Of course, The Rails Way might not work for your project, and in that case, Rails might not be the right framework choice. But, you have to learn The Rails Way in order to know.

I can't claim to explain The Rails Way completely, but I've been helping a new (to Rails) developer on my team get up and running, so I've been having to coach him to see The Rails Way.

I started by talking about "entities". An entity is a thing like a "widget"...some object in our application domain that we need to represent. By convention, the names of the database table, unit tests, models, controllers, views, layouts and helpers must always coordinated in Rails to the name of the entity. So, with our "widget" entity, this is how they should be:


  • DB Table: widgets
  • Model: app/models/widget.rb
  • Controller: app/controllers/widgets_controller.rb
  • View: app/views/widget/...
  • Layout: app/views/layouts/widgets.rhtml
  • Helper: app/helpers/widgets_helper.rb

The beautiful thing about Rails is that by using generators, this gets managed for you. Here’s the approach I always take when creating an entity. This can be done through an IDE (such as RadRails, which is what I use) or on the command line...I’ll use the command line here:

  • cd path/to/rails/app
  • ruby ./script/generate model Widget which will generate your model file and your migration file with the proper table name.
  • Open up the migration, add the appropriate columns and save.
  • rake db:migrate to apply your migration to the DB.
  • ruby ./script/generate scaffold Widget which will create your controller, views, layout and helper.

Generators create a starting point to start editing, and it also gets all the file names correct. Let’s talk about the editing that needs to happen. Personally, I always do my editing in this order:

  1. Migration
  2. Model
  3. Controllers
  4. Views
  5. Layouts

This makes the most sense to me because I’m starting with the raw data, and working my way forward to what the user sees on the web page.

A little background on controllers (a good article to read is this: http://www.softiesonrails.com/search?q=rest+101): Your standard CRUD controller has 8 actions:

  • Create (Create): /widgets/create
  • Show (Read): /widgets/show/:id
  • Update (Update): /widgets/update/:id
  • Destroy (Delete): /widgets/destroy/:id
  • New (precursor to create): /widgets/new
  • Edit (precursor to update): /widgets/edit/:id
  • List (lists all entities): /widgets/list
  • Index (the default action): /widgets

When, you generate your scaffold, it’s going to automatically generate the 8 actions above. When designing entities if you have methods that don't fit within one of those actions then it's typically an indication that your entity model could our should be designed differently.

(A great example of how it's all about entity design is the login and logout actions for a User. I was thinking the other day about where the login and logout methods belong because they make sense on a User, but they aren't one of the above eight actions. Of course, I was thinking about it all wrong: login and logout are really the Create and Destroy methods on the Session entity, and Session entities belong to a User.)

Finally, a note in regard to the relationships between views, controllers and models in the context of validation. The typical user pattern in relation to creating an entity is:

  • User visits /widgets/new page
  • Enters data, click submit.
  • /widgets/new POSTs to Widgets#create
  • Widgets#create puts the data in the DB, then (if you look at the scaffold generated controller)

    • If success, calls redirect_to
    • If fails, calls render :action => :new

Why in one case does it redirect, and the other does it render? It all has to do with a little method call that should appear in your new widgets view, i.e. /widgets/new:
<%= error_messages_for 'widget' %>

See, a model has the capability to validate it’s attributes (e.g. lookup validates_presence_of in the Rails docs for examples of these capabilities). When you call Object#save the validation routines are executed and if the model doesn’t validate (i.e. Object#save returns false) we render the new action again. error_messages_for looks up the failures and automagically renders error messaging.

The beauty of generators is that script/generate scaffold Widget sets all of this up for you.

No comments: