grape 1.3.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -0
  3. data/LICENSE +1 -1
  4. data/README.md +104 -21
  5. data/UPGRADING.md +243 -39
  6. data/lib/grape.rb +4 -5
  7. data/lib/grape/api.rb +4 -4
  8. data/lib/grape/api/instance.rb +32 -31
  9. data/lib/grape/content_types.rb +34 -0
  10. data/lib/grape/dsl/helpers.rb +2 -1
  11. data/lib/grape/dsl/inside_route.rb +76 -42
  12. data/lib/grape/dsl/parameters.rb +4 -4
  13. data/lib/grape/dsl/routing.rb +8 -8
  14. data/lib/grape/dsl/validations.rb +18 -1
  15. data/lib/grape/eager_load.rb +1 -1
  16. data/lib/grape/endpoint.rb +8 -6
  17. data/lib/grape/exceptions/base.rb +0 -4
  18. data/lib/grape/exceptions/validation_errors.rb +11 -12
  19. data/lib/grape/http/headers.rb +26 -0
  20. data/lib/grape/middleware/base.rb +3 -4
  21. data/lib/grape/middleware/error.rb +10 -12
  22. data/lib/grape/middleware/formatter.rb +3 -3
  23. data/lib/grape/middleware/stack.rb +19 -5
  24. data/lib/grape/middleware/versioner/header.rb +4 -4
  25. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
  26. data/lib/grape/middleware/versioner/path.rb +1 -1
  27. data/lib/grape/namespace.rb +12 -2
  28. data/lib/grape/path.rb +13 -3
  29. data/lib/grape/request.rb +13 -8
  30. data/lib/grape/router.rb +26 -30
  31. data/lib/grape/router/attribute_translator.rb +25 -4
  32. data/lib/grape/router/pattern.rb +17 -16
  33. data/lib/grape/router/route.rb +5 -24
  34. data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
  35. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
  36. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
  37. data/lib/grape/util/base_inheritable.rb +15 -8
  38. data/lib/grape/util/cache.rb +20 -0
  39. data/lib/grape/util/lazy_object.rb +43 -0
  40. data/lib/grape/util/lazy_value.rb +1 -0
  41. data/lib/grape/util/reverse_stackable_values.rb +2 -0
  42. data/lib/grape/util/stackable_values.rb +7 -20
  43. data/lib/grape/validations/params_scope.rb +6 -5
  44. data/lib/grape/validations/types.rb +6 -5
  45. data/lib/grape/validations/types/array_coercer.rb +14 -5
  46. data/lib/grape/validations/types/build_coercer.rb +5 -8
  47. data/lib/grape/validations/types/custom_type_coercer.rb +14 -2
  48. data/lib/grape/validations/types/dry_type_coercer.rb +36 -1
  49. data/lib/grape/validations/types/file.rb +15 -12
  50. data/lib/grape/validations/types/json.rb +40 -36
  51. data/lib/grape/validations/types/primitive_coercer.rb +15 -6
  52. data/lib/grape/validations/types/set_coercer.rb +6 -4
  53. data/lib/grape/validations/types/variant_collection_coercer.rb +1 -1
  54. data/lib/grape/validations/validators/as.rb +1 -1
  55. data/lib/grape/validations/validators/base.rb +2 -4
  56. data/lib/grape/validations/validators/coerce.rb +4 -11
  57. data/lib/grape/validations/validators/default.rb +3 -5
  58. data/lib/grape/validations/validators/exactly_one_of.rb +4 -2
  59. data/lib/grape/validations/validators/except_values.rb +1 -1
  60. data/lib/grape/validations/validators/regexp.rb +1 -1
  61. data/lib/grape/validations/validators/values.rb +1 -1
  62. data/lib/grape/version.rb +1 -1
  63. data/spec/grape/api/instance_spec.rb +50 -0
  64. data/spec/grape/api_spec.rb +82 -6
  65. data/spec/grape/dsl/inside_route_spec.rb +182 -33
  66. data/spec/grape/endpoint/declared_spec.rb +590 -0
  67. data/spec/grape/endpoint_spec.rb +0 -521
  68. data/spec/grape/entity_spec.rb +6 -0
  69. data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
  70. data/spec/grape/integration/rack_sendfile_spec.rb +12 -8
  71. data/spec/grape/middleware/auth/strategies_spec.rb +1 -1
  72. data/spec/grape/middleware/error_spec.rb +1 -1
  73. data/spec/grape/middleware/formatter_spec.rb +3 -3
  74. data/spec/grape/middleware/stack_spec.rb +12 -1
  75. data/spec/grape/path_spec.rb +4 -4
  76. data/spec/grape/validations/instance_behaivour_spec.rb +1 -1
  77. data/spec/grape/validations/params_scope_spec.rb +26 -0
  78. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  79. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  80. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  81. data/spec/grape/validations/types_spec.rb +1 -1
  82. data/spec/grape/validations/validators/coerce_spec.rb +329 -77
  83. data/spec/grape/validations/validators/default_spec.rb +170 -0
  84. data/spec/grape/validations/validators/exactly_one_of_spec.rb +12 -12
  85. data/spec/grape/validations/validators/except_values_spec.rb +1 -0
  86. data/spec/grape/validations/validators/values_spec.rb +1 -1
  87. data/spec/grape/validations_spec.rb +30 -30
  88. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  89. data/spec/spec_helper.rb +3 -10
  90. data/spec/support/chunks.rb +14 -0
  91. data/spec/support/eager_load.rb +19 -0
  92. data/spec/support/versioned_helpers.rb +3 -5
  93. metadata +121 -105
  94. data/lib/grape/util/content_types.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 826e1365751c85c3f1f7d433e842a76c8a7f5dc6c4a1b13128679e4d56b2ccf5
