Skip to content
This repository

Patch verb #505

Closed
wants to merge 3 commits into from
dlee
dlee commented May 10, 2011

Update: as ncreuschling points out, there was a typo in the pull request message that said "HTML verb" instead of "HTTP verb".

Updated: update forms default to PUT instead of PATCH for current apps. Defaults to PATCH for new apps.

PATCH is the correct HTTP verb to map to the #update action. The semantics for
PATCH allows for partial updates, whereas PUT requires a complete replacement.

Changes:

  • adds the #patch verb to routes to detect PATCH requests
  • adds #patch? to Request
  • adds the PATCH -> update mapping in the #resource(s) routes.
  • changes default form helpers to prefer :patch instead of :put for updates only for new apps
  • changes documentation and comments to indicate the preference for PATCH

This change tries to maintain complete backwards compatibility by keeping the
original PUT -> update mapping. Users using the #resource(s) routes should not
notice a change in behavior since both PUT and PATCH requests get mapped to
update.

added some commits May 06, 2011
dlee Use PATCH instead of PUT; Fixes issue #348
PATCH is the correct HTML verb to map to the #update action. The semantics for
PATCH allows for partial updates, whereas PUT requires a complete replacement.

Changes:
* adds the #patch verb to routes to detect PATCH requests
* adds #patch? to Request
* adds the PATCH -> update mapping in the #resource(s) routes.
* changes default form helpers to prefer :patch instead of :put for updates
* changes documentation and comments to indicate the preference for PATCH

This change tries to maintain complete backwards compatibility by keeping the
original PUT -> update mapping. Users using the #resource(s) routes should not
notice a change in behavior since both PUT and PATCH requests get mapped to
update.
3f9a09c
dlee Make method for update forms configurable
Make :put the default method for backwards compatibility. The generator for new
Rails projects configures :patch a the default method in config/application.rb.
985bec5
Daniel Schierbeck
dasch commented May 13, 2011

GitHub needs a +1 button.

benatkin

I googled for HTTP verbs and clicked the first result and PATCH isn't listed.

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

Where is it?

Also I hardly think that updating everything except the primary key and the created_at timestamp is a PATCH.

dlee
dlee commented May 13, 2011

@benatkin http://www.w3.org/Protocols/rfc2068/rfc2068

For past discussion, see issues #348 and #425.

actionpack/lib/action_dispatch/routing/mapper.rb
@@ -484,6 +484,16 @@ module ActionDispatch
484 484
           map_method(:post, *args, &block)
485 485
         end
486 486
 
  487
+        # Define a route that only recognizes HTTP PATCH.
  488
+        # For supported arguments, see <tt>Base#match</tt>.
  489
+        #
  490
+        # Example:
  491
+        #
  492
+        # patch 'bacon', :to => 'food#bacon'
1
Daniel Schierbeck
dasch added a note June 12, 2011

The example needs to be indented 2 spaces so it'll show up correctly in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
dlee
dlee commented June 24, 2011

@josevalim, @fxn: Hey guys, this pull request has been sitting here for over a month unnoticed. It seems like master is now hosting code for Rails 3.2, so I'm thinking it can be merged in?

José Valim
Owner

It seems fine for me with the view configuration option. I am a bit concerned about generating more routes (routes generation is already slow), but I don't consider it a blocker at first. /cc @nzkoz

Xavier Noria
Owner
fxn commented June 25, 2011

Yes I had it in mind, but waiting for 3.1 to start working on 3.2 proper.

Xavier Noria
Owner
fxn commented July 27, 2011

Just a ping to say I am still waiting for 3.1 and have not forgotten this patch. That's because albeit 3-1-stable is branched, I prefer a master that does not move too much because cherry-picking back to 3-1-stable is very common these days. Focus is 3.1 now.

The patch does not merge cleanly anymore, by the way, in case you want to maintain it in sync.

dlee
dlee commented August 05, 2011

@fxn, cool, glad to know this hasn't been forgotten. Let me know when you're ready to merge so I can update the pull request.

Jeremy Kemper
Owner

Nice patch!

I think this will break apps (and engines) that use explicit put routes to override a default resources route. After upgrading Rails, users will see that the form seems to bypass their put override.

Steve Klabnik
Collaborator

+1 from me.

I wish GitHub had a better way to subscribe to issues instead of just commenting. I feel like "+1" is just noise, but I want to see what happens with this pull...

Brian Cardarella

@steveklabnik can't you just click the 'Enable notifications for this Pull Request' link at the bottom?

Guillermo Iguaran

@steveklabnik test clicking in the link at the end of this page:

Notifications for new comments on this Pull Request are off. Enable notifications for this Pull Request

@jeremy This one is candidate for Rails 3.2 or 4.0?

Jeremy Kemper
Owner

3.2 candidate for sure. This needs thorough attention for a clean upgrade, however.

The guides need closer attention. Perusing the diff, I see a lot of search/replace changes from PUT to PATCH. I think having PUT just disappear will be too confusing. Gotta explain this change each step of the way!

Anyone care to take up the torch on this?

Stevie Graham

i must be the only person in the world that disagrees with "PUT requires a complete replacement", as per RFC2616 "HTTP/1.1 does not define how a PUT method affects the state of an origin server"

that said i like the name PATCH better than PUT for the purposes of what it is used for here.

dlee

I can refine this patch if the core team decides it will definitely go into Rails 3.2... don't want to write another huge patch just to be pushed off to the next version :).

@jeremy can you give an example scenario where the put overrides would break? We might be able to provide a workaround, or at least put some notes in the documentation so that users aren't caught off-guard. Please suggest a workaround or an excerpt that can be added to the documentation.

Xavier Noria
Owner

Yes, we talked about this patch after 3.1 and it is in the "roadmap" for 3.2 if it is good to go by then.

@stevegraham RFCs are not axiomatic systems, but I think there's no controversy in that PUT means "put this resource at that URL" ("The PUT method requests that the enclosed entity be stored under the supplied Request-URI.").

You're sending the resource itself, and in the target URL you either create a new resource ("If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI."), or else replace the existing one ("If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.").

It is the app's choice to conform to that spec. Nowadays to do partial updates in a RESTFul way you need to define ad-hoc resources. For example, to toggle the "paid" flag of an invoice you may PUT to /invoices/:id/paid, but if you play by the rules you need PATCH for partial updates to /invoices/:id.

José Valim
Owner

Aside the docs concern, this pull request looks good to me. I would just add the following changes (but I can do it myself in later commits after this is merged, this is more of a mental note):

1) We could move config.action_view.default_method_for_update to config.default_method_for_update so other frameworks can read it as well (see 2 below);

2) There is no need to generate routes for both PUT and PATCH. We could read the config in 1) and generate just one of the routes;

3) config.default_method_for_update should be uncommented in new applications as we already changed all docs

José Valim josevalim commented on the diff November 13, 2011
railties/guides/source/form_helpers.textile
@@ -312,6 +312,11 @@ Rails will also automatically set the +class+ and +id+ of the form appropriately
312 312
 
313 313
 WARNING: When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, +:url+, and +:method+ explicitly.
314 314
 
  315
+NOTE: Rails can use the +PATCH+ method instead of +PUT+ for update forms if you set the following option in your +config/application.rb+:
  316
+<ruby>
  317
+config.action_view.default_method_for_update = :update
1
José Valim Owner

Typo: should be = :patch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Xavier Noria
Owner

@stevegraham I have found a crystal clear and authoritative answer to whether PUT allows partial updates: The very RFC 5789 (http://tools.ietf.org/html/rfc5789) states that "A new method is necessary to improve interoperability and prevent errors. The PUT method is already defined to overwrite a resource with a complete new body, and cannot be reused to do partial changes."

Michael Koziarski
Owner

I like the idea of moving this to be config.action_view.default_method_for_update as it lets people who care about these things choose to use patch without hitting anyone with 2x route proliferation.

However I'm pretty strongly opposed to switching the default to PATCH just because of a neurotic interpretation of the RFC. There are people with blog posts, printed books and screen casts where they do

form_for(@user, :html=>{:method=>:put}) 

Or

link_to("something", @user, :remote=>true, :method=>:put)

Switching the default will make all of those tutorials and chunks of code fail with routing errors, and "the RFC says X" doesn't seem like anywhere near a good enough reason to do that.

Maybe for a 4.0 change but absent some genuine bug or difficulty we can't do this in a random minor release.

Steve Klabnik
Collaborator

Maybe for a 4.0 change but absent some genuine bug or difficulty we can't do this in a random minor release.

Yeah, sure, I mean, the asset pipeline from 3.0 -> 3.1 was a waaaaaaay smaller change. ;)

of a neurotic interpretation of the RFC

This is not neurotic. It's simply following the way that things are supposed to work. Many more people want partial updates than an upsert, that's what PATCH is.

Jeremy Kemper
Owner

@dlee see my previous comment for a concrete example

dlee

@jeremey I was actually responding to your first comment in this thread. You say that explicit put overrides would break apps, but I don't see how they would. Can you clarify what you mean by put overrides?

Jeremy Kemper
Owner

@dlee Declare a resource in config/routes. Declare a put route that overrides the resource's update. Now upgrade the app and enable PATCH. Oops, now your forms submit to patch and your put route override is bypassed.

Not the end of the world, and docs should be enough to cover that.

dlee

@jeremy One of the points of the change was:

  • changes default form helpers to prefer :patch instead of :put for updates only for new apps

Note that form helpers use :patch only for new apps. Would this still not cover that case?

Michael Hutchinson

I agree with @NZKoz that this should not be included before 4.0 at the earliest. Unless I'm mistaken, RFC-5789 is still just a proposed standard, and it can still be changed or retracted. See: http://www.rfc-editor.org/info/rfc5789

@steveklabnik This isn't the way things are supposed to work yet, and even without all of the recent changes, we should only implement the standard when we know it won't change or be retracted- to do otherwise would be irresponsible. PATCH has been discussed by the IETF for almost ten years, why the rush to implement it in Rails now?

José Valim
Owner
benatkin

@mhutchin Interesting. I looked back at the link I posted at the top of the discussion and the RFC @dlee cited, which contains PATCH, appears to be obsoleted by one that I cited, which only has a reference to PATCH at the end. Here are the two documents:

The reference to PATCH at the end says:

The PATCH, LINK, UNLINK methods were defined but not commonly implemented in previous versions of this specification. See RFC 2068 [33].

Michael Hutchinson

@benatkin Thanks, I knew they had been discussing it for a long time, I underestimated how long. :-)

I would only point out for those interested that RFC-2616 for HTTP/1.1 is now a DRAFT STANDARD, but at least it is further along in the process than the RFC that now covers PATCH, RFC-5789, which is a PROPOSED STANDARD, the "entry-level maturity for the standards track". For the meaning of the statuses see: http://www.rfc-editor.org/rfc/rfc2026.txt

benatkin

Probably not the best way to check this, but on this list RFC-2616 is on Draft Standards and RFC-5789 is on Proposed Standards.

http://www.rfc-editor.org/rfcxx00.html

benatkin

@mhutchin I didn't see your most recent comment until I posted mine. You said exactly the same thing! It took me a bit of thinking to see that there's a big difference between draft and proposed besides draft being higher up the list, but I can see it now.

dlee

@NZKoz I don't think the "route proliferation" is so bad. In fact, even if Rails ends up supporting PATCH in the distant future, I'd prefer PUT be left as a standard route, perhaps linked to a different action (replace/upsert/etc.) This might not jive with a must-map-to-CRUD mindset, but it definitely jives with HTTP.

The interpretation of the RFC is not neurotic; I think we all share a clear and straightforward interpretation. I'll assume you meant "neurotic adherence to the RFC".

If so, let me reiterate that supporting PATCH is not to merely adhere to an RFC. The reason is because Rails currently has broken support for HTTP as commonly understood--leading to potentially problematic interoperability with RESTful clients, caching mechanisms, and proxies.

I'd say implementing, documenting, and promoting a broken implementation of an HTTP VERB is a genuine bug, warranting a fix in a minor release, but I'll leave the decision up to the Rails core team.

Finally, Rails shouldn't inhibit change just to retain backwards compatibility with blogs, books, and screencasts. In fact, I don't think Rails ever did care about that.

dlee

@mhutchin, @benatkin, the Draft Standard is actually going away: http://www.rfc-editor.org/rfc/rfc6410.txt. RFCs currently in Draft Standard status will either be promoted to Internet Standard or demoted to Proposed Standard.

BTW, Rails supports modern cookies and content-disposition headers which are also Proposed Standards, so there's no basis to reject PATCH just because it's a Proposed Standard.

In fact, Rails should be the framework that helps PATCH attain the Internet Standard status since Rails clearly needs PATCH in order to support non-replacing updates.

Whether that happens in 4.0 or 3.2, I defer to the Rails core team.

José Valim
Owner
dlee

If so, let me reiterate that supporting PATCH is not to merely adhere to an RFC. The reason is because Rails currently has broken support for HTTP as commonly understood--leading to potentially problematic interoperability with RESTful clients, caching mechanisms, and proxies.

Do you have anything to support this? PATCH is a new verb in the specs, so I doubt proxies and caching mechanisms went crazy to support it and change PUT behavior.

I was referring to the broken behavior of PUT in Rails, not the absence of PATCH.

For example, caches can reasonably assume that if I PUT {"a" => "b"} to a resource, then that's what it should return on a GET. However, if the original resource was {"c" => "d"}, Rails would by convention make the resource {"a" => "b", "c" => "d"}, not the expected {"a" => "b"}.

BTW, how did you reply to a comment? Or did you just insert ">" before each line?

Xavier Noria
Owner
Michael Hutchinson

@mhutchin, @benatkin, the Draft Standard is actually going away: http://www.rfc-editor.org/rfc/rfc6410.txt. RFCs currently in Draft Standard status will either be promoted to Internet Standard or demoted to Proposed Standard.

To be clear, this would only alter the status of RFC-2616 for HTTP/1.1, which isn't under discussion, in no more than two years. RFC-5789 for PATCH, and the Proposed Standard tier, are unchanged.

BTW, Rails supports modern cookies and content-disposition headers which are also Proposed Standards, so there's no basis to reject PATCH just because it's a Proposed Standard.

No, but it shouldn't be used as a selling point either.

I'm not saying that we should reject PATCH just because it is only a Proposed Standard. I do think that we need to be very clear though, since people started saying that this was to conform to the RFCs, that the RFC in question is only a Proposed Standard, and has been for a long time. The status of the RFC dramatically impacts the cost-benefit analysis. Most would readily agree that Rails should do just about anything to conform to relevant Internet Standards and that those should be the defaults, but we obviously can't and shouldn't conform to every Proposed Standard.

This should be evaluated without regard to the RFC.

In fact, Rails should be the framework that helps PATCH attain the Internet Standard status since Rails clearly needs PATCH in order to support non-replacing updates.

If we can do so with minimal disruption.

José Valim
Owner
benatkin

Note also that RFC-2616 has Roy Fielding and Tim Berners-Lee among the names, while RFC-5789 only has two names, not including those two, on it. Even if it had the same status (Draft Standard), it wouldn't carry quite the same weight to me unless I saw an endorsement from Roy Fielding. The obsolete standard, 2068, doesn't count.

Here's the first thing I found while searching for a new comment by Roy Fielding on PATCH:

PATCH is another option that, once it is sufficiently deployed, might be preferred over POST for sub-resource updates. However, we make these choices for a principled reason, not just because it seems RESTful. Ultimately, the methods should be chosen by the origin server and communicated somehow to the client via the media type or relationship processing rules. If the choice of method doesn’t make any difference to the other components (i.e., neither client nor intermediaries gain any value by using PUT or PATCH over POST), then we should admit that it just doesn’t matter to the architectural style.

So it sounds like we can be pretty RESTful without PATCH, and especially without PATCH as the default.

Xavier Noria
Owner

I was referring to the broken behavior of PUT in Rails, not the absence of PATCH.

About this point, in what sense is Rails behavior broken? Rails does what is in its hands to let you use PUT in an easy way, even imitating the correct way of doing things in browsers where that's not possible.

The user is responsible for using those tools correctly. You are free to define as many ad-hoc resources as you want to speak proper HTTP. If you construct forms for partial updates and use update_attributes that's up to you.

Stevie Graham

Deleted original comment because replying from email borked my formatting :(

@stevegraham I have found a crystal clear and authoritative answer to whether PUT allows partial updates: The very RFC 5789 (http://tools.ietf.org/html/rfc5789) states that "A new method is necessary to improve interoperability and prevent errors. The PUT method is already defined to overwrite a resource with a complete new body, and cannot be reused to do partial changes."

@fxn - I'm sorry I think you're mistaken here and I have to respectfully disagree with you. First of all RFC5789 is not HTTP, let's instead look at RFC2616

"The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI."

The word replace does not appear at all, in fact if you continue to read on you see "HTTP/1.1 does not define how a PUT method affects the state of an origin server." This implies to me that there server is free to choose whether to replace the resource in it's entirety or apply the enclosed entity as a patch. It's as clear as day as far as HTTP 1.1 is concerned and putting aside my view own personal views, neither of these outcomes are incorrect.

I think the only reason RFC5789 exists is that the authors have misinterpreted RFC2616. There is simply no language in RFC2616 that mandates "The existing HTTP PUT method only allow a complete replacement of a document." or that "The PUT method is already defined to overwrite a resource with a complete new body, and cannot be reused to do partial changes." It's simply not there. There maybe an argument that "a new method is necessary to improve interoperability and prevent errors." I don't know.

I also don't see how PUT breaks caching? As far as I understand it a successful PUT invalidates the caches for that given URI but the request response itself is not allowed to be cached as per HTTP 1.1. Maybe someone would be kind enough to explain that for me.

Having said all of the above, it would be nice if someone could actually refute my point to my satisfaction, which you have not done so far. It's not very comfortable being the only dissenting voice.

Xavier Noria
Owner

@stevegraham I agree with you in that the authors of the RFC5789 could be wrong, though I want to believe that you wouldn't be able to push such a proposal for so many time like they have done based on such a gross false premise. But yes, that's an hypothesis.

I was for a while in rest-discuss and my feeling is that there's consensus about that interpretation of PUT. Also O'Reilly's book about REST is clear. What's a modified version of the resource? Resources in HTTP are not partial, they are resources, all or nothing, in my understanding of the terminology. You can only request and submit resources or resource representations. So in that sense I believe no verb can work with partial resources. That's where ad-hoc resources come to play, like the paid flag of an invoice in my example above.

But this thread is pulling in different directions. It's interesting. There's the objections about whether it is premature to support such a RFC because of its status. There's the objections about whether using PUT for partial updates is incorrect at all. There's the argument that Rails should be pioneer and push. Then there will be practical issues because web servers and proxies won't understand PATCH in the short term and we will only be able to tunnel it through POST.

All in all, I've written to Roy Fielding to ask for advice. I think that's the best we can do. Should Rails work in that direction? If yes, should we work on it today? Let's see whether Roy is so kind as to shed some light about this.

benatkin

@fxn I don't see where @stevegraham suggested that the authors of RFC-2616 are wrong. I only see where he suggested that the authors of RFC-5789 are wrong.

I think that it's great that you've contacted Roy Fielding. I hope to see an interesting reply.

I wonder why this couldn't have been implemented as a plugin this summer when this and other issues were being discussed. If one was written, would it have been used? How about releasing a plugin based on this and seeing whether it takes off?

Xavier Noria
Owner

@benatkin oh yes my bad, wrong copy & paste. I've edited the comment.

Michael Koziarski
Owner

All this discussion of RFCs and the like seems to miss the point entirely. What's the negative impact of the current behavior to real users. Are there actual practical things which people are being bitten by because we use PUT?

Unless there's an answer to that then this entire thread is a massive distraction, we should add the config option, not change the defaults, and move on with our lives.

Xavier Noria
Owner

The discussion in my view is relevant because there are people arguing that NO support for PATCH should be added at all.

Arguments are: 1) it is too premature given the status of the RFC. 2) It is not clear in HTTP/1.1 that PATCH is needed at all. 3) Current web servers and proxies won't understand it, so this should add support only for tunneling.

I am enumerating, not subscribing them.

We need to answer that first question. I think the point about defaults is minor in the thread.

Michael Koziarski
Owner

Supporting it as an option, with no change of defaults, circumvents all 3 of those objections. Those who wish to use it can, those who don't care won't be affected in any way.

It's really the simplest solution that could possibly work and it won't break anyone's apps ever under any circumstances. Unless there's a compelling reason to do something else, why don't we add the config option and move on.

benatkin
Michael Hutchinson

Wait a minute. It was never my position that these changes are too premature given the status of the RFCs. The only reason I even mentioned the status of the RFCs was because they were being used in this thread, and the other threads mentioned, for various reasons, including trying to define the problem and justifying the changes, but without any mention of the fact that all of the RFCs mentioned were in various stages of maturity, and none of them were even final standards yet.

Other than that, I couldn't care less. I agreed with @NZKoz then, and I agree with @NZKoz now.

Xavier Noria
Owner

+1 to what Benjamin said. I don't think it is a good idea to push code, docs, and tests to maintain if the feature ends up being dubious. If people want to play with something experimental better a plugin than a black hole in the code base hidden by a flag to the end user.

I mean, we are touching one of the strongest conventions in Rails, it touches the blessed actions. We have to do that with extra care in my view.

If I had to bet, I'd say go, but I'd prefer to have a clear understanding of the convenience of pushing this from someone as Roy Fielding, given nobody in the thread AFAICT seems to have a definitive authoritative answer. Because pushing support for PATCH has implications, it sends a message to the user base. The guide and blog post need to explain to users when or why they should use PATCH, and from this thread we have no conclusions. A blog post saying "in case you want it, why? we don't really know, you just have it there, use at your risk, have fun" doesn't sound right to me.

Xavier Noria
Owner

To summarize, it is even easier to go with the solution that says partial updates with PUT are OK. No changes needed.

Konstantin Haase

Partial updates with PUT violate RFC 2616. You have to use POST.

Stevie Graham

Where?

Konstantin Haase

I understand Section 9.6 as "if you send a PUT request, don't care if the resource already exist, the server will tell us with a 200 or a 201" (i.e. the client will transmit the complete resource), how should that allow a partial update?

Xavier Noria
Owner

@rkh That's also what I think, but the point is "I understand", we always say in advance something like that, "my understanding", "my interpretation", which is telling us we cannot derive without doubt that from the text in the RFC. So we need to resort to "which is the intention", or "which is the general consensus".

Roy Fielding has not replied to my email so far.

On the other hand @steveklabnik has some pointers that support that PATCH is needed for partial updates I'll post later.

Steve Klabnik
Collaborator

For example, in the (now obsolete) RFC2068:

The PATCH method is similar to PUT except that the entity contains a
list of differences between the original version of the resource
identified by the Request-URI and the desired content of the resource
after the PATCH action has been applied.

The definition of PUT:

The PUT method requests that the enclosed entity be stored under the
supplied Request-URI. If the Request-URI refers to an already
existing resource, the enclosed entity SHOULD be considered as a
modified version of the one residing on the origin server.

The enclosed entity be stored. Not 'some random stuff that changes things.' It's an entire entity. 'a modified version of the one,' not 'changes that should update the one.'

@fxn has the links I gave earlier, where Fielding also explicitly says PUT requires an entire representation. I don't have them handy.

Stevie Graham

Please post these links because if they're the same ones I have seen Fielding does not say anything of the sort.

Konstantin Haase
benatkin

@steveklabnik As you say RFC2068 has been obsoleted by RFC2616. http://tools.ietf.org/html/rfc2068

RFC2616, a draft standard, doesn't include a definition of PATCH. All it has to say is The PATCH, LINK, UNLINK methods were defined but not commonly implemented in previous versions of this specification. See RFC 2068 [33]. To me this doesn't say that PATCH is a part of HTTP 1.1. Also the proposed standard for PATCH (not a draft standard) doesn't include any of the authors of RFC2616 in its list of authors, and I couldn't find any endorsement for RFC5789 from Fielding online.

So PATCH feels pretty random to me. Much more random than semantically replacing a document with an updated version but leaving created_at, updated_at, and id intact, and maybe omitting some unchanged fields for efficiency.

Konstantin Haase
Stevie Graham

@rkh NOPE NOPE NOPE

benatkin

