Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patch verb #505

Closed
wants to merge 3 commits into from
Closed

Patch verb #505

wants to merge 3 commits into from

Conversation

dlee
Copy link
Contributor

@dlee dlee commented May 11, 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.

dlee added 2 commits May 10, 2011 15:53
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.
Make :put the default method for backwards compatibility. The generator for new
Rails projects configures :patch a the default method in config/application.rb.
@dasch
Copy link
Contributor

dasch commented May 13, 2011

GitHub needs a +1 button.

@benatkin
Copy link

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
Copy link
Contributor Author

dlee commented May 14, 2011

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

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

#
# Example:
#
# patch 'bacon', :to => 'food#bacon'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@dlee
Copy link
Contributor Author

dlee commented Jun 25, 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?

@josevalim
Copy link
Contributor

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

@fxn
Copy link
Member

fxn commented Jun 25, 2011

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

@fxn
Copy link
Member

fxn commented Jul 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
Copy link
Contributor Author

dlee commented Aug 5, 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
Copy link
Member

jeremy commented Oct 9, 2011

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.

@steveklabnik
Copy link
Member

+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...

@bcardarella
Copy link
Contributor

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

@guilleiguaran
Copy link
Member

@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
Copy link
Member

jeremy commented Nov 13, 2011

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?

@stevegraham
Copy link
Contributor

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
Copy link
Contributor Author

dlee commented Nov 13, 2011

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.

@fxn
Copy link
Member

fxn commented Nov 13, 2011

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.

@josevalim
Copy link
Contributor

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

@@ -312,6 +312,11 @@ Rails will also automatically set the +class+ and +id+ of the form appropriately

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.

NOTE: Rails can use the +PATCH+ method instead of +PUT+ for update forms if you set the following option in your +config/application.rb+:
<ruby>
config.action_view.default_method_for_update = :update
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: should be = :patch.

@fxn
Copy link
Member

fxn commented Nov 16, 2011

@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."

@NZKoz
Copy link
Member

NZKoz commented Nov 16, 2011

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.

@steveklabnik
Copy link
Member

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
Copy link
Member

jeremy commented Nov 16, 2011

@dlee see my previous comment for a concrete example

@dlee
Copy link
Contributor Author

dlee commented Nov 16, 2011

@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?

@dlee
Copy link
Contributor Author

dlee commented Nov 16, 2011

@jeremy not @jeremey

@jeremy
Copy link
Member

jeremy commented Nov 16, 2011

@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
Copy link
Contributor Author

dlee commented Nov 16, 2011

@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?

@fxn
Copy link
Member

fxn commented Dec 19, 2011

@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.

@mikekelly
Copy link

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)

@fxn
Copy link
Member

fxn commented Dec 19, 2011

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.

@gducharme
Copy link

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
Copy link
Contributor Author

dlee commented Dec 20, 2011

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

@steveklabnik
Copy link
Member

@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.

@josevalim
Copy link
Contributor

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.

@josevalim
Copy link
Contributor

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.

@steveklabnik
Copy link
Member

@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
Copy link
Contributor Author

dlee commented Dec 20, 2011

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

@fxn
Copy link
Member

fxn commented Dec 20, 2011

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.

@josevalim
Copy link
Contributor

@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.

@fxn
Copy link
Member

fxn commented Dec 20, 2011

@josevalim 👍

@josevalim
Copy link
Contributor

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.

@myronmarston
Copy link
Contributor

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
Copy link
Contributor Author

dlee commented Dec 22, 2011

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.

@josevalim
Copy link
Contributor

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.

@myronmarston
Copy link
Contributor

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).

@josevalim
Copy link
Contributor

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.

@fxn
Copy link
Member

fxn commented Dec 22, 2011

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.

@myronmarston
Copy link
Contributor

@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.

@arunagw
Copy link
Member

arunagw commented Feb 22, 2012

#5130 merged.

@miguelsan
Copy link

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!

@miguelsan
Copy link

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

@bf4
Copy link
Contributor

bf4 commented Feb 11, 2014

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

@nextofsearch
Copy link

Hi @bf4,

Your patch doesn't work with my app. Rails 3.2.22.5

It crashes when running the web server with the following error:

.rvm/gems/ruby-1.9.3-p551@rails3.2/gems/actionpack-3.2.22.5/lib/action_dispatch/routing/mapper.rb:178:in `default_controller_and_action': missing :action (ArgumentError)

When running 'rake routes'

rake aborted!
ArgumentError: missing :action
/Users/danolee/.rvm/gems/ruby-1.9.3-p551@rails3.2/gems/actionpack-3.2.22.5/lib/action_dispatch/routing/mapper.rb:178:in `default_controller_and_action'

Any help will be greatly appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet