grape 1.1.0 → 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +128 -43
- data/LICENSE +1 -1
- data/README.md +394 -47
- data/UPGRADING.md +111 -0
- data/grape.gemspec +3 -1
- data/lib/grape.rb +98 -66
- data/lib/grape/api.rb +136 -175
- data/lib/grape/api/instance.rb +280 -0
- data/lib/grape/config.rb +32 -0
- data/lib/grape/dsl/callbacks.rb +20 -0
- data/lib/grape/dsl/desc.rb +39 -7
- data/lib/grape/dsl/inside_route.rb +12 -6
- data/lib/grape/dsl/middleware.rb +7 -0
- data/lib/grape/dsl/parameters.rb +9 -4
- data/lib/grape/dsl/routing.rb +5 -1
- data/lib/grape/dsl/validations.rb +4 -3
- data/lib/grape/eager_load.rb +18 -0
- data/lib/grape/endpoint.rb +42 -26
- data/lib/grape/error_formatter.rb +1 -1
- data/lib/grape/exceptions/base.rb +9 -1
- data/lib/grape/exceptions/invalid_response.rb +9 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -2
- data/lib/grape/formatter.rb +1 -1
- data/lib/grape/locale/en.yml +2 -0
- data/lib/grape/middleware/auth/base.rb +2 -4
- data/lib/grape/middleware/base.rb +2 -0
- data/lib/grape/middleware/error.rb +9 -4
- data/lib/grape/middleware/helpers.rb +10 -0
- data/lib/grape/middleware/stack.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +4 -4
- data/lib/grape/parser.rb +1 -1
- data/lib/grape/request.rb +1 -1
- data/lib/grape/router/attribute_translator.rb +2 -0
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/util/base_inheritable.rb +34 -0
- data/lib/grape/util/endpoint_configuration.rb +6 -0
- data/lib/grape/util/inheritable_values.rb +5 -25
- data/lib/grape/util/lazy_block.rb +25 -0
- data/lib/grape/util/lazy_value.rb +95 -0
- data/lib/grape/util/reverse_stackable_values.rb +7 -36
- data/lib/grape/util/stackable_values.rb +19 -22
- data/lib/grape/validations/attributes_iterator.rb +5 -3
- data/lib/grape/validations/multiple_attributes_iterator.rb +11 -0
- data/lib/grape/validations/params_scope.rb +20 -14
- data/lib/grape/validations/single_attribute_iterator.rb +13 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
- data/lib/grape/validations/types/file.rb +1 -1
- data/lib/grape/validations/validator_factory.rb +6 -11
- data/lib/grape/validations/validators/all_or_none.rb +6 -13
- data/lib/grape/validations/validators/as.rb +2 -3
- data/lib/grape/validations/validators/at_least_one_of.rb +5 -13
- data/lib/grape/validations/validators/base.rb +11 -10
- data/lib/grape/validations/validators/coerce.rb +4 -0
- data/lib/grape/validations/validators/default.rb +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +6 -23
- data/lib/grape/validations/validators/multiple_params_base.rb +14 -10
- data/lib/grape/validations/validators/mutual_exclusion.rb +6 -18
- data/lib/grape/validations/validators/same_as.rb +23 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/defines_boolean_in_params_spec.rb +37 -0
- data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
- data/spec/grape/api_remount_spec.rb +466 -0
- data/spec/grape/api_spec.rb +379 -1
- data/spec/grape/config_spec.rb +17 -0
- data/spec/grape/dsl/desc_spec.rb +40 -16
- data/spec/grape/dsl/middleware_spec.rb +8 -0
- data/spec/grape/dsl/routing_spec.rb +10 -0
- data/spec/grape/endpoint_spec.rb +40 -4
- data/spec/grape/exceptions/base_spec.rb +65 -0
- data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +6 -4
- data/spec/grape/integration/rack_spec.rb +22 -6
- data/spec/grape/middleware/auth/dsl_spec.rb +3 -3
- data/spec/grape/middleware/base_spec.rb +8 -0
- data/spec/grape/middleware/exception_spec.rb +1 -1
- data/spec/grape/middleware/formatter_spec.rb +15 -5
- data/spec/grape/middleware/versioner/header_spec.rb +6 -0
- data/spec/grape/named_api_spec.rb +19 -0
- data/spec/grape/request_spec.rb +24 -0
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +29 -0
- data/spec/grape/validations/params_scope_spec.rb +184 -8
- data/spec/grape/validations/single_attribute_iterator_spec.rb +33 -0
- data/spec/grape/validations/validators/all_or_none_spec.rb +138 -30
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +173 -29
- data/spec/grape/validations/validators/coerce_spec.rb +10 -2
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +202 -38
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +184 -27
- data/spec/grape/validations/validators/same_as_spec.rb +63 -0
- data/spec/grape/validations_spec.rb +33 -21
- data/spec/spec_helper.rb +4 -1
- metadata +35 -23
- data/Appraisals +0 -32
- data/Dangerfile +0 -2
- data/Gemfile +0 -33
- data/Gemfile.lock +0 -231
- data/Guardfile +0 -10
- data/RELEASING.md +0 -111
- data/Rakefile +0 -25
- data/benchmark/simple.rb +0 -27
- data/benchmark/simple_with_type_coercer.rb +0 -22
- data/gemfiles/multi_json.gemfile +0 -35
- data/gemfiles/multi_xml.gemfile +0 -35
- data/gemfiles/rack_1.5.2.gemfile +0 -35
- data/gemfiles/rack_edge.gemfile +0 -35
- data/gemfiles/rails_3.gemfile +0 -36
- data/gemfiles/rails_4.gemfile +0 -35
- data/gemfiles/rails_5.gemfile +0 -35
- data/gemfiles/rails_edge.gemfile +0 -35
- data/pkg/grape-0.17.0.gem +0 -0
- data/pkg/grape-0.19.0.gem +0 -0
data/UPGRADING.md
CHANGED
@@ -1,6 +1,117 @@
|
|
1
1
|
Upgrading Grape
|
2
2
|
===============
|
3
3
|
|
4
|
+
### Upgrading to >= 1.2.4
|
5
|
+
|
6
|
+
#### Headers in `error!` call
|
7
|
+
|
8
|
+
Headers in `error!` will be merged with `headers` hash. If any header need to be cleared on `error!` call, make sure to move it to the `after` block.
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
class SampleApi < Grape::API
|
12
|
+
before do
|
13
|
+
header 'X-Before-Header', 'before_call'
|
14
|
+
end
|
15
|
+
|
16
|
+
get 'ping' do
|
17
|
+
header 'X-App-Header', 'on_call'
|
18
|
+
error! :pong, 400, 'X-Error-Details' => 'Invalid token'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
```
|
22
|
+
**Former behaviour**
|
23
|
+
```ruby
|
24
|
+
response.headers['X-Before-Header'] # => nil
|
25
|
+
response.headers['X-App-Header'] # => nil
|
26
|
+
response.headers['X-Error-Details'] # => Invalid token
|
27
|
+
```
|
28
|
+
|
29
|
+
**Current behaviour**
|
30
|
+
```ruby
|
31
|
+
response.headers['X-Before-Header'] # => 'before_call'
|
32
|
+
response.headers['X-App-Header'] # => 'on_call'
|
33
|
+
response.headers['X-Error-Details'] # => Invalid token
|
34
|
+
```
|
35
|
+
|
36
|
+
### Upgrading to >= 1.2.1
|
37
|
+
|
38
|
+
#### Obtaining the name of a mounted class
|
39
|
+
|
40
|
+
In order to make obtaining the name of a mounted class simpler, we've delegated `.to_s` to `base.name`
|
41
|
+
|
42
|
+
**Deprecated in 1.2.0**
|
43
|
+
```ruby
|
44
|
+
payload[:endpoint].options[:for].name
|
45
|
+
```
|
46
|
+
**New**
|
47
|
+
```ruby
|
48
|
+
payload[:endpoint].options[:for].to_s
|
49
|
+
```
|
50
|
+
|
51
|
+
### Upgrading to >= 1.2.0
|
52
|
+
|
53
|
+
#### Changes in the Grape::API class
|
54
|
+
|
55
|
+
##### Patching the class
|
56
|
+
|
57
|
+
In an effort to make APIs re-mountable, The class `Grape::API` no longer refers to an API instance,
|
58
|
+
rather, what used to be `Grape::API` is now `Grape::API::Instance` and `Grape::API` was replaced
|
59
|
+
with a class that can contain several instances of `Grape::API`.
|
60
|
+
|
61
|
+
This changes were done in such a way that no code-changes should be required.
|
62
|
+
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`.
|
63
|
+
|
64
|
+
Note, this is particularly relevant if you are opening the class `Grape::API` for modification.
|
65
|
+
|
66
|
+
**Deprecated**
|
67
|
+
```ruby
|
68
|
+
class Grape::API
|
69
|
+
# your patched logic
|
70
|
+
...
|
71
|
+
end
|
72
|
+
```
|
73
|
+
**New**
|
74
|
+
```ruby
|
75
|
+
class Grape::API::Instance
|
76
|
+
# your patched logic
|
77
|
+
...
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
##### `name` (and other caveats) of the mounted API
|
82
|
+
|
83
|
+
After the patch, the mounted API is no longer a Named class inheriting from `Grape::API`, it is an anonymous class
|
84
|
+
which inherit from `Grape::API::Instance`.
|
85
|
+
What this means in practice, is:
|
86
|
+
- Generally: you can access the named class from the instance calling the getter `base`.
|
87
|
+
- In particular: If you need the `name`, you can use `base`.`name`
|
88
|
+
|
89
|
+
**Deprecated**
|
90
|
+
```ruby
|
91
|
+
payload[:endpoint].options[:for].name
|
92
|
+
```
|
93
|
+
**New**
|
94
|
+
```ruby
|
95
|
+
payload[:endpoint].options[:for].base.name
|
96
|
+
```
|
97
|
+
|
98
|
+
#### Changes in rescue_from returned object
|
99
|
+
|
100
|
+
Grape will now check the object returned from `rescue_from` and ensure that it is a `Rack::Response`. That makes sure response is valid and avoids exposing service information. Change any code that invoked `Rack::Response.new(...).finish` in a custom `rescue_from` block to `Rack::Response.new(...)` to comply with the validation.
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
class Twitter::API < Grape::API
|
104
|
+
rescue_from :all do |e|
|
105
|
+
# version prior to 1.2.0
|
106
|
+
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
|
107
|
+
# 1.2.0 version
|
108
|
+
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' })
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
See [#1757](https://github.com/ruby-grape/grape/pull/1757) and [#1776](https://github.com/ruby-grape/grape/pull/1776) for more information.
|
114
|
+
|
4
115
|
### Upgrading to >= 1.1.0
|
5
116
|
|
6
117
|
#### Changes in HTTP Response Code for Unsupported Content Type
|
data/grape.gemspec
CHANGED
@@ -19,7 +19,9 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.add_runtime_dependency 'rack-accept'
|
20
20
|
s.add_runtime_dependency 'virtus', '>= 1.0.0'
|
21
21
|
|
22
|
-
s.files =
|
22
|
+
s.files = %w[CHANGELOG.md CONTRIBUTING.md README.md grape.png UPGRADING.md LICENSE]
|
23
|
+
s.files += %w[grape.gemspec]
|
24
|
+
s.files += Dir['lib/**/*']
|
23
25
|
s.test_files = Dir['spec/**/*']
|
24
26
|
s.require_paths = ['lib']
|
25
27
|
end
|
data/lib/grape.rb
CHANGED
@@ -34,7 +34,6 @@ module Grape
|
|
34
34
|
autoload :Namespace
|
35
35
|
|
36
36
|
autoload :Path
|
37
|
-
|
38
37
|
autoload :Cookies
|
39
38
|
autoload :Validations
|
40
39
|
autoload :ErrorFormatter
|
@@ -55,105 +54,125 @@ module Grape
|
|
55
54
|
|
56
55
|
module Exceptions
|
57
56
|
extend ::ActiveSupport::Autoload
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
57
|
+
eager_autoload do
|
58
|
+
autoload :Base
|
59
|
+
autoload :Validation
|
60
|
+
autoload :ValidationArrayErrors
|
61
|
+
autoload :ValidationErrors
|
62
|
+
autoload :MissingVendorOption
|
63
|
+
autoload :MissingMimeType
|
64
|
+
autoload :MissingOption
|
65
|
+
autoload :InvalidFormatter
|
66
|
+
autoload :InvalidVersionerOption
|
67
|
+
autoload :UnknownValidator
|
68
|
+
autoload :UnknownOptions
|
69
|
+
autoload :UnknownParameter
|
70
|
+
autoload :InvalidWithOptionForRepresent
|
71
|
+
autoload :IncompatibleOptionValues
|
72
|
+
autoload :MissingGroupTypeError, 'grape/exceptions/missing_group_type'
|
73
|
+
autoload :UnsupportedGroupTypeError, 'grape/exceptions/unsupported_group_type'
|
74
|
+
autoload :InvalidMessageBody
|
75
|
+
autoload :InvalidAcceptHeader
|
76
|
+
autoload :InvalidVersionHeader
|
77
|
+
autoload :MethodNotAllowed
|
78
|
+
autoload :InvalidResponse
|
79
|
+
end
|
78
80
|
end
|
79
81
|
|
80
82
|
module Extensions
|
81
83
|
extend ::ActiveSupport::Autoload
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
eager_autoload do
|
85
|
+
autoload :DeepMergeableHash
|
86
|
+
autoload :DeepSymbolizeHash
|
87
|
+
autoload :DeepHashWithIndifferentAccess
|
88
|
+
autoload :Hash
|
89
|
+
end
|
88
90
|
module ActiveSupport
|
89
91
|
extend ::ActiveSupport::Autoload
|
90
|
-
|
91
|
-
|
92
|
+
eager_autoload do
|
93
|
+
autoload :HashWithIndifferentAccess
|
94
|
+
end
|
92
95
|
end
|
93
96
|
|
94
97
|
module Hashie
|
95
98
|
extend ::ActiveSupport::Autoload
|
96
|
-
|
97
|
-
|
99
|
+
eager_autoload do
|
100
|
+
autoload :Mash
|
101
|
+
end
|
98
102
|
end
|
99
103
|
end
|
100
104
|
|
101
105
|
module Middleware
|
102
106
|
extend ::ActiveSupport::Autoload
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
107
|
+
eager_autoload do
|
108
|
+
autoload :Base
|
109
|
+
autoload :Versioner
|
110
|
+
autoload :Formatter
|
111
|
+
autoload :Error
|
112
|
+
autoload :Globals
|
113
|
+
autoload :Stack
|
114
|
+
autoload :Helpers
|
115
|
+
end
|
109
116
|
|
110
117
|
module Auth
|
111
118
|
extend ::ActiveSupport::Autoload
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
119
|
+
eager_autoload do
|
120
|
+
autoload :Base
|
121
|
+
autoload :DSL
|
122
|
+
autoload :StrategyInfo
|
123
|
+
autoload :Strategies
|
124
|
+
end
|
116
125
|
end
|
117
126
|
|
118
127
|
module Versioner
|
119
128
|
extend ::ActiveSupport::Autoload
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
129
|
+
eager_autoload do
|
130
|
+
autoload :Path
|
131
|
+
autoload :Header
|
132
|
+
autoload :Param
|
133
|
+
autoload :AcceptVersionHeader
|
134
|
+
end
|
124
135
|
end
|
125
136
|
end
|
126
137
|
|
127
138
|
module Util
|
128
139
|
extend ::ActiveSupport::Autoload
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
140
|
+
eager_autoload do
|
141
|
+
autoload :InheritableValues
|
142
|
+
autoload :StackableValues
|
143
|
+
autoload :ReverseStackableValues
|
144
|
+
autoload :InheritableSetting
|
145
|
+
autoload :StrictHashConfiguration
|
146
|
+
autoload :Registrable
|
147
|
+
end
|
135
148
|
end
|
136
149
|
|
137
150
|
module ErrorFormatter
|
138
151
|
extend ::ActiveSupport::Autoload
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
152
|
+
eager_autoload do
|
153
|
+
autoload :Base
|
154
|
+
autoload :Json
|
155
|
+
autoload :Txt
|
156
|
+
autoload :Xml
|
157
|
+
end
|
143
158
|
end
|
144
159
|
|
145
160
|
module Formatter
|
146
161
|
extend ::ActiveSupport::Autoload
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
162
|
+
eager_autoload do
|
163
|
+
autoload :Json
|
164
|
+
autoload :SerializableHash
|
165
|
+
autoload :Txt
|
166
|
+
autoload :Xml
|
167
|
+
end
|
151
168
|
end
|
152
169
|
|
153
170
|
module Parser
|
154
171
|
extend ::ActiveSupport::Autoload
|
155
|
-
|
156
|
-
|
172
|
+
eager_autoload do
|
173
|
+
autoload :Json
|
174
|
+
autoload :Xml
|
175
|
+
end
|
157
176
|
end
|
158
177
|
|
159
178
|
module DSL
|
@@ -177,26 +196,38 @@ module Grape
|
|
177
196
|
|
178
197
|
class API
|
179
198
|
extend ::ActiveSupport::Autoload
|
180
|
-
|
199
|
+
eager_autoload do
|
200
|
+
autoload :Helpers
|
201
|
+
end
|
181
202
|
end
|
182
203
|
|
183
204
|
module Presenters
|
184
205
|
extend ::ActiveSupport::Autoload
|
185
|
-
|
206
|
+
eager_autoload do
|
207
|
+
autoload :Presenter
|
208
|
+
end
|
186
209
|
end
|
187
210
|
|
188
211
|
module ServeFile
|
189
212
|
extend ::ActiveSupport::Autoload
|
190
|
-
|
191
|
-
|
192
|
-
|
213
|
+
eager_autoload do
|
214
|
+
autoload :FileResponse
|
215
|
+
autoload :FileBody
|
216
|
+
autoload :SendfileResponse
|
217
|
+
end
|
193
218
|
end
|
194
219
|
end
|
195
220
|
|
221
|
+
require 'grape/config'
|
196
222
|
require 'grape/util/content_types'
|
223
|
+
require 'grape/util/lazy_value'
|
224
|
+
require 'grape/util/lazy_block'
|
225
|
+
require 'grape/util/endpoint_configuration'
|
197
226
|
|
198
227
|
require 'grape/validations/validators/base'
|
199
228
|
require 'grape/validations/attributes_iterator'
|
229
|
+
require 'grape/validations/single_attribute_iterator'
|
230
|
+
require 'grape/validations/multiple_attributes_iterator'
|
200
231
|
require 'grape/validations/validators/allow_blank'
|
201
232
|
require 'grape/validations/validators/as'
|
202
233
|
require 'grape/validations/validators/at_least_one_of'
|
@@ -206,6 +237,7 @@ require 'grape/validations/validators/exactly_one_of'
|
|
206
237
|
require 'grape/validations/validators/mutual_exclusion'
|
207
238
|
require 'grape/validations/validators/presence'
|
208
239
|
require 'grape/validations/validators/regexp'
|
240
|
+
require 'grape/validations/validators/same_as'
|
209
241
|
require 'grape/validations/validators/values'
|
210
242
|
require 'grape/validations/validators/except_values'
|
211
243
|
require 'grape/validations/params_scope'
|
data/lib/grape/api.rb
CHANGED
@@ -1,233 +1,194 @@
|
|
1
1
|
require 'grape/router'
|
2
|
+
require 'grape/api/instance'
|
2
3
|
|
3
4
|
module Grape
|
4
5
|
# The API class is the primary entry point for creating Grape APIs. Users
|
5
6
|
# should subclass this class in order to build an API.
|
6
7
|
class API
|
7
|
-
|
8
|
+
# Class methods that we want to call on the API rather than on the API object
|
9
|
+
NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration compile!]).freeze
|
8
10
|
|
9
11
|
class << self
|
10
|
-
|
12
|
+
attr_accessor :base_instance, :instances
|
11
13
|
|
12
|
-
#
|
13
|
-
|
14
|
-
|
14
|
+
# Rather than initializing an object of type Grape::API, create an object of type Instance
|
15
|
+
def new(*args, &block)
|
16
|
+
base_instance.new(*args, &block)
|
17
|
+
end
|
15
18
|
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
# When inherited, will create a list of all instances (times the API was mounted)
|
20
|
+
# It will listen to the setup required to mount that endpoint, and replicate it on any new instance
|
21
|
+
def inherited(api, base_instance_parent = Grape::API::Instance)
|
22
|
+
api.initial_setup(base_instance_parent)
|
23
|
+
api.override_all_methods!
|
24
|
+
make_inheritable(api)
|
21
25
|
end
|
22
26
|
|
23
|
-
#
|
24
|
-
#
|
25
|
-
def
|
26
|
-
@
|
27
|
+
# Initialize the instance variables on the remountable class, and the base_instance
|
28
|
+
# an instance that will be used to create the set up but will not be mounted
|
29
|
+
def initial_setup(base_instance_parent)
|
30
|
+
@instances = []
|
31
|
+
@setup = []
|
32
|
+
@base_parent = base_instance_parent
|
33
|
+
@base_instance = mount_instance
|
27
34
|
end
|
28
35
|
|
29
|
-
#
|
30
|
-
def
|
31
|
-
|
36
|
+
# Redefines all methods so that are forwarded to add_setup and be recorded
|
37
|
+
def override_all_methods!
|
38
|
+
(base_instance.methods - NON_OVERRIDABLE).each do |method_override|
|
39
|
+
define_singleton_method(method_override) do |*args, &block|
|
40
|
+
add_setup(method_override, *args, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Configure an API from the outside. If a block is given, it'll pass a
|
46
|
+
# configuration hash to the block which you can use to configure your
|
47
|
+
# API. If no block is given, returns the configuration hash.
|
48
|
+
# The configuration set here is accessible from inside an API with
|
49
|
+
# `configuration` as normal.
|
50
|
+
def configure
|
51
|
+
config = @base_instance.configuration
|
52
|
+
if block_given?
|
53
|
+
yield config
|
54
|
+
self
|
55
|
+
else
|
56
|
+
config
|
57
|
+
end
|
32
58
|
end
|
33
59
|
|
34
60
|
# This is the interface point between Rack and Grape; it accepts a request
|
35
61
|
# from Rack and ultimately returns an array of three values: the status,
|
36
62
|
# the headers, and the body. See [the rack specification]
|
37
63
|
# (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
|
38
|
-
|
39
|
-
|
40
|
-
call
|
64
|
+
# NOTE: This will only be called on an API directly mounted on RACK
|
65
|
+
def call(*args, &block)
|
66
|
+
instance_for_rack.call(*args, &block)
|
41
67
|
end
|
42
68
|
|
43
|
-
#
|
44
|
-
def
|
45
|
-
|
69
|
+
# Allows an API to itself be inheritable:
|
70
|
+
def make_inheritable(api)
|
71
|
+
# When a child API inherits from a parent API.
|
72
|
+
def api.inherited(child_api)
|
73
|
+
# The instances of the child API inherit from the instances of the parent API
|
74
|
+
Grape::API.inherited(child_api, base_instance)
|
75
|
+
end
|
46
76
|
end
|
47
77
|
|
48
|
-
#
|
49
|
-
def
|
50
|
-
if
|
51
|
-
|
78
|
+
# Alleviates problems with autoloading by tring to search for the constant
|
79
|
+
def const_missing(*args)
|
80
|
+
if base_instance.const_defined?(*args)
|
81
|
+
base_instance.const_get(*args)
|
52
82
|
else
|
53
|
-
|
83
|
+
super
|
54
84
|
end
|
55
85
|
end
|
56
86
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
87
|
+
# The remountable class can have a configuration hash to provide some dynamic class-level variables.
|
88
|
+
# For instance, a descripcion could be done using: `desc configuration[:description]` if it may vary
|
89
|
+
# depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
|
90
|
+
# too much, you may actually want to provide a new API rather than remount it.
|
91
|
+
def mount_instance(opts = {})
|
92
|
+
instance = Class.new(@base_parent)
|
93
|
+
instance.configuration = Grape::Util::EndpointConfiguration.new(opts[:configuration] || {})
|
94
|
+
instance.base = self
|
95
|
+
replay_setup_on(instance)
|
96
|
+
instance
|
61
97
|
end
|
62
98
|
|
63
|
-
|
99
|
+
# Replays the set up to produce an API as defined in this class, can be called
|
100
|
+
# on classes that inherit from Grape::API
|
101
|
+
def replay_setup_on(instance)
|
102
|
+
@setup.each do |setup_step|
|
103
|
+
replay_step_on(instance, setup_step)
|
104
|
+
end
|
105
|
+
end
|
64
106
|
|
65
|
-
def
|
66
|
-
|
107
|
+
def respond_to?(method, include_private = false)
|
108
|
+
super(method, include_private) || base_instance.respond_to?(method, include_private)
|
67
109
|
end
|
68
110
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
reset_validations!
|
111
|
+
def respond_to_missing?(method, include_private = false)
|
112
|
+
base_instance.respond_to?(method, include_private)
|
113
|
+
end
|
114
|
+
|
115
|
+
def method_missing(method, *args, &block)
|
116
|
+
# If there's a missing method, it may be defined on the base_instance instead.
|
117
|
+
if respond_to_missing?(method)
|
118
|
+
base_instance.send(method, *args, &block)
|
78
119
|
else
|
79
|
-
|
120
|
+
super
|
80
121
|
end
|
81
122
|
end
|
82
123
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
124
|
+
def compile!
|
125
|
+
require 'grape/eager_load'
|
126
|
+
instance_for_rack.compile! # See API::Instance.compile!
|
86
127
|
end
|
87
128
|
|
88
|
-
|
89
|
-
top_level_setting.inherit_from other_settings.point_in_time_copy
|
129
|
+
private
|
90
130
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
131
|
+
def instance_for_rack
|
132
|
+
if never_mounted?
|
133
|
+
base_instance
|
134
|
+
else
|
135
|
+
mounted_instances.first
|
96
136
|
end
|
97
|
-
|
98
|
-
reset_routes!
|
99
137
|
end
|
100
|
-
end
|
101
|
-
|
102
|
-
attr_reader :router
|
103
138
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
139
|
+
# Adds a new stage to the set up require to get a Grape::API up and running
|
140
|
+
def add_setup(method, *args, &block)
|
141
|
+
setup_step = { method: method, args: args, block: block }
|
142
|
+
@setup << setup_step
|
143
|
+
last_response = nil
|
144
|
+
@instances.each do |instance|
|
145
|
+
last_response = replay_step_on(instance, setup_step)
|
146
|
+
end
|
147
|
+
last_response
|
111
148
|
end
|
112
149
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
result
|
122
|
-
end
|
123
|
-
|
124
|
-
# Some requests may return a HTTP 404 error if grape cannot find a matching
|
125
|
-
# route. In this case, Grape::Router adds a X-Cascade header to the response
|
126
|
-
# and sets it to 'pass', indicating to grape's parents they should keep
|
127
|
-
# looking for a matching route on other resources.
|
128
|
-
#
|
129
|
-
# In some applications (e.g. mounting grape on rails), one might need to trap
|
130
|
-
# errors from reaching upstream. This is effectivelly done by unsetting
|
131
|
-
# X-Cascade. Default :cascade is true.
|
132
|
-
def cascade?
|
133
|
-
return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
|
134
|
-
return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
|
135
|
-
true
|
136
|
-
end
|
137
|
-
|
138
|
-
reset!
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
# For every resource add a 'OPTIONS' route that returns an HTTP 204 response
|
143
|
-
# with a list of HTTP methods that can be called. Also add a route that
|
144
|
-
# will return an HTTP 405 response for any HTTP method that the resource
|
145
|
-
# cannot handle.
|
146
|
-
def add_head_not_allowed_methods_and_options_methods
|
147
|
-
routes_map = {}
|
148
|
-
|
149
|
-
self.class.endpoints.each do |endpoint|
|
150
|
-
routes = endpoint.routes
|
151
|
-
routes.each do |route|
|
152
|
-
# using the :any shorthand produces [nil] for route methods, substitute all manually
|
153
|
-
route_key = route.pattern.to_regexp
|
154
|
-
routes_map[route_key] ||= {}
|
155
|
-
route_settings = routes_map[route_key]
|
156
|
-
route_settings[:pattern] = route.pattern
|
157
|
-
route_settings[:requirements] = route.requirements
|
158
|
-
route_settings[:path] = route.origin
|
159
|
-
route_settings[:methods] ||= []
|
160
|
-
route_settings[:methods] << route.request_method
|
161
|
-
route_settings[:endpoint] = route.app
|
162
|
-
|
163
|
-
# using the :any shorthand produces [nil] for route methods, substitute all manually
|
164
|
-
route_settings[:methods] = %w[GET PUT POST DELETE PATCH HEAD OPTIONS] if route_settings[:methods].include?('*')
|
150
|
+
def replay_step_on(instance, setup_step)
|
151
|
+
return if skip_immediate_run?(instance, setup_step[:args])
|
152
|
+
args = evaluate_arguments(instance.configuration, *setup_step[:args])
|
153
|
+
response = instance.send(setup_step[:method], *args, &setup_step[:block])
|
154
|
+
if skip_immediate_run?(instance, [response])
|
155
|
+
response
|
156
|
+
else
|
157
|
+
evaluate_arguments(instance.configuration, response).first
|
165
158
|
end
|
166
159
|
end
|
167
160
|
|
168
|
-
#
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
without_versioning do
|
174
|
-
routes_map.each do |_, config|
|
175
|
-
methods = config[:methods]
|
176
|
-
allowed_methods = methods.dup
|
177
|
-
|
178
|
-
unless self.class.namespace_inheritable(:do_not_route_head)
|
179
|
-
allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
|
180
|
-
end
|
181
|
-
|
182
|
-
allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')
|
161
|
+
# Skips steps that contain arguments to be lazily executed (on re-mount time)
|
162
|
+
def skip_immediate_run?(instance, args)
|
163
|
+
instance.base_instance? &&
|
164
|
+
(any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) })
|
165
|
+
end
|
183
166
|
|
184
|
-
|
185
|
-
|
186
|
-
|
167
|
+
def any_lazy?(args)
|
168
|
+
args.any? { |argument| argument.respond_to?(:lazy?) && argument.lazy? }
|
169
|
+
end
|
187
170
|
|
188
|
-
|
189
|
-
|
171
|
+
def evaluate_arguments(configuration, *args)
|
172
|
+
args.map do |argument|
|
173
|
+
if argument.respond_to?(:lazy?) && argument.lazy?
|
174
|
+
argument.evaluate_from(configuration)
|
175
|
+
elsif argument.is_a?(Hash)
|
176
|
+
argument.map { |key, value| [key, evaluate_arguments(configuration, value).first] }.to_h
|
177
|
+
elsif argument.is_a?(Array)
|
178
|
+
evaluate_arguments(configuration, *argument)
|
179
|
+
else
|
180
|
+
argument
|
190
181
|
end
|
191
182
|
end
|
192
183
|
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# Generate a route that returns an HTTP 405 response for a user defined
|
196
|
-
# path on methods not specified
|
197
|
-
def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
|
198
|
-
not_allowed_methods = %w[GET PUT POST DELETE PATCH HEAD] - allowed_methods
|
199
|
-
not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
|
200
184
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
# Allows definition of endpoints that ignore the versioning configuration
|
207
|
-
# used by the rest of your API.
|
208
|
-
def without_versioning(&_block)
|
209
|
-
old_version = self.class.namespace_inheritable(:version)
|
210
|
-
old_version_options = self.class.namespace_inheritable(:version_options)
|
211
|
-
|
212
|
-
self.class.namespace_inheritable_to_nil(:version)
|
213
|
-
self.class.namespace_inheritable_to_nil(:version_options)
|
214
|
-
|
215
|
-
yield
|
216
|
-
|
217
|
-
self.class.namespace_inheritable(:version, old_version)
|
218
|
-
self.class.namespace_inheritable(:version_options, old_version_options)
|
219
|
-
end
|
220
|
-
|
221
|
-
# Allows definition of endpoints that ignore the root prefix used by the
|
222
|
-
# rest of your API.
|
223
|
-
def without_root_prefix(&_block)
|
224
|
-
old_prefix = self.class.namespace_inheritable(:root_prefix)
|
225
|
-
|
226
|
-
self.class.namespace_inheritable_to_nil(:root_prefix)
|
227
|
-
|
228
|
-
yield
|
185
|
+
def never_mounted?
|
186
|
+
mounted_instances.empty?
|
187
|
+
end
|
229
188
|
|
230
|
-
|
189
|
+
def mounted_instances
|
190
|
+
instances - [base_instance]
|
191
|
+
end
|
231
192
|
end
|
232
193
|
end
|
233
194
|
end
|