grape 0.11.0 → 0.12.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.

Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +23 -80
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +27 -0
  5. data/Gemfile +1 -1
  6. data/Guardfile +1 -1
  7. data/LICENSE +1 -1
  8. data/README.md +131 -30
  9. data/Rakefile +1 -1
  10. data/UPGRADING.md +110 -1
  11. data/gemfiles/rails_3.gemfile +1 -1
  12. data/gemfiles/rails_4.gemfile +1 -1
  13. data/grape.gemspec +4 -4
  14. data/lib/grape.rb +92 -62
  15. data/lib/grape/api.rb +10 -10
  16. data/lib/grape/cookies.rb +1 -1
  17. data/lib/grape/dsl/configuration.rb +7 -7
  18. data/lib/grape/dsl/helpers.rb +3 -3
  19. data/lib/grape/dsl/inside_route.rb +50 -21
  20. data/lib/grape/dsl/parameters.rb +25 -6
  21. data/lib/grape/dsl/request_response.rb +1 -1
  22. data/lib/grape/dsl/routing.rb +11 -10
  23. data/lib/grape/dsl/settings.rb +1 -1
  24. data/lib/grape/endpoint.rb +21 -19
  25. data/lib/grape/error_formatter/json.rb +1 -1
  26. data/lib/grape/exceptions/base.rb +1 -1
  27. data/lib/grape/exceptions/validation.rb +1 -1
  28. data/lib/grape/exceptions/validation_errors.rb +2 -2
  29. data/lib/grape/formatter/base.rb +1 -1
  30. data/lib/grape/formatter/json.rb +1 -1
  31. data/lib/grape/formatter/serializable_hash.rb +4 -4
  32. data/lib/grape/formatter/txt.rb +1 -1
  33. data/lib/grape/formatter/xml.rb +1 -1
  34. data/lib/grape/http/headers.rb +27 -0
  35. data/lib/grape/http/request.rb +1 -1
  36. data/lib/grape/middleware/error.rb +10 -4
  37. data/lib/grape/middleware/formatter.rb +13 -9
  38. data/lib/grape/middleware/globals.rb +2 -1
  39. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -2
  40. data/lib/grape/middleware/versioner/header.rb +4 -4
  41. data/lib/grape/middleware/versioner/param.rb +2 -2
  42. data/lib/grape/middleware/versioner/path.rb +1 -1
  43. data/lib/grape/namespace.rb +2 -1
  44. data/lib/grape/parser/json.rb +1 -1
  45. data/lib/grape/parser/xml.rb +1 -1
  46. data/lib/grape/path.rb +3 -3
  47. data/lib/grape/presenters/presenter.rb +9 -0
  48. data/lib/grape/validations/params_scope.rb +3 -3
  49. data/lib/grape/validations/validators/allow_blank.rb +1 -1
  50. data/lib/grape/validations/validators/coerce.rb +6 -5
  51. data/lib/grape/validations/validators/default.rb +2 -2
  52. data/lib/grape/validations/validators/multiple_params_base.rb +1 -0
  53. data/lib/grape/validations/validators/regexp.rb +1 -1
  54. data/lib/grape/version.rb +1 -1
  55. data/spec/grape/api/custom_validations_spec.rb +47 -0
  56. data/spec/grape/api/deeply_included_options_spec.rb +56 -0
  57. data/spec/grape/api_spec.rb +64 -42
  58. data/spec/grape/dsl/configuration_spec.rb +2 -2
  59. data/spec/grape/dsl/helpers_spec.rb +1 -1
  60. data/spec/grape/dsl/inside_route_spec.rb +75 -19
  61. data/spec/grape/dsl/parameters_spec.rb +59 -10
  62. data/spec/grape/dsl/request_response_spec.rb +62 -2
  63. data/spec/grape/dsl/routing_spec.rb +116 -18
  64. data/spec/grape/endpoint_spec.rb +57 -5
  65. data/spec/grape/entity_spec.rb +1 -1
  66. data/spec/grape/exceptions/body_parse_errors_spec.rb +5 -5
  67. data/spec/grape/exceptions/invalid_accept_header_spec.rb +32 -32
  68. data/spec/grape/exceptions/validation_errors_spec.rb +1 -1
  69. data/spec/grape/integration/rack_spec.rb +4 -3
  70. data/spec/grape/middleware/auth/strategies_spec.rb +2 -2
  71. data/spec/grape/middleware/base_spec.rb +2 -2
  72. data/spec/grape/middleware/error_spec.rb +1 -1
  73. data/spec/grape/middleware/exception_spec.rb +5 -5
  74. data/spec/grape/middleware/formatter_spec.rb +10 -10
  75. data/spec/grape/middleware/globals_spec.rb +27 -0
  76. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
  77. data/spec/grape/middleware/versioner/header_spec.rb +1 -1
  78. data/spec/grape/middleware/versioner/param_spec.rb +1 -1
  79. data/spec/grape/middleware/versioner/path_spec.rb +1 -1
  80. data/spec/grape/path_spec.rb +6 -4
  81. data/spec/grape/presenters/presenter_spec.rb +70 -0
  82. data/spec/grape/util/inheritable_values_spec.rb +1 -1
  83. data/spec/grape/util/stackable_values_spec.rb +1 -1
  84. data/spec/grape/util/strict_hash_configuration_spec.rb +1 -1
  85. data/spec/grape/validations/params_scope_spec.rb +64 -0
  86. data/spec/grape/validations/validators/allow_blank_spec.rb +10 -0
  87. data/spec/grape/validations/validators/coerce_spec.rb +48 -18
  88. data/spec/grape/validations/validators/default_spec.rb +110 -20
  89. data/spec/grape/validations/validators/presence_spec.rb +41 -3
  90. data/spec/grape/validations/validators/regexp_spec.rb +7 -2
  91. data/spec/grape/validations_spec.rb +20 -1
  92. data/spec/support/file_streamer.rb +11 -0
  93. data/spec/support/versioned_helpers.rb +1 -1
  94. metadata +14 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cce57801c840dd9a64018350c6dd1eb955fc7b70
4
- data.tar.gz: e09089d477bba3cf9ff2bd53a5f164ecffefe494
3
+ metadata.gz: 219bc6e20332454502d7e7955db53424e5828880
4
+ data.tar.gz: 69cdeb3133100f91ebb0fd32e2ee1e52a130defc
5
5
  SHA512:
6
- metadata.gz: 1a7d4e52cdc91c6ee4f623b4a14d986b60a0f574b2885392487ff22993c6465c923165e9214382e9465265f8e1240519be0b95fec0c19c7d3f4fc603827edd1c
7
- data.tar.gz: 7ec02b2a4bcc083f6034c681cf82356d97e6d3aa65ad2d6663fe2c437c5e0da7f344371f003391ff9c838b7b190c9415e5abe986f9449670a76ab545e0e7aa5d
6
+ metadata.gz: 591cb91662a9b1a5f93889103eca65ed73c2979490d36c01f47f7cfabf926b5988693bfc4502defb394236003669fe60d2cb42b3005778f9be4c2c737975afce
7
+ data.tar.gz: ca855e03121beca28d6f0888e843c93e118f38acce88870732f9ec5e3073382d76bde1eb831236f64f6fe04fa7681022a4a677553a89abecc3d54a36ae05153a
data/.rubocop_todo.yml CHANGED
@@ -1,63 +1,53 @@
1
1
  # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2014-12-16 11:52:50 -0500 using RuboCop version 0.28.0.
2
+ # on 2015-06-04 09:15:17 -0400 using RuboCop version 0.31.0.
3
3
  # The point is for the user to remove these configuration records
4
4
  # one by one as the offenses are removed from the code base.
5
5
  # Note that changes in the inspected code, or installation of new
6
6
  # versions of RuboCop, may require this file to be generated again.
7
7
 
8
- # Offense count: 29
9
- # Cop supports --auto-correct.
10
- Lint/UnusedBlockArgument:
11
- Enabled: false
12
-
13
- # Offense count: 26
14
- # Cop supports --auto-correct.
15
- Lint/UnusedMethodArgument:
16
- Enabled: false
17
-
18
- # Offense count: 35
8
+ # Offense count: 37
19
9
  Metrics/AbcSize:
20
- Max: 50
10
+ Max: 48
21
11
 
22
- # Offense count: 1
12
+ # Offense count: 2
23
13
  Metrics/BlockNesting:
24
14
  Max: 4
25
15
 
26
16
  # Offense count: 4
27
17
  # Configuration parameters: CountComments.
28
18
  Metrics/ClassLength:
29
- Max: 243
19
+ Max: 246
30
20
 
31
- # Offense count: 15
21
+ # Offense count: 23
32
22
  Metrics/CyclomaticComplexity:
33
- Max: 19
23
+ Max: 20
34
24
 
35
- # Offense count: 545
25
+ # Offense count: 675
36
26
  # Configuration parameters: AllowURI, URISchemes.
37
27
  Metrics/LineLength:
38
28
  Max: 198
39
29
 
40
- # Offense count: 42
30
+ # Offense count: 44
41
31
  # Configuration parameters: CountComments.
42
32
  Metrics/MethodLength:
43
33
  Max: 35
44
34
 
45
- # Offense count: 13
35
+ # Offense count: 8
36
+ # Configuration parameters: CountComments.
37
+ Metrics/ModuleLength:
38
+ Max: 243
39
+
40
+ # Offense count: 17
46
41
  Metrics/PerceivedComplexity:
47
- Max: 21
42
+ Max: 22
48
43
 
49
44
  # Offense count: 26
50
45
  # Cop supports --auto-correct.
51
- Style/Blocks:
46
+ # Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
47
+ Style/BlockDelimiters:
52
48
  Enabled: false
53
49
 
54
- # Offense count: 6
55
- # Cop supports --auto-correct.
56
- # Configuration parameters: EnforcedStyle, SupportedStyles.
57
- Style/ClassCheck:
58
- Enabled: false
59
-
60
- # Offense count: 157
50
+ # Offense count: 174
61
51
  Style/Documentation:
62
52
  Enabled: false
63
53
 
@@ -69,39 +59,18 @@ Style/DoubleNegation:
69
59
  Style/EachWithObject:
70
60
  Enabled: false
71
61
 
72
- # Offense count: 1
73
- Style/EmptyElse:
74
- Enabled: false
75
-
76
- # Offense count: 14
62
+ # Offense count: 15
77
63
  # Configuration parameters: MinBodyLength.
78
64
  Style/GuardClause:
79
65
  Enabled: false
80
66
 
81
- # Offense count: 3
82
- # Cop supports --auto-correct.
83
- # Configuration parameters: EnforcedStyle, SupportedStyles.
84
- Style/HashSyntax:
85
- Enabled: false
86
-
87
- # Offense count: 15
67
+ # Offense count: 4
88
68
  # Cop supports --auto-correct.
89
- Style/IndentArray:
90
- Enabled: false
91
-
92
- # Offense count: 18
93
69
  Style/Lambda:
94
70
  Enabled: false
95
71
 
96
- # Offense count: 1
97
- # Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
98
- Style/Next:
99
- Enabled: false
100
-
101
72
  # Offense count: 3
102
- # Cop supports --auto-correct.
103
- # Configuration parameters: PreferredDelimiters.
104
- Style/PercentLiteralDelimiters:
73
+ Style/MultilineTernaryOperator:
105
74
  Enabled: false
106
75
 
107
76
  # Offense count: 3
@@ -109,33 +78,7 @@ Style/PercentLiteralDelimiters:
109
78
  Style/PredicateName:
110
79
  Enabled: false
111
80
 
112
- # Offense count: 9
81
+ # Offense count: 13
113
82
  # Configuration parameters: EnforcedStyle, SupportedStyles.
114
83
  Style/RaiseArgs:
115
84
  Enabled: false
116
-
117
- # Offense count: 4
118
- # Configuration parameters: MaxSlashes.
119
- Style/RegexpLiteral:
120
- Enabled: false
121
-
122
- # Offense count: 4
123
- # Cop supports --auto-correct.
124
- # Configuration parameters: EnforcedStyle, SupportedStyles.
125
- Style/SpaceBeforeBlockBraces:
126
- Enabled: false
127
-
128
- # Offense count: 12
129
- # Cop supports --auto-correct.
130
- Style/SpaceBeforeSemicolon:
131
- Enabled: false
132
-
133
- # Offense count: 1
134
- # Cop supports --auto-correct.
135
- Style/SpecialGlobalVars:
136
- Enabled: false
137
-
138
- # Offense count: 2
139
- # Cop supports --auto-correct.
140
- Style/UnneededPercentQ:
141
- Enabled: false
data/.travis.yml CHANGED
@@ -1,9 +1,9 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
+ - 2.2
4
5
  - 2.1
5
6
  - 2.0.0
6
- - 1.9.3
7
7
  - rbx-2.2.10
8
8
  - jruby-19mode
9
9
  - ruby-head
data/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
1
+ 0.12.0 (6/18/2015)
2
+ ==================
3
+
4
+ #### Features
5
+
6
+ * [#995](https://github.com/intridea/grape/issues/995): Added support for coercion to Set or Set[Other] - [@jordansexton](https://github.com/jordansexton) [@u2](https://github.com/u2).
7
+ * [#980](https://github.com/intridea/grape/issues/980): Grape is now eager-loaded - [@u2](https://github.com/u2).
8
+ * [#956](https://github.com/intridea/grape/issues/956): Support `present` with `Grape::Presenters::Presenter` - [@u2](https://github.com/u2).
9
+ * [#974](https://github.com/intridea/grape/pull/974): Added `error!` to `rescue_from` blocks - [@whatasunnyday](https://github.com/whatasunnyday).
10
+ * [#950](https://github.com/intridea/grape/pull/950): Status method can now accept one of Rack::Utils status code symbols (:ok, :found, :bad_request, etc.) - [@dabrorius](https://github.com/dabrorius).
11
+ * [#952](https://github.com/intridea/grape/pull/952): Status method now raises error when called with invalid status code - [@dabrorius](https://github.com/dabrorius).
12
+ * [#957](https://github.com/intridea/grape/pull/957): Regexp validator now supports `allow_blank`, `nil` value behavior changed - [@calfzhou](https://giihub.com/calfzhou).
13
+ * [#962](https://github.com/intridea/grape/pull/962): The `default` attribute with `false` value is documented now - [@ajvondrak](https://github.com/ajvondrak).
14
+ * [#1026](https://github.com/intridea/grape/pull/1026): Added `file` method, explicitly setting a file-like response object - [@dblock](https://github.com/dblock).
15
+
16
+ #### Fixes
17
+
18
+ * [#994](https://github.com/intridea/grape/pull/994): Fixed optional Array params default to Hash - [@u2](https://github.com/u2).
19
+ * [#988](https://github.com/intridea/grape/pull/988): Fixed duplicate identical endpoints - [@u2](https://github.com/u2).
20
+ * [#936](https://github.com/intridea/grape/pull/936): Fixed default params processing for optional groups - [@dm1try](https://github.com/dm1try).
21
+ * [#942](https://github.com/intridea/grape/pull/942): Fixed forced presence for optional params when based on a reused entity that was also required in another context - [@croeck](https://github.com/croeck).
22
+ * [#1001](https://github.com/intridea/grape/pull/1001): Fixed calling endpoint with specified format with format in its path - [@hodak](https://github.com/hodak).
23
+ * [#1005](https://github.com/intridea/grape/pull/1005): Fixed the Grape::Middleware::Globals - [@urkle](https://github.com/urkle).
24
+ * [#1012](https://github.com/intridea/grape/pull/1012): Fixed `allow_blank: false` with a Boolean value of `false` - [@mfunaro](https://github.com/mfunaro).
25
+ * [#1023](https://github.com/intridea/grape/issues/1023): Fixes unexpected beahvior with `present` and an object that responds to `merge` but isn't a Hash - [@dblock](https://github.com/dblock).
26
+ * [#1017](https://github.com/intridea/grape/pull/1017): Fixed `undefined method stringify_keys` with nested mutual exclusive params - [@quickpay](https://github.com/quickpay).
27
+
1
28
  0.11.0 (2/23/2015)
2
29
  ==================
3
30
 
data/Gemfile CHANGED
@@ -3,7 +3,7 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :development, :test do
6
- gem 'rubocop', '~> 0.28.0'
6
+ gem 'rubocop', '~> 0.31.0'
7
7
  gem 'guard'
8
8
  gem 'guard-rspec'
9
9
  gem 'guard-rubocop'
data/Guardfile CHANGED
@@ -5,6 +5,6 @@ guard :rspec, all_on_start: true, cmd: 'bundle exec rspec' do
5
5
  end
6
6
 
7
7
  guard :rubocop do
8
- watch(%r{.+\.rb$})
8
+ watch(/.+\.rb$/)
9
9
  watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
10
10
  end
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Michael Bleigh and Intridea, Inc.
1
+ Copyright (c) 2010-2015 Michael Bleigh and Intridea, Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -90,7 +90,8 @@ content negotiation, versioning and much more.
90
90
 
91
91
  ## Stable Release
92
92
 
93
- You're reading the documentation for the stable release of Grape, [0.11.0](https://github.com/intridea/grape/blob/v0.11.0/README.md).
93
+ You're reading the documentation for the stable release of Grape 0.12.0.
94
+ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
94
95
 
95
96
  ## Project Resources
96
97
 
@@ -272,11 +273,11 @@ Modify `config/routes`:
272
273
  mount Twitter::API => '/'
273
274
  ```
274
275
 
275
- Additionally, if the version of your Rails is 4.0+ and the application uses the default model layer of ActiveRecord, you will want to use the `hashie_rails` [gem](https://rubygems.org/gems/hashie_rails). This gem disables the security feature of `strong_params` at the model layer, allowing you the use of Grape's own params validation instead.
276
+ Additionally, if the version of your Rails is 4.0+ and the application uses the default model layer of ActiveRecord, you will want to use the [hashie-forbidden_attributes gem](https://github.com/Maxim-Filimonov/hashie-forbidden_attributes). This gem disables the security feature of `strong_params` at the model layer, allowing you the use of Grape's own params validation instead.
276
277
 
277
278
  ```ruby
278
279
  # Gemfile
279
- gem "hashie_rails"
280
+ gem "hashie-forbidden_attributes"
280
281
  ```
281
282
 
282
283
  See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
@@ -314,7 +315,7 @@ version 'v1', using: :path
314
315
 
315
316
  Using this versioning strategy, clients should pass the desired version in the URL.
316
317
 
317
- curl -H http://localhost:9292/v1/statuses/public_timeline
318
+ curl http://localhost:9292/v1/statuses/public_timeline
318
319
 
319
320
  ### Header
320
321
 
@@ -350,6 +351,14 @@ post do
350
351
  end
351
352
  ```
352
353
 
354
+ You can also use one of status codes symbols that are provided by [Rack utils](http://www.rubydoc.info/github/rack/rack/Rack/Utils#HTTP_STATUS_CODES-constant)
355
+
356
+ ```ruby
357
+ post do
358
+ status :no_content
359
+ end
360
+ ```
361
+
353
362
  ### Accept-Version Header
354
363
 
355
364
  ```ruby
@@ -374,7 +383,7 @@ version 'v1', using: :param
374
383
  Using this versioning strategy, clients should pass the desired version as a request parameter,
375
384
  either in the URL query string or in the request body.
376
385
 
377
- curl -H http://localhost:9292/statuses/public_timeline?apiver=v1
386
+ curl http://localhost:9292/statuses/public_timeline?apiver=v1
378
387
 
379
388
  The default name for the query parameter is 'apiver' but can be specified using the `:parameter` option.
380
389
 
@@ -382,7 +391,7 @@ The default name for the query parameter is 'apiver' but can be specified using
382
391
  version 'v1', using: :param, parameter: "v"
383
392
  ```
384
393
 
385
- curl -H http://localhost:9292/statuses/public_timeline?v=v1
394
+ curl http://localhost:9292/statuses/public_timeline?v=v1
386
395
 
387
396
 
388
397
  ## Describing Methods
@@ -451,7 +460,7 @@ Multipart POSTs and PUTs are supported as well.
451
460
  The request:
452
461
 
453
462
  ```
454
- curl --form image_file=@image.jpg http://localhost:9292/upload
463
+ curl --form image_file='@image.jpg;type=image/jpg' http://localhost:9292/upload
455
464
  ```
456
465
 
457
466
  The Grape endpoint:
@@ -810,7 +819,7 @@ end
810
819
  #### `regexp`
811
820
 
812
821
  Parameters can be restricted to match a specific regular expression with the `:regexp` option. If the value
813
- is nil or does not match the regular expression an error will be returned. Note that this is true for both `requires`
822
+ does not match the regular expression an error will be returned. Note that this is true for both `requires`
814
823
  and `optional` parameters.
815
824
 
816
825
  ```ruby
@@ -819,6 +828,13 @@ params do
819
828
  end
820
829
  ```
821
830
 
831
+ The validator will pass if the parameter was sent without value. To ensure that the parameter contains a value, use `allow_blank: false`.
832
+
833
+ ```ruby
834
+ params do
835
+ requires :email, allow_blank: false, regexp: /.+@.+/
836
+ end
837
+ ```
822
838
 
823
839
  #### `mutually_exclusive`
824
840
 
@@ -963,7 +979,7 @@ end
963
979
  class AlphaNumeric < Grape::Validations::Base
964
980
  def validate_param!(attr_name, params)
965
981
  unless params[attr_name] =~ /^[[:alnum:]]+$/
966
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must consist of alpha-numeric characters"
982
+ fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must consist of alpha-numeric characters"
967
983
  end
968
984
  end
969
985
  end
@@ -981,7 +997,7 @@ You can also create custom classes that take parameters.
981
997
  class Length < Grape::Validations::Base
982
998
  def validate_param!(attr_name, params)
983
999
  unless params[attr_name].length <= @option
984
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
1000
+ fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
985
1001
  end
986
1002
  end
987
1003
  end
@@ -1014,7 +1030,7 @@ You can rescue a `Grape::Exceptions::ValidationErrors` and respond with a custom
1014
1030
  ```ruby
1015
1031
  format :json
1016
1032
  subject.rescue_from Grape::Exceptions::ValidationErrors do |e|
1017
- rack_response e.to_json, 400
1033
+ error! e, 400
1018
1034
  end
1019
1035
  ```
1020
1036
 
@@ -1425,17 +1441,29 @@ class Twitter::API < Grape::API
1425
1441
  end
1426
1442
  ```
1427
1443
 
1428
- You can rescue all exceptions with a code block. The `error_response` wrapper
1444
+ You can rescue all exceptions with a code block. The `error!` wrapper
1429
1445
  automatically sets the default error code and content-type.
1430
1446
 
1431
1447
  ```ruby
1432
1448
  class Twitter::API < Grape::API
1433
1449
  rescue_from :all do |e|
1434
- error_response({ message: "rescued from #{e.class.name}" })
1450
+ error!("rescued from #{e.class.name}")
1435
1451
  end
1436
1452
  end
1437
1453
  ```
1438
1454
 
1455
+ Optionally, you can set the format, status code and headers.
1456
+
1457
+ ```ruby
1458
+ class Twitter::API < Grape::API
1459
+ format :json
1460
+ rescue_from :all do |e|
1461
+ error!({ error: "Server error.", 500, { 'Content-Type' => 'text/error' } })
1462
+ end
1463
+ end
1464
+ ```
1465
+
1466
+
1439
1467
  You can also rescue specific exceptions with a code block and handle the Rack
1440
1468
  response at the lowest level.
1441
1469
 
@@ -1452,10 +1480,11 @@ Or rescue specific exceptions.
1452
1480
  ```ruby
1453
1481
  class Twitter::API < Grape::API
1454
1482
  rescue_from ArgumentError do |e|
1455
- Rack::Response.new([ "ArgumentError: #{e.message}" ], 500).finish
1483
+ error!("ArgumentError: #{e.message}")
1456
1484
  end
1485
+
1457
1486
  rescue_from NotImplementedError do |e|
1458
- Rack::Response.new([ "NotImplementedError: #{e.message}" ], 500).finish
1487
+ error!("NotImplementedError: #{e.message}")
1459
1488
  end
1460
1489
  end
1461
1490
  ```
@@ -1475,10 +1504,10 @@ Then the following `rescue_from` clause will rescue exceptions of type `APIError
1475
1504
 
1476
1505
  ```ruby
1477
1506
  rescue_from APIErrors::ParentError do |e|
1478
- Rack::Response.new({
1507
+ error!({
1479
1508
  error: "#{e.class} error",
1480
1509
  message: e.message
1481
- }.to_json, e.status).finish
1510
+ }, e.status)
1482
1511
  end
1483
1512
  ```
1484
1513
 
@@ -1487,11 +1516,11 @@ The code below will rescue exceptions of type `RuntimeError` but _not_ its subcl
1487
1516
 
1488
1517
  ```ruby
1489
1518
  rescue_from RuntimeError, rescue_subclasses: false do |e|
1490
- Rack::Response.new({
1519
+ error!({
1491
1520
  status: e.status,
1492
1521
  message: e.message,
1493
1522
  errors: e.errors
1494
- }.to_json, e.status).finish
1523
+ }, e.status)
1495
1524
  end
1496
1525
  ```
1497
1526
 
@@ -1559,6 +1588,8 @@ class API < Grape::API
1559
1588
  end
1560
1589
  ```
1561
1590
 
1591
+ For similar to Rails request logging try the [grape_logging](https://github.com/aserafin/grape_logging) gem.
1592
+
1562
1593
  ## API Formats
1563
1594
 
1564
1595
  Your API can declare which content-types to support by using `content_type`. If you do not specify any, Grape will support
@@ -1648,7 +1679,7 @@ end
1648
1679
  ```
1649
1680
 
1650
1681
  You can have your API only respond to a single format with `format`. If you use this, the API will **not** respond to file
1651
- extensions. For example, consider the following API.
1682
+ extensions other than specified in `format`. For example, consider the following API.
1652
1683
 
1653
1684
  ```ruby
1654
1685
  class SingleFormatAPI < Grape::API
@@ -1661,7 +1692,8 @@ end
1661
1692
  ```
1662
1693
 
1663
1694
  * `GET /hello` will respond with JSON.
1664
- * `GET /hello.xml`, `GET /hello.json`, `GET /hello.foobar`, or *any* other extension will respond with an HTTP 404 error code.
1695
+ * `GET /hello.json` will respond with JSON.
1696
+ * `GET /hello.xml`, `GET /hello.foobar`, or *any* other extension will respond with an HTTP 404 error code.
1665
1697
  * `GET /hello?format=xml` will respond with an HTTP 406 error code, because the XML format specified by the request parameter
1666
1698
  is not supported.
1667
1699
  * `GET /hello` with an `Accept: application/xml` header will still respond with JSON, since it could not negotiate a
@@ -1922,6 +1954,31 @@ Grape will automatically detect that there is a `Status::Entity` class and use t
1922
1954
  representative entity. This can still be overridden by using the `:with` option or an explicit
1923
1955
  `represents` call.
1924
1956
 
1957
+ You can present `hash` with `Grape::Presenters::Presenter` to keep things consistent.
1958
+
1959
+ ```ruby
1960
+ get '/users' do
1961
+ present { id: 10, name: :dgz }, with: Grape::Presenters::Presenter
1962
+ end
1963
+ ````
1964
+ The response will be
1965
+
1966
+ ```ruby
1967
+ {
1968
+ id: 10,
1969
+ name: 'dgz'
1970
+ }
1971
+ ```
1972
+
1973
+ It has the same result with
1974
+
1975
+ ```ruby
1976
+ get '/users' do
1977
+ present :id, 10
1978
+ present :name, :dgz
1979
+ end
1980
+ ```
1981
+
1925
1982
  ### Hypermedia and Roar
1926
1983
 
1927
1984
  You can use [Roar](https://github.com/apotonick/roar) to render HAL or Collection+JSON with the help of [grape-roar](https://github.com/dblock/grape-roar), which defines a custom JSON formatter and enables presenting entities with Grape's `present` keyword.
@@ -1951,7 +2008,7 @@ class API < Grape::API
1951
2008
  end
1952
2009
  ```
1953
2010
 
1954
- You can also set the response body explicitly with `body`.
2011
+ You can set the response body explicitly with `body`.
1955
2012
 
1956
2013
  ```ruby
1957
2014
  class API < Grape::API
@@ -1965,6 +2022,28 @@ end
1965
2022
 
1966
2023
  Use `body false` to return `204 No Content` without any data or content-type.
1967
2024
 
2025
+ You can also set the response to a file-like object with `file`.
2026
+
2027
+ ```ruby
2028
+ class FileStreamer
2029
+ def initialize(file_path)
2030
+ @file_path = file_path
2031
+ end
2032
+
2033
+ def each(&blk)
2034
+ File.open(@file_path, 'rb') do |file|
2035
+ file.each(10, &blk)
2036
+ end
2037
+ end
2038
+ end
2039
+
2040
+ class API < Grape::API
2041
+ get '/' do
2042
+ file FileStreamer.new('file.bin')
2043
+ end
2044
+ end
2045
+ ```
2046
+
1968
2047
  ## Authentication
1969
2048
 
1970
2049
  ### Basic and Digest Auth
@@ -2253,14 +2332,14 @@ end
2253
2332
 
2254
2333
  ## Writing Tests
2255
2334
 
2256
- You can test a Grape API with RSpec by making HTTP requests and examining the response.
2257
-
2258
2335
  ### Writing Tests with Rack
2259
2336
 
2260
2337
  Use `rack-test` and define your API as `app`.
2261
2338
 
2262
2339
  #### RSpec
2263
2340
 
2341
+ You can test a Grape API with RSpec by making HTTP requests and examining the response.
2342
+
2264
2343
  ```ruby
2265
2344
  require 'spec_helper'
2266
2345
 
@@ -2290,12 +2369,34 @@ describe Twitter::API do
2290
2369
  end
2291
2370
  ```
2292
2371
 
2372
+ #### Airborne
2373
+
2374
+ You can test with other RSpec-based frameworks, including [Airborne](https://github.com/brooklynDev/airborne), which uses `rack-test` to make requests.
2375
+
2376
+ ```ruby
2377
+ require 'airborne'
2378
+
2379
+ Airborne.configure do |config|
2380
+ config.rack_app = Twitter::API
2381
+ end
2382
+
2383
+ describe Twitter::API do
2384
+ describe "GET /api/statuses/:id" do
2385
+ it "returns a status by id" do
2386
+ status = Status.create!
2387
+ get "/api/statuses/#{status.id}"
2388
+ expect_json(status.as_json)
2389
+ end
2390
+ end
2391
+ end
2392
+ ```
2393
+
2293
2394
  #### MiniTest
2294
2395
 
2295
2396
  ```ruby
2296
2397
  require "test_helper"
2297
2398
 
2298
- class Twitter::APITest < MiniTest::Unit::TestCase
2399
+ class Twitter::APITest < MiniTest::Test
2299
2400
  include Rack::Test::Methods
2300
2401
 
2301
2402
  def app
@@ -2305,13 +2406,13 @@ class Twitter::APITest < MiniTest::Unit::TestCase
2305
2406
  def test_get_api_statuses_public_timeline_returns_an_empty_array_of_statuses
2306
2407
  get "/api/statuses/public_timeline"
2307
2408
  assert last_response.ok?
2308
- assert_equal JSON.parse(last_response.body), []
2409
+ assert_equal [], JSON.parse(last_response.body)
2309
2410
  end
2310
2411
 
2311
2412
  def test_get_api_statuses_id_returns_a_status_by_id
2312
2413
  status = Status.create!
2313
2414
  get "/api/statuses/#{status.id}"
2314
- assert_equal last_response.body, status.to_json
2415
+ assert_equal status.to_json, last_response.body
2315
2416
  end
2316
2417
  end
2317
2418
  ```
@@ -2361,13 +2462,13 @@ class Twitter::APITest < ActiveSupport::TestCase
2361
2462
  test "GET /api/statuses/public_timeline returns an empty array of statuses" do
2362
2463
  get "/api/statuses/public_timeline"
2363
2464
  assert last_response.ok?
2364
- assert_equal JSON.parse(last_response.body), []
2465
+ assert_equal [], JSON.parse(last_response.body)
2365
2466
  end
2366
2467
 
2367
2468
  test "GET /api/statuses/:id returns a status by id" do
2368
2469
  status = Status.create!
2369
2470
  get "/api/statuses/#{status.id}"
2370
- assert_equal last_response.body, status.to_json
2471
+ assert_equal status.to_json, last_response.body
2371
2472
  end
2372
2473
  end
2373
2474
  ```
@@ -2457,4 +2558,4 @@ MIT License. See LICENSE for details.
2457
2558
 
2458
2559
  ## Copyright
2459
2560
 
2460
- Copyright (c) 2010-2013 Michael Bleigh, and Intridea, Inc.
2561
+ Copyright (c) 2010-2015 Michael Bleigh, and Intridea, Inc.