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 @counter.save 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? Thanks in advance, -- Avdi -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
dblack-TKXtfPMJ4Ozk1uMJSBkQmQ@public.gmane.org
2007-Feb-25 03:58 UTC
Re: RESTful PUT and button_to
Hi -- On Sun, 25 Feb 2007, Avdi Grimm wrote:> > 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 > @counter.save > 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'' :-) David -- Q. What is THE Ruby book for Rails developers? A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black) (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf) Q. Where can I get Ruby/Rails on-site training, consulting, coaching? A. Ruby Power and Light, LLC (http://www.rubypal.com) --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
unknown wrote:> 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.Thank you for your thoughts. I follow what you''re saying. Unfortunately for my purpose I''m not sure it helps. I chose the "counter" example because I thought it distilled the problem at hand without introducing any distracting factors; my actual app code is more complex than that. To put it briefly it involves nested topics in an outline, which need the ability to be indented or outdented with a single click. In this case there''s no default behaviour - the topic''s new parent topic depends on whether the user clicks to indent or outdent. I guess I just need to manually generate the hidden form if I want to do it RESTfully. Thanks for confirming that this is a real issue, and not just something I was overlooking. Cheers, -- Avdi -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---