RFC5789 is nearly two years old, is still a proposed standard, and I can't find any implementations of a write-through cache with it, nor can I find evidence of the two authors trying to push it forward since then.

I would be happy to see a rails plugin and an apache or nginx module written to show how it can improve how HTTP works, and then a better discussion about whether the complexity of adding another verb is needed could take place. I think headers might be better because to me whether something is being replaced or just modified isn't a yes/no question when metadata is considered.

Steve Klabnik
Collaborator

Here's one quote from Fielding:

Stefan, I think it is better to say that we only use PUT when the update action is idempotent and the representation is complete.

He goes on further to mention PATCH:

PATCH is another option that, once it is sufficiently deployed, might be preferred over POST for sub-resource updates. However, we make these choices for a principled reason, not just because it seems RESTful.

In the text of that post:

... complete replacement of a representation (PUT),

The text of the PATCH RFC, which is again, just a draft:

The PUT method is already defined to overwrite a resource
with a complete new body, and cannot be reused to do partial changes.

My point is this: there's overwhelming consensus that PUT means 'whole representation.' Well, modified consensus, since it's not 100%. ;) However, it is true that PATCH is a draft standard, so I can see not implementing it in Rails. That's quite valid. However, continuing to use PUT is also wrong. If it's a partial update, POST makes much more sense.

Steve Klabnik
Collaborator

@benatkin

So PATCH feels pretty random to me. Much more random than semantically replacing a document with an updated version but leaving created_at, updated_at, and id intact, and maybe omitting some unchanged fields for efficiency.

No, not at all. That's a direct violation of PUT's semantics. POST is the random grab-bag method, anyway.

Konstantin Haase
benatkin

Not because it's a proposed standard, no. But there are other reasons to support cookies: it's been implemented and experimented with in dozens of server, client, and middleware implementations long before the first version of Ruby On Rails came out, and it's a feature that's important to Ruby On Rails users.

Mike Kelly

fwiw, I brought this up on SO about 18 months ago (the answer was auto-accepted):

http://stackoverflow.com/questions/2364110/whats-the-justification-behind-disallowing-partial-put

Michael Koziarski NZKoz closed this December 14, 2011
Michael Koziarski
Owner

We've gone back and forth on this for more than 6 months, there has never been a single case provided why we should change the defaults other than bible, sorry RFC, thumping.

As I mentioned earlier, we should simply add an option for users who want to use PATCH to be able to use it, and move on with our lives. We can address the defaults in some far distant release once we actually have real experience with the benefits.

In the interest of bringing this to a close I'm going to close this pull request, please feel free to submit another which only adds a configuration option allowing users to switch on PATCH

dlee

Would rails core accept the minimal PATCH commit? I don't want to work on the pull request just to have it debated for months and closed without resolution.

Xavier Noria
Owner

RFCs are the tool we have to discuss this. Why don't we support MOVE in Rails. Well, because it is not in the RFC right? And if someone proposed MOVE we wouldn't accept it on the ground of not being there.

So let's accept this has to be discussed taking RFCs into account, and let's accept RFCs are not axiomatic systems and are subject to interpretation in some spots.

Having said that, if verb V is not clearly justified I see no point in hiding it behind a flag, as I said above. If the need for verb V is debatable it should not be in core.

Steve Klabnik
Collaborator

@NZKoz There are good reasons for this. I've cross-posted about this to rest-discuss, and as Jan Algermissen mentions,

When a client (or intermediary, for that matter) re-does a PUT N-times (e.g.
because it did not receive any response the first N-1 times due to network
problems) the result on the server might not be what the client assumes it is,
given the idempotent nature of PUT.

A client that is aware of the server's tunneling-partial-update-through-PUT
semantics might not redo the PUT but any intermediary in between might (because
they would not be aware of the out-of-band knowledge / just like Google
accelerator in the case of GET-to-delete-account).

What is the problem of just using POST for the partial update in the first
place? This is what POST is for.

Also, 'bible-thumping' is just a way to dismiss arguments; you either follow web standards or you don't. Rails has brought REST(ish) to many, many people, and the choices Rails makes are important. When IE6 didn't follow the standard initially, it was an excellent browser. Years later, we can see how that was a mistake.

I don't even mean this as 'let's implement PATCH,' as that's not yet a true standard. But update should really use POST by default, since most people aren't actually PUT-ing full representations.

Justin Searls

@steveklabnik +1, well said.

Mike Kelly

@steveklabnik that post from Jan is written on the basis that it is impossible to make an idempotent partial update. I disagree.

In some ways it's irrelevant anyway; people are already using partial PUT requests that are intended as idempotent.

Moving to POST would:

  • have a negative impact on clients since its non-idempotent nature has now made the requests non-repeatable
  • seem to produce zero appreciable benefits over the current practice

Supposedly, RFC2616 has been enforcing 'full PUT' for over 12 years and we're still yet to see any significant mechanisms that leverage this systemic agreement on the fullness of PUT requests.. that should tell us something (i.e. it's a useless semantic). Add in the fact we have implementations, like Rails, which blatantly disregard it and don't appear to suffer any real negative consequences.. I think it's a serious stretch to compare this to IE6.

Steve Klabnik
Collaborator

@mikekelly Honestly, if Rails devs knew what 'idempotent' meant, they'd be happy. I don't think they intend such things at all, I think they're just using the Rails defaults.

And if the semantics of PUT aren't useful, then they should be changed. In the spec for HTTP. And then we should follow it.

Agreements are useless if people don't actually follow them.

Xavier Noria fxn reopened this December 15, 2011
Xavier Noria
Owner

Guys I am reopening this PR. There's a legitimate discussion going on here, and I was the guy that took this PR on his plate from day one.

If the discussion takes 6 months or 2 years, it just doesn't matter, it has to take the time it needs.

People tired of the thread can unsubscribe.

Steve Klabnik
Collaborator

Two more things, while I think of it:

1) if PUT didn't require a full representation, there'd be no way to do the 'or create' part of PUT's semantics. While not totally damning, I think it's also a strong supporting line of reasoning.

2) Rails hasn't suffered for 'implementing PUT wrong' (really app dev's fault, but assisted by Rails' shoddy defaults) because Rails apps haven't been receiving PUT requests: they receive POST requests with a _method parameter saying PUT. This is basically exactly what I'm saying should happen with the removal of support for PATCH but without the extra parameter, pretending to be a PUT.

Michael Koziarski
Owner

I certainly don't agree with continuing the discussion, but as you say I can click the little "off" button :)

My objection is solely to switching the default without any actual downside to end users. It's not about whether someone's app will break (which we can obviously work around) but about yet-more additional conceptual overhead for users picking up rails. There's tonnes of books, screencasts and blog posts which show how RESTful routing works and they all say "put will update".

Users will read those blog posts or books, write :method=>:put in their html and get routing errors.

We can't just do that for the sake of feeling better about our purity. I'd suggest we really need a web-accelerator level of pain to justify changing the defaults.

Have fun guys ;)

Myron Marston

Supposedly, RFC2616 has been enforcing 'full PUT' for over 12 years and we're still yet to see any significant mechanisms that leverage this systemic agreement on the fullness of PUT requests.. that should tell us something (i.e. it's a useless semantic). Add in the fact we have implementations, like Rails, which blatantly disregard it and don't appear to suffer any real negative consequences.. I think it's a serious stretch to compare this to IE6.

@mikekelly: I don't think it's a matter of "real negative consequences"; rather, there are very, very positive benefits to implementing PUT correctly that are simply impossible to achieve when the server is not following proper PUT semantics. I've got a recent internal HTTP service that I built with sinatra and it was very easy to get proper PUT semantics. The app uses PUT both for create and update, and only allows full representations to be used. Here are some of the benefits we've received:

  • Since PUTs are idempotent, they can be re-tried as many times as you want. We rely on this a lot. The service is consumed by our main rails app. The rails app performs the PUT to the sinatra service using a resque job, and it's very easy to retry the job when we have a transient failure. It just works.
  • The fact that we use PUT for both create and update makes the client code exceptionally simple. We don't need to deal with two separate types of requests to ensure the sinatra service has the resource in the proper state. We just use a PUT, and it doesn't matter if the resource exists or not, or whether or not the resource is already in the desired state; the end result is the same. We also leverage this in a cron job that runs every few days for the express purpose of reconciling any discrepancies between the user configuration data in the rails app and the data in the sinatra app. It goes over every user, does a GET to the sinatra app to get the data, compares the data to what it has locally, and does a PUT if there is a discrepancy or if the sinatra app responds with 404 not found. Again, the idempotent, create-or-update nature of the PUT makes this extremely easy.
  • The fact that we use PUT for create (and thus allow the client to pick the identifier) makes the service very transparent and easy to troubleshoot. When one of the users of the rails app has some missing data I don't have to go look up the id of the corresponding record in the sinatra service; I can easily construct URLs directly in my browser using the user ID I already know.

None of these benefits would be possible if we had implemented the normal rails PUT semantics in our sinatra service.

So here's my two cents (not that anyone asked for it....). I'd love to see rails provide support to easily implement proper PUT semantics. As part of this, some other HTTP verb should be used for partial updates. PATCH looks interesting, but POST can be used today and is the correct verb to use. In addition, it would be great if ActiveRecord/ActiveModel provided an replace_attributes method that only supported FULL updates and no partial updates. It would effectively be like update_attributes except that it would set any unmentioned attributes to nil. Most of the time, this would cause the partial update to fail with validation errors, which is the correct behavior you would usually want.

Mike Kelly

@myronmarston right, this is a common approach e.g. Riak uses it

The issue here is that your application doesn't rely on completeness being guaranteed for PUT across the web; it's an over-specification of HTTP. If your application does need full PUT (many do), then design and document it that way. I agree it's a good idea to try and make this behaviour easier to achieve in rails.

Given we now have a lot of mobile clients with unreliable connectivity - allowing them by default to submit small, partial updates idempotently is a huge benefit. Neither POST or PATCH would allow this since they are non-idempotent.

Steve Klabnik
Collaborator

allowing them by default to submit small, partial updates idempotently is a huge benefit.

What's your issue with the 'expose a sub-resource' strategy?

Mike Kelly

What's your issue with the 'expose a sub-resource' strategy?

off the top of my head:

  • splitting up every potential partial into individual resources is not practical if you want to be able to partially update each individual attribute of a resource.
  • it's much more efficient to put (oh snap!) this optimisation in the hands of the clients, rather than try and second guess what they want.
  • smearing your resource state is a pain for things like caching; it's redundant, you introduce shared state between resources which cases problems with cache invalidation, etc etc

having said that, I do favour using sub-resource strategy based around deletion, which does tend to overlap somewhat. There are also ways to get around the third point about caching, but at a cost of increased complexity to your system.

Myron Marston

The issue here is that your application doesn't rely on completeness being guaranteed for PUT across the web; it's an over-specification of HTTP. If your application does need full PUT (many do), then design and document it that way. I agree it's a good idea to try and make this behaviour easier to achieve in rails.

Actually, my application does rely on it being the complete resource. That's how it implements PIT as create or update. It wouldn't be able to implement create if it allowed partial resources.

Given we now have a lot of mobile clients with unreliable connectivity - allowing them by default to submit small, partial updates idempotently is a huge benefit. Neither POST or PATCH would allow this since they are non-idempotent.

The spec does not mandate that POST or PATCH be idempotent--but it doesn't say they can't be. I think it makes sense to implement the partial update as an idempotent POST or PATCH and simply document that it is idempotent (even though the HTTP verb used does not require it).

Also, while it's possible to implement some kinds of partial updates idempotently, it's not universally true that ALL partial updates are idempotent. Consider the common case of using accepts_nested_attributes_for for a has_many association. When an update comes in that is adding new associated records, it is not idempotent, because each time the update runs it will add additional records.

Xavier Noria
Owner
Xavier Noria
Owner

Well, that could happen also for full requests... Indeed, regarding idempotence the spec says "(aside from errors or expiration issues)". So idempotence is defined for successful requests I interpret.

If that is correct, then I see no problem in allowing creation of sub-resources.

@myronmarston then, why do you think partial updates would not allow you to use PUT for creation and update?

Myron Marston

@myronmarston then, why do you think partial updates would not allow you to use PUT for creation and update?

It's possible, but it adds lots of complexity. With how I've implemented PUT, the client assumes no knowledge of what state the resource is in on the server. It simply doesn't matter. It always PUTs the entire resource.

Likewise, the server doesn't need to know if the request body is intended to be a partial or full representation; it uniformly treats it as a complete resource.

It makes for simpler, more maintainable code all around.

Xavier Noria
Owner

@myronmarston excellent, thanks.

So in your case the application is designed that way. Metaphorically speaking, in my invoicing applications the paid flag is an ordinary resource from the client's point of view. It is convenient, better for the semantics of the applications, we like the model, etc.

But if we focus the conversation on the raw protocol, the more I read about it, the less I am convinced that HTTP requires full representation in PUT requests.

However, Roy Fielding has stated that is the intention in a few public places. @steveklabnik quoted him, and I've also seen this patch http://trac.tools.ietf.org/wg/httpbis/trac/changeset/1158#file1 Roy points to in this thread http://tech.groups.yahoo.com/group/rest-discuss/message/17415 (@mikekelly participated in that one).

So, my personal take on this is: the HTTP spec is a bit ambiguous about partial updates via PUT. This opens the door to endless controversy because it does not follow from the spec crystal clear that you cannot do it. But Roy Fielding says you should send full representations. If you want an authoritative answer, Roy's the one. Thus, if Rails as a framework has to take sides, I think the most sensible choice is agreeing that partial updates via PUT are not the way to go.

That does not mean the path to address that in Rails is to support PATCH, but at least I believe we have a sensible conclusion about this.

Does anybody disagree?

dlee

@fxn I agree PUT should not be used for partial updates.

PATCH sounds like the perfect solution for this (in fact, designed specifically for this purpose). Other than backwards compatibility issues (which we can avoid by making PATCH optional) and lacking the stamp of RFC Internet Standard (which wasn't an issue for cookies and content-disposition), what reasons are there for not using PATCH?

Steve Klabnik
Collaborator

Oh, also, some clarification from a changeset authored by fielding 9 months ago in the upcoming changes to the spec.

An origin server SHOULD reject any PUT request that contains a Content-Range header field, since it might be misinterpreted as partial content (or might be partial content that is being mistakenly PUT as a full representation). Partial content updates are possible by targeting a separately identified resource with state that overlaps a portion of the larger resource, or by using a different method that has been specifically defined for partial updates (for example, the PATCH method defined in RFC5789).

Mike Kelly

personally I think it's better to ignore the specifications and focus on the practical problems inflicted by using PUT for partial updates - what are they?

reschke

@dlee "lacking the stamp of RFC Internet Standard"

How exactly are PATCH (RFC 5789), Content-Disposition (RFC 6266) or Cookies (RFC 6265) "lacking the stamp of RFC Internet Standard"?

dlee

@mikekelly The practical problems stem precisely from not following specifications. It's as if your server did an update on a GET, or a delete on a PUT--you're breaking conventions/expectations. HTTP participants expect certain behavior following specifications, and breaking that expectation will lead to troubles.

Xavier Noria
Owner

@mikekelly I think there are two ways to look at it.

One way is the pragmatic choice one does for a particular application. People can do whatever they want. You can use GET for deletion, you can tunnel everything trough POST. You can submit partial updates through PUT... Individual programmers for particular projects decide to adhere to the spec or not and in what degree.

The other point of view is the one of a web framework. In my view it is the duty of Rails to follow and promote best practices. When designing the framework, specs are what we need to base the design on, they are what define what's a good web citizen.

If you are writing your own private links manager running in localhost, and you want to delete links with GET quick & dirty, please go ahead. But Rails should push the message you should be using DELETE. By design, the easy path does that. And it does going as far as having builtin macros, routing, conventions, and even tunneling the method through a hack while at the same time routing the verb correctly (BTW, someone said people do POSTs with _method really not PUTs anyway. No, browsers do POSTs, Rails routes PUT).

This PUT vs PATCH is no different. Though we need to take into account backwards compatibility, I think it is our responsibility to give the user the tools to do partial updates in a proper way.

Mike Kelly

@dlee @fxn what is this unexpected behaviour exactly?

Is it that a client might make a PUT request intended to 'shrink' a resource but instead only partially updates it as a partial? If so; is that a real problem? Aren't client devs reading documentation telling them what can be PUT where anyway?

dlee

@reschke #505 (comment)

@mikekelly What you're describing is one of the problems. Another could be that a PUT request could be cached with a partial representation. Still another is that a client might assume the idempotent properties of PUT.

Mike Kelly

@dlee it's not practical to prime a cache with the contents of a PUT request - do you know of implementations which actually do this? The client is meant assume the idempotent properties of PUT, so that's not really a problem either.

Olek Janiszewski

Let's agree that for vast majority of Rails applications, a "partial update" (a.k.a. update_attributes) is the most common use-case for an update-like request. Just imagine having to re-submit full representations of large/complex resources on every update (like nested attributes or file attachments).

Having said that, I'd vote for routing PATCH to the update action, and coming up with a separate action with PUT semantics (create/update full representations) in Rails 4.

reschke

@dlee I'm aware of that.

I'm just confused about "lacking the stamp of RFC Internet Standard". PATCH, Cookies, and C-D are Proposed Standards. HTTP/1.1 currently is a draft standard, but will be back at "proposed" soonish. As a matter of fact, most of the internet runs on "proposed" standards.

dlee

@mikekelly No, I don't know of any implementations off the top of my head. As for idempotent PUTs, the problem is precisely that clients assume the idempotent properties of PUT, but the server might not do idempotent updates in PUT.

@exviva I agree. But I think there should be some sort of transitional stage in 3.x that introduces devs to PATCH and allows them to opt-in before Rails 4.

Mike Kelly

@dlee ok, but what does the invalid use of PUT for non-idempotent requests have to do with the use of idempotent partial updates? Are you implying that idempotent partial updates aren't possible?

dlee

@mikekelly Sorry, I don't understand why you're talking about "idempotent partial updates". My worry is with non-idempotent partial updates happening behind a PUT request (expected to be idempotent).

Xavier Noria
Owner

@mikekelly If you document you can delete a resource via POST requests, that's your business. Users of your API can certainly read that documentation and understand it.

If you allow idempotent partial updates via PUT requests also that's your business. Users of your API can read that documentation and understand it.

But If you are a Rails programmer and want to provide DELETE for deletion and PUT for full updates the framework provides the means[*]. As of today there's no builtin way to do partial updates, because there's no builtin way in HTTP. This thread is about whether Rails as a framework should do something about this, not about the practical urgency you may feel. People have done delete via POST for ages.

[*] The current idioms with update_attributes are perfectly fine for full representations via PUT. "Full representation" belongs to your interface, update_attributes is your implementation. If your API requires a full representation (except perhaps for some internal stuff like cache counts), update_attributes allows you to store one just fine.

Mike Kelly

@fxn

As of today there's no builtin way to do partial updates, because there's no builtin way in HTTP

it's already built into rails and works ok over HTTP, afaict:

curl -H "Accept: application/json" http://test.dev/widgets/1

{"id":1,"foo":"bar", "fizz": "buzz", "car": "blue" }

curl -H "Content-Type: application/json" -X PUT -d "{ \"foo\": \"partially updated\" }" http://test.dev/widgets/1

curl -H "Accept: application/json" http://test.dev/widgets/1

{"id":1,"foo":"partially updated", "fizz": "buzz", "car": "blue" }

Steve Klabnik
Collaborator

Sure, and so does GET /foos/1&action=delete to delete a resource, but that doesn't mean you're not violating HTTP's semantics to do it.

Mike Kelly

@steveklabnik most people agree that 2616 is not clear enough for this to be a serious issue, hence why httpbis has looked to improve it. I'm yet to figure out why exactly that semantic needs to be put in place by httpbis. It doesn't appear to be necessary and it prevents partial idempotent updates (PIUs).

PIUs are important, particularly for mobile devices that operate on crappy networks and need to be able to make extremely granular requests (i.e. partials) that can be retried easily on failure (i.e. idempotently).

The fact is, rails behaves this way - it's demonstrably useful, and non-demonstrably detrimental to either rails applications or the web as a whole.

I think it's probably better to leave it as it is - given the contention over 2616 - and wait to see what happens with httpbis.

dlee

@mikekelly I think 2616 is clear enough on the issue of PUT being full-resource replacements, but just in case it wasn't clear enough, there's also the RFC 5789 to make it explicitly clear.

PIUs, and just partial updates in general, are important, we all agree. That's the whole point of this pull request--let's support the proposed standard of using PATCH to do partial updates.

PUT mandates idempotent full (non-partial) updates.
PATCH allows full or partial updates, idempotent or not.

@reschke Yes, a lot of the internet runs on Proposed Standards. That's why we shouldn't dismiss PATCH even though it's a Proposed Standard, because it's too restrictive to only support Internet Standard.

Mike Kelly

@dlee I don't want to argue semantics because it won't go anywhere, but would you agree there must've be some ambiguity that causes it to need updating by httpbis?

The problem with moving to PATCH is that it's not equivalent since its definition is not idempotent: "PATCH is neither safe nor idempotent", this means that - on the web - these requests cannot be considered idempotent (even if your client and server know better). This means that any supporting infrastructure e.g. HTTP client libraries which know they can retry PUT requests on network failure, or generic accelerating proxies that perform a similar function cannot do so on these PATCH requests - even if they are intended idempotently, on the network they aren't.

Fwiw, that kind of argument does not hold up in the same way for the guaranteed fullness of a PUT request, which is exactly my problem with that (over)specification.

dlee

@mikekelly From what I can tell, it's clear that PUT means full-resource updates, and the purpose of RFC 5789 (PATCH) is not mainly to clarify PUT's definition (it does clarify, if only by taking for granted that PUT is full-resource replacement), but to provide a solution for a glaring hole in HTTP--namely, the lack of partial updates.

I agree with you that PATCH does not guarantee idempotent updates. I agree with you it might be useful to have a verb that allows partial and idempotent updates. However, PUT is not it (no partial updates). Neither is PATCH (updates not guaranteed to be idempotent). There is no verb in HTTP that guarantees idempotent behavior while allowing partial updates.

Your desire for such a verb should not prevent PATCH from being supported in Rails. As for idempotent partial updates, it seems like using subresources (as discussed in previous comments) is the best strategy for now.

After all this, though, it seems we're pretty much on the same page. I think you'd agree it would be good to support PATCH in Rails if it's clear that PUT does not allow partial updates. Our task, then, is to ascertain whether or not PUT supports partial updates. And between RFCs (new and old) and Roy Fielding, it seems pretty clear the authorities are against partial updates in PUT. Can we agree on this?

Xavier Noria
Owner

(With infinite love to @tenderlove).

@mikekelly Ruby on Rails does not define HTTP semantics, as a web framework it has to provide means to its users to be able to adhere to them if they want to, promote proper usage of them, and still allow users to do delete via GET and partial updates via PUT (ie, promoting, not lecturing).

Today, to toggle the paid flag of an invoice and follow proper HTTP semantics, you have to make that flag a resource. That's for me a conclusion of this thread, based on quotes from Roy Fielding.

So the topic of the thread is: given these definitions, what should Rails do next? Perhaps the answer is PATCH, or perhaps is nothing, we still don't know.

And if Rails had some sort of support for PATCH, you still would not be able to do idempotent partial updates and adhere to HTTP semantics at the same time. That's just how things are defined, it's not under the control of Rails. To discuss the convenience of this you need to discuss with the HTTP guys, not the Rails guys.

Mike Kelly

@fxn the problem here is that you're insisting that your interpretation of the semantics in 2616 is the only valid one, which is not the case. Debating semantics is utterly pointless, but it is worth acknowledging that this particular discussion about PUT has been around for lot longer than this PR - which suggests that there is, in fact, more than one valid interpretation.

So even if "we must follow the spec at all costs" was a valid logical argument - in this instance it doesn't really move us forward due to the unavoidable reality that part of the spec in question is up to interpretation.

So, as I've said previously in this thread, I think we're far better off focusing on the actual benefits and problems of moving away from or staying with partial idempotent updates. I've already made a case for sticking with what we have:

PIUs are important, particularly for mobile devices that operate on crappy networks and need to be able to make extremely granular requests (i.e. partials) that can be retried easily on failure (i.e. idempotently).

The fact is, rails behaves this way - it's demonstrably useful, and non-demonstrably detrimental to either rails applications or the web as a whole.

I think it's probably better to leave it as it is - given the contention over 2616 - and wait to see what happens with httpbis.

Aside from the concerns about following the spec, why do you think partial updates via PUT are bad for either rails apps or the web?

reschke

@mikekelly you can make a PATCH request idempotent by adding "If-Match".

Xavier Noria
Owner

@mikekelly I don't say this is the only valid interpretation. At the beginning of the thread it was my reading of the spec, and it was what other sources interpreted. It is also clear that we are not inventing this controversy, it is old.

After this thread, we have a pulse of the generic consensus and Roy Fielding's view on this topic. And I believe that opinion is key. If Roy said "go do partial updates with PUT" we had already closed the PR.

This tells me that if I have to bet, I know where to bet. Not saying the other interpretation is wrong. The spec is ambiguous, so there's no right or wrong. It could be the case that the ambiguity was an overlook, I personally don't know. But what we have is consensus (different from unanimity), and the +1 of Roy Fielding regarding not doing partial updates with PUT.

So, Rails is (probably) going to provide a way for the majority of people participating in this thread that would like to do partial updates without PUT. At the same time, you will be able to continue doing partial updates via PUT.

Mind you, if we do something I don't expect it to be a dramatic change. At least at the beginning. It could be something discreet, like being able to say :method => :patch, and adding routing and conventions without changing defaults. Backwards compatibility must be respected, and indeed you may be doing form_for with a full-representation, which is the canonical use case of form_for reused in create and update views.

And yes, adhering to the spec/semantics is enough for me to consider adding support for this. Why does Rails support DELETE? Couldn't people delete via POST? Sure, and we are doing it all the time, but that's irrelevant, it is the duty of Rails to give you a way to do proper DELETE. When you work with polynomials you don't say "superscript", you say "exponent". And when people mean partial update, they are going to be able to say PATCH.

Mike Kelly

@reschke is that an explicit part of PATCH in its latest incarnation? Of course - that approach would also prevent non-conditional PIUs, right?

@fxn I'm definitely not keen on this change - it's unnecessary, more complex, and stands to invalidate existing standard rails practices. It's unnecessary because it doesn't actually appear to achieve very much other than to satisfy:

  • a part of 2616 that doesn't even have to exist according to a valid interpretation
  • Roy Fielding's proposed clarifications in an spec that's not been published yet

we're going round in circles here and I have nothing to add at the moment, so I'm going to bow out for now

reschke

@mikekelly this applies to any HTTP method; for PATCH, see http://greenbytes.de/tech/webdav/rfc5789.html#rfc.section.2.p.4

re: "that's not been published yet" -- oh, it is published as Internet Draft, just not approved as RFC yet. Publication of Internet Drafts is for community review and gathering feedback. Hint, hint.

Myron Marston

@mikekelly -- Over and over gain you've pointed out that rails supports partial idempotent updates. This is true, but update_attributes and the common idioms of rails (i.e. using accepts_nested_attributes_for) often result in a non-idempotent partial update. Consider this params hash from the API docs:

params = { :member => {
  :name => 'joe', :posts_attributes => [
    { :title => 'Kari, the awesome Ruby documentation browser!' },
    { :title => 'The egalitarian assumption of the modern citizen' },
    { :title => '', :_destroy => '1' } # this will be ignored
  ]
}}

This is not idempotent, because every time the update_attributes processes this hash, it creates two new posts (one with the title "Kari, the awesome Ruby documentation browser!", one with the title "The egalitarian assumption of the modern citizen").

Thus the conventions and idioms that rails encourages developers to use result in non-idempotent PUTs. Putting aside the full vs. partial question for a second, the non-idempotency here is a big problem.

Steve Klabnik
Collaborator

And Fielding with a non-snarky answer:

PUT means PUT. There are no
partial updates in PUT. There was a half-assed attempt to add those
semantics by committee in the midst of standardizing HTTP, but that
attempt failed because PUT's existing semantics had already been deployed
and we can't graft partial updates on top of existing replace semantics.
Period. End of story. Hence, PATCH was defined in 1995 (and finally
standardized much later because the WebDAV group was lazy).

This answer is final. If anyone implements it differently in Rails,
then Rails will be neither compliant with HTTP nor compliant with REST.
Whether that matters to anyone developing Rails is besides the point.

Game over, man. Game over.

Now, to the question he addresses in his final paragraph: does this actually matter, to us? I obviously think it does.

Xavier Noria
Owner

Case closed!

Yes, it matters.

We are about to release 3.2 RC, it's too late for 3.2, but I think the time it took to discuss this has been worthwhile because we didn't have such a clear and authoritative point of view at the beginning. Knowing which are the rules without doubt, we have the right mindset to work on the PR.

Mike Kelly

please consider my response to that, and wait for the discussion to play out.

obligatory meme:

recognize, bitches.

Mike Kelly

Roy is responsible for the changes in httpbis so it's hardly an earth shattering revelation that he believes 2616 should be interpreted that way.

Steve Klabnik
Collaborator

He's also an author of 2616, so I'd expect him to be a pretty valid source when requesting clarification of it, too.

Knowing which are the rules without doubt, we have the right mindset to work on the PR.

Right. The real question is this: how far do we want Rails to go? Obviously, Rails cannot make every web service RESTful; that relies on human design. I would like to see Rails make it easier to comply with HTTP and HATEOAS, though, by providing helpful things in that regard. That's obviously longer-term and out of the scope of this pull request. However, this is a good start. I think the correct option as far as this pull request goes is this:

1) Add two new actions: patch and upsert. The update action should match to POST, the patch to PATCH, and upsert to PUT.
2) Start off with having this as a configuration option, ActionController.update_verb, which is set to :post in new rails apps, but has a default of :put so that old apps still work.
3) In Rails+1, change the default to :post but leave the option so that people can set it if they still don't want to update.

'upsert' isn't a great name. I've run across some people who don't know what this means, but it does capture what PUT is supposed to do in one, short word.

reschke

@steveklabnik keep in mind that just changing PUT to PATCH doesn't make things better magically, you also need an Internet Media Type describing the patch format. And no, "application/json" doesn't.

Steve Klabnik
Collaborator

Also, @mikekelly asked me to make something perfectly clear, so here it is:

I think that standards should be followed, because they're standards. They're the working agreements that we all go by, and if we discover that we're not compliant, software should changed based on that and nothing else. This doesn't mean they're infallible, and when they get revised, things should be changed again. Because compliance with a spec is the most important thing.

We now return to your regularly scheduled pull request.

Steve Klabnik
Collaborator

@reschke right. Baby steps. That's why I think that POST actually works better for what rails calls update.

Mike Kelly

it was tongue in cheek, and my point was that until httpbis makes it completely clear - there is no requirement to churn rails, because there are valid interpretations of 2616 which are perfectly fine with the current behaviour.

So, essentially, jumping the httpbis gun w/ this PR is driven by speculation and/or pedantry and not a need to conform to a spec which unquestionably requires the change (there isn't one, HTTPbis is a WIP) or fix a functional problem with rails (there isn't one of these, either). I think everyone should be clear about that.

Myron Marston

So, essentially, jumping the httpbis gun w/ this PR is driven by speculation and/or pedantry rather than conforming to a spec which unquestionably requires the change (there isn't one, HTTPbis is a WIP) or fixing a functional problem with rails (there isn't one of these, either). I think everyone should be clear about that.

That's not it at all. We have the one of the authors of the HTTP 1.1 spec, and the inventor of REST (which rails at least tries to adhere to...) saying that PUT means only full representations, and that rails is not in line with the HTTP or REST to allow it any other way.

Mike Kelly

tl;dr cliffnotes for @tenderlove: change from this PR is significant, doesn't need to happen - may do in future if/when next revision of HTTP invalidates the approach. Roy Fielding is the ineffable fountain of HTTP wisdom.

Mike Kelly

That's not it at all. We have the one of the authors of the HTTP 1.1 spec, and the inventor of REST (which rails at least tries to adhere to...) saying that PUT means only full representations, and that rails is not in line with the HTTP or REST to allow it any other way.

Roy is an insanely smart guy and definitely an authoritative source on HTTP. His insight and opinion is important. But his opinion does not magically make 2616 more clear on this issue than it is. It's not clear. This is why Roy himself has revised it.

Xavier Noria
Owner

@mikekelly we have not outlined yet the path, as I said before I don't expect to do any dramatic changes any time soon. Let's explore.

At this point in the thread, the conclusion is that we are going to add some support for PATCH and promote good practices regarding PUT/PATCH in Rails applications. Good practices as described by Roy Fielding.

But progressively, and with backwards compatibility as a must. At least for the time being.

Mike Kelly

At this point in the thread, the conclusion is that we are going to add some support for PATCH and promote good practices regarding PUT/PATCH in Rails applications. Good practices as described by Roy Fielding.

But progressively, and with backwards compatibility as a must. At least for the time being.

Ok fair enough, in that case it looks like @NZKoz hit the nail on the head over a month ago.. #505 (comment)

Xavier Noria
Owner

The point of following the discussion was to know whether we should have to add anything at all. At that point we didn't know.

If Roy said "yeah, just do partial updates with PUT" no flag should be added, no code should be in core.

If Roy says "PUT means PUT. There are no partial updates in PUT." that justifies working on the PR. Working on the PR means what I said above, it does not mean applying the very patch.

Geoffrey Ducharme

Allowing access to PATCH as a configuration option will help drive awareness and adoption. Further down the line, we might well see a larger number of devs and applications clamoring for a change of default configuration, in much the same way we saw jQuery make its way in core.

Count this as +1 to a configuration option.

dlee

Rails 4 has been announced for the summer. How much PATCH support should we get in? There have been various proposals of how to support PATCH in Rails 4:

  • make PATCH and PUT both map to update and:
    • make edit forms use PATCH by default and PUT by configuration opt-in
    • make edit forms use PUT by default and PATCH by configuration opt-in
  • split update into different actions that map better to PUT and PATCH
Steve Klabnik
Collaborator

@dhh mentioned on Twitter that he supports some kind of support.

Really, to be honest, I think this pull request should probably be closed, and a discussion thread should be made on rails-core about it. That's sorta out of the scope of this particular PR.

José Valim
Owner

We should have a config option called config.method_for_update. Both routes and forms should respect this option. There is no need to generate both routes. It makes no sense to split update in two actions.

José Valim
Owner

In other words, this pull request is almost good imo. It just needs to provide a config.method_for_update, make both AV and the router read from this config and change the router to stop generating both put and patch for update.

Steve Klabnik
Collaborator

@josevalim It really depends on how far you actually want to go. A 'rails 4' gives a big opportunity to step back and evaluate the whole 'Rails REST' thing in general.

Then again, it seems that rails core wants rails 4 to be more of a normal release, so...

Regardless, I agree about this patch. It's good regardless of that stuff.

dlee

If Rails core gives the OK, I can fix the patch according to @josevalim's recommendations.

Xavier Noria
Owner

PATCH and PUT have different semantics. PUT means create/replace and it is idempotent. PATCH means update (full or partial) and it is not idempotent.

If your application is a s3 clone, you want to upload assets with PUT. I think we need to be able to have both working, rather than a exclusive option.

José Valim
Owner

@fxn I agree both options should work, it is just a matter of what we choose as default. For instance, the router should generate just put or patch routes by default for resources, however if for some specific case I want to use both PUT and PATCH or another, I should be able to do it. The same for the view, responders, etc... they should work for both put and patch.

Xavier Noria
Owner
José Valim
Owner

So as both me and @fxn agree, we would love to receive a new pull request that adds those small changes to this already existing pull request. Thanks @dlee for holding on all this time.

Myron Marston

I hinted at this above, but to reiterate: besides changing rails so that it supports PATCH, I'd also like to see improved support for proper PUT semantics. Currently, rails idioms and conventions do not make this easy. Specifically:

  • The common AR method used for updates is update_attributes. It supports updates of the full representation, but it also supports partial updates, which, as we've discussed above, is a violation of PUT semantics (at least in the mind of Fielding and other HTTP experts).
  • update_attributes also supports non-idempotent updates when used in combination w/ accepts_nested_attributes_for.
  • I think the simplest way to provide an update method that does provide proper PUT semantics is a new method, replace_attributes. It would set any attribute that is not included in the hash to nil, and thus treat the given attribute hash as the full resource.

Would the rails core team by interested in the addition of this API? I'm willing to take a stab at it, and can open another ticket to discuss further details, but figured it was worth inquiring here first.

dlee

I like that idea; it'd make adhering to HTTP much easier.

I'd prefer a method name that more strongly indicates the full-resource replacement. What do you think about full_replace, replace_all_attributes, replace_resource, replace_with, or simply replace? An even stronger word might be supplant, but that just doesn't have a nice ring to it.

José Valim
Owner

If there would be a method, I would simply call it replace_attributes. BUT in my opinion and experience it doesn't make sense at all to have a method like this in Rails because replace_attributes would be tied to the model while its behavior should actually be given by the resource (which is then defined by the application). Remember that different actors in the system (for example user and admin) may see the same model as two different resources where PUT would behave differently for each. Even without those actors, a single model may be exposed as two different resources or vice-versa.

In other words, replace_attributes (or anything like that) would be a poor fit and ultimately lead to poorly designed APIs as people would believe that a call to replace_attributes would be everything you would need to have proper PUT semantics.

Myron Marston

You bring up good points, @josevalim. I agree that people tend to think "model == HTTP resource" (or not even really think in terms of HTTP resources) and replace_attributes may seem to implicitly condone that line of thinking.

That said, rails doesn't currently have anything that helps make it easy to implement proper PUT semantics. If you want to implement proper PUTs you are essentially totally on your own. Do you have a better suggestion? (Also, should we take this to another thread? I don't want to derail the discussion here, but it's certainly related).

As I see it, replace_attributes isn't the perfect answer to HTTP spec or REST compliance any more than the rails scaffolds are. But it is a tool that can help make it easier to implement proper semantics (FWIW--I implemented this in one of my models on a recent project, and it worked great).

José Valim
Owner

How Rails doesn't provide anything? Rails is a framework, anyone can provide his own PUT setup according to his application semantics, as you did. And probably in less than 30LOC! Please start a new discussion as I have no interest in the direction this is going.

Xavier Noria
Owner

Yeah, me neither.

Resources are conceptual, ARs model database tables. There's a gap there that I think would show as a complicated setup for such a simple thing.

I prefer to discuss that in another issue or PR.

Myron Marston

@josevalim and @fxn: no need for another ticket. Two core rails members have weighed in against the idea. Thanks for the input. That's one less open source contribution I need to worry about; I have enough on my plate as it is.

Xavier Noria fxn closed this February 22, 2012
Arun Agrawal
Collaborator

#5130 merged.

Miguel Gomez

In the risk on being pedant by arguing on a closed discussion against the last three comments (@josevalim, @fxn and @myronmarston), where I even haven't any personal use case yet, I will still make my point here, since I don't know where it goes better.

Rails does not provide a clean way to map resources to models "to and fro", which makes josevalim's point somewhat valid. I mean, devs usually call a model into a variable (normally in the controlle's show method), but they might as well make a mix of some related models to represent the resource that they want to expose. In this second case, they then have to rip the params off in the update action, so that they get the different models to validate and save and so on. That's what I see as a weak part of the framework. I feel that josevalim's argument could backing up a kind of "presenters-and-back" as a best way to translate/map models into resources, and that the implementation of such a paradigm into Rails should be seriously weighted. Were they dismissed (which I suppose, for that is a whole subject in itself), I would then come to my conclusion: I dislike disregarding a new method called replace_resource , which sets all empty attributes to nil (or blank, depending on validation rules), supposed that it gets applied on a single model. You can afterwards put all needed warnings in the documentation about the semantic meaning of it. I mean, Rails 4 is going to promote a new verb just to adhere to standards, without any appreciable gain in the short term, just for the sake of it? Do you really mean that you are going to leave devs on their own, having to understand why to use this or that, without further help? "Boy, if you want to make proper use of this new feature that we advocate, you'll have to write your own methods for it, it's just 30LOC, boy. After all, Rails is just a framework, don't expect too much from us."

See my point? I know I am a bit sarcastic at giving reasons for my petition, but I do it just in order to make it crystal clear and avoid another six months of discussion on the need of such method. I hope I got it.

PS. Sorry for coming in so late, I just saw the thread linked in the Rails Blog. I got through the whole of it, oh my!

Miguel Gomez

I elaborated a bit more on the subject of ResourceAccessor and how and why the replace_resource method is needed..

Benjamin Fleischer

fwiw, I backported the HTTP PATCH verb work for Rails 3.2 https://gist.github.com/bf4/8940203

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 3 unique commits by 1 author.

May 10, 2011
dlee Use PATCH instead of PUT; Fixes issue #348
PATCH is the correct HTML verb to map to the #update action. The semantics for
PATCH allows for partial updates, whereas PUT requires a complete replacement.

Changes:
* adds the #patch verb to routes to detect PATCH requests
* adds #patch? to Request
* adds the PATCH -> update mapping in the #resource(s) routes.
* changes default form helpers to prefer :patch instead of :put for updates
* changes documentation and comments to indicate the preference for PATCH

This change tries to maintain complete backwards compatibility by keeping the
original PUT -> update mapping. Users using the #resource(s) routes should not
notice a change in behavior since both PUT and PATCH requests get mapped to
update.
3f9a09c
dlee Make method for update forms configurable
Make :put the default method for backwards compatibility. The generator for new
Rails projects configures :patch a the default method in config/application.rb.
985bec5
Jun 24, 2011
dlee Indent examples for proper rendering in docs 8a18692
This page is out of date. Refresh to see the latest.

Showing 37 changed files with 326 additions and 87 deletions. Show diff stats Hide diff stats

  1. 4  actionpack/lib/action_controller/metal/http_authentication.rb
  2. 5  actionpack/lib/action_controller/metal/responder.rb
  3. 7  actionpack/lib/action_controller/test_case.rb
  4. 6  actionpack/lib/action_dispatch/http/request.rb
  5. 15  actionpack/lib/action_dispatch/routing.rb
  6. 35  actionpack/lib/action_dispatch/routing/mapper.rb
  7. 26  actionpack/lib/action_dispatch/testing/integration.rb
  8. 6  actionpack/lib/action_view/helpers/form_helper.rb
  9. 6  actionpack/lib/action_view/helpers/form_tag_helper.rb
  10. 8  actionpack/lib/action_view/helpers/url_helper.rb
  11. 2  actionpack/test/controller/caching_test.rb
  12. 24  actionpack/test/controller/integration_test.rb
  13. 64  actionpack/test/controller/mime_responds_test.rb
  14. 15  actionpack/test/controller/request_forgery_protection_test.rb
  15. 31  actionpack/test/controller/resources_test.rb
  16. 13  actionpack/test/controller/routing_test.rb
  17. 8  actionpack/test/dispatch/request_test.rb
  18. 1  actionpack/test/dispatch/routing_assertions_test.rb
  19. 9  actionpack/test/template/form_helper_test.rb
  20. 6  actionpack/test/template/form_tag_helper_test.rb
  21. 6  activemodel/lib/active_model/lint.rb
  22. 7  activeresource/lib/active_resource/connection.rb
  23. 10  activeresource/lib/active_resource/custom_methods.rb
  24. 8  activeresource/lib/active_resource/http_mock.rb
  25. 12  activeresource/test/cases/format_test.rb
  26. 4  activeresource/test/cases/http_mock_test.rb
  27. 2  railties/guides/source/action_controller_overview.textile
  28. 5  railties/guides/source/ajax_on_rails.textile
  29. 7  railties/guides/source/configuring.textile
  30. 15  railties/guides/source/form_helpers.textile
  31. 20  railties/guides/source/routing.textile
  32. 2  railties/guides/source/security.textile
  33. 6  railties/guides/source/testing.textile
  34. 6  railties/lib/rails/generators/active_model.rb
  35. 3  railties/lib/rails/generators/rails/app/templates/config/application.rb
  36. 4  railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb
  37. 5  railties/test/generators/app_generator_test.rb
4  actionpack/lib/action_controller/metal/http_authentication.rb
@@ -276,7 +276,7 @@ def secret_token(request)
276 276
       #
277 277
       # An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
278 278
       # protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
279  
-      # POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
  279
+      # POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
280 280
       # of this document.
281 281
       #
282 282
       # The nonce is opaque to the client. Composed of Time, and hash of Time with secret
@@ -290,7 +290,7 @@ def nonce(secret_key, time = Time.now)
290 290
       end
291 291
 
292 292
       # Might want a shorter timeout depending on whether the request
293  
-      # is a PUT or POST, and if client is browser or web service.
  293
+      # is a PATCH, PUT, or POST, and if client is browser or web service.
294 294
       # Can be much shorter if the Stale directive is implemented. This would
295 295
       # allow a user to use new nonce without prompting user again for their
296 296
       # username and password.
5  actionpack/lib/action_controller/metal/responder.rb
@@ -53,7 +53,7 @@ module ActionController #:nodoc:
53 53
   #     end
54 54
   #   end
55 55
   #
56  
-  # The same happens for PUT and DELETE requests.
  56
+  # The same happens for PATCH/PUT and DELETE requests.
57 57
   #
58 58
   # === Nested resources
59 59
   #
@@ -118,6 +118,7 @@ class Responder
118 118
 
119 119
     ACTIONS_FOR_VERBS = {
120 120
       :post => :new,
  121
+      :patch => :edit,
121 122
       :put => :edit
122 123
     }
123 124
 
@@ -133,7 +134,7 @@ def initialize(controller, resources, options={})
133 134
     end
134 135
 
135 136
     delegate :head, :render, :redirect_to,   :to => :controller
136  
-    delegate :get?, :post?, :put?, :delete?, :to => :request
  137
+    delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request
137 138
 
138 139
     # Undefine :to_json and :to_yaml since it's defined on Object
139 140
     undef_method(:to_json) if method_defined?(:to_json)
7  actionpack/lib/action_controller/test_case.rb
@@ -224,7 +224,7 @@ def exists?
224 224
   # == Basic example
225 225
   #
226 226
   # Functional tests are written as follows:
227  
-  # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate
  227
+  # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
228 228
   #    an HTTP request.
229 229
   # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
230 230
   #    the controller's HTTP response, the database contents, etc.
@@ -369,6 +369,11 @@ def post(action, parameters = nil, session = nil, flash = nil)
369 369
         process(action, parameters, session, flash, "POST")
370 370
       end
371 371
 
  372
+      # Executes a request simulating PATCH HTTP method and set/volley the response
  373
+      def patch(action, parameters = nil, session = nil, flash = nil)
  374
+        process(action, parameters, session, flash, "PATCH")
  375
+      end
  376
+
372 377
       # Executes a request simulating PUT HTTP method and set/volley the response
373 378
       def put(action, parameters = nil, session = nil, flash = nil)
374 379
         process(action, parameters, session, flash, "PUT")
6  actionpack/lib/action_dispatch/http/request.rb
@@ -105,6 +105,12 @@ def post?
105 105
       HTTP_METHOD_LOOKUP[request_method] == :post
106 106
     end
107 107
 
  108
+    # Is this a PATCH request?
  109
+    # Equivalent to <tt>request.request_method == :patch</tt>.
  110
+    def patch?
  111
+      HTTP_METHOD_LOOKUP[request_method] == :patch
  112
+    end
  113
+
108 114
     # Is this a PUT request?
109 115
     # Equivalent to <tt>request.request_method == :put</tt>.
110 116
     def put?
15  actionpack/lib/action_dispatch/routing.rb
@@ -182,10 +182,13 @@ module ActionDispatch
182 182
   #
183 183
   # == HTTP Methods
184 184
   #
185  
-  # Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
186  
-  # Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
187  
-  # If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
188  
-  # The default value is <tt>:any</tt> which means that the route will respond to any of the HTTP methods.
  185
+  # Using the <tt>:via</tt> option when specifying a route allows you to
  186
+  # restrict it to a specific HTTP method.  Possible values are <tt>:post</tt>,
  187
+  # <tt>:get</tt>, <tt>:patch</tt>, <tt>:put</tt>, <tt>:delete</tt> and
  188
+  # <tt>:any</tt>.  If your route needs to respond to more than one method you
  189
+  # can use an array, e.g. <tt>[ :get, :post ]</tt>.  The default value is
  190
+  # <tt>:any</tt> which means that the route will respond to any of the HTTP
  191
+  # methods.
189 192
   #
190 193
   # Examples:
191 194
   #
@@ -198,7 +201,7 @@ module ActionDispatch
198 201
   # === HTTP helper methods
199 202
   #
200 203
   # An alternative method of specifying which HTTP method a route should respond to is to use the helper
201  
-  # methods <tt>get</tt>, <tt>post</tt>, <tt>put</tt> and <tt>delete</tt>.
  204
+  # methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>.
202 205
   #
203 206
   # Examples:
204 207
   #
@@ -284,7 +287,7 @@ module Routing
284 287
     autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes'
285 288
 
286 289
     SEPARATORS = %w( / . ? ) #:nodoc:
287  
-    HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] #:nodoc:
  290
+    HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:
288 291
 
289 292
     # A helper module to hold URL related helpers.
290 293
     module Helpers #:nodoc:
35  actionpack/lib/action_dispatch/routing/mapper.rb
@@ -469,7 +469,7 @@ module HttpHelpers
469 469
         #
470 470
         # Example:
471 471
         #
472  
-        # get 'bacon', :to => 'food#bacon'
  472
+        #   get 'bacon', :to => 'food#bacon'
473 473
         def get(*args, &block)
474 474
           map_method(:get, *args, &block)
475 475
         end
@@ -479,27 +479,37 @@ def get(*args, &block)
479 479
         #
480 480
         # Example:
481 481
         #
482  
-        # post 'bacon', :to => 'food#bacon'
  482
+        #   post 'bacon', :to => 'food#bacon'
483 483
         def post(*args, &block)
484 484
           map_method(:post, *args, &block)
485 485
         end
486 486
 
  487
+        # Define a route that only recognizes HTTP PATCH.
  488
+        # For supported arguments, see <tt>Base#match</tt>.
  489
+        #
  490
+        # Example:
  491
+        #
  492
+        #   patch 'bacon', :to => 'food#bacon'
  493
+        def patch(*args, &block)
  494
+          map_method(:patch, *args, &block)
  495
+        end
  496
+
487 497
         # Define a route that only recognizes HTTP PUT.
488 498
         # For supported arguments, see <tt>Base#match</tt>.
489 499
         #
490 500
         # Example:
491 501
         #
492  
-        # put 'bacon', :to => 'food#bacon'
  502
+        #   put 'bacon', :to => 'food#bacon'
493 503
         def put(*args, &block)
494 504
           map_method(:put, *args, &block)
495 505
         end
496 506
 
497  
-        # Define a route that only recognizes HTTP PUT.
  507
+        # Define a route that only recognizes HTTP DELETE.
498 508
         # For supported arguments, see <tt>Base#match</tt>.
499 509
         #
500 510
         # Example:
501 511
         #
502  
-        # delete 'broccoli', :to => 'food#broccoli'
  512
+        #   delete 'broccoli', :to => 'food#broccoli'
503 513
         def delete(*args, &block)
504 514
           map_method(:delete, *args, &block)
505 515
         end
@@ -532,6 +542,7 @@ def map_method(method, *args, &block)
532 542
       #   POST	  /admin/posts
533 543
       #   GET	    /admin/posts/1
534 544
       #   GET	    /admin/posts/1/edit
  545
+      #   PATCH	  /admin/posts/1
535 546
       #   PUT	    /admin/posts/1
536 547
       #   DELETE  /admin/posts/1
537 548
       #
@@ -566,6 +577,7 @@ def map_method(method, *args, &block)
566 577
       #   POST	  /admin/posts
567 578
       #   GET	    /admin/posts/1
568 579
       #   GET	    /admin/posts/1/edit
  580
+      #   PATCH	  /admin/posts/1
569 581
       #   PUT	    /admin/posts/1
570 582
       #   DELETE  /admin/posts/1
571 583
       module Scoping
@@ -661,6 +673,7 @@ def controller(controller, options={})
661 673
         #    new_admin_post GET    /admin/posts/new(.:format)      {:action=>"new", :controller=>"admin/posts"}
662 674
         #   edit_admin_post GET    /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
663 675
         #        admin_post GET    /admin/posts/:id(.:format)      {:action=>"show", :controller=>"admin/posts"}
  676
+        #        admin_post PATCH  /admin/posts/:id(.:format)      {:action=>"update", :controller=>"admin/posts"}
664 677
         #        admin_post PUT    /admin/posts/:id(.:format)      {:action=>"update", :controller=>"admin/posts"}
665 678
         #        admin_post DELETE /admin/posts/:id(.:format)      {:action=>"destroy", :controller=>"admin/posts"}
666 679
         #
@@ -974,7 +987,7 @@ def resources_path_names(options)
974 987
         #
975 988
         #   resource :geocoder
976 989
         #
977  
-        # creates six different routes in your application, all mapping to
  990
+        # creates seven different routes in your application, all mapping to
978 991
         # the GeoCoders controller (note that the controller is named after
979 992
         # the plural):
980 993
         #
@@ -982,6 +995,7 @@ def resources_path_names(options)
982 995
         #   POST    /geocoder
983 996
         #   GET     /geocoder
984 997
         #   GET     /geocoder/edit
  998
+        #   PATCH   /geocoder
985 999
         #   PUT     /geocoder
986 1000
         #   DELETE  /geocoder
987 1001
         #
@@ -1008,6 +1022,7 @@ def resource(*resources, &block)
1008 1022
             member do
1009 1023
               get    :edit if parent_resource.actions.include?(:edit)
1010 1024
               get    :show if parent_resource.actions.include?(:show)
  1025
+              patch  :update if parent_resource.actions.include?(:update)
1011 1026
               put    :update if parent_resource.actions.include?(:update)
1012 1027
               delete :destroy if parent_resource.actions.include?(:destroy)
1013 1028
             end
@@ -1023,13 +1038,15 @@ def resource(*resources, &block)
1023 1038
         #
1024 1039
         #   resources :photos
1025 1040
         #
1026  
-        # creates seven different routes in your application, all mapping to
  1041
+        # creates eight different routes in your application, all mapping to
1027 1042
         # the Photos controller:
1028 1043
         #
  1044
+        #   GET     /photos
1029 1045
         #   GET     /photos/new
1030 1046
         #   POST    /photos
1031 1047
         #   GET     /photos/:id
1032 1048
         #   GET     /photos/:id/edit
  1049
+        #   PATCH   /photos/:id
1033 1050
         #   PUT     /photos/:id
1034 1051
         #   DELETE  /photos/:id
1035 1052
         #
@@ -1041,10 +1058,12 @@ def resource(*resources, &block)
1041 1058
         #
1042 1059
         # This generates the following comments routes:
1043 1060
         #
  1061
+        #   GET     /photos/:id/comments
1044 1062
         #   GET     /photos/:id/comments/new
1045 1063
         #   POST    /photos/:id/comments
1046 1064
         #   GET     /photos/:id/comments/:id
1047 1065
         #   GET     /photos/:id/comments/:id/edit
  1066
+        #   PATCH   /photos/:id/comments/:id
1048 1067
         #   PUT     /photos/:id/comments/:id
1049 1068
         #   DELETE  /photos/:id/comments/:id
1050 1069
         #
@@ -1102,6 +1121,7 @@ def resource(*resources, &block)
1102 1121
         #     new_post_comment GET    /sekret/posts/:post_id/comments/new(.:format)
1103 1122
         #     edit_comment     GET    /sekret/comments/:id/edit(.:format)
1104 1123
         #     comment          GET    /sekret/comments/:id(.:format)
  1124
+        #     comment          PATCH  /sekret/comments/:id(.:format)
1105 1125
         #     comment          PUT    /sekret/comments/:id(.:format)
1106 1126
         #     comment          DELETE /sekret/comments/:id(.:format)
1107 1127
         #
@@ -1134,6 +1154,7 @@ def resources(*resources, &block)
1134 1154
             member do
1135 1155
               get    :edit if parent_resource.actions.include?(:edit)
1136 1156
               get    :show if parent_resource.actions.include?(:show)
  1157
+              patch  :update if parent_resource.actions.include?(:update)
1137 1158
               put    :update if parent_resource.actions.include?(:update)
1138 1159
               delete :destroy if parent_resource.actions.include?(:destroy)
1139 1160
             end
26  actionpack/lib/action_dispatch/testing/integration.rb
@@ -27,8 +27,8 @@ module RequestHelpers
27 27
       # object's <tt>@response</tt> instance variable will point to the same
28 28
       # response object.
29 29
       #
30  
-      # You can also perform POST, PUT, DELETE, and HEAD requests with +#post+,
31  
-      # +#put+, +#delete+, and +#head+.
  30
+      # You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
  31
+      # +#post+, +#patch+, +#put+, +#delete+, and +#head+.
32 32
       def get(path, parameters = nil, headers = nil)
33 33
         process :get, path, parameters, headers
34 34
       end
@@ -39,6 +39,12 @@ def post(path, parameters = nil, headers = nil)
39 39
         process :post, path, parameters, headers
40 40
       end
41 41
 
  42
+      # Performs a PATCH request with the given parameters. See +#get+ for more
  43
+      # details.
  44
+      def patch(path, parameters = nil, headers = nil)
  45
+        process :patch, path, parameters, headers
  46
+      end
  47
+
42 48
       # Performs a PUT request with the given parameters. See +#get+ for more
43 49
       # details.
44 50
       def put(path, parameters = nil, headers = nil)
@@ -60,10 +66,10 @@ def head(path, parameters = nil, headers = nil)
60 66
       # Performs an XMLHttpRequest request with the given parameters, mirroring
61 67
       # a request from the Prototype library.
62 68
       #
63  
-      # The request_method is +:get+, +:post+, +:put+, +:delete+ or +:head+; the
64  
-      # parameters are +nil+, a hash, or a url-encoded or multipart string;
65  
-      # the headers are a hash.  Keys are automatically upcased and prefixed
66  
-      # with 'HTTP_' if not already.
  69
+      # The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
  70
+      # +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
  71
+      # string; the headers are a hash.  Keys are automatically upcased and
  72
+      # prefixed with 'HTTP_' if not already.
67 73
       def xml_http_request(request_method, path, parameters = nil, headers = nil)
68 74
         headers ||= {}
69 75
         headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
@@ -103,6 +109,12 @@ def post_via_redirect(path, parameters = nil, headers = nil)
103 109
         request_via_redirect(:post, path, parameters, headers)
104 110
       end
105 111
 
  112
+      # Performs a PATCH request, following any subsequent redirect.
  113
+      # See +request_via_redirect+ for more information.
  114
+      def patch_via_redirect(path, parameters = nil, headers = nil)
  115
+        request_via_redirect(:patch, path, parameters, headers)
  116
+      end
  117
+
106 118
       # Performs a PUT request, following any subsequent redirect.
107 119
       # See +request_via_redirect+ for more information.
108 120
       def put_via_redirect(path, parameters = nil, headers = nil)
@@ -316,7 +328,7 @@ def reset!
316 328
         @integration_session = Integration::Session.new(app)
317 329
       end
318 330
 
