RESTful PUT and button_to

Hi --

I'm working on a RESTful app, and I've hit a snag. Here's a simplified example of the problem. (Excuse me if this example code isn't strictly correct; I'm typing from memory.)

Suppose I have a table which models a simple counter. It has a single column, "count":

class CreateCounters < ActiveRecord::Migration def self.up    create_table :counters do |t|      t.column :count, :integer    end end

def self.down    drop_table :counters end

I've created a RESTful controller using the scaffold_resource generator. In the "show" view, I'd like to put a link which increments the counter by one:

<%= h @counter.content %> <%= button_to "++", ??? %>

If this were a traditional rails app, the next step would be obvious. I'd add an "increment" action to the controller:

CounterController    ...    def increment      @counter = Counter.find(params[:id])      @counter.count += 1      redirect_to :action => "show"    end end

And I'd link the action to the controller:

<%= h @counter.content %> <%= button_to "++", :action => "increment" %>

However, this isn't RESTful. The REST way, as best I understand, is that updates to an existing resource are accomplished by PUTing an updated version of the resource to the resource's path, as if the user had opened the "edit" view, manually incremented the count, and clicked "submit".

And that's where I get hung up. How do I create a link (or button) in the view which will call "update" in this way?

Here's one attempt I made:

<%= h @counter.content %> <%= button_to "++",                counter_path(                  :id => @counter,                  :counter => { :count => @counter.count + 1 } ),                  :method => :put %>

But that's wrong for two reasons. First, the counter_path helper flattens the nested hash, so the for a counter with id 1 and count==42, the URL comes out like "/counters/1?count43" - which won't be parsed correctly into the params hash by the "update" action.

Second, and more importantly, it's not truly RESTful, because it's PUTing to "/counters/1?count43", not "/counters/1".

As far as I can tell what I really need is a way to add extra hidden fields to the form that button_to generates. But I can't find any way to do that. And from my reading of the button_to (and link_to) source, there is no way to do this.

So: am I missing something? What's the right way to do this? Do I have to construct the form and hidden fields manually, without the help of button_to? Or is there another way to go about it entirely?

That's an interesting question. It's also a nice distillation of some REST-related issues.

I follow what you're saying about the form fields, and normalizing the ++ request so that it's really just a specific case of the general "Reset this number" request where the number being reset to happens to be the current number plus one. My current thinking, though, is that if there is no general update process -- that is, if PUT counters/:id always maps exactly to incrementing the counter by one and returning a representation of it in its new state, then that's OK because it's unambiguous. Even though the number count+1 does not reside in the representation in the request, I think, at least tentatively, that that doesn't matter as long as the semantics of the resource and its identifiers give the system everything it needs to update the resource.

I'd even tend to think that it's OK to have a default behavior in the update method -- i.e., if params[:counter][:count] is absent, assume that it's an increment of one. The reasoning here is partly that the form input is not, in any case, the resource itself; it's just a way of telling the server what the resource should look like. So the absence of HTML form fields should not necessarily mean that, from a REST perspective, the request is uninterpretable.

Anyway, that's my current train of thought. Back at ya' :slight_smile: