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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions actionpack/lib/action_controller/metal/http_authentication.rb
Expand Up @@ -276,7 +276,7 @@ def secret_token(request)
#
# An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
# protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
# POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
# POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
# of this document.
#
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
Expand All @@ -290,7 +290,7 @@ def nonce(secret_key, time = Time.now)
end

# Might want a shorter timeout depending on whether the request
# is a PUT or POST, and if client is browser or web service.
# is a PATCH, PUT, or POST, and if client is browser or web service.
# Can be much shorter if the Stale directive is implemented. This would
# allow a user to use new nonce without prompting user again for their
# username and password.
Expand Down
5 changes: 3 additions & 2 deletions actionpack/lib/action_controller/metal/responder.rb
Expand Up @@ -53,7 +53,7 @@ module ActionController #:nodoc:
# end
# end
#
# The same happens for PUT and DELETE requests.
# The same happens for PATCH/PUT and DELETE requests.
#
# === Nested resources
#
Expand Down Expand Up @@ -118,6 +118,7 @@ class Responder

ACTIONS_FOR_VERBS = {
:post => :new,
:patch => :edit,
:put => :edit
}

Expand All @@ -133,7 +134,7 @@ def initialize(controller, resources, options={})
end

delegate :head, :render, :redirect_to, :to => :controller
delegate :get?, :post?, :put?, :delete?, :to => :request
delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request

# Undefine :to_json and :to_yaml since it's defined on Object
undef_method(:to_json) if method_defined?(:to_json)
Expand Down
7 changes: 6 additions & 1 deletion actionpack/lib/action_controller/test_case.rb
Expand Up @@ -224,7 +224,7 @@ def exists?
# == Basic example
#
# Functional tests are written as follows:
# 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate
# 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
# an HTTP request.
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
# the controller's HTTP response, the database contents, etc.
Expand Down Expand Up @@ -369,6 +369,11 @@ def post(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "POST")
end

# Executes a request simulating PATCH HTTP method and set/volley the response
def patch(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "PATCH")
end

# Executes a request simulating PUT HTTP method and set/volley the response
def put(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "PUT")
Expand Down
6 changes: 6 additions & 0 deletions actionpack/lib/action_dispatch/http/request.rb
Expand Up @@ -105,6 +105,12 @@ def post?
HTTP_METHOD_LOOKUP[request_method] == :post
end

# Is this a PATCH request?
# Equivalent to <tt>request.request_method == :patch</tt>.
def patch?
HTTP_METHOD_LOOKUP[request_method] == :patch
end

# Is this a PUT request?
# Equivalent to <tt>request.request_method == :put</tt>.
def put?
Expand Down
15 changes: 9 additions & 6 deletions actionpack/lib/action_dispatch/routing.rb
Expand Up @@ -182,10 +182,13 @@ module ActionDispatch
#
# == HTTP Methods
#
# Using the <tt>:via</tt> option when specifying a route allows you to restrict it to a specific HTTP method.
# Possible values are <tt>:post</tt>, <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>.
# If your route needs to respond to more than one method you can use an array, e.g. <tt>[ :get, :post ]</tt>.
# The default value is <tt>:any</tt> which means that the route will respond to any of the HTTP methods.
# Using the <tt>:via</tt> option when specifying a route allows you to
# restrict it to a specific HTTP method. Possible values are <tt>:post</tt>,
# <tt>:get</tt>, <tt>:patch</tt>, <tt>:put</tt>, <tt>:delete</tt> and
# <tt>:any</tt>. If your route needs to respond to more than one method you
# can use an array, e.g. <tt>[ :get, :post ]</tt>. The default value is
# <tt>:any</tt> which means that the route will respond to any of the HTTP
# methods.
#
# Examples:
#
Expand All @@ -198,7 +201,7 @@ module ActionDispatch
# === HTTP helper methods
#
# An alternative method of specifying which HTTP method a route should respond to is to use the helper
# methods <tt>get</tt>, <tt>post</tt>, <tt>put</tt> and <tt>delete</tt>.
# methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>.
#
# Examples:
#
Expand Down Expand Up @@ -284,7 +287,7 @@ module Routing
autoload :PolymorphicRoutes, 'action_dispatch/routing/polymorphic_routes'

SEPARATORS = %w( / . ? ) #:nodoc:
HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] #:nodoc:
HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:

# A helper module to hold URL related helpers.
module Helpers #:nodoc:
Expand Down
35 changes: 28 additions & 7 deletions actionpack/lib/action_dispatch/routing/mapper.rb
Expand Up @@ -469,7 +469,7 @@ module HttpHelpers
#
# Example:
#
# get 'bacon', :to => 'food#bacon'
# get 'bacon', :to => 'food#bacon'
def get(*args, &block)
map_method(:get, *args, &block)
end
Expand All @@ -479,27 +479,37 @@ def get(*args, &block)
#
# Example:
#
# post 'bacon', :to => 'food#bacon'
# post 'bacon', :to => 'food#bacon'
def post(*args, &block)
map_method(:post, *args, &block)
end

# Define a route that only recognizes HTTP PATCH.
# For supported arguments, see <tt>Base#match</tt>.
#
# Example:
#
# patch 'bacon', :to => 'food#bacon'
def patch(*args, &block)
map_method(:patch, *args, &block)
end

# Define a route that only recognizes HTTP PUT.
# For supported arguments, see <tt>Base#match</tt>.
#
# Example:
#
# put 'bacon', :to => 'food#bacon'
# put 'bacon', :to => 'food#bacon'
def put(*args, &block)
map_method(:put, *args, &block)
end

# Define a route that only recognizes HTTP PUT.
# Define a route that only recognizes HTTP DELETE.
# For supported arguments, see <tt>Base#match</tt>.
#
# Example:
#
# delete 'broccoli', :to => 'food#broccoli'
# delete 'broccoli', :to => 'food#broccoli'
def delete(*args, &block)
map_method(:delete, *args, &block)
end
Expand Down Expand Up @@ -532,6 +542,7 @@ def map_method(method, *args, &block)
# POST /admin/posts
# GET /admin/posts/1
# GET /admin/posts/1/edit
# PATCH /admin/posts/1
# PUT /admin/posts/1
# DELETE /admin/posts/1
#
Expand Down Expand Up @@ -566,6 +577,7 @@ def map_method(method, *args, &block)
# POST /admin/posts
# GET /admin/posts/1
# GET /admin/posts/1/edit
# PATCH /admin/posts/1
# PUT /admin/posts/1
# DELETE /admin/posts/1
module Scoping
Expand Down Expand Up @@ -661,6 +673,7 @@ def controller(controller, options={})
# new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
# edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
# admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
# admin_post PATCH /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
# admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
# admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
#
Expand Down Expand Up @@ -974,14 +987,15 @@ def resources_path_names(options)
#
# resource :geocoder
#
# creates six different routes in your application, all mapping to
# creates seven different routes in your application, all mapping to
# the GeoCoders controller (note that the controller is named after
# the plural):
#
# GET /geocoder/new
# POST /geocoder
# GET /geocoder
# GET /geocoder/edit
# PATCH /geocoder
# PUT /geocoder
# DELETE /geocoder
#
Expand All @@ -1008,6 +1022,7 @@ def resource(*resources, &block)
member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
patch :update if parent_resource.actions.include?(:update)
put :update if parent_resource.actions.include?(:update)
delete :destroy if parent_resource.actions.include?(:destroy)
end
Expand All @@ -1023,13 +1038,15 @@ def resource(*resources, &block)
#
# resources :photos
#
# creates seven different routes in your application, all mapping to
# creates eight different routes in your application, all mapping to
# the Photos controller:
#
# GET /photos
# GET /photos/new
# POST /photos
# GET /photos/:id
# GET /photos/:id/edit
# PATCH /photos/:id
# PUT /photos/:id
# DELETE /photos/:id
#
Expand All @@ -1041,10 +1058,12 @@ def resource(*resources, &block)
#
# This generates the following comments routes:
#
# GET /photos/:id/comments
# GET /photos/:id/comments/new
# POST /photos/:id/comments
# GET /photos/:id/comments/:id
# GET /photos/:id/comments/:id/edit
# PATCH /photos/:id/comments/:id
# PUT /photos/:id/comments/:id
# DELETE /photos/:id/comments/:id
#
Expand Down Expand Up @@ -1102,6 +1121,7 @@ def resource(*resources, &block)
# new_post_comment GET /sekret/posts/:post_id/comments/new(.:format)
# edit_comment GET /sekret/comments/:id/edit(.:format)
# comment GET /sekret/comments/:id(.:format)
# comment PATCH /sekret/comments/:id(.:format)
# comment PUT /sekret/comments/:id(.:format)
# comment DELETE /sekret/comments/:id(.:format)
#
Expand Down Expand Up @@ -1134,6 +1154,7 @@ def resources(*resources, &block)
member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
patch :update if parent_resource.actions.include?(:update)
put :update if parent_resource.actions.include?(:update)
delete :destroy if parent_resource.actions.include?(:destroy)
end
Expand Down
26 changes: 19 additions & 7 deletions actionpack/lib/action_dispatch/testing/integration.rb
Expand Up @@ -27,8 +27,8 @@ module RequestHelpers
# object's <tt>@response</tt> instance variable will point to the same
# response object.
#
# You can also perform POST, PUT, DELETE, and HEAD requests with +#post+,
# +#put+, +#delete+, and +#head+.
# You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
# +#post+, +#patch+, +#put+, +#delete+, and +#head+.
def get(path, parameters = nil, headers = nil)
process :get, path, parameters, headers
end
Expand All @@ -39,6 +39,12 @@ def post(path, parameters = nil, headers = nil)
process :post, path, parameters, headers
end

# Performs a PATCH request with the given parameters. See +#get+ for more
# details.
def patch(path, parameters = nil, headers = nil)
process :patch, path, parameters, headers
end

# Performs a PUT request with the given parameters. See +#get+ for more
# details.
def put(path, parameters = nil, headers = nil)
Expand All @@ -60,10 +66,10 @@ def head(path, parameters = nil, headers = nil)
# Performs an XMLHttpRequest request with the given parameters, mirroring
# a request from the Prototype library.
#
# The request_method is +:get+, +:post+, +:put+, +:delete+ or +:head+; the
# parameters are +nil+, a hash, or a url-encoded or multipart string;
# the headers are a hash. Keys are automatically upcased and prefixed
# with 'HTTP_' if not already.
# The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
# +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
# string; the headers are a hash. Keys are automatically upcased and
# prefixed with 'HTTP_' if not already.
def xml_http_request(request_method, path, parameters = nil, headers = nil)
headers ||= {}
headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
Expand Down Expand Up @@ -103,6 +109,12 @@ def post_via_redirect(path, parameters = nil, headers = nil)
request_via_redirect(:post, path, parameters, headers)
end

# Performs a PATCH request, following any subsequent redirect.
# See +request_via_redirect+ for more information.
def patch_via_redirect(path, parameters = nil, headers = nil)
request_via_redirect(:patch, path, parameters, headers)
end

# Performs a PUT request, following any subsequent redirect.
# See +request_via_redirect+ for more information.
def put_via_redirect(path, parameters = nil, headers = nil)
Expand Down Expand Up @@ -316,7 +328,7 @@ def reset!
@integration_session = Integration::Session.new(app)
end

%w(get post put head delete cookies assigns
%w(get post patch put head delete cookies assigns
xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
define_method(method) do |*args|
reset! unless integration_session
Expand Down
6 changes: 4 additions & 2 deletions actionpack/lib/action_view/helpers/form_helper.rb
Expand Up @@ -245,7 +245,7 @@ def convert_to_model(object)
#
# You can force the form to use the full array of HTTP verbs by setting
#
# :method => (:get|:post|:put|:delete)
# :method => (:get|:post|:patch|:put|:delete)
#
# in the options hash. If the verb is not GET or POST, which are natively supported by HTML forms, the
# form will be set to POST and a hidden input called _method will carry the intended verb for the server
Expand Down Expand Up @@ -381,7 +381,7 @@ def apply_form_for_options!(object_or_array, options) #:nodoc:
object = convert_to_model(object)

as = options[:as]
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :put] : [:new, :post]
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, ActionView::Base.default_method_for_update] : [:new, :post]
options[:html].reverse_merge!(
:class => as ? "#{as}_#{action}" : dom_class(object, action),
:id => as ? "#{as}_#{action}" : dom_id(object, action),
Expand Down Expand Up @@ -1383,7 +1383,9 @@ def convert_to_model(object)
ActiveSupport.on_load(:action_view) do
class ActionView::Base
cattr_accessor :default_form_builder
cattr_accessor :default_method_for_update
@@default_form_builder = ::ActionView::Helpers::FormBuilder
@@default_method_for_update = :put
end
end
end
6 changes: 3 additions & 3 deletions actionpack/lib/action_view/helpers/form_tag_helper.rb
Expand Up @@ -23,7 +23,7 @@ module FormTagHelper
# ==== Options
# * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
# * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
# If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
# If "patch", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
# is added to simulate the verb over post.
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
# pass custom authenticity token string, or to not add authenticity_token field at all
Expand All @@ -36,8 +36,8 @@ module FormTagHelper
# form_tag('/posts')
# # => <form action="/posts" method="post">
#
# form_tag('/posts/1', :method => :put)
# # => <form action="/posts/1" method="put">
# form_tag('/posts/1', :method => :patch)
# # => <form action="/posts/1" method="patch">
#
# form_tag('/upload', :multipart => true)
# # => <form action="/upload" method="post" enctype="multipart/form-data">
Expand Down