319  
-      %w(get post put head delete cookies assigns
  331
+      %w(get post patch put head delete cookies assigns
320 332
          xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
321 333
         define_method(method) do |*args|
322 334
           reset! unless integration_session
6  actionpack/lib/action_view/helpers/form_helper.rb
@@ -245,7 +245,7 @@ def convert_to_model(object)
245 245
       #
246 246
       # You can force the form to use the full array of HTTP verbs by setting 
247 247
       #
248  
-      #    :method => (:get|:post|:put|:delete)
  248
+      #    :method => (:get|:post|:patch|:put|:delete)
249 249
       #
250 250
       # in the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the
251 251
       # form will be set to POST and a hidden input called _method will carry the intended verb for the server
@@ -381,7 +381,7 @@ def apply_form_for_options!(object_or_array, options) #:nodoc:
381 381
         object = convert_to_model(object)
382 382
 
383 383
         as = options[:as]
384  
-        action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post]
  384
+        action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, ActionView::Base.default_method_for_update] : [:new, :post]
385 385
         options[:html].reverse_merge!(
386 386
           :class  => as ? "#{as}_#{action}" : dom_class(object, action),
387 387
           :id     => as ? "#{as}_#{action}" : dom_id(object, action),
@@ -1383,7 +1383,9 @@ def convert_to_model(object)
1383 1383
   ActiveSupport.on_load(:action_view) do
1384 1384
     class ActionView::Base
1385 1385
       cattr_accessor :default_form_builder
  1386
+      cattr_accessor :default_method_for_update
1386 1387
       @@default_form_builder = ::ActionView::Helpers::FormBuilder
  1388
+      @@default_method_for_update = :put
1387 1389
     end
1388 1390
   end
1389 1391
 end
6  actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -23,7 +23,7 @@ module FormTagHelper
23 23
       # ==== Options
24 24
       # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
25 25
       # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
26  
-      #   If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
  26
+      #   If "patch", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
27 27
       #   is added to simulate the verb over post.
28 28
       # * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
29 29
       #   pass custom authenticity token string, or to not add authenticity_token field at all
@@ -36,8 +36,8 @@ module FormTagHelper
36 36
       #   form_tag('/posts')
37 37
       #   # => <form action="/posts" method="post">
38 38
       #
39  
-      #   form_tag('/posts/1', :method => :put)
40  
-      #   # => <form action="/posts/1" method="put">
  39
+      #   form_tag('/posts/1', :method => :patch)
  40
+      #   # => <form action="/posts/1" method="patch">
41 41
       #
42 42
       #   form_tag('/upload', :multipart => true)
43 43
       #   # => <form action="/upload" method="post" enctype="multipart/form-data">
8  actionpack/lib/action_view/helpers/url_helper.rb
@@ -146,12 +146,12 @@ def url_for(options = {})
146 146
       #   create an HTML form and immediately submit the form for processing using
147 147
       #   the HTTP verb specified. Useful for having links perform a POST operation
148 148
       #   in dangerous actions like deleting a record (which search bots can follow
149  
-      #   while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt> and <tt>:put</tt>.
  149
+      #   while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
150 150
       #   Note that if the user has JavaScript disabled, the request will fall back
151 151
       #   to using GET. If <tt>:href => '#'</tt> is used and the user has JavaScript
152 152
       #   disabled clicking the link will have no effect. If you are relying on the
153 153
       #   POST behavior, you should check for it in your controller's action by using
154  
-      #   the request object's methods for <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
  154
+      #   the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>:patch</tt>, or <tt>put?</tt>.
155 155
       # * <tt>:remote => true</tt> - This will allow the unobtrusive JavaScript
156 156
       #   driver to make an Ajax request to the URL in question instead of following
157 157
       #   the link. The drivers each provide mechanisms for listening for the
@@ -272,7 +272,7 @@ def link_to(*args, &block)
272 272
       #
273 273
       # There are a few special +html_options+:
274 274
       # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
275  
-      #   <tt>:delete</tt> and <tt>:put</tt>. By default it will be <tt>:post</tt>.
  275
+      #   <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
276 276
       # * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
277 277
       # * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
278 278
       #   prompt with the question specified. If the user accepts, the link is
@@ -319,7 +319,7 @@ def button_to(name, options = {}, html_options = {})
319 319
         convert_boolean_attributes!(html_options, %w( disabled ))
320 320
 
321 321
         method_tag = ''
322  
-        if (method = html_options.delete('method')) && %w{put delete}.include?(method.to_s)
  322
+        if (method = html_options.delete('method')) && %w{patch put delete}.include?(method.to_s)
323 323
           method_tag = tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
324 324
         end
325 325
 
2  actionpack/test/controller/caching_test.rb
@@ -140,7 +140,7 @@ def test_should_cache_ok_at_custom_path
140 140
   end
141 141
 
142 142
   [:ok, :no_content, :found, :not_found].each do |status|
143  
-    [:get, :post, :put, :delete].each do |method|
  143
+    [:get, :post, :patch, :put, :delete].each do |method|
144 144
       unless method == :get and status == :ok
145 145
         define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
146 146
           send(method, status)
24  actionpack/test/controller/integration_test.rb
@@ -63,6 +63,12 @@ def test_post_via_redirect
63 63
     @session.post_via_redirect(path, args, headers)
64 64
   end
65 65
 
  66
+  def test_patch_via_redirect
  67
+    path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
  68
+    @session.expects(:request_via_redirect).with(:patch, path, args, headers)
  69
+    @session.patch_via_redirect(path, args, headers)
  70
+  end
  71
+
66 72
   def test_put_via_redirect
67 73
     path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
68 74
     @session.expects(:request_via_redirect).with(:put, path, args, headers)
@@ -87,6 +93,12 @@ def test_post
87 93
     @session.post(path,params,headers)
88 94
   end
89 95
 
  96
+  def test_patch
  97
+    path = "/index"; params = "blah"; headers = {:location => 'blah'}
  98
+    @session.expects(:process).with(:patch,path,params,headers)
  99
+    @session.patch(path,params,headers)
  100
+  end
  101
+
90 102
   def test_put
91 103
     path = "/index"; params = "blah"; headers = {:location => 'blah'}
92 104
     @session.expects(:process).with(:put,path,params,headers)
@@ -125,6 +137,16 @@ def test_xml_http_request_post
125 137
     @session.xml_http_request(:post,path,params,headers)
126 138
   end
127 139
 
  140
+  def test_xml_http_request_patch
  141
+    path = "/index"; params = "blah"; headers = {:location => 'blah'}
  142
+    headers_after_xhr = headers.merge(
  143
+      "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
  144
+      "HTTP_ACCEPT"           => "text/javascript, text/html, application/xml, text/xml, */*"
  145
+    )
  146
+    @session.expects(:process).with(:patch,path,params,headers_after_xhr)
  147
+    @session.xml_http_request(:patch,path,params,headers)
  148
+  end
  149
+
128 150
   def test_xml_http_request_put
129 151
     path = "/index"; params = "blah"; headers = {:location => 'blah'}
130 152
     headers_after_xhr = headers.merge(
@@ -212,7 +234,7 @@ def test_integration_methods_called
212 234
     @integration_session.stubs(:generic_url_rewriter)
213 235
     @integration_session.stubs(:process)
214 236
 
215  
-    %w( get post head put delete ).each do |verb|
  237
+    %w( get post head patch put delete ).each do |verb|
216 238
       assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') }
217 239
     end
218 240
   end
64  actionpack/test/controller/mime_responds_test.rb
@@ -723,6 +723,69 @@ def test_using_resource_for_post_with_xml_yields_unprocessable_entity_on_failure
723 723
     end
724 724
   end
725 725
 
  726
+  def test_using_resource_for_patch_with_html_redirects_on_success
  727
+    with_test_route_set do
  728
+      patch :using_resource
  729
+      assert_equal "text/html", @response.content_type
  730
+      assert_equal 302, @response.status
  731
+      assert_equal "http://www.example.com/customers/13", @response.location
  732
+      assert @response.redirect?
  733
+    end
  734
+  end
  735
+
  736
+  def test_using_resource_for_patch_with_html_rerender_on_failure
  737
+    with_test_route_set do
  738
+      errors = { :name => :invalid }
  739
+      Customer.any_instance.stubs(:errors).returns(errors)
  740
+      patch :using_resource
  741
+      assert_equal "text/html", @response.content_type
  742
+      assert_equal 200, @response.status
  743
+      assert_equal "Edit world!\n", @response.body
  744
+      assert_nil @response.location
  745
+    end
  746
+  end
  747
+
  748
+  def test_using_resource_for_patch_with_html_rerender_on_failure_even_on_method_override
  749
+    with_test_route_set do
  750
+      errors = { :name => :invalid }
  751
+      Customer.any_instance.stubs(:errors).returns(errors)
  752
+      @request.env["rack.methodoverride.original_method"] = "POST"
  753
+      patch :using_resource
  754
+      assert_equal "text/html", @response.content_type
  755
+      assert_equal 200, @response.status
  756
+      assert_equal "Edit world!\n", @response.body
  757
+      assert_nil @response.location
  758
+    end
  759
+  end
  760
+
  761
+  def test_using_resource_for_patch_with_xml_yields_ok_on_success
  762
+    @request.accept = "application/xml"
  763
+    patch :using_resource
  764
+    assert_equal "application/xml", @response.content_type
  765
+    assert_equal 200, @response.status
  766
+    assert_equal " ", @response.body
  767
+  end
  768
+
  769
+  def test_using_resource_for_patch_with_json_yields_ok_on_success
  770
+    Customer.any_instance.stubs(:to_json).returns('{"name": "David"}')
  771
+    @request.accept = "application/json"
  772
+    patch :using_resource
  773
+    assert_equal "application/json", @response.content_type
  774
+    assert_equal 200, @response.status
  775
+    assert_equal "{}", @response.body
  776
+  end
  777
+
  778
+  def test_using_resource_for_patch_with_xml_yields_unprocessable_entity_on_failure
  779
+    @request.accept = "application/xml"
  780
+    errors = { :name => :invalid }
  781
+    Customer.any_instance.stubs(:errors).returns(errors)
  782
+    patch :using_resource
  783
+    assert_equal "application/xml", @response.content_type
  784
+    assert_equal 422, @response.status
  785
+    assert_equal errors.to_xml, @response.body
  786
+    assert_nil @response.location
  787
+  end
  788
+
726 789
   def test_using_resource_for_put_with_html_redirects_on_success
727 790
     with_test_route_set do
728 791
       put :using_resource
@@ -786,6 +849,7 @@ def test_using_resource_for_put_with_xml_yields_unprocessable_entity_on_failure
786 849
     assert_nil @response.location
787 850
   end
788 851
 
  852
+
789 853
   def test_using_resource_for_delete_with_html_redirects_on_success
790 854
     with_test_route_set do
791 855
       Customer.any_instance.stubs(:destroyed?).returns(true)
15  actionpack/test/controller/request_forgery_protection_test.rb
@@ -115,6 +115,10 @@ def test_should_not_allow_post_without_token_irrespective_of_format
115 115
     assert_blocked { post :index, :format=>'xml' }
116 116
   end
117 117
 
  118
+  def test_should_not_allow_patch_without_token
  119
+    assert_blocked { patch :index }
  120
+  end
  121
+
118 122
   def test_should_not_allow_put_without_token
119 123
     assert_blocked { put :index }
120 124
   end
@@ -131,6 +135,10 @@ def test_should_allow_post_with_token
131 135
     assert_not_blocked { post :index, :authenticity_token => @token }
132 136
   end
133 137
 
  138
+  def test_should_allow_patch_with_token
  139
+    assert_not_blocked { patch :index, :authenticity_token => @token }
  140
+  end
  141
+
134 142
   def test_should_allow_put_with_token
135 143
     assert_not_blocked { put :index, :authenticity_token => @token }
136 144
   end
@@ -149,6 +157,11 @@ def test_should_allow_delete_with_token_in_header
149 157
     assert_not_blocked { delete :index }
150 158
   end
151 159
 
  160
+  def test_should_allow_patch_with_token_in_header
  161
+    @request.env['HTTP_X_CSRF_TOKEN'] = @token
  162
+    assert_not_blocked { patch :index }
  163
+  end
  164
+
152 165
   def test_should_allow_put_with_token_in_header
153 166
     @request.env['HTTP_X_CSRF_TOKEN'] = @token
154 167
     assert_not_blocked { put :index }
@@ -210,7 +223,7 @@ def test_should_not_render_button_to_with_token_tag
210 223
   end
211 224
 
212 225
   def test_should_allow_all_methods_without_token
213  
-    [:post, :put, :delete].each do |method|
  226
+    [:post, :patch, :put, :delete].each do |method|
214 227
       assert_nothing_raised { send(method, :index)}
215 228
     end
216 229
   end
31  actionpack/test/controller/resources_test.rb
@@ -150,7 +150,7 @@ def test_with_name_prefix
150 150
   end
151 151
 
152 152
   def test_with_collection_actions
153  
-    actions = { 'a' => :get, 'b' => :put, 'c' => :post, 'd' => :delete }
  153
+    actions = { 'a' => :get, 'b' => :put, 'c' => :post, 'd' => :delete, 'e' => :patch }
154 154
 
155 155
     with_routing do |set|
156 156