4
- data.tar.gz: af8aef528c40764a53a3e397c306835745943a1d376ab3738bf7aa2547ef75d1
3
+ metadata.gz: f34ddc6f9f11346067b19a6d7f8e333e433a14c1866e68714815a9e8a457487a
4
+ data.tar.gz: c95b6956bcb2e5f2e61035ec4af7ee3dcaaa53f7743bde69e7363c9a24edc317
5
5
  SHA512:
6
- metadata.gz: 0af9a855214f1fbacd92403111aa1f4761b7257adf6ca9dbd6ab4e1cb889469b76a489971d058a58e98d3bf6906dacb67eb8a5eae11aad16782f318b3453988b
7
- data.tar.gz: 25004acd2431ab7cd174466e82ca5240b888ca045036227909ed9c8c886cd8706f1d3b696f4b1ede8eaa69c68c156fe064557afea8cdf53c9d39b136db7ae4b1
6
+ metadata.gz: fef19eceff9814554a70cc0fa04d24e2cdbbba91af8f663a07a3278694c4cda2664b521d5390a57f8f1ccf7dc41dff85d602423d47aa27c87ca7d73b9d6e64ba
7
+ data.tar.gz: f1b2f404754216ba613e0250a6788ce9bbd02bd0560f2981c1847ca917398ede0f429ae03cdaf31bd3447e20248310f4ba7837511fe811ceb4ddee3b7c59df23
@@ -1,3 +1,93 @@
1
+ ### 1.5.0 (2020/10/05)
2
+
3
+ #### Fixes
4
+
5
+ * [#2104](https://github.com/ruby-grape/grape/pull/2104): Fix Ruby 2.7 keyword deprecation warning - [@stanhu](https://github.com/stanhu).
6
+ * [#2103](https://github.com/ruby-grape/grape/pull/2103): Ensure complete declared params structure is present - [@tlconnor](https://github.com/tlconnor).
7
+ * [#2099](https://github.com/ruby-grape/grape/pull/2099): Added truffleruby to Travis-CI - [@gogainda](https://github.com/gogainda).
8
+ * [#2089](https://github.com/ruby-grape/grape/pull/2089): Specify order of mounting Grape with Rack::Cascade in README - [@jonmchan](https://github.com/jonmchan).
9
+ * [#2083](https://github.com/ruby-grape/grape/pull/2083): Set `Cache-Control` header only for streamed responses - [@stanhu](https://github.com/stanhu).
10
+ * [#2092](https://github.com/ruby-grape/grape/pull/2092): Correct an example params in Include Missing doc - [@huyvohcmc](https://github.com/huyvohcmc).
11
+ * [#2091](https://github.com/ruby-grape/grape/pull/2091): Fix ruby 2.7 keyword deprecations - [@dim](https://github.com/dim).
12
+ * [#2097](https://github.com/ruby-grape/grape/pull/2097): Skip to set default value unless `meets_dependency?` - [@wanabe](https://github.com/wanabe).
13
+ * [#2096](https://github.com/ruby-grape/grape/pull/2096): Fix redundant dependency check - [@braktar](https://github.com/braktar).
14
+ * [#2096](https://github.com/ruby-grape/grape/pull/2098): Fix nested coercion - [@braktar](https://github.com/braktar).
15
+ * [#2102](https://github.com/ruby-grape/grape/pull/2102): Fix retaining setup blocks when remounting APIs - [@jylamont](https://github.com/jylamont).
16
+
17
+ ### 1.4.0 (2020/07/10)
18
+
19
+ #### Features
20
+
21
+ * [#1520](https://github.com/ruby-grape/grape/pull/1520): Un-deprecate stream-like objects - [@urkle](https://github.com/urkle).
22
+ * [#2060](https://github.com/ruby-grape/grape/pull/2060): Drop support for Ruby 2.4 - [@dblock](https://github.com/dblock).
23
+ * [#2060](https://github.com/ruby-grape/grape/pull/2060): Upgraded Rubocop to 0.84.0 - [@dblock](https://github.com/dblock).
24
+ * [#2077](https://github.com/ruby-grape/grape/pull/2077): Simplify logic for defining declared params - [@dnesteryuk](https://github.com/dnesteryuk).
25
+ * [#2076](https://github.com/ruby-grape/grape/pull/2076): Make route information available for hooks when the automatically generated endpoints are invoked - [@anakinj](https://github.com/anakinj).
26
+
27
+ #### Fixes
28
+
29
+ * [#2067](https://github.com/ruby-grape/grape/pull/2067): Coerce empty String to `nil` for all primitive types except `String` - [@petekinnecom](https://github.com/petekinnecom).
30
+ * [#2064](https://github.com/ruby-grape/grape/pull/2064): Fix Ruby 2.7 deprecation warning in `Grape::Middleware::Base#initialize` - [@skarger](https://github.com/skarger).
31
+ * [#2072](https://github.com/ruby-grape/grape/pull/2072): Fix `Grape.eager_load!` and `compile!` - [@stanhu](https://github.com/stanhu).
32
+ * [#2084](https://github.com/ruby-grape/grape/pull/2084): Fix memory leak in path normalization - [@fcheung](https://github.com/fcheung).
33
+
34
+ ### 1.3.3 (2020/05/23)
35
+
36
+ #### Features
37
+
38
+ * [#2048](https://github.com/ruby-grape/grape/issues/2034): Grape Enterprise support is now available [via TideLift](https://tidelift.com/subscription/request-a-demo?utm_source=rubygems-grape&utm_medium=referral&utm_campaign=enterprise) - [@dblock](https://github.com/dblock).
39
+ * [#2039](https://github.com/ruby-grape/grape/pull/2039): Travis - update rails versions - [@ericproulx](https://github.com/ericproulx).
40
+ * [#2038](https://github.com/ruby-grape/grape/pull/2038): Travis - update ruby versions - [@ericproulx](https://github.com/ericproulx).
41
+ * [#2050](https://github.com/ruby-grape/grape/pull/2050): Refactor route public_send to AttributeTranslator - [@ericproulx](https://github.com/ericproulx).
42
+
43
+ #### Fixes
44
+
45
+ * [#2049](https://github.com/ruby-grape/grape/pull/2049): Coerce an empty string to nil in case of the bool type - [@dnesteryuk](https://github.com/dnesteryuk).
46
+ * [#2043](https://github.com/ruby-grape/grape/pull/2043): Modify declared for nested array and hash - [@kadotami](https://github.com/kadotami).
47
+ * [#2040](https://github.com/ruby-grape/grape/pull/2040): Fix a regression with Array of type nil - [@ericproulx](https://github.com/ericproulx).
48
+ * [#2054](https://github.com/ruby-grape/grape/pull/2054): Coercing of nested arrays - [@dnesteryuk](https://github.com/dnesteryuk).
49
+ * [#2050](https://github.com/ruby-grape/grape/pull/2053): Fix broken multiple mounts - [@Jack12816](https://github.com/Jack12816).
50
+
51
+ ### 1.3.2 (2020/04/12)
52
+
53
+ #### Features
54
+
55
+ * [#2020](https://github.com/ruby-grape/grape/pull/2020): Reduce array allocation - [@ericproulx](https://github.com/ericproulx).
56
+ * [#2015](https://github.com/ruby-grape/grape/pull/2014): Reduce MatchData allocation - [@ericproulx](https://github.com/ericproulx).
57
+ * [#2014](https://github.com/ruby-grape/grape/pull/2014): Reduce total allocated arrays - [@ericproulx](https://github.com/ericproulx).
58
+ * [#2011](https://github.com/ruby-grape/grape/pull/2011): Reduce total retained regexes - [@ericproulx](https://github.com/ericproulx).
59
+
60
+ #### Fixes
61
+
62
+ * [#2033](https://github.com/ruby-grape/grape/pull/2033): Ensure `Float` params are correctly coerced to `BigDecimal` - [@tlconnor](https://github.com/tlconnor).
63
+ * [#2031](https://github.com/ruby-grape/grape/pull/2031): Fix a regression with an array of a custom type - [@dnesteryuk](https://github.com/dnesteryuk).
64
+ * [#2026](https://github.com/ruby-grape/grape/pull/2026): Fix a regression in `coerce_with` when coercion returns `nil` - [@misdoro](https://github.com/misdoro).
65
+ * [#2025](https://github.com/ruby-grape/grape/pull/2025): Fix Decimal type category - [@kdoya](https://github.com/kdoya).
66
+ * [#2019](https://github.com/ruby-grape/grape/pull/2019): Avoid coercing parameter with multiple types to an empty Array - [@stanhu](https://github.com/stanhu).
67
+
68
+ ### 1.3.1 (2020/03/11)
69
+
70
+ #### Features
71
+
72
+ * [#2005](https://github.com/ruby-grape/grape/pull/2005): Content types registrable - [@ericproulx](https://github.com/ericproulx).
73
+ * [#2003](https://github.com/ruby-grape/grape/pull/2003): Upgraded Rubocop to 0.80.1 - [@ericproulx](https://github.com/ericproulx).
74
+ * [#2002](https://github.com/ruby-grape/grape/pull/2002): Objects allocation optimization (lazy_lookup) - [@ericproulx](https://github.com/ericproulx).
75
+
76
+ #### Fixes
77
+
78
+ * [#2006](https://github.com/ruby-grape/grape/pull/2006): Fix explicit rescue StandardError - [@ericproulx](https://github.com/ericproulx).
79
+ * [#2004](https://github.com/ruby-grape/grape/pull/2004): Rubocop fixes - [@ericproulx](https://github.com/ericproulx).
80
+ * [#1995](https://github.com/ruby-grape/grape/pull/1995): Fix: "undefined instance variables" and "method redefined" warnings - [@nbeyer](https://github.com/nbeyer).
81
+ * [#1994](https://github.com/ruby-grape/grape/pull/1993): Fix typos in README - [@bellmyer](https://github.com/bellmyer).
82
+ * [#1993](https://github.com/ruby-grape/grape/pull/1993): Lazy join allow header - [@ericproulx](https://github.com/ericproulx).
83
+ * [#1987](https://github.com/ruby-grape/grape/pull/1987): Re-add exactly_one_of mutually exclusive error message - [@ZeroInputCtrl](https://github.com/ZeroInputCtrl).
84
+ * [#1977](https://github.com/ruby-grape/grape/pull/1977): Skip validation for a file if it is optional and nil - [@dnesteryuk](https://github.com/dnesteryuk).
85
+ * [#1976](https://github.com/ruby-grape/grape/pull/1976): Ensure classes/modules listed for autoload really exist - [@dnesteryuk](https://github.com/dnesteryuk).
86
+ * [#1971](https://github.com/ruby-grape/grape/pull/1971): Fix BigDecimal coercion - [@FlickStuart](https://github.com/FlickStuart).
87
+ * [#1968](https://github.com/ruby-grape/grape/pull/1968): Fix args forwarding in Grape::Middleware::Stack#merge_with for ruby 2.7.0 - [@dm1try](https://github.com/dm1try).
88
+ * [#1988](https://github.com/ruby-grape/grape/pull/1988): Refactor the full_messages method and stop overriding full_message - [@hosseintoussi](https://github.com/hosseintoussi).
89
+ * [#1956](https://github.com/ruby-grape/grape/pull/1956): Comply with Rack spec, fix `undefined method [] for nil:NilClass` error when upgrading Rack - [@ioquatix](https://github.com/ioquatix).
90
+
1
91
  ### 1.3.0 (2020/01/11)
2
92
 
3
93
  #### Features
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010-2019 Michael Bleigh, Intridea Inc. and Contributors.
1
+ Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.
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
@@ -12,6 +12,7 @@
12
12
  - [What is Grape?](#what-is-grape)
13
13
  - [Stable Release](#stable-release)
14
14
  - [Project Resources](#project-resources)
15
+ - [Grape for Enterprise](#grape-for-enterprise)
15
16
  - [Installation](#installation)
16
17
  - [Basic Usage](#basic-usage)
17
18
  - [Mounting](#mounting)
@@ -141,6 +142,7 @@
141
142
  - [format_response.grape](#format_responsegrape)
142
143
  - [Monitoring Products](#monitoring-products)
143
144
  - [Contributing to Grape](#contributing-to-grape)
145
+ - [Security](#security)
144
146
  - [License](#license)
145
147
  - [Copyright](#copyright)
146
148
 
@@ -154,7 +156,7 @@ content negotiation, versioning and much more.
154
156
 
155
157
  ## Stable Release
156
158
 
157
- You're reading the documentation for the stable release of Grape, **1.3.0**.
159
+ You're reading the documentation for the stable release of Grape, 1.5.0.
158
160
  Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
159
161
 
160
162
  ## Project Resources
@@ -164,6 +166,14 @@ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
164
166
  * Need help? Try [Grape Google Group](http://groups.google.com/group/ruby-grape) or [Gitter](https://gitter.im/ruby-grape/grape)
165
167
  * [Follow us on Twitter](https://twitter.com/grapeframework)
166
168
 
169
+ ## Grape for Enterprise
170
+
171
+ Available as part of the Tidelift Subscription.
172
+
173
+ The maintainers of Grape are working with Tidelift to deliver commercial support and maintenance. Save time, reduce risk, and improve code health, while paying the maintainers of Grape. Click [here](https://tidelift.com/subscription/request-a-demo?utm_source=rubygems-grape&utm_medium=referral&utm_campaign=enterprise) for more details.
174
+
175
+ In 2020, we plan to use the money towards gathering Grape contributors for dinner in New York City.
176
+
167
177
  ## Installation
168
178
 
169
179
  Ruby 2.4 or newer is required.
@@ -339,9 +349,12 @@ class Web < Sinatra::Base
339
349
  end
340
350
 
341
351
  use Rack::Session::Cookie
342
- run Rack::Cascade.new [API, Web]
352
+ run Rack::Cascade.new [Web, API]
343
353
  ```
344
354
 
355
+ Note that order of loading apps using `Rack::Cascade` matters. The grape application must be last if you want to raise custom 404 errors from grape (such as `error!('Not Found',404)`). If the grape application is not last and returns 404 or 405 response, [cascade utilizes that as a signal to try the next app](https://www.rubydoc.info/gems/rack/Rack/Cascade). This may lead to undesirable behavior showing the [wrong 404 page from the wrong app](https://github.com/ruby-grape/grape/issues/1515).
356
+
357
+
345
358
  ### Rails
346
359
 
347
360
  Place API files into `app/api`. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for `Twitter::API` should be `app/api/twitter/api.rb`.
@@ -773,7 +786,12 @@ Available parameter builders are `Grape::Extensions::Hash::ParamBuilder`, `Grape
773
786
 
774
787
  ### Declared
775
788
 
776
- Grape allows you to access only the parameters that have been declared by your `params` block. It filters out the params that have been passed, but are not allowed. Consider the following API endpoint:
789
+ Grape allows you to access only the parameters that have been declared by your `params` block. It will:
790
+
791
+ * Filter out the params that have been passed, but are not allowed.
792
+ * Include any optional params that are declared but not passed.
793
+
794
+ Consider the following API endpoint:
777
795
 
778
796
  ````ruby
779
797
  format :json
@@ -806,9 +824,9 @@ Once we add parameters requirements, grape will start returning only the declare
806
824
  format :json
807
825
 
808
826
  params do
809
- requires :user, type: Hash do
810
- requires :first_name, type: String
811
- requires :last_name, type: String
827
+ optional :user, type: Hash do
828
+ optional :first_name, type: String
829
+ optional :last_name, type: String
812
830
  end
813
831
  end
814
832
 
@@ -836,6 +854,44 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
836
854
  }
837
855
  ````
838
856
 
857
+ Missing params that are declared as type `Hash` or `Array` will be included.
858
+
859
+ ````ruby
860
+ format :json
861
+
862
+ params do
863
+ optional :user, type: Hash do
864
+ optional :first_name, type: String
865
+ optional :last_name, type: String
866
+ end
867
+ optional :widgets, type: Array
868
+ end
869
+
870
+ post 'users/signup' do
871
+ { 'declared_params' => declared(params) }
872
+ end
873
+ ````
874
+
875
+ **Request**
876
+
877
+ ````bash
878
+ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{}'
879
+ ````
880
+
881
+ **Response**
882
+
883
+ ````json
884
+ {
885
+ "declared_params": {
886
+ "user": {
887
+ "first_name": null,
888
+ "last_name": null
889
+ },
890
+ "widgets": []
891
+ }
892
+ }
893
+ ````
894
+
839
895
  The returned hash is an `ActiveSupport::HashWithIndifferentAccess`.
840
896
 
841
897
  The `#declared` method is not available to `before` filters, as those are evaluated prior to parameter coercion.
@@ -894,8 +950,10 @@ By default `declared(params)` includes parameters that have `nil` values. If you
894
950
  format :json
895
951
 
896
952
  params do
897
- requires :first_name, type: String
898
- optional :last_name, type: String
953
+ requires :user, type: Hash do
954
+ requires :first_name, type: String
955
+ optional :last_name, type: String
956
+ end
899
957
  end
900
958
 
901
959
  post 'users/signup' do
@@ -1048,13 +1106,13 @@ params do
1048
1106
  end
1049
1107
  ```
1050
1108
 
1051
- Note that default values will be passed through to any validation options specified.
1052
- The following example will always fail if `:color` is not explicitly provided.
1053
-
1054
1109
  Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
1055
1110
  number for each call to the endpoint of this `params` block. To have the default evaluate
1056
1111
  lazily with each request use a lambda, like `:random_number` above.
1057
1112
 
1113
+ Note that default values will be passed through to any validation options specified.
1114
+ The following example will always fail if `:color` is not explicitly provided.
1115
+
1058
1116
  ```ruby
1059
1117
  params do
1060
1118
  optional :color, type: String, default: 'blue', values: ['red', 'green']
@@ -1722,7 +1780,7 @@ params do
1722
1780
  end
1723
1781
  ```
1724
1782
 
1725
- Every validation will have it's own instance of the validator, which means that the validator can have a state.
1783
+ Every validation will have its own instance of the validator, which means that the validator can have a state.
1726
1784
 
1727
1785
  ### Validation Errors
1728
1786
 
@@ -3157,17 +3215,19 @@ end
3157
3215
 
3158
3216
  Use `body false` to return `204 No Content` without any data or content-type.
3159
3217
 
3160
- You can also set the response to a file with `file`.
3218
+ You can also set the response to a file with `sendfile`. This works with the
3219
+ [Rack::Sendfile](https://www.rubydoc.info/gems/rack/Rack/Sendfile) middleware to optimally send
3220
+ the file through your web server software.
3161
3221
 
3162
3222
  ```ruby
3163
3223
  class API < Grape::API
3164
3224
  get '/' do
3165
- file '/path/to/file'
3225
+ sendfile '/path/to/file'
3166
3226
  end
3167
3227
  end
3168
3228
  ```
3169
3229
 
3170
- If you want a file to be streamed using Rack::Chunked, use `stream`.
3230
+ To stream a file in chunks use `stream`
3171
3231
 
3172
3232
  ```ruby
3173
3233
  class API < Grape::API
@@ -3177,6 +3237,26 @@ class API < Grape::API
3177
3237
  end
3178
3238
  ```
3179
3239
 
3240
+ If you want to stream non-file data use the `stream` method and a `Stream` object.
3241
+ This is an object that responds to `each` and yields for each chunk to send to the client.
3242
+ Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
3243
+
3244
+ ```ruby
3245
+ class MyStream
3246
+ def each
3247
+ yield 'part 1'
3248
+ yield 'part 2'
3249
+ yield 'part 3'
3250
+ end
3251
+ end
3252
+
3253
+ class API < Grape::API
3254
+ get '/' do
3255
+ stream MyStream.new
3256
+ end
3257
+ end
3258
+ ```
3259
+
3180
3260
  ## Authentication
3181
3261
 
3182
3262
  ### Basic and Digest Auth
@@ -3188,14 +3268,13 @@ applies to the current namespace and any children, but not parents.
3188
3268
  ```ruby
3189
3269
  http_basic do |username, password|
3190
3270
  # verify user's password here
3191
- { 'test' => 'password1' }[username] == password
3271
+ # IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
3192
3272
  end
3193
3273
  ```
3194
3274
 
3195
3275
  ```ruby
3196
3276
  http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
3197
3277
  # lookup the user's password here
3198
- { 'user1' => 'password1' }[username]
3199
3278
  end
3200
3279
  ```
3201
3280
 
@@ -3301,7 +3380,7 @@ end
3301
3380
 
3302
3381
  Blocks can be executed before or after every API call, using `before`, `after`,
3303
3382
  `before_validation` and `after_validation`.
3304
- If the API fails the `after` call will not be trigered, if you need code to execute for sure
3383
+ If the API fails the `after` call will not be triggered, if you need code to execute for sure
3305
3384
  use the `finally`.
3306
3385
 
3307
3386
  Before and after callbacks execute in the following order:
@@ -3852,7 +3931,7 @@ Grape integrates with following third-party tools:
3852
3931
  * **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
3853
3932
  * **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
3854
3933
  * **[AppSignal](https://www.appsignal.com)** - [appsignal-ruby](https://github.com/appsignal/appsignal-ruby) gem, [documentation](http://docs.appsignal.com/getting-started/supported-frameworks.html#grape)
3855
- * **[ElasticAPM](https://www.elastic.co/products/apm) - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
3934
+ * **[ElasticAPM](https://www.elastic.co/products/apm)** - [elastic-apm](https://github.com/elastic/apm-agent-ruby) gem, [documentation](https://www.elastic.co/guide/en/apm/agent/ruby/3.x/getting-started-rack.html#getting-started-grape)
3856
3935
 
3857
3936
  ## Contributing to Grape
3858
3937
 
@@ -3861,10 +3940,14 @@ features and discuss issues.
3861
3940
 
3862
3941
  See [CONTRIBUTING](CONTRIBUTING.md).
3863
3942
 
3943
+ ## Security
3944
+
3945
+ See [SECURITY](SECURITY.md) for details.
3946
+
3864
3947
  ## License
3865
3948
 
3866
- MIT License. See LICENSE for details.
3949
+ MIT License. See [LICENSE](LICENSE) for details.
3867
3950
 
3868
3951
  ## Copyright
3869
3952
 
3870
- Copyright (c) 2010-2019 Michael Bleigh, Intridea Inc. and Contributors.
3953
+ Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.
@@ -1,6 +1,170 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 1.5.0
5
+
6
+ Prior to 1.3.3, the `declared` helper would always return the complete params structure if `include_missing=true` was set. In 1.3.3 a regression was introduced such that a missing Hash with or without nested parameters would always resolve to `{}`.
7
+
8
+ In 1.5.0 this behavior is reverted, so the whole params structure will always be available via `declared`, regardless of whether any params are passed.
9
+
10
+ The following rules now apply to the `declared` helper when params are missing and `include_missing=true`:
11
+
12
+ * Hash params with children will resolve to a Hash with keys for each declared child.
13
+ * Hash params with no children will resolve to `{}`.
14
+ * Set params will resolve to `Set.new`.
15
+ * Array params will resolve to `[]`.
16
+ * All other params will resolve to `nil`.
17
+
18
+ #### Example
19
+
20
+ ```ruby
21
+ class Api < Grape::API
22
+ params do
23
+ optional :outer, type: Hash do
24
+ optional :inner, type: Hash do
25
+ optional :value, type: String
26
+ end
27
+ end
28
+ end
29
+ get 'example' do
30
+ declared(params, include_missing: true)
31
+ end
32
+ end
33
+ ```
34
+
35
+ ```
36
+ get '/example'
37
+ # 1.3.3 = {}
38
+ # 1.5.0 = {outer: {inner: {value:null}}}
39
+ ```
40
+
41
+ For more information see [#2103](https://github.com/ruby-grape/grape/pull/2103).
42
+
43
+ ### Upgrading to >= 1.4.0
44
+
45
+ #### Reworking stream and file and un-deprecating stream like-objects
46
+
47
+ Previously in 0.16 stream-like objects were deprecated. This release restores their functionality for use-cases other than file streaming.
48
+
49
+ This release deprecated `file` in favor of `sendfile` to better document its purpose.
50
+
51
+ To deliver a file via the Sendfile support in your web server and have the Rack::Sendfile middleware enabled. See [`Rack::Sendfile`](https://www.rubydoc.info/gems/rack/Rack/Sendfile).
52
+ ```ruby
53
+ class API < Grape::API
54
+ get '/' do
55
+ sendfile '/path/to/file'
56
+ end
57
+ end
58
+ ```
59
+
60
+ Use `stream` to stream file content in chunks.
61
+
62
+ ```ruby
63
+ class API < Grape::API
64
+ get '/' do
65
+ stream '/path/to/file'
66
+ end
67
+ end
68
+ ```
69
+
70
+ Or use `stream` to stream other kinds of content. In the following example a streamer class
71
+ streams paginated data from a database.
72
+
73
+ ```ruby
74
+ class MyObject
75
+ attr_accessor :result
76
+
77
+ def initialize(query)
78
+ @result = query
79
+ end
80
+
81
+ def each
82
+ yield '['
83
+ # Do paginated DB fetches and return each page formatted
84
+ first = false
85
+ result.find_in_batches do |records|
86
+ yield process_records(records, first)
87
+ first = false
88
+ end
89
+ yield ']'
90
+ end
91
+
92
+ def process_records(records, first)
93
+ buffer = +''
94
+ buffer << ',' unless first
95
+ buffer << records.map(&:to_json).join(',')
96
+ buffer
97
+ end
98
+ end
99
+
100
+ class API < Grape::API
101
+ get '/' do
102
+ stream MyObject.new(Sprocket.all)
103
+ end
104
+ end
105
+ ```
106
+
107
+ ### Upgrading to >= 1.3.3
108
+
109
+ #### Nil values for structures
110
+
111
+ Nil values always been a special case when dealing with types especially with the following structures:
112
+
113
+ - Array
114
+ - Hash
115
+ - Set
116
+
117
+ The behavior for these structures has change through out the latest releases. For example:
118
+
119
+ ```ruby
120
+ class Api < Grape::API
121
+ params do
122
+ require :my_param, type: Array[Integer]
123
+ end
124
+
125
+ get 'example' do
126
+ params[:my_param]
127
+ end
128
+ get '/example', params: { my_param: nil }
129
+ # 1.3.1 = []
130
+ # 1.3.2 = nil
131
+ end
132
+ ```
133
+
134
+ For now on, `nil` values stay `nil` values for all types, including arrays, sets and hashes.
135
+
136
+ If you want to have the same behavior as 1.3.1, apply a `default` validator:
137
+
138
+ ```ruby
139
+ class Api < Grape::API
140
+ params do
141
+ require :my_param, type: Array[Integer], default: []
142
+ end
143
+
144
+ get 'example' do
145
+ params[:my_param]
146
+ end
147
+ get '/example', params: { my_param: nil } # => []
148
+ end
149
+ ```
150
+
151
+ #### Default validator
152
+
153
+ Default validator is now applied for `nil` values.
154
+
155
+ ```ruby
156
+ class Api < Grape::API
157
+ params do
158
+ requires :my_param, type: Integer, default: 0
159
+ end
160
+
161
+ get 'example' do
162
+ params[:my_param]
163
+ end
164
+ get '/example', params: { my_param: nil } #=> before: nil, after: 0
165
+ end
166
+ ```
167
+
4
168
  ### Upgrading to >= 1.3.0
5
169
 
6
170
  #### Ruby
@@ -9,38 +173,87 @@ After adding dry-types, Ruby 2.4 or newer is required.
9
173
 
10
174
  #### Coercion
11
175
 
12
- [Virtus](https://github.com/solnic/virtus) has been replaced by [dry-types](https://dry-rb.org/gems/dry-types/1.2/) for parameter coercion. If your project depends on Virtus, explicitly add it to your `Gemfile`. Also, if Virtus is used for defining custom types
176
+ [Virtus](https://github.com/solnic/virtus) has been replaced by [dry-types](https://dry-rb.org/gems/dry-types/1.2/) for parameter coercion. If your project depends on Virtus outside of Grape, explicitly add it to your `Gemfile`.
177
+
178
+ Here's an example of how to migrate a custom type from Virtus to dry-types:
13
179
 
14
180
  ```ruby
15
- class User
16
- include Virtus.model
181
+ # Legacy Grape parser
182
+ class SecureUriType < Virtus::Attribute
183
+ def coerce(input)
184
+ URI.parse value
185
+ end
17
186
 
18
- attribute :id, Integer
19
- attribute :name, String
187
+ def value_coerced?(input)
188
+ value.is_a? String
189
+ end
20
190
  end
21
191
 
22
- # somewhere in your API
23
192
  params do
24
- requires :user, type: User
193
+ requires :secure_uri, type: SecureUri
25
194
  end
26
195
  ```
27
196
 
28
- Add a class-level `parse` method to the model:
197
+ To use dry-types, we need to:
29
198
 
30
- ```ruby
31
- class User
32
- include Virtus.model
199
+ 1. Remove the inheritance of `Virtus::Attribute`
200
+ 1. Rename `coerce` to `self.parse`
201
+ 1. Rename `value_coerced?` to `self.parsed?`
202
+
203
+ The custom type must have a class-level `parse` method to the model. A class-level `parsed?` is needed if the parsed type differs from the defined type. In the example below, since `SecureUri` is not the same as `URI::HTTPS`, `self.parsed?` is needed:
33
204
 
34
- attribute :id, Integer
35
- attribute :name, String
205
+ ```ruby
206
+ # New dry-types parser
207
+ class SecureUri
208
+ def self.parse(value)
209
+ URI.parse value
210
+ end
36
211
 
37
- def self.parse(attrs)
38
- new(attrs)
212
+ def self.parsed?(value)
213
+ value.is_a? URI::HTTPS
39
214
  end
40
215
  end
216
+
217
+ params do
218
+ requires :secure_uri, type: SecureUri
219
+ end
220
+ ```
221
+
222
+ #### Coercing to `FalseClass` or `TrueClass` no longer works
223
+
224
+ Previous Grape versions allowed this, though it wasn't documented:
225
+
226
+ ```ruby
227
+ requires :true_value, type: TrueClass
228
+ requires :bool_value, types: [FalseClass, TrueClass]
229
+ ```
230
+
231
+ This is no longer supported, if you do this, your values will never be valid. Instead you should do this:
232
+
233
+ ```ruby
234
+ requires :true_value, type: Boolean # in your endpoint you should validate if this is actually `true`
235
+ requires :bool_value, type: Boolean
41
236
  ```
42
237
 
43
- Custom types which don't depend on Virtus don't require any changes.
238
+ #### Ensure that Array types have explicit coercions
239
+
240
+ Unlike Virtus, dry-types does not perform any implict coercions. If you have any uses of `Array[String]`, `Array[Integer]`, etc. be sure they use a `coerce_with` block. For example:
241
+
242
+ ```ruby
243
+ requires :values, type: Array[String]
244
+ ```
245
+
246
+ It's quite common to pass a comma-separated list, such as `tag1,tag2` as `values`. Previously Virtus would implicitly coerce this to `Array(values)` so that `["tag1,tag2"]` would pass the type checks, but with `dry-types` the values are no longer coerced for you. To fix this, you might do:
247
+
248
+ ```ruby
249
+ requires :values, type: Array[String], coerce_with: ->(val) { val.split(',').map(&:strip) }
250
+ ```
251
+
252
+ Likewise, for `Array[Integer]`, you might do:
253
+
254
+ ```ruby
255
+ requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(',').map(&:strip).map(&:to_i) }
256
+ ```
44
257
 
45
258
  For more information see [#1920](https://github.com/ruby-grape/grape/pull/1920).
46
259
 
@@ -97,12 +310,9 @@ In order to make obtaining the name of a mounted class simpler, we've delegated
97
310
 
98
311
  ##### Patching the class
99
312
 
100
- In an effort to make APIs re-mountable, The class `Grape::API` no longer refers to an API instance,
101
- rather, what used to be `Grape::API` is now `Grape::API::Instance` and `Grape::API` was replaced
102
- with a class that can contain several instances of `Grape::API`.
313
+ In an effort to make APIs re-mountable, The class `Grape::API` no longer refers to an API instance, rather, what used to be `Grape::API` is now `Grape::API::Instance` and `Grape::API` was replaced with a class that can contain several instances of `Grape::API`.
103
314
 
104
- This changes were done in such a way that no code-changes should be required.
105
- However, if experiencing problems, or relying on private methods and internal behaviour too deeply, it is possible to restore the prior behaviour by replacing the references from `Grape::API` to `Grape::API::Instance`.
315
+ This changes were done in such a way that no code-changes should be required. However, if experiencing problems, or relying on private methods and internal behaviour too deeply, it is possible to restore the prior behaviour by replacing the references from `Grape::API` to `Grape::API::Instance`.
106
316
 
107
317
  Note, this is particularly relevant if you are opening the class `Grape::API` for modification.
108
318
 
@@ -125,15 +335,20 @@ end
125
335
 
126
336
  After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
127
337
  which inherit from `Grape::API::Instance`.
338
+
128
339
  What this means in practice, is:
340
+
129
341
  - Generally: you can access the named class from the instance calling the getter `base`.
130
- - In particular: If you need the `name`, you can use `base`.`name`
342
+ - In particular: If you need the `name`, you can use `base`.`name`.
131
343
 
132
344
  **Deprecated**
345
+
133
346
  ```ruby
134
347
  payload[:endpoint].options[:for].name
135
348
  ```
349
+
136
350
  **New**
351
+
137
352
  ```ruby
138
353
  payload[:endpoint].options[:for].base.name
139
354
  ```
@@ -224,8 +439,7 @@ See [#1610](https://github.com/ruby-grape/grape/pull/1610) for more information.
224
439
 
225
440
  #### The `except`, `except_message`, and `proc` options of the `values` validator are deprecated.
226
441
 
227
- The new `except_values` validator should be used in place of the `except` and `except_message` options of
228
- the `values` validator.
442
+ The new `except_values` validator should be used in place of the `except` and `except_message` options of the `values` validator.
229
443
 
230
444
  Arity one Procs may now be used directly as the `values` option to explicitly test param values.
231
445
 
@@ -301,9 +515,7 @@ get '/example' #=> before: 405, after: 404
301
515
 
302
516
  #### Removed param processing from built-in OPTIONS handler
303
517
 
304
- When a request is made to the built-in `OPTIONS` handler, only the `before` and `after`
305
- callbacks associated with the resource will be run. The `before_validation` and
306
- `after_validation` callbacks and parameter validations will be skipped.
518
+ When a request is made to the built-in `OPTIONS` handler, only the `before` and `after` callbacks associated with the resource will be run. The `before_validation` and `after_validation` callbacks and parameter validations will be skipped.
307
519
 
308
520
  See [#1505](https://github.com/ruby-grape/grape/pull/1505) for more information.
309
521
 
@@ -324,8 +536,7 @@ See [#1510](https://github.com/ruby-grape/grape/pull/1510) for more information.
324
536
 
325
537
  #### The default status code for DELETE is now 204 instead of 200.
326
538
 
327
- Breaking change: Sets the default response status code for a delete request to 204.
328
- A status of 204 makes the response more distinguishable and therefore easier to handle on the client side, particularly because a DELETE request typically returns an empty body as the resource was deleted or voided.
539
+ Breaking change: Sets the default response status code for a delete request to 204. A status of 204 makes the response more distinguishable and therefore easier to handle on the client side, particularly because a DELETE request typically returns an empty body as the resource was deleted or voided.
329
540
 
330
541
  To achieve the old behavior, one has to set it explicitly:
331
542
  ```ruby
@@ -503,18 +714,14 @@ See [#1114](https://github.com/ruby-grape/grape/pull/1114) for more information.
503
714
 
504
715
  #### Bypasses formatters when status code indicates no content
505
716
 
506
- To be consistent with rack and it's handling of standard responses
507
- associated with no content, both default and custom formatters will now
717
+ To be consistent with rack and it's handling of standard responses associated with no content, both default and custom formatters will now
508
718
  be bypassed when processing responses for status codes defined [by rack](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
509
719
 
510
720
  See [#1190](https://github.com/ruby-grape/grape/pull/1190) for more information.
511
721
 
512
722
  #### Redirects respond as plain text with message
513
723
 
514
- `#redirect` now uses `text/plain` regardless of whether that format has
515
- been enabled. This prevents formatters from attempting to serialize the
516
- message body and allows for a descriptive message body to be provided - and
517
- optionally overridden - that better fulfills the theme of the HTTP spec.
724
+ `#redirect` now uses `text/plain` regardless of whether that format has been enabled. This prevents formatters from attempting to serialize the message body and allows for a descriptive message body to be provided - and optionally overridden - that better fulfills the theme of the HTTP spec.
518
725
 
519
726
  See [#1194](https://github.com/ruby-grape/grape/pull/1194) for more information.
520
727
 
@@ -548,10 +755,7 @@ end
548
755
 
549
756
  See [#1029](https://github.com/ruby-grape/grape/pull/1029) for more information.
550
757
 
551
- There is a known issue because of this change. When Grape is used with an older
552
- than 1.2.4 version of [warden](https://github.com/hassox/warden) there may be raised
553
- the following exception having the [rack-mount](https://github.com/jm/rack-mount) gem's
554
- lines as last ones in the backtrace:
758
+ There is a known issue because of this change. When Grape is used with an older than 1.2.4 version of [warden](https://github.com/hassox/warden) there may be raised the following exception having the [rack-mount](https://github.com/jm/rack-mount) gem's lines as last ones in the backtrace:
555
759
 
556
760
  ```
557
761
  NoMethodError: undefined method `[]' for nil:NilClass