Skip to content

Instantly share code, notes, and snippets.

@bf4
Last active June 4, 2018 09:04
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bf4/8940203 to your computer and use it in GitHub Desktop.
Save bf4/8940203 to your computer and use it in GitHub Desktop.
HTTP PATCH support in Rails 3.2
# Rails 3.2 support for HTTP PATCH.
fail "Remove this patch" if Rails::VERSION::MAJOR > 3
# see http://weblog.rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates/
# https://github.com/rails/rails/pull/505
# Be very conservative not to monkey-patch any methods until
# the relevant files are loaded.
ActiveSupport.on_load(:action_controller) do
ActionDispatch::Request.instance_eval do
# Is this a PATCH request?
# Equivalent to <tt>request.request_method == :patch</tt>.
def patch?
HTTP_METHOD_LOOKUP[request_method] == :patch
end
end
module ActionDispatch::Routing
HTTP_METHODS << :patch unless HTTP_METHODS.include?(:patch)
end
ActionDispatch::Routing::Mapper::HttpHelpers.instance_eval do
# 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
end
ActionDispatch::Integration::RequestHelpers.instance_eval do
# 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 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
end
ActionDispatch::Integration::Runner.class_eval do
%w(patch).each do |method|
define_method(method) do |*args|
reset! unless integration_session
# reset the html_document variable, but only for new get/post calls
@html_document = nil unless method.in?(["cookies", "assigns"])
integration_session.__send__(method, *args).tap do
copy_session_variables!
end
end
end
end
module ActionController::TestCase::Behavior
def patch(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "PATCH")
end
end
class ActionController::Responder
ACTIONS_FOR_VERBS.update(:patch => :edit)
delegate :patch?, :to => :request
end
ActionView::Helpers::FormHelper.instance_eval do
# = Action View Form Helpers
def apply_form_for_options!(record, object, options) #:nodoc:
object = convert_to_model(object)
as = options[:as]
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
options[:html].reverse_merge!(
:class => as ? "#{action}_#{as}" : dom_class(object, action),
:id => as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
:method => method
)
options[:url] ||= polymorphic_path(record, :format => options.delete(:format))
end
private :apply_form_for_options!
end
module ActionDispatch::Routing::Mapper::Resources
class SingletonResource
def resource(*resources, &block)
options = resources.extract_options!.dup
if apply_common_behavior_for(:resource, resources, options, &block)
return self
end
resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
yield if block_given?
collection do
post :create
end if parent_resource.actions.include?(:create)
new do
get :new
end if parent_resource.actions.include?(:new)
member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
if parent_resource.actions.include?(:update)
# all that for this PATCH
patch :update
put :update
end
delete :destroy if parent_resource.actions.include?(:destroy)
end
end
self
end
end
end
end
require_dependency Rails.root.join("lib/patches/add_patch_method").to_s
@joshco
Copy link

joshco commented Apr 12, 2014

help. glad to have found this but doesnt work as expected. I was hoping using this would make my existing "resources" routes work with patch, but I still get the error.
Do I need to change my routes?

 namespace :api, defaults: { format: 'json'} do
    namespace :v1 do
      resources :people do
        resources :addresses

@razum2um
Copy link

ActionDispatch::Routing::Mapper::HttpHelpers must be loaded before ActiveSupport.on_load(:action_controller)

fixed version https://gist.github.com/razum2um/56a50c4ad73530a170ba
just put in into config/initializers

@fgreg
Copy link

fgreg commented Apr 14, 2015

To help any others who come across this... in order to add this to your project:

  1. Create a file called 'add_patch_method.rb' in 'lib/patches' directory
  2. Paste the contents of the 'rails32_http_patch_support.rb' snippet above into the new 'add_patch_method.rb' file
  3. Create a file called 'rails32_http_patch_support_initializer.rb' in 'config/initializers' directory
  4. Paste contents of the 'rails32_http_patch_support_initializer.rb' snippet above into the new 'rails32_http_patch_support_initializer.rb' file

@nextofsearch
Copy link

Hi,
It doesn't work with my app. Rails 3.2.22.5

It crashes when running

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

@nextofsearch
Copy link

nextofsearch commented Dec 1, 2016

UPDATE: It's not working. I forgot to restart webserver. :(

Never mind. I used the gist of @razum2um instead of the original one. This gist works just fine.

@nextofsearch
Copy link

With the original gist, it crashes when running web server or 'rake routes'

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