I’ve just implemented the My Profile tab for Sano:


Can I write 500 words about? Well, I can try.

I like using RESTful routes. In case you don’t know what they are let me try to explain it quick, at least the relevant part. You normally have a set of route rules that would point /movies to the movie listing, /movies/new to a form to add a new movie, /movies/123 to see the movie 123. With RESTful routes in Rails all that is done automatic in a single line:

map.resources :movies

What you are doing is defining a resource. The resource has several actions that can be performed on them:

  • index (a.k.a.: listing)
  • new
  • edit
  • create
  • update
  • destroy

In Sano I have a weights resource that is a very fine example of it:

map.resources :weights

Running the rake routes command we can see all the routes it generate:

    weights GET    /weights(.:format)          {:action=>"index", :controller=>"weights"}
            POST   /weights(.:format)          {:action=>"create", :controller=>"weights"}
 new_weight GET    /weights/new(.:format)      {:action=>"new", :controller=>"weights"}
edit_weight GET    /weights/:id/edit(.:format) {:action=>"edit", :controller=>"weights"}
     weight GET    /weights/:id(.:format)      {:action=>"show", :controller=>"weights"}
            PUT    /weights/:id(.:format)      {:action=>"update", :controller=>"weights"}
            DELETE /weights/:id(.:format)      {:action=>"destroy", :controller=>"weights"}

You see the (.:format) in there? That means that every route is also accessible in alternative formats. For example: xml. Go and try it, add some weights and access http://sano.pupeno.com/weights.xml.

If you are curious, the code for that is this:

def index
  @weights = user.weights.all

  respond_to do |format|
    format.xml  { render :xml => @weights }

I now want everything to be a resource. How can “my profile” be a resource? Well, it’s not hard. It’s not a collection resource, it’s a single resource. There’s no list of profiles, no creation of new profiles or destruction of profiles. There’s only editing and updating of a single profile (which is actually your user).

It turns out that in Rails, that’s very easy to define:

map.resource :profile, :only => [:edit, :update]

Notice how it says “resource” instead of “resources” and it only allows certain actions. Rails is really quite flexible here, logging in is also a resource. It’s called session and you can create them, by logging in, or destroy them, by logging out (no editing). There’s also an extra action needed by OpenID. This is the route definition:

map.resource :session, :only => [:new, :create, :destroy], :member => { :finish_creating => :get }

The “member” part specifies that action to be only for items, not for the whole collection. If it was a collection resource, you could have extra listings. The same way you have index, you could have sorted_index.

The form in the my-profile-page is an example of what Formtastic is good at. This is the whole form:

<% semantic_form_for @user, :url => profile_url do |f| %>
  <% f.inputs do -%>
    <%= f.input :name %>
    <%= f.input :email %>
    <%= f.input :height, :hint => "meters" %>
    <%= f.input :gender, :as => :radio, :collection => [["Male", false], ["Female", true]] %>
    <%= f.input :birthday, :start_year => 1900, :end_year => Time.now.year %>
  <% end -%>
  <% f.buttons do -%>
   <%= f.commit_button :label => "Update profile" %>
  <% end -%>
<% end %>

Special thanks to Ryan Bates who covered the gender case in Railscasts episode 184 and Eifion Bedford of ASCIIcasts for making it easy to find. It surely would have take me some time to figure it out.

Can you please update your profile on Sano?

There you go 553 words!