Skip to content
Browse files

Improve MethodOverride middleware and specs

By default, the middleware now rewrites all requests except GET and
POST. A `:rewrite` option can be passed to whitelist only the methods
which should be overridden.

References #48
  • Loading branch information...
1 parent 89d3595 commit c02003542198f351fe9fde60826df899f7f97bbd @mislav mislav committed Aug 30, 2012
Showing with 102 additions and 101 deletions.
  1. +31 −22 lib/faraday_middleware/request/method_override.rb
  2. +71 −79 spec/method_override_spec.rb
View
53 lib/faraday_middleware/request/method_override.rb
@@ -1,42 +1,51 @@
require 'faraday'
module FaradayMiddleware
-
- # Rewrites poorly supported HTTP request methods to a custom
- # X-Http-Method-Override header and sends the request as POST.
+ # Public: Writes the original HTTP method to "X-Http-Method-Override" header
+ # and sends the request as POST.
#
- # This is supported by default in Rack / Rails apps via the
- # Rack::MethodOverride module.
+ # This can be used to work around technical issues with making non-POST
+ # requests, e.g. faulty HTTP client or server router.
#
- # See: http://rack.rubyforge.org/doc/classes/Rack/MethodOverride.html
+ # This header is recognized in Rack apps by default, courtesy of the
+ # Rack::MethodOverride module. See
+ # http://rack.rubyforge.org/doc/classes/Rack/MethodOverride.html
class MethodOverride < Faraday::Middleware
- HEADER = "X-Http-Method-Override"
+ HEADER = "X-Http-Method-Override".freeze
- def initialize(app, *methods)
+ # Public: Initialize the middleware.
+ #
+ # app - the Faraday app to wrap
+ # options - (optional)
+ # :rewrite - Array of HTTP methods to rewrite
+ # (default: all but GET and POST)
+ def initialize(app, options = nil)
super(app)
- @methods = methods.map { |m| normalize(m) }
+ @methods = options && options.fetch(:rewrite).map { |method|
+ method = method.downcase if method.respond_to? :downcase
+ method.to_sym
+ }
end
def call(env)
- method = normalize(env[:method])
- rewrite_env(env, method) if @methods.include?(method)
+ method = env[:method]
+ rewrite_request(env, method) if rewrite_request?(method)
@app.call(env)
end
- private
-
- # Move the real request method to a header, send the request as HTTP POST.
- def rewrite_env(env, method)
- env[:request_headers][HEADER] = method
- env[:method] = :post
+ def rewrite_request?(method)
+ if @methods.nil? or @methods.empty?
+ method != :get and method != :post
+ else
+ @methods.include? method
+ end
end
- # Normalize an HTTP method (String or Symbol) to an upper-case string.
- def normalize(method)
- method.to_s.upcase
+ # Internal: Write the original HTTP method to header, change method to POST.
+ def rewrite_request(env, original_method)
+ env[:request_headers][HEADER] = original_method.to_s.upcase
+ env[:method] = :post
end
-
end
-
end
View
150 spec/method_override_spec.rb
@@ -3,98 +3,90 @@
describe FaradayMiddleware::MethodOverride do
- subject do
- FaradayMiddleware::MethodOverride.new(
- lambda { |env| env },
- *options
- )
+ let(:middleware) { described_class.new(lambda {|env| env }, *options) }
+ let(:env) { middleware.call request_env(request_method) }
+
+ def request_env(method)
+ { :method => method,
+ :request_headers => Faraday::Utils::Headers.new
+ }
+ end
+
+ shared_examples "overrides method" do |method|
+ it "sets physical method to POST" do
+ env[:method].should eq(:post)
+ end
+
+ it "sets header to #{method}" do
+ env[:request_headers]['X-Http-Method-Override'].should eq(method)
+ end
end
- # A minimal Faraday request env for the given HTTP method.
- def env_for_method(method)
- { :method => method, :request_headers => Faraday::Utils::Headers.new }
+ shared_examples "doesn't override method" do |method|
+ it "keeps original method" do
+ env[:method].should eq(method)
+ end
+
+ it "doesn't set header value" do
+ env[:request_headers].should_not have_key('X-Http-Method-Override')
+ end
+
end
- # A simple test, before getting into more complicated declarative tests.
- describe "for a simple use case" do
- let(:options) { [:patch] }
+ context "with default options" do
+ let(:options) { nil }
+
+ context "GET" do
+ let(:request_method) { :get }
+ include_examples "doesn't override method", :get
+ end
- it "rewrites a PATCH request" do
- env = subject.call(env_for_method(:patch))
- env[:method].should == :post
- env[:request_headers]["X-Http-Method-Override"].should == "PATCH"
+ context "POST" do
+ let(:request_method) { :post }
+ include_examples "doesn't override method", :post
end
- it "does not rewrite a GET request" do
- env = subject.call(env_for_method(:get))
- env[:method].should == :get
- env[:request_headers].should_not have_key("X-Http-Method-Override")
+ context "PUT" do
+ let(:request_method) { :put }
+ include_examples "overrides method", 'PUT'
end
end
- # Declarative mapping of whether a request method should be rewritten under a
- # given configuration.
- {
- # A normal use case.
- [:delete, :put, :patch] => {
- :delete => true,
- :get => false,
- :patch => true,
- :post => false,
- :put => true,
- },
- # Without any methods specified.
- [] => {
- :get => false,
- :post => false,
- :put => false,
- },
- # Of little value, but valid.
- [:post] => {
- :post => true,
- },
- # Configured with strings instead of symbols.
- ["patch", "PUT"] => {
- :get => false,
- :patch => true,
- :put => true,
- },
- # With methods as strings in faraday env.
- [:patch, :put] => {
- "GET" => false,
- "PATCH" => true,
- "post" => false,
- "put" => true,
- },
- }.each do |options, expectations|
-
- context "configured for #{options.inspect}" do
-
- let(:options) { options }
-
- expectations.each do |method, should_override|
- method_up = method.to_s.upcase
- if should_override
-
- it "sends #{method_up} as POST with #{method_up} in header" do
- env = subject.call(env_for_method(method))
- env[:method].should == :post
- env[:request_headers]["X-Http-Method-Override"].should == method_up
- end
-
- else
-
- it "sends #{method_up} unmodified with no header" do
- env = subject.call(env_for_method(method))
- env[:method].should == method
- env[:request_headers].should_not have_key("X-Http-Method-Override")
- end
-
- end
- end
+ context "configured to rewrite [:patch, :delete]" do
+ let(:options) { [{ :rewrite => [:patch, :delete] }] }
+ context "PUT" do
+ let(:request_method) { :put }
+ include_examples "doesn't override method", :put
end
+ context "PATCH" do
+ let(:request_method) { :patch }
+ include_examples "overrides method", 'PATCH'
+ end
+
+ context "DELETE" do
+ let(:request_method) { :delete }
+ include_examples "overrides method", 'DELETE'
+ end
+ end
+
+ context "configured to rewrite ['PATCH']" do
+ let(:options) { [{ :rewrite => %w[PATCH] }] }
+
+ context "PATCH" do
+ let(:request_method) { :patch }
+ include_examples "overrides method", 'PATCH'
+ end
+ end
+
+ context "with invalid option" do
+ let(:options) { [{ :hello => 'world' }] }
+ let(:request_method) { :get }
+
+ it "raises KeyError" do
+ expect { env }.to raise_error(KeyError, "key not found: :rewrite")
+ end
end
end

0 comments on commit c020035

Please sign in to comment.
Something went wrong with that request. Please try again.