grape 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- data/CHANGELOG.md +15 -0
- data/Gemfile +1 -0
- data/README.md +58 -11
- data/grape.gemspec +1 -1
- data/lib/grape.rb +3 -1
- data/lib/grape/api.rb +4 -4
- data/lib/grape/endpoint.rb +20 -16
- data/lib/grape/formatter/json.rb +0 -1
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/formatter.rb +27 -17
- data/lib/grape/namespace.rb +24 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +68 -7
- data/spec/grape/endpoint_spec.rb +40 -12
- data/spec/grape/entity_spec.rb +22 -6
- data/spec/grape/validations/presence_spec.rb +5 -5
- data/spec/shared/versioning_examples.rb +6 -2
- metadata +155 -208
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
0.4.0 (3/17/2013)
|
2
|
+
=================
|
3
|
+
|
4
|
+
* [#356](https://github.com/intridea/grape/pull/356): Fix: presenting collections other than `Array` (eg. `ActiveRecord::Relation`) - [@zimbatm](https://github.com/zimbatm).
|
5
|
+
* [#352](https://github.com/intridea/grape/pull/352): Fix: using `Rack::JSONP` with `Grape::Entity` responses - [@deckchair](https://github.com/deckchair).
|
6
|
+
* [#347](https://github.com/intridea/grape/issues/347): Grape will accept any valid JSON as PUT or POST, including strings, symbols and arrays - [@qqshfox](https://github.com/qqshfox), [@dblock](https://github.com/dblock).
|
7
|
+
* [#347](https://github.com/intridea/grape/issues/347): JSON format APIs always return valid JSON, eg. strings are now returned as `"string"` and no longer `string` - [@dblock](https://github.com/dblock).
|
8
|
+
* Raw body input from POST and PUT requests (`env['rack.input'].read`) is now available in `api.request.input` - [@dblock](https://github.com/dblock).
|
9
|
+
* Parsed body input from POST and PUT requests is now available in `api.request.body` - [@dblock](https://github.com/dblock).
|
10
|
+
* [#343](https://github.com/intridea/grape/pull/343): Fix: return `Content-Type: text/plain` with error 405 - [@gustavosaume](https://github.com/gustavosaume), [@wyattisimo](https://github.com/wyattisimo).
|
11
|
+
* [#357](https://github.com/intridea/grape/pull/357): Grape now requires Rack 1.3.0 or newer - [@jhecking](https://github.com/jhecking).
|
12
|
+
* [#320](https://github.com/intridea/grape/issues/320): API `namespace` now supports `requirements` - [@niedhui](https://github.com/niedhui).
|
13
|
+
* [#353](https://github.com/intridea/grape/issues/353): Revert to standard Ruby logger formatter, `require active_support/all` if you want old behavior - [@rhunter](https://github.com/rhunter), [@dblock](https://github.com/dblock).
|
14
|
+
* Fix: `undefined method `call' for nil:NilClass` for an API method implementation without a block, now returns an empty string - [@dblock](https://github.com/dblock).
|
15
|
+
|
1
16
|
0.3.2 (2/28/2013)
|
2
17
|
=================
|
3
18
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -8,11 +8,11 @@ providing a simple DSL to easily develop RESTful APIs. It has built-in support
|
|
8
8
|
for common conventions, including multiple formats, subdomain/prefix restriction,
|
9
9
|
content negotiation, versioning and much more.
|
10
10
|
|
11
|
-
[![Build Status](https://travis-ci.org/intridea/grape.png?branch=master)](http://travis-ci.org/intridea/grape)
|
11
|
+
[![Build Status](https://travis-ci.org/intridea/grape.png?branch=master)](http://travis-ci.org/intridea/grape) [![Code Climate](https://codeclimate.com/github/intridea/grape.png)](https://codeclimate.com/github/intridea/grape)
|
12
12
|
|
13
13
|
## Stable Release
|
14
14
|
|
15
|
-
You're reading the documentation for
|
15
|
+
You're reading the documentation for the stable release 0.4.0.
|
16
16
|
|
17
17
|
## Project Tracking
|
18
18
|
|
@@ -363,12 +363,12 @@ end
|
|
363
363
|
|
364
364
|
### Validation Errors
|
365
365
|
|
366
|
-
When validation and coercion errors occur an exception of type `Grape::Exceptions::
|
366
|
+
When validation and coercion errors occur an exception of type `Grape::Exceptions::Validation` is raised.
|
367
367
|
If the exception goes uncaught it will respond with a status of 400 and an error message.
|
368
|
-
You can rescue a `Grape::Exceptions::
|
368
|
+
You can rescue a `Grape::Exceptions::Validation` and respond with a custom response.
|
369
369
|
|
370
370
|
```ruby
|
371
|
-
rescue_from Grape::Exceptions::
|
371
|
+
rescue_from Grape::Exceptions::Validation do |e|
|
372
372
|
Rack::Response.new({
|
373
373
|
'status' => e.status,
|
374
374
|
'message' => e.message,
|
@@ -402,12 +402,20 @@ header "X-Robots-Tag", "noindex"
|
|
402
402
|
## Routes
|
403
403
|
|
404
404
|
Optionally, you can define requirements for your named route parameters using regular
|
405
|
-
expressions. The route will match only if all requirements are met.
|
405
|
+
expressions on namespace or endpoint. The route will match only if all requirements are met.
|
406
406
|
|
407
407
|
```ruby
|
408
408
|
get ':id', :requirements => { :id => /[0-9]*/ } do
|
409
409
|
Status.find(params[:id])
|
410
410
|
end
|
411
|
+
|
412
|
+
namespace :outer, :requirements => { :id => /[0-9]*/ } do
|
413
|
+
get :id do
|
414
|
+
end
|
415
|
+
|
416
|
+
get ":id/edit" do
|
417
|
+
end
|
418
|
+
end
|
411
419
|
```
|
412
420
|
|
413
421
|
## Helpers
|
@@ -721,9 +729,8 @@ By default, Grape supports _XML_, _JSON_, and _TXT_ content-types. The default f
|
|
721
729
|
|
722
730
|
Serialization takes place automatically. For example, you do not have to call `to_json` in each JSON API implementation.
|
723
731
|
|
724
|
-
Your API can declare which types to support by using `content_type`. Response format
|
725
|
-
|
726
|
-
string, or `Accept` header.
|
732
|
+
Your API can declare which types to support by using `content_type`. Response format is determined by the
|
733
|
+
request's extension, an explicit `format` parameter in the query string, or `Accept` header.
|
727
734
|
|
728
735
|
The following API will only respond to the JSON content-type and will not parse any other input than `application/json`,
|
729
736
|
`application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and `multipart/mixed`. All other requests
|
@@ -795,6 +802,45 @@ The order for choosing the format is the following.
|
|
795
802
|
* Use the default format, if specified by the `default_format` option.
|
796
803
|
* Default to `:txt`.
|
797
804
|
|
805
|
+
### JSONP
|
806
|
+
|
807
|
+
Grape suports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the
|
808
|
+
[rack-contrib](https://github.com/rack/rack-contrib) gem. Add `rack-contrib` to your `Gemfile`.
|
809
|
+
|
810
|
+
```ruby
|
811
|
+
require 'rack/contrib'
|
812
|
+
|
813
|
+
class API < Grape::API
|
814
|
+
use Rack::JSONP
|
815
|
+
format :json
|
816
|
+
get '/' do
|
817
|
+
'Hello World'
|
818
|
+
end
|
819
|
+
end
|
820
|
+
```
|
821
|
+
|
822
|
+
### CORS
|
823
|
+
|
824
|
+
Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the
|
825
|
+
[rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`.
|
826
|
+
|
827
|
+
```ruby
|
828
|
+
require 'rack/cors'
|
829
|
+
|
830
|
+
class API < Grape::API
|
831
|
+
use Rack::Cors do
|
832
|
+
allow do
|
833
|
+
origins '*'
|
834
|
+
resource '*', :headers => :any, :methods => :get
|
835
|
+
end
|
836
|
+
end
|
837
|
+
format :json
|
838
|
+
get '/' do
|
839
|
+
'Hello World'
|
840
|
+
end
|
841
|
+
end
|
842
|
+
```
|
843
|
+
|
798
844
|
## Content-type
|
799
845
|
|
800
846
|
Content-type is set by the formatter. You can override the content-type of the response at runtime
|
@@ -816,7 +862,8 @@ section above. It also supports custom data formats. You must declare additional
|
|
816
862
|
`content_type` and optionally supply a parser via `parser` unless a parser is already available within
|
817
863
|
Grape to enable a custom format. Such a parser can be a function or a class.
|
818
864
|
|
819
|
-
|
865
|
+
With a parser, parsed data is available "as-is" in `env['api.request.body']`.
|
866
|
+
Without a parser, data is available "as-is" and in `env['api.request.input']`.
|
820
867
|
|
821
868
|
The following example is a trivial parser that will assign any input with the "text/custom" content-type
|
822
869
|
to `:value`. The parameter will be available via `params[:value]` inside the API call.
|
@@ -1153,4 +1200,4 @@ MIT License. See LICENSE for details.
|
|
1153
1200
|
|
1154
1201
|
## Copyright
|
1155
1202
|
|
1156
|
-
Copyright (c) 2010-
|
1203
|
+
Copyright (c) 2010-2013 Michael Bleigh, and Intridea, Inc.
|
data/grape.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.rubyforge_project = "grape"
|
16
16
|
|
17
|
-
s.add_runtime_dependency 'rack'
|
17
|
+
s.add_runtime_dependency 'rack', '>= 1.3.0'
|
18
18
|
s.add_runtime_dependency 'rack-mount'
|
19
19
|
s.add_runtime_dependency 'rack-accept'
|
20
20
|
s.add_runtime_dependency 'activesupport'
|
data/lib/grape.rb
CHANGED
@@ -6,7 +6,8 @@ require 'rack/accept'
|
|
6
6
|
require 'rack/auth/basic'
|
7
7
|
require 'rack/auth/digest/md5'
|
8
8
|
require 'hashie'
|
9
|
-
require 'active_support/
|
9
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
10
|
+
require 'active_support/ordered_hash'
|
10
11
|
require 'grape/util/deep_merge'
|
11
12
|
require 'grape/util/content_types'
|
12
13
|
require 'multi_json'
|
@@ -20,6 +21,7 @@ module Grape
|
|
20
21
|
autoload :API, 'grape/api'
|
21
22
|
autoload :Endpoint, 'grape/endpoint'
|
22
23
|
autoload :Route, 'grape/route'
|
24
|
+
autoload :Namespace, 'grape/namespace'
|
23
25
|
autoload :Cookies, 'grape/cookies'
|
24
26
|
autoload :Validations, 'grape/validations'
|
25
27
|
|
data/lib/grape/api.rb
CHANGED
@@ -356,17 +356,17 @@ module Grape
|
|
356
356
|
def options(paths = ['/'], options = {}, &block); route('OPTIONS', paths, options, &block) end
|
357
357
|
def patch(paths = ['/'], options = {}, &block); route('PATCH', paths, options, &block) end
|
358
358
|
|
359
|
-
def namespace(space = nil, &block)
|
359
|
+
def namespace(space = nil, options = {}, &block)
|
360
360
|
if space || block_given?
|
361
361
|
previous_namespace_description = @namespace_description
|
362
362
|
@namespace_description = (@namespace_description || {}).deep_merge(@last_description || {})
|
363
363
|
@last_description = nil
|
364
364
|
nest(block) do
|
365
|
-
set(:namespace, space
|
365
|
+
set(:namespace, Namespace.new(space, options)) if space
|
366
366
|
end
|
367
367
|
@namespace_description = previous_namespace_description
|
368
368
|
else
|
369
|
-
|
369
|
+
Namespace.joined_space_path(settings)
|
370
370
|
end
|
371
371
|
end
|
372
372
|
|
@@ -507,7 +507,7 @@ module Grape
|
|
507
507
|
not_allowed_methods = %w(GET PUT POST DELETE PATCH HEAD) - methods
|
508
508
|
not_allowed_methods << "OPTIONS" if self.class.settings[:do_not_route_options]
|
509
509
|
not_allowed_methods.each do |bad_method|
|
510
|
-
@route_set.add_route( proc { [405, { 'Allow' => allow_header }, []]}, {
|
510
|
+
@route_set.add_route( proc { [405, { 'Allow' => allow_header, 'Content-Type' => 'text/plain' }, []]}, {
|
511
511
|
:path_info => path_info,
|
512
512
|
:request_method => bad_method
|
513
513
|
})
|
data/lib/grape/endpoint.rb
CHANGED
@@ -35,9 +35,9 @@ module Grape
|
|
35
35
|
def initialize(settings, options = {}, &block)
|
36
36
|
@settings = settings
|
37
37
|
if block_given?
|
38
|
-
method_name = [
|
38
|
+
method_name = [
|
39
39
|
options[:method],
|
40
|
-
|
40
|
+
Namespace.joined_space(settings),
|
41
41
|
settings.gather(:mount_path).join("/"),
|
42
42
|
Array(options[:path]).join("/")
|
43
43
|
].join(" ")
|
@@ -88,7 +88,11 @@ module Grape
|
|
88
88
|
anchor = options[:route_options][:anchor]
|
89
89
|
anchor = anchor.nil? ? true : anchor
|
90
90
|
|
91
|
-
|
91
|
+
endpoint_requirements = options[:route_options][:requirements] || {}
|
92
|
+
all_requirements = (settings.gather(:namespace).map(&:requirements) << endpoint_requirements)
|
93
|
+
requirements = all_requirements.reduce({}) do |base_requirements, single_requirements|
|
94
|
+
base_requirements.merge!(single_requirements)
|
95
|
+
end
|
92
96
|
|
93
97
|
path = compile_path(prepared_path, anchor && !options[:app], requirements)
|
94
98
|
regex = Rack::Mount::RegexpWithNamedGroups.new(path)
|
@@ -137,7 +141,7 @@ module Grape
|
|
137
141
|
end
|
138
142
|
|
139
143
|
def namespace
|
140
|
-
|
144
|
+
@namespace ||= Namespace.joined_space_path(settings)
|
141
145
|
end
|
142
146
|
|
143
147
|
def compile_path(prepared_path, anchor = true, requirements = {})
|
@@ -316,7 +320,7 @@ module Grape
|
|
316
320
|
entity_class = options.delete(:with)
|
317
321
|
|
318
322
|
# auto-detect the entity from the first object in the collection
|
319
|
-
object_instance = object.
|
323
|
+
object_instance = object.respond_to?(:first) ? object.first : object
|
320
324
|
|
321
325
|
object_instance.class.ancestors.each do |potential|
|
322
326
|
entity_class ||= (settings[:representations] || {})[potential]
|
@@ -379,7 +383,7 @@ module Grape
|
|
379
383
|
|
380
384
|
run_filters after_validations
|
381
385
|
|
382
|
-
response_text = @block.call(self)
|
386
|
+
response_text = @block ? @block.call(self) : nil
|
383
387
|
run_filters afters
|
384
388
|
cookies.write(header)
|
385
389
|
|
@@ -399,6 +403,16 @@ module Grape
|
|
399
403
|
:rescue_options => settings[:rescue_options],
|
400
404
|
:rescue_handlers => merged_setting(:rescue_handlers)
|
401
405
|
|
406
|
+
aggregate_setting(:middleware).each do |m|
|
407
|
+
m = m.dup
|
408
|
+
block = m.pop if m.last.is_a?(Proc)
|
409
|
+
if block
|
410
|
+
b.use *m, &block
|
411
|
+
else
|
412
|
+
b.use *m
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
402
416
|
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
|
403
417
|
b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
|
404
418
|
|
@@ -417,16 +431,6 @@ module Grape
|
|
417
431
|
:formatters => settings[:formatters],
|
418
432
|
:parsers => settings[:parsers]
|
419
433
|
|
420
|
-
aggregate_setting(:middleware).each do |m|
|
421
|
-
m = m.dup
|
422
|
-
block = m.pop if m.last.is_a?(Proc)
|
423
|
-
if block
|
424
|
-
b.use *m, &block
|
425
|
-
else
|
426
|
-
b.use *m
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
434
|
b
|
431
435
|
end
|
432
436
|
|
data/lib/grape/formatter/json.rb
CHANGED
data/lib/grape/locale/en.yml
CHANGED
@@ -7,7 +7,7 @@ en:
|
|
7
7
|
regexp: 'invalid parameter: %{attribute}'
|
8
8
|
missing_vendor_option:
|
9
9
|
problem: 'missing :vendor option.'
|
10
|
-
summary: 'when version using header, you must specify :
|
10
|
+
summary: 'when version using header, you must specify :vendor option. '
|
11
11
|
resolution: "eg: version 'v1', :using => :header, :vendor => 'twitter'"
|
12
12
|
missing_mime_type:
|
13
13
|
problem: 'missing mime type for %{new_format}'
|
@@ -37,29 +37,39 @@ module Grape
|
|
37
37
|
|
38
38
|
private
|
39
39
|
|
40
|
+
# store read input in env['api.request.input']
|
40
41
|
def read_body_input
|
41
42
|
if (request.post? || request.put? || request.patch?) && (! request.form_data?) && (! request.parseable_data?) && (request.content_length.to_i > 0)
|
42
|
-
if env['rack.input'] && (body = env['rack.input'].read).length > 0
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
43
|
+
if env['rack.input'] && (body = (env['api.request.input'] = env['rack.input'].read)).length > 0
|
44
|
+
read_rack_input body
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# store parsed input in env['api.request.body']
|
50
|
+
def read_rack_input(body)
|
51
|
+
begin
|
52
|
+
fmt = mime_types[request.media_type] if request.media_type
|
53
|
+
if content_type_for(fmt)
|
54
|
+
parser = Grape::Parser::Base.parser_for fmt, options
|
55
|
+
if parser
|
56
|
+
begin
|
57
|
+
body = (env['api.request.body'] = parser.call(body, env))
|
58
|
+
if body.is_a?(Hash)
|
59
|
+
env['rack.request.form_hash'] = env['rack.request.form_hash'] ?
|
60
|
+
env['rack.request.form_hash'].merge(body) :
|
61
|
+
body
|
55
62
|
end
|
56
|
-
|
57
|
-
|
63
|
+
env['rack.request.form_input'] = env['rack.input']
|
64
|
+
rescue Exception => e
|
65
|
+
throw :error, :status => 400, :message => e.message
|
58
66
|
end
|
59
|
-
ensure
|
60
|
-
env['rack.input'].rewind
|
61
67
|
end
|
68
|
+
else
|
69
|
+
throw :error, :status => 406, :message => "The requested content-type '#{request.media_type}' is not supported."
|
62
70
|
end
|
71
|
+
ensure
|
72
|
+
env['rack.input'].rewind
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Grape
|
2
|
+
class Namespace
|
3
|
+
attr_reader :space, :options
|
4
|
+
|
5
|
+
# options:
|
6
|
+
# requirements: a hash
|
7
|
+
def initialize(space, options = {})
|
8
|
+
@space, @options = space.to_s, options
|
9
|
+
end
|
10
|
+
|
11
|
+
def requirements
|
12
|
+
options[:requirements] || {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.joined_space(settings)
|
16
|
+
settings.gather(:namespace).map(&:space).join("/")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.joined_space_path(settings)
|
20
|
+
Rack::Mount::Utils.normalize_path(joined_space(settings))
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
data/lib/grape/version.rb
CHANGED
data/spec/grape/api_spec.rb
CHANGED
@@ -57,6 +57,7 @@ describe Grape::API do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
60
|
+
|
60
61
|
describe '.version using param' do
|
61
62
|
it_should_behave_like 'versioning' do
|
62
63
|
let(:macro_options) do
|
@@ -93,9 +94,7 @@ describe Grape::API do
|
|
93
94
|
# end
|
94
95
|
end
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
end
|
97
|
+
# pending 'routes if any media type is allowed'
|
99
98
|
end
|
100
99
|
|
101
100
|
describe '.represent' do
|
@@ -108,7 +107,6 @@ describe Grape::API do
|
|
108
107
|
subject.represent Object, :with => klass
|
109
108
|
subject.settings[:representations][Object].should == klass
|
110
109
|
end
|
111
|
-
|
112
110
|
end
|
113
111
|
|
114
112
|
describe '.namespace' do
|
@@ -189,7 +187,6 @@ describe Grape::API do
|
|
189
187
|
get do
|
190
188
|
"Votes"
|
191
189
|
end
|
192
|
-
|
193
190
|
post do
|
194
191
|
"Created a Vote"
|
195
192
|
end
|
@@ -201,15 +198,22 @@ describe Grape::API do
|
|
201
198
|
last_response.body.should eql 'Created a Vote'
|
202
199
|
end
|
203
200
|
|
201
|
+
it 'handles empty calls' do
|
202
|
+
subject.get "/"
|
203
|
+
get "/"
|
204
|
+
last_response.body.should eql ""
|
205
|
+
end
|
206
|
+
|
204
207
|
describe 'root routes should work with' do
|
205
208
|
before do
|
209
|
+
subject.format :txt
|
206
210
|
def subject.enable_root_route!
|
207
|
-
self.get("/") {"root"}
|
211
|
+
self.get("/") { "root" }
|
208
212
|
end
|
209
213
|
end
|
210
214
|
|
211
215
|
after do
|
212
|
-
last_response.body.should eql
|
216
|
+
last_response.body.should eql "root"
|
213
217
|
end
|
214
218
|
|
215
219
|
describe 'path versioned APIs' do
|
@@ -314,6 +318,31 @@ describe Grape::API do
|
|
314
318
|
last_response.body.should eql 'hiya'
|
315
319
|
end
|
316
320
|
|
321
|
+
[ :put, :post ].each do |verb|
|
322
|
+
context verb do
|
323
|
+
[ 'string', :symbol, 1, -1.1, {}, [], true, false, nil ].each do |object|
|
324
|
+
it "allows a(n) #{object.class} json object in params" do
|
325
|
+
subject.format :json
|
326
|
+
subject.send(verb) do
|
327
|
+
env['api.request.body']
|
328
|
+
end
|
329
|
+
send verb, '/', MultiJson.dump(object), { 'CONTENT_TYPE' => 'application/json' }
|
330
|
+
last_response.status.should == (verb == :post ? 201 : 200)
|
331
|
+
last_response.body.should eql MultiJson.dump(object)
|
332
|
+
end
|
333
|
+
it "stores input in api.request.input" do
|
334
|
+
subject.format :json
|
335
|
+
subject.send(verb) do
|
336
|
+
env['api.request.input']
|
337
|
+
end
|
338
|
+
send verb, '/', MultiJson.dump(object), { 'CONTENT_TYPE' => 'application/json' }
|
339
|
+
last_response.status.should == (verb == :post ? 201 : 200)
|
340
|
+
last_response.body.should eql MultiJson.dump(object).to_json
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
317
346
|
it 'allows for multipart paths' do
|
318
347
|
|
319
348
|
subject.route([:get, :post], '/:id/first') do
|
@@ -395,6 +424,17 @@ describe Grape::API do
|
|
395
424
|
last_response.headers['Allow'].should eql 'OPTIONS, GET, POST, HEAD'
|
396
425
|
end
|
397
426
|
|
427
|
+
specify '405 responses includes an Content-Type header' do
|
428
|
+
subject.get 'example' do
|
429
|
+
"example"
|
430
|
+
end
|
431
|
+
subject.post 'example' do
|
432
|
+
"example"
|
433
|
+
end
|
434
|
+
put '/example'
|
435
|
+
last_response.headers['Content-Type'].should eql 'text/plain'
|
436
|
+
end
|
437
|
+
|
398
438
|
it 'adds an OPTIONS route that returns a 204 and an Allow header' do
|
399
439
|
subject.get 'example' do
|
400
440
|
"example"
|
@@ -631,6 +671,20 @@ describe Grape::API do
|
|
631
671
|
last_response.body.should == 'true'
|
632
672
|
end
|
633
673
|
end
|
674
|
+
|
675
|
+
it 'mounts behind error middleware' do
|
676
|
+
m = Class.new(Grape::Middleware::Base) do
|
677
|
+
def before
|
678
|
+
throw :error, :message => "Caught in the Net", :status => 400
|
679
|
+
end
|
680
|
+
end
|
681
|
+
subject.use m
|
682
|
+
subject.get "/" do
|
683
|
+
end
|
684
|
+
get "/"
|
685
|
+
last_response.status.should == 400
|
686
|
+
last_response.body.should == "Caught in the Net"
|
687
|
+
end
|
634
688
|
end
|
635
689
|
end
|
636
690
|
describe '.basic' do
|
@@ -685,6 +739,13 @@ describe Grape::API do
|
|
685
739
|
mylogger.should_receive(:info).exactly(1).times
|
686
740
|
subject.logger.info "this will be logged"
|
687
741
|
end
|
742
|
+
|
743
|
+
it "defaults to a standard logger log format" do
|
744
|
+
t = Time.at(100)
|
745
|
+
Time.stub(:now).and_return(t)
|
746
|
+
STDOUT.should_receive(:write).with("I, [#{Logger::Formatter.new.send(:format_datetime, t)}\##{Process.pid}] INFO -- : this will be logged\n")
|
747
|
+
subject.logger.info "this will be logged"
|
748
|
+
end
|
688
749
|
end
|
689
750
|
|
690
751
|
describe '.helpers' do
|