grape 0.16.2 → 0.17.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.
- checksums.yaml +4 -4
- data/Appraisals +4 -0
- data/CHANGELOG.md +54 -27
- data/Dangerfile +80 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +61 -27
- data/README.md +135 -7
- data/Rakefile +34 -30
- data/UPGRADING.md +21 -0
- data/gemfiles/rack_1.5.2.gemfile +21 -0
- data/gemfiles/rails_3.gemfile +22 -1
- data/gemfiles/rails_4.gemfile +21 -0
- data/gemfiles/rails_5.gemfile +34 -0
- data/grape.gemspec +0 -14
- data/lib/grape.rb +2 -0
- data/lib/grape/api.rb +9 -2
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +15 -17
- data/lib/grape/dsl/middleware.rb +15 -1
- data/lib/grape/dsl/parameters.rb +16 -14
- data/lib/grape/dsl/request_response.rb +24 -20
- data/lib/grape/dsl/routing.rb +11 -10
- data/lib/grape/dsl/settings.rb +16 -0
- data/lib/grape/endpoint.rb +77 -60
- data/lib/grape/exceptions/validation.rb +5 -2
- data/lib/grape/exceptions/validation_array_errors.rb +11 -0
- data/lib/grape/formatter/xml.rb +1 -1
- data/lib/grape/middleware/error.rb +34 -25
- data/lib/grape/middleware/formatter.rb +9 -9
- data/lib/grape/middleware/stack.rb +110 -0
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +3 -3
- data/lib/grape/path.rb +10 -2
- data/lib/grape/request.rb +1 -1
- data/lib/grape/router.rb +10 -19
- data/lib/grape/router/pattern.rb +2 -2
- data/lib/grape/router/route.rb +3 -3
- data/lib/grape/util/content_types.rb +1 -1
- data/lib/grape/util/inheritable_setting.rb +7 -2
- data/lib/grape/util/reverse_stackable_values.rb +45 -0
- data/lib/grape/util/stackable_values.rb +10 -11
- data/lib/grape/validations/attributes_iterator.rb +32 -7
- data/lib/grape/validations/params_scope.rb +33 -21
- data/lib/grape/validations/types.rb +4 -4
- data/lib/grape/validations/types/build_coercer.rb +9 -1
- data/lib/grape/validations/validators/all_or_none.rb +2 -2
- data/lib/grape/validations/validators/allow_blank.rb +10 -11
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
- data/lib/grape/validations/validators/base.rb +16 -6
- data/lib/grape/validations/validators/coerce.rb +3 -6
- data/lib/grape/validations/validators/default.rb +26 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +1 -1
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
- data/lib/grape/validations/validators/presence.rb +1 -1
- data/lib/grape/validations/validators/regexp.rb +1 -1
- data/lib/grape/validations/validators/values.rb +1 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +3 -3
- data/spec/grape/api/parameters_modification_spec.rb +41 -0
- data/spec/grape/api_spec.rb +335 -108
- data/spec/grape/dsl/logger_spec.rb +1 -1
- data/spec/grape/dsl/middleware_spec.rb +25 -5
- data/spec/grape/dsl/request_response_spec.rb +20 -6
- data/spec/grape/dsl/validations_spec.rb +1 -1
- data/spec/grape/endpoint_spec.rb +166 -23
- data/spec/grape/entity_spec.rb +0 -2
- data/spec/grape/exceptions/body_parse_errors_spec.rb +37 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
- data/spec/grape/exceptions/validation_spec.rb +10 -0
- data/spec/grape/integration/global_namespace_function_spec.rb +1 -1
- data/spec/grape/integration/rack_spec.rb +1 -1
- data/spec/grape/middleware/base_spec.rb +1 -1
- data/spec/grape/middleware/exception_spec.rb +2 -2
- data/spec/grape/middleware/formatter_spec.rb +4 -4
- data/spec/grape/middleware/stack_spec.rb +123 -0
- data/spec/grape/middleware/versioner/header_spec.rb +6 -6
- data/spec/grape/request_spec.rb +22 -22
- data/spec/grape/util/inheritable_setting_spec.rb +23 -0
- data/spec/grape/util/reverse_stackable_values_spec.rb +131 -0
- data/spec/grape/validations/params_scope_spec.rb +88 -1
- data/spec/grape/validations/validators/allow_blank_spec.rb +5 -0
- data/spec/grape/validations/validators/coerce_spec.rb +5 -5
- data/spec/grape/validations/validators/default_spec.rb +44 -0
- data/spec/grape/validations/validators/values_spec.rb +1 -1
- data/spec/grape/validations_spec.rb +36 -17
- data/spec/spec_helper.rb +1 -8
- data/spec/support/versioned_helpers.rb +3 -3
- metadata +13 -188
- data/gemfiles/rails_3.gemfile.lock +0 -225
- data/pkg/grape-0.16.1.gem +0 -0
- data/pkg/patch.diff +0 -24
- data/tmp/Gemfile.lock +0 -63
data/grape.gemspec
CHANGED
@@ -22,20 +22,6 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.add_runtime_dependency 'virtus', '>= 1.0.0'
|
23
23
|
s.add_runtime_dependency 'builder'
|
24
24
|
|
25
|
-
s.add_development_dependency 'grape-entity', '0.5.0'
|
26
|
-
s.add_development_dependency 'rake', '~> 10'
|
27
|
-
s.add_development_dependency 'maruku'
|
28
|
-
s.add_development_dependency 'yard'
|
29
|
-
s.add_development_dependency 'rack-test'
|
30
|
-
s.add_development_dependency 'rspec', '~> 3.0'
|
31
|
-
s.add_development_dependency 'bundler'
|
32
|
-
s.add_development_dependency 'cookiejar'
|
33
|
-
s.add_development_dependency 'rack-contrib'
|
34
|
-
s.add_development_dependency 'mime-types', '< 3.0'
|
35
|
-
s.add_development_dependency 'appraisal'
|
36
|
-
s.add_development_dependency 'benchmark-ips'
|
37
|
-
s.add_development_dependency 'rubocop', '0.35.1'
|
38
|
-
|
39
25
|
s.files = Dir['**/*'].keep_if { |file| File.file?(file) }
|
40
26
|
s.test_files = Dir['spec/**/*']
|
41
27
|
s.require_paths = ['lib']
|
data/lib/grape.rb
CHANGED
@@ -58,6 +58,7 @@ module Grape
|
|
58
58
|
extend ActiveSupport::Autoload
|
59
59
|
autoload :Base
|
60
60
|
autoload :Validation
|
61
|
+
autoload :ValidationArrayErrors
|
61
62
|
autoload :ValidationErrors
|
62
63
|
autoload :MissingVendorOption
|
63
64
|
autoload :MissingMimeType
|
@@ -106,6 +107,7 @@ module Grape
|
|
106
107
|
extend ActiveSupport::Autoload
|
107
108
|
autoload :InheritableValues
|
108
109
|
autoload :StackableValues
|
110
|
+
autoload :ReverseStackableValues
|
109
111
|
autoload :InheritableSetting
|
110
112
|
autoload :StrictHashConfiguration
|
111
113
|
autoload :Registrable
|
data/lib/grape/api.rb
CHANGED
@@ -88,6 +88,13 @@ module Grape
|
|
88
88
|
def inherit_settings(other_settings)
|
89
89
|
top_level_setting.inherit_from other_settings.point_in_time_copy
|
90
90
|
|
91
|
+
# Propagate any inherited params down to our endpoints, and reset any
|
92
|
+
# compiled routes.
|
93
|
+
endpoints.each do |e|
|
94
|
+
e.inherit_settings(top_level_setting.namespace_stackable)
|
95
|
+
e.reset_routes!
|
96
|
+
end
|
97
|
+
|
91
98
|
reset_routes!
|
92
99
|
end
|
93
100
|
end
|
@@ -123,8 +130,8 @@ module Grape
|
|
123
130
|
# errors from reaching upstream. This is effectivelly done by unsetting
|
124
131
|
# X-Cascade. Default :cascade is true.
|
125
132
|
def cascade?
|
126
|
-
return
|
127
|
-
return
|
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)
|
128
135
|
true
|
129
136
|
end
|
130
137
|
|
data/lib/grape/dsl/headers.rb
CHANGED
@@ -29,9 +29,12 @@ module Grape
|
|
29
29
|
def declared(params, options = {}, declared_params = nil)
|
30
30
|
options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
|
31
31
|
|
32
|
-
|
32
|
+
all_declared_params = route_setting(:declared_params)
|
33
|
+
current_namespace_declared_params = route_setting(:saved_declared_params).last
|
33
34
|
|
34
|
-
|
35
|
+
declared_params ||= options[:include_parent_namespaces] ? all_declared_params : current_namespace_declared_params
|
36
|
+
|
37
|
+
raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
|
35
38
|
|
36
39
|
if params.is_a? Array
|
37
40
|
params.map do |param|
|
@@ -70,7 +73,7 @@ module Grape
|
|
70
73
|
# options. `:include_parent_namespaces` defaults to true, hence must be set to false if
|
71
74
|
# you want only to return params declared against the current/target endpoint.
|
72
75
|
def declared(*)
|
73
|
-
|
76
|
+
raise MethodNotYetAvailable, '#declared is not available prior to parameter validation.'
|
74
77
|
end
|
75
78
|
|
76
79
|
# The API version as specified in the URL.
|
@@ -100,14 +103,12 @@ module Grape
|
|
100
103
|
if permanent
|
101
104
|
status 301
|
102
105
|
body_message ||= "This resource has been moved permanently to #{url}."
|
106
|
+
elsif env[Grape::Http::Headers::HTTP_VERSION] == 'HTTP/1.1' && request.request_method.to_s.upcase != Grape::Http::Headers::GET
|
107
|
+
status 303
|
108
|
+
body_message ||= "An alternate resource is located at #{url}."
|
103
109
|
else
|
104
|
-
|
105
|
-
|
106
|
-
body_message ||= "An alternate resource is located at #{url}."
|
107
|
-
else
|
108
|
-
status 302
|
109
|
-
body_message ||= "This resource has been moved temporarily to #{url}."
|
110
|
-
end
|
110
|
+
status 302
|
111
|
+
body_message ||= "This resource has been moved temporarily to #{url}."
|
111
112
|
end
|
112
113
|
header 'Location', url
|
113
114
|
content_type 'text/plain'
|
@@ -120,11 +121,8 @@ module Grape
|
|
120
121
|
def status(status = nil)
|
121
122
|
case status
|
122
123
|
when Symbol
|
123
|
-
|
124
|
-
|
125
|
-
else
|
126
|
-
fail ArgumentError, "Status code :#{status} is invalid."
|
127
|
-
end
|
124
|
+
raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.keys.include?(status)
|
125
|
+
@status = Rack::Utils.status_code(status)
|
128
126
|
when Fixnum
|
129
127
|
@status = status
|
130
128
|
when nil
|
@@ -136,7 +134,7 @@ module Grape
|
|
136
134
|
200
|
137
135
|
end
|
138
136
|
else
|
139
|
-
|
137
|
+
raise ArgumentError, 'Status code must be Fixnum or Symbol.'
|
140
138
|
end
|
141
139
|
end
|
142
140
|
|
@@ -260,7 +258,7 @@ module Grape
|
|
260
258
|
if key
|
261
259
|
representation = (@body || {}).merge(key => representation)
|
262
260
|
elsif entity_class.present? && @body
|
263
|
-
|
261
|
+
raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
|
264
262
|
representation = @body.merge(representation)
|
265
263
|
end
|
266
264
|
|
data/lib/grape/dsl/middleware.rb
CHANGED
@@ -15,7 +15,21 @@ module Grape
|
|
15
15
|
# @param middleware_class [Class] The class of the middleware you'd like
|
16
16
|
# to inject.
|
17
17
|
def use(middleware_class, *args, &block)
|
18
|
-
arr = [middleware_class, *args]
|
18
|
+
arr = [:use, middleware_class, *args]
|
19
|
+
arr << block if block_given?
|
20
|
+
|
21
|
+
namespace_stackable(:middleware, arr)
|
22
|
+
end
|
23
|
+
|
24
|
+
def insert_before(*args, &block)
|
25
|
+
arr = [:insert_before, *args]
|
26
|
+
arr << block if block_given?
|
27
|
+
|
28
|
+
namespace_stackable(:middleware, arr)
|
29
|
+
end
|
30
|
+
|
31
|
+
def insert_after(*args, &block)
|
32
|
+
arr = [:insert_after, *args]
|
19
33
|
arr << block if block_given?
|
20
34
|
|
21
35
|
namespace_stackable(:middleware, arr)
|
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -34,13 +34,13 @@ module Grape
|
|
34
34
|
options = names.extract_options!
|
35
35
|
names.each do |name|
|
36
36
|
params_block = named_params.fetch(name) do
|
37
|
-
|
37
|
+
raise "Params :#{name} not found!"
|
38
38
|
end
|
39
39
|
instance_exec(options, ¶ms_block)
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
43
|
-
|
42
|
+
alias use_scope use
|
43
|
+
alias includes use
|
44
44
|
|
45
45
|
# Require one or more parameters for the current endpoint.
|
46
46
|
#
|
@@ -121,8 +121,8 @@ module Grape
|
|
121
121
|
|
122
122
|
# check type for optional parameter group
|
123
123
|
if attrs && block_given?
|
124
|
-
|
125
|
-
|
124
|
+
raise Grape::Exceptions::MissingGroupTypeError.new if type.nil?
|
125
|
+
raise Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
|
126
126
|
end
|
127
127
|
|
128
128
|
if opts[:using]
|
@@ -167,7 +167,8 @@ module Grape
|
|
167
167
|
# @yield a parameter definition DSL
|
168
168
|
def given(*attrs, &block)
|
169
169
|
attrs.each do |attr|
|
170
|
-
|
170
|
+
attr_ = attr.is_a?(Hash) ? attr.keys[0] : attr
|
171
|
+
raise Grape::Exceptions::UnknownParameter.new(attr_) unless declared_param?(attr_)
|
171
172
|
end
|
172
173
|
new_lateral_scope(dependent_on: attrs, &block)
|
173
174
|
end
|
@@ -181,7 +182,7 @@ module Grape
|
|
181
182
|
@declared_params.flatten.include?(param)
|
182
183
|
end
|
183
184
|
|
184
|
-
|
185
|
+
alias group requires
|
185
186
|
|
186
187
|
# @param params [Hash] initial hash of parameters
|
187
188
|
# @return hash of parameters relevant for the current scope
|
@@ -189,13 +190,14 @@ module Grape
|
|
189
190
|
def params(params)
|
190
191
|
params = @parent.params(params) if @parent
|
191
192
|
if @element
|
192
|
-
if params.is_a?(Array)
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
193
|
+
params = if params.is_a?(Array)
|
194
|
+
# used for calculating parent array indices for error messages
|
195
|
+
params.map { |el| el[@element] || {} }
|
196
|
+
elsif params.is_a?(Hash)
|
197
|
+
params[@element] || {}
|
198
|
+
else
|
199
|
+
{}
|
200
|
+
end
|
199
201
|
end
|
200
202
|
params
|
201
203
|
end
|
@@ -23,7 +23,7 @@ module Grape
|
|
23
23
|
namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(new_format, {}))
|
24
24
|
# define a single mime type
|
25
25
|
mime_type = content_types[new_format.to_sym]
|
26
|
-
|
26
|
+
raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
|
27
27
|
namespace_stackable(:content_types, new_format.to_sym => mime_type)
|
28
28
|
else
|
29
29
|
namespace_inheritable(:format)
|
@@ -51,11 +51,11 @@ module Grape
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def error_formatter(format, options)
|
54
|
-
if options.is_a?(Hash) && options.key?(:with)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
formatter = if options.is_a?(Hash) && options.key?(:with)
|
55
|
+
options[:with]
|
56
|
+
else
|
57
|
+
options
|
58
|
+
end
|
59
59
|
|
60
60
|
namespace_stackable(:error_formatters, format.to_sym => formatter)
|
61
61
|
end
|
@@ -106,23 +106,27 @@ module Grape
|
|
106
106
|
|
107
107
|
options = args.extract_options!
|
108
108
|
if block_given? && options.key?(:with)
|
109
|
-
|
109
|
+
raise ArgumentError, 'both :with option and block cannot be passed'
|
110
110
|
end
|
111
111
|
handler ||= extract_with(options)
|
112
112
|
|
113
|
-
|
113
|
+
case
|
114
|
+
when args.include?(:all)
|
114
115
|
namespace_inheritable(:rescue_all, true)
|
115
116
|
namespace_inheritable :all_rescue_handler, handler
|
117
|
+
when args.include?(:grape_exceptions)
|
118
|
+
namespace_inheritable(:rescue_all, true)
|
119
|
+
namespace_inheritable(:rescue_grape_exceptions, true)
|
116
120
|
else
|
117
121
|
handler_type =
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
122
|
+
case options[:rescue_subclasses]
|
123
|
+
when nil, true
|
124
|
+
:rescue_handlers
|
125
|
+
else
|
126
|
+
:base_only_rescue_handlers
|
127
|
+
end
|
128
|
+
|
129
|
+
namespace_reverse_stackable handler_type, Hash[args.map { |arg| [arg, handler] }]
|
126
130
|
end
|
127
131
|
|
128
132
|
namespace_stackable(:rescue_options, options)
|
@@ -149,7 +153,7 @@ module Grape
|
|
149
153
|
# @param model_class [Class] The model class that will be represented.
|
150
154
|
# @option options [Class] :with The entity class that will represent the model.
|
151
155
|
def represent(model_class, options)
|
152
|
-
|
156
|
+
raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with] && options[:with].is_a?(Class)
|
153
157
|
namespace_stackable(:representations, model_class => options[:with])
|
154
158
|
end
|
155
159
|
|
@@ -157,10 +161,10 @@ module Grape
|
|
157
161
|
|
158
162
|
def extract_with(options)
|
159
163
|
return unless options.key?(:with)
|
160
|
-
with_option = options
|
164
|
+
with_option = options.delete(:with)
|
161
165
|
return with_option if with_option.instance_of?(Proc)
|
162
|
-
return if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
|
163
|
-
|
166
|
+
return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
|
167
|
+
raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
|
164
168
|
end
|
165
169
|
end
|
166
170
|
end
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -30,20 +30,21 @@ module Grape
|
|
30
30
|
if args.any?
|
31
31
|
options = args.extract_options!
|
32
32
|
options = options.reverse_merge(using: :path)
|
33
|
+
requested_versions = args.flatten
|
33
34
|
|
34
|
-
|
35
|
+
raise Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.key?(:vendor)
|
35
36
|
|
36
|
-
@versions = versions |
|
37
|
+
@versions = versions | requested_versions
|
37
38
|
|
38
39
|
if block_given?
|
39
40
|
within_namespace do
|
40
|
-
namespace_inheritable(:version,
|
41
|
+
namespace_inheritable(:version, requested_versions)
|
41
42
|
namespace_inheritable(:version_options, options)
|
42
43
|
|
43
44
|
instance_eval(&block)
|
44
45
|
end
|
45
46
|
else
|
46
|
-
namespace_inheritable(:version,
|
47
|
+
namespace_inheritable(:version, requested_versions)
|
47
48
|
namespace_inheritable(:version_options, options)
|
48
49
|
end
|
49
50
|
end
|
@@ -121,9 +122,9 @@ module Grape
|
|
121
122
|
method: methods,
|
122
123
|
path: paths,
|
123
124
|
for: self,
|
124
|
-
route_options:
|
125
|
+
route_options: {
|
125
126
|
params: namespace_stackable_with_hash(:params) || {}
|
126
|
-
}
|
127
|
+
}.deep_merge(route_setting(:description) || {}).deep_merge(route_options || {})
|
127
128
|
}
|
128
129
|
|
129
130
|
new_endpoint = Grape::Endpoint.new(inheritable_setting, endpoint_options, &block)
|
@@ -170,10 +171,10 @@ module Grape
|
|
170
171
|
end
|
171
172
|
end
|
172
173
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
174
|
+
alias group namespace
|
175
|
+
alias resource namespace
|
176
|
+
alias resources namespace
|
177
|
+
alias segment namespace
|
177
178
|
|
178
179
|
# An array of API routes.
|
179
180
|
def routes
|
data/lib/grape/dsl/settings.rb
CHANGED
@@ -94,12 +94,28 @@ module Grape
|
|
94
94
|
get_or_set :namespace_stackable, key, value
|
95
95
|
end
|
96
96
|
|
97
|
+
def namespace_reverse_stackable(key, value = nil)
|
98
|
+
get_or_set :namespace_reverse_stackable, key, value
|
99
|
+
end
|
100
|
+
|
97
101
|
def namespace_stackable_with_hash(key)
|
98
102
|
settings = get_or_set :namespace_stackable, key, nil
|
99
103
|
return if settings.blank?
|
100
104
|
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
|
101
105
|
end
|
102
106
|
|
107
|
+
def namespace_reverse_stackable_with_hash(key)
|
108
|
+
settings = get_or_set :namespace_reverse_stackable, key, nil
|
109
|
+
return if settings.blank?
|
110
|
+
result = {}
|
111
|
+
settings.each do |setting|
|
112
|
+
setting.each do |field, value|
|
113
|
+
result[field] ||= value
|
114
|
+
end
|
115
|
+
end
|
116
|
+
result
|
117
|
+
end
|
118
|
+
|
103
119
|
# (see #unset_global_setting)
|
104
120
|
def unset_namespace_stackable(key)
|
105
121
|
unset :namespace_stackable, key
|
data/lib/grape/endpoint.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'grape/middleware/stack'
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
# An Endpoint is the proxy scope in which all routing
|
3
5
|
# blocks are executed. In other words, any methods
|
@@ -12,21 +14,14 @@ module Grape
|
|
12
14
|
|
13
15
|
class << self
|
14
16
|
def new(*args, &block)
|
15
|
-
|
16
|
-
Class.new(Endpoint).new(*args, &block)
|
17
|
-
else
|
18
|
-
super
|
19
|
-
end
|
17
|
+
self == Endpoint ? Class.new(Endpoint).new(*args, &block) : super
|
20
18
|
end
|
21
19
|
|
22
20
|
def before_each(new_setup = false, &block)
|
23
21
|
@before_each ||= []
|
24
22
|
if new_setup == false
|
25
|
-
|
26
|
-
|
27
|
-
else
|
28
|
-
return @before_each
|
29
|
-
end
|
23
|
+
return @before_each unless block_given?
|
24
|
+
@before_each << block
|
30
25
|
else
|
31
26
|
@before_each = [new_setup]
|
32
27
|
end
|
@@ -34,9 +29,7 @@ module Grape
|
|
34
29
|
|
35
30
|
def run_before_each(endpoint)
|
36
31
|
superclass.run_before_each(endpoint) unless self == Endpoint
|
37
|
-
before_each.each
|
38
|
-
blk.call(endpoint) if blk.respond_to? :call
|
39
|
-
end
|
32
|
+
before_each.each { |blk| blk.call(endpoint) if blk.respond_to?(:call) }
|
40
33
|
end
|
41
34
|
|
42
35
|
# @api private
|
@@ -54,7 +47,7 @@ module Grape
|
|
54
47
|
# @raise [NameError] an instance method with the same name already exists
|
55
48
|
def generate_api_method(method_name, &block)
|
56
49
|
if instance_methods.include?(method_name.to_sym) || instance_methods.include?(method_name.to_s)
|
57
|
-
|
50
|
+
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
|
58
51
|
end
|
59
52
|
|
60
53
|
define_method(method_name, &block)
|
@@ -69,6 +62,18 @@ module Grape
|
|
69
62
|
end
|
70
63
|
end
|
71
64
|
|
65
|
+
# Create a new endpoint.
|
66
|
+
# @param new_settings [InheritableSetting] settings to determine the params,
|
67
|
+
# validations, and other properties from.
|
68
|
+
# @param options [Hash] attributes of this endpoint
|
69
|
+
# @option options path [String or Array] the path to this endpoint, within
|
70
|
+
# the current scope.
|
71
|
+
# @option options method [String or Array] which HTTP method(s) can be used
|
72
|
+
# to reach this endpoint.
|
73
|
+
# @option options route_options [Hash]
|
74
|
+
# @note This happens at the time of API definition, so in this context the
|
75
|
+
# endpoint does not know if it will be mounted under a different endpoint.
|
76
|
+
# @yield a block defining what your API should do when this endpoint is hit
|
72
77
|
def initialize(new_settings, options = {}, &block)
|
73
78
|
require_option(options, :path)
|
74
79
|
require_option(options, :method)
|
@@ -97,8 +102,22 @@ module Grape
|
|
97
102
|
@block = self.class.generate_api_method(method_name, &block)
|
98
103
|
end
|
99
104
|
|
105
|
+
# Update our settings from a given set of stackable parameters. Used when
|
106
|
+
# the endpoint's API is mounted under another one.
|
107
|
+
def inherit_settings(namespace_stackable)
|
108
|
+
inheritable_setting.route[:saved_validations] += namespace_stackable[:validations]
|
109
|
+
parent_declared_params = namespace_stackable[:declared_params]
|
110
|
+
|
111
|
+
if parent_declared_params
|
112
|
+
inheritable_setting.route[:declared_params] ||= []
|
113
|
+
inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten)
|
114
|
+
end
|
115
|
+
|
116
|
+
endpoints && endpoints.each { |e| e.inherit_settings(namespace_stackable) }
|
117
|
+
end
|
118
|
+
|
100
119
|
def require_option(options, key)
|
101
|
-
|
120
|
+
raise Grape::Exceptions::MissingOption.new(key) unless options.key?(key)
|
102
121
|
end
|
103
122
|
|
104
123
|
def method_name
|
@@ -171,7 +190,7 @@ module Grape
|
|
171
190
|
|
172
191
|
def prepare_version
|
173
192
|
version = namespace_inheritable(:version) || []
|
174
|
-
return if version.
|
193
|
+
return if version.empty?
|
175
194
|
version.length == 1 ? version.first.to_s : version
|
176
195
|
end
|
177
196
|
|
@@ -229,9 +248,12 @@ module Grape
|
|
229
248
|
|
230
249
|
run_filters befores, :before
|
231
250
|
|
251
|
+
allowed_methods = env[Grape::Env::GRAPE_METHOD_NOT_ALLOWED]
|
252
|
+
raise Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allowed_methods) if allowed_methods
|
253
|
+
|
232
254
|
run_filters before_validations, :before_validation
|
233
255
|
|
234
|
-
run_validators validations, request
|
256
|
+
run_validators validations, request
|
235
257
|
|
236
258
|
run_filters after_validations, :after_validation
|
237
259
|
|
@@ -245,53 +267,48 @@ module Grape
|
|
245
267
|
end
|
246
268
|
end
|
247
269
|
|
248
|
-
def build_stack
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
block ? b.use(*m, &block) : b.use(*m)
|
268
|
-
end
|
270
|
+
def build_stack(helpers)
|
271
|
+
stack = Grape::Middleware::Stack.new
|
272
|
+
|
273
|
+
stack.use Rack::Head
|
274
|
+
stack.use Class.new(Grape::Middleware::Error),
|
275
|
+
helpers: helpers,
|
276
|
+
format: namespace_inheritable(:format),
|
277
|
+
content_types: namespace_stackable_with_hash(:content_types),
|
278
|
+
default_status: namespace_inheritable(:default_error_status),
|
279
|
+
rescue_all: namespace_inheritable(:rescue_all),
|
280
|
+
rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
|
281
|
+
default_error_formatter: namespace_inheritable(:default_error_formatter),
|
282
|
+
error_formatters: namespace_stackable_with_hash(:error_formatters),
|
283
|
+
rescue_options: namespace_stackable_with_hash(:rescue_options) || {},
|
284
|
+
rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers) || {},
|
285
|
+
base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {},
|
286
|
+
all_rescue_handler: namespace_inheritable(:all_rescue_handler)
|
287
|
+
|
288
|
+
stack.concat namespace_stackable(:middleware)
|
269
289
|
|
270
290
|
if namespace_inheritable(:version)
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
291
|
+
stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
|
292
|
+
versions: namespace_inheritable(:version) ? namespace_inheritable(:version).flatten : nil,
|
293
|
+
version_options: namespace_inheritable(:version_options),
|
294
|
+
prefix: namespace_inheritable(:root_prefix)
|
276
295
|
end
|
277
296
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
b.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
297
|
+
stack.use Grape::Middleware::Formatter,
|
298
|
+
format: namespace_inheritable(:format),
|
299
|
+
default_format: namespace_inheritable(:default_format) || :txt,
|
300
|
+
content_types: namespace_stackable_with_hash(:content_types),
|
301
|
+
formatters: namespace_stackable_with_hash(:formatters),
|
302
|
+
parsers: namespace_stackable_with_hash(:parsers)
|
286
303
|
|
287
|
-
|
304
|
+
builder = stack.build
|
305
|
+
builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
|
306
|
+
builder.to_app
|
288
307
|
end
|
289
308
|
|
290
309
|
def build_helpers
|
291
310
|
helpers = namespace_stackable(:helpers) || []
|
292
|
-
Module.new
|
293
|
-
helpers.each { |mod_to_include| include mod_to_include }
|
294
|
-
end
|
311
|
+
Module.new { helpers.each { |mod_to_include| include mod_to_include } }
|
295
312
|
end
|
296
313
|
|
297
314
|
private :build_stack, :build_helpers
|
@@ -306,10 +323,8 @@ module Grape
|
|
306
323
|
@lazy_initialize_lock.synchronize do
|
307
324
|
return true if @lazy_initialized
|
308
325
|
|
309
|
-
@
|
310
|
-
@
|
311
|
-
self.class.send(:include, mod)
|
312
|
-
end
|
326
|
+
@helpers = build_helpers.tap { |mod| self.class.send(:include, mod) }
|
327
|
+
@app = options[:app] || build_stack(@helpers)
|
313
328
|
|
314
329
|
@lazy_initialized = true
|
315
330
|
end
|
@@ -323,10 +338,12 @@ module Grape
|
|
323
338
|
validator.validate(request)
|
324
339
|
rescue Grape::Exceptions::Validation => e
|
325
340
|
validation_errors << e
|
341
|
+
rescue Grape::Exceptions::ValidationArrayErrors => e
|
342
|
+
validation_errors += e.errors
|
326
343
|
end
|
327
344
|
end
|
328
345
|
|
329
|
-
validation_errors.any? &&
|
346
|
+
validation_errors.any? && raise(Grape::Exceptions::ValidationErrors, errors: validation_errors, headers: header)
|
330
347
|
end
|
331
348
|
|
332
349
|
def run_filters(filters, type = :other)
|