More RESTlessness: "unbalanced" routes

I’m working on an application (on Edge Rails) that has a really long
user
profile page - viewed, of course, with a GET to /users/:id.

The editing side of things needs to be broken up into four
logically-grouped screens - say, contact info, work history, education,
and
personal info. These screens are essentially update-only - their “show”
counterpart is the whole profile at /users/:id. And the update can only
be
performed by current_user, so the :id is superfluous.

There is no Create function, and there is no Delete function.

So far, I see only two choices:

  1. Don’t use RESTful routes at all. Change the GET to /users/show/:id,
    and
    do the updates with POSTs to /users/edit_contact_info,
    /users/edit_education, etc. Put it all in users_controller. This is
    the
    path I’ve been taking, but it loses some of the simply_helpful magic.

  2. Create RESTful routes, and manually draw them in routes.rb. This
    seems
    brittle, and I have to lie about (e.g.) “contact_info” belonging to the
    collection instead of the member, since I don’t want the :id in the URL.
    Plus, I think that means each of the four mini-edit pages has to go in
    its
    own, one-action controller! Ugly.

Is there a third way?

Jay L.

Sure. The Rails implementation of REST allows you to extend the
RESTful routes with a similar concept to choice #2 but without
“lying.”

Say you want a unique url that updates only the contact_info of a
user. You can extend the user routes in routes.rb like this:

map.resources :users, :member=>{:update_contact_info_of=>:post}

What this says is that there should be a url for an individual user
resource (a member of the users collection) with an action mapping
to “update_contact_info_of” that accepts only POST data. Just in case
it’s not obvious, :member is a hash for defining actions for
individual resources, keyed by the name of an action and with a value
of the allowed http methods (:get, :put, :post, :delete, :any).

By extending the users routes in that way, the form_for that wraps the
contact info can looks like this:

<% form_for :user, update_contact_info_of_user_path(@user) do |f| %>

<% end %>

HTH,
AndyV

On Aug 21, 2007, at 8:59 AM, AndyV wrote:

of the allowed http methods (:get, :put, :post, :delete, :any).
To me, this smells like RPC. There’s a verb in your URL, “update”.
You don’t need that, you’ve already said you’re updating because of PUT.

What I would do is define a singleton resource for profile.

map.resource :profile

The view for that profile is broken up, I gather from the original
question, into multiple forms. They can all PUT to the same
reasource, though, /profile. There’s nothing that says a PUT/update
has to include ALL the data for the resource.

Personally, I would add extra bits onto my profile resource, so a GET
of /profile/contact_info would show the view for editing the contact
info and /profile/work_history would show the view for editing the
work history.

Rails routing is extremely flexible. There is almost never a “has to
go somewhere I don’t want it to” with Rails routing.

HTH,
Michael

See below…

Michael D. Ivey wrote:

it’s not obvious, :member is a hash for defining actions for

You could also

map.resources :users, :collection => {:contact_info => :put}