summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGibheer <gibheer@gmail.com>2013-08-14 08:12:39 +0200
committerGibheer <gibheer@gmail.com>2013-08-14 08:12:39 +0200
commit603dce8628246a17009c3a5f30cb57e21b146672 (patch)
tree3db49a576aafba2d0878dda708ad21e0203b3760
parent00e0c0170385cc2bd8a9ac599f5462d75292efde (diff)
add request method override for browsers
Browsers are not able to send put, delete or any other request from a plain html form. This limits the possibilities with APIs so an override was introduced in many frameworks in the form, that `_method` could be defined in a post payload. With this, zero also supports `_method` in the post payload to make it possible to use all functions of the API with javascript through plain html.
-rw-r--r--lib/zero/request.rb30
-rw-r--r--spec/unit/zero/request/method_spec.rb43
2 files changed, 72 insertions, 1 deletions
diff --git a/lib/zero/request.rb b/lib/zero/request.rb
index 36ef705..e8f4513 100644
--- a/lib/zero/request.rb
+++ b/lib/zero/request.rb
@@ -68,7 +68,8 @@ module Zero
# get the method of the request
# @return [Symbol] the symbol representation of the method
def method
- @method ||= @env[CONST_REQUEST_METHOD].downcase.to_sym
+ return @method if @method
+ @method = extract_method
end
# is the method 'GET'?
# @return [Boolean] true if this is a get request
@@ -103,5 +104,32 @@ module Zero
# constant for the request method key
# @api private
CONST_REQUEST_METHOD = 'REQUEST_METHOD'
+ # regex to match for valid http methods
+ CONST_VALID_METHODS = /\A(get|post|put|delete|head|link|unlink|patch)\Z/
+ # constant for post
+ CONST_POST = 'post'
+ # constant for the method keyword
+ CONST_METHOD = '_method'
+
+ # this function tries to figure out what method the request used
+ #
+ # The problem with extracting the request method is, that every client out
+ # there can speak all methods (get, post, put, whatever) but not browsers.
+ # When sending a form, they can only send get or post forms, but not put
+ # or delete, which makes them a pain to use. So instead an override was
+ # introduced in rails and other frameworks to support `_method` in the post
+ # payload.
+ # So this function does essentially the same, so please do not remove it
+ # until browsers learned how to do it.
+ # @return [Symbol] the extracted method
+ def extract_method
+ method = @env[CONST_REQUEST_METHOD].downcase
+ return method.to_sym unless method == CONST_POST
+ if params.payload.has_key?(CONST_METHOD)
+ method = params.payload[CONST_METHOD].downcase
+ return method.to_sym if CONST_VALID_METHODS.match(method)
+ end
+ CONST_POST.to_sym
+ end
end
end
diff --git a/spec/unit/zero/request/method_spec.rb b/spec/unit/zero/request/method_spec.rb
index 42ea56a..b9352e1 100644
--- a/spec/unit/zero/request/method_spec.rb
+++ b/spec/unit/zero/request/method_spec.rb
@@ -5,4 +5,47 @@ describe Zero::Request, '#method' do
let(:env) { EnvGenerator.get('/foo') }
its(:method) { should == :get }
+
+ context 'with post requests' do
+ context 'and _method defined' do
+ let(:env) do
+ EnvGenerator.post('/foo', {
+ :input => '_method=put',
+ 'CONTENT_TYPE' => 'multipart/form-data'
+ })
+ end
+ it 'uses _method from the payload to change the method' do
+ expect(subject.method).to be(:put)
+ end
+ end
+
+ context 'and _method not defined' do
+ let(:env) do
+ EnvGenerator.post('/foo', {
+ :input => 'foo=bar',
+ 'CONTENT_TYPE' => 'multipart/form-data'
+ })
+ end
+ its(:method) { should == :post }
+ end
+
+ context 'and _method has wrong content' do
+ let(:env) do
+ EnvGenerator.post('/foo', {
+ :input => '_method=foobar',
+ 'CONTENT_TYPE' => 'multipart/form-data'
+ })
+ end
+ its(:method) { should == :post }
+ end
+
+ context 'and no payload' do
+ let(:env) do
+ EnvGenerator.post('/foo', {
+ 'CONTENT_TYPE' => 'multipart/form-data'
+ })
+ end
+ its(:method) { should == :post }
+ end
+ end
end