grape 1.0.1 → 1.0.2
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 +5 -5
- data/Appraisals +1 -1
- data/CHANGELOG.md +18 -0
- data/Dangerfile +1 -0
- data/Gemfile +9 -10
- data/Gemfile.lock +42 -40
- data/LICENSE +1 -1
- data/README.md +89 -40
- data/Rakefile +1 -46
- data/gemfiles/multi_json.gemfile +9 -10
- data/gemfiles/multi_xml.gemfile +9 -10
- data/gemfiles/rack_1.5.2.gemfile +9 -10
- data/gemfiles/rack_edge.gemfile +9 -10
- data/gemfiles/rails_3.gemfile +9 -10
- data/gemfiles/rails_4.gemfile +9 -10
- data/gemfiles/rails_5.gemfile +9 -10
- data/gemfiles/rails_edge.gemfile +9 -10
- data/grape.gemspec +3 -3
- data/lib/grape/api.rb +2 -2
- data/lib/grape/dsl/inside_route.rb +30 -10
- data/lib/grape/dsl/routing.rb +1 -1
- data/lib/grape/dsl/settings.rb +6 -6
- data/lib/grape/endpoint.rb +1 -1
- data/lib/grape/exceptions/incompatible_option_values.rb +0 -1
- data/lib/grape/exceptions/invalid_accept_header.rb +0 -1
- data/lib/grape/exceptions/invalid_formatter.rb +0 -1
- data/lib/grape/exceptions/invalid_message_body.rb +0 -1
- data/lib/grape/exceptions/invalid_version_header.rb +0 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +0 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +0 -1
- data/lib/grape/exceptions/method_not_allowed.rb +0 -1
- data/lib/grape/exceptions/missing_group_type.rb +0 -1
- data/lib/grape/exceptions/missing_mime_type.rb +0 -1
- data/lib/grape/exceptions/missing_option.rb +0 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +0 -1
- data/lib/grape/exceptions/unknown_options.rb +0 -1
- data/lib/grape/exceptions/unknown_parameter.rb +0 -1
- data/lib/grape/exceptions/unknown_validator.rb +0 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +0 -1
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/router.rb +2 -0
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +14 -14
- data/lib/grape/validations/params_scope.rb +3 -4
- data/lib/grape/validations/types.rb +14 -1
- data/lib/grape/validations/types/build_coercer.rb +8 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -1
- data/lib/grape/validations/types/custom_type_collection_coercer.rb +71 -0
- data/lib/grape/validations/validators/allow_blank.rb +1 -1
- data/lib/grape/validations/validators/base.rb +1 -0
- data/lib/grape/validations/validators/coerce.rb +6 -0
- data/lib/grape/version.rb +1 -1
- data/pkg/grape-1.0.1.gem +0 -0
- data/spec/grape/api_spec.rb +22 -12
- data/spec/grape/dsl/inside_route_spec.rb +1 -1
- data/spec/grape/dsl/parameters_spec.rb +5 -5
- data/spec/grape/dsl/settings_spec.rb +2 -2
- data/spec/grape/endpoint_spec.rb +25 -12
- data/spec/grape/entity_spec.rb +1 -1
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -1
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -1
- data/spec/grape/exceptions/missing_option_spec.rb +0 -1
- data/spec/grape/exceptions/unknown_options_spec.rb +1 -2
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -1
- data/spec/grape/exceptions/validation_errors_spec.rb +1 -1
- data/spec/grape/middleware/exception_spec.rb +36 -12
- data/spec/grape/middleware/formatter_spec.rb +1 -1
- data/spec/grape/middleware/versioner/header_spec.rb +1 -1
- data/spec/grape/middleware/versioner/param_spec.rb +1 -1
- data/spec/grape/middleware/versioner/path_spec.rb +1 -1
- data/spec/grape/path_spec.rb +3 -3
- data/spec/grape/util/inheritable_setting_spec.rb +2 -2
- data/spec/grape/util/reverse_stackable_values_spec.rb +13 -13
- data/spec/grape/util/stackable_values_spec.rb +13 -13
- data/spec/grape/util/strict_hash_configuration_spec.rb +1 -1
- data/spec/grape/validations/params_scope_spec.rb +19 -7
- data/spec/grape/validations/validators/all_or_none_spec.rb +1 -1
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +1 -1
- data/spec/grape/validations/validators/coerce_spec.rb +94 -13
- data/spec/grape/validations/validators/default_spec.rb +40 -0
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +1 -1
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +1 -1
- data/spec/grape/validations/validators/values_spec.rb +3 -3
- data/spec/grape/validations_spec.rb +9 -9
- data/spec/shared/versioning_examples.rb +58 -0
- data/spec/support/content_type_helpers.rb +1 -1
- metadata +26 -24
data/lib/grape/dsl/routing.rb
CHANGED
@@ -136,7 +136,7 @@ module Grape
|
|
136
136
|
reset_validations!
|
137
137
|
end
|
138
138
|
|
139
|
-
%w
|
139
|
+
%w[get post put head delete options patch].each do |meth|
|
140
140
|
define_method meth do |*args, &block|
|
141
141
|
options = args.extract_options!
|
142
142
|
paths = args.first || ['/']
|
data/lib/grape/dsl/settings.rb
CHANGED
@@ -165,13 +165,13 @@ module Grape
|
|
165
165
|
|
166
166
|
private
|
167
167
|
|
168
|
-
# Builds the current class :inheritable_setting. If available, it
|
169
|
-
#
|
168
|
+
# Builds the current class :inheritable_setting. If available, it inherits from
|
169
|
+
# the superclass's :inheritable_setting.
|
170
170
|
def build_top_level_setting
|
171
|
-
|
172
|
-
superclass.inheritable_setting
|
173
|
-
|
174
|
-
|
171
|
+
Grape::Util::InheritableSetting.new.tap do |setting|
|
172
|
+
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API
|
173
|
+
setting.inherit_from superclass.inheritable_setting
|
174
|
+
end
|
175
175
|
end
|
176
176
|
end
|
177
177
|
end
|
data/lib/grape/endpoint.rb
CHANGED
@@ -44,7 +44,7 @@ module Grape
|
|
44
44
|
# @return [Proc]
|
45
45
|
# @raise [NameError] an instance method with the same name already exists
|
46
46
|
def generate_api_method(method_name, &block)
|
47
|
-
if
|
47
|
+
if method_defined?(method_name)
|
48
48
|
raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
|
49
49
|
end
|
50
50
|
|
data/lib/grape/namespace.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Grape
|
2
2
|
# A container for endpoints or other namespaces, which allows for both
|
3
|
-
# logical grouping of endpoints as well as sharing
|
3
|
+
# logical grouping of endpoints as well as sharing common configuration.
|
4
4
|
# May also be referred to as group, segment, or resource.
|
5
5
|
class Namespace
|
6
6
|
attr_reader :space, :options
|
data/lib/grape/router.rb
CHANGED
@@ -104,6 +104,8 @@ module Grape
|
|
104
104
|
) if neighbor && method == 'OPTIONS' && !cascade
|
105
105
|
|
106
106
|
route = match?(input, '*')
|
107
|
+
return neighbor.endpoint.call(env) if neighbor && cascade && route
|
108
|
+
|
107
109
|
if route
|
108
110
|
response = process_route(route, env)
|
109
111
|
return response if response && !(cascade = cascade?(response))
|
data/lib/grape/router/pattern.rb
CHANGED
@@ -5,7 +5,7 @@ module Grape
|
|
5
5
|
class Router
|
6
6
|
class Pattern
|
7
7
|
DEFAULT_PATTERN_OPTIONS = { uri_decode: true, type: :grape }.freeze
|
8
|
-
DEFAULT_SUPPORTED_CAPTURE = [
|
8
|
+
DEFAULT_SUPPORTED_CAPTURE = %i[format version].freeze
|
9
9
|
|
10
10
|
attr_reader :origin, :path, :capture, :pattern
|
11
11
|
|
data/lib/grape/router/route.rb
CHANGED
@@ -8,7 +8,7 @@ module Grape
|
|
8
8
|
class Route
|
9
9
|
ROUTE_ATTRIBUTE_REGEXP = /route_([_a-zA-Z]\w*)/
|
10
10
|
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')?$/
|
11
|
-
FIXED_NAMED_CAPTURES = %w
|
11
|
+
FIXED_NAMED_CAPTURES = %w[format version].freeze
|
12
12
|
|
13
13
|
attr_accessor :pattern, :translator, :app, :index, :regexp, :options
|
14
14
|
|
@@ -32,19 +32,19 @@ module Grape
|
|
32
32
|
ROUTE_ATTRIBUTE_REGEXP.match(method_id.to_s)
|
33
33
|
end
|
34
34
|
|
35
|
-
[
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
35
|
+
%i[
|
36
|
+
prefix
|
37
|
+
version
|
38
|
+
settings
|
39
|
+
format
|
40
|
+
description
|
41
|
+
http_codes
|
42
|
+
headers
|
43
|
+
entity
|
44
|
+
details
|
45
|
+
requirements
|
46
|
+
request_method
|
47
|
+
namespace
|
48
48
|
].each do |method_name|
|
49
49
|
define_method method_name do
|
50
50
|
attributes.public_send method_name
|
@@ -39,8 +39,7 @@ module Grape
|
|
39
39
|
# @return [Boolean] whether or not this entire scope needs to be
|
40
40
|
# validated
|
41
41
|
def should_validate?(parameters)
|
42
|
-
return false if @optional && (params(parameters).blank? ||
|
43
|
-
any_element_blank?(parameters))
|
42
|
+
return false if @optional && (params(parameters).blank? || all_element_blank?(parameters))
|
44
43
|
|
45
44
|
return true if parent.nil?
|
46
45
|
parent.should_validate?(parameters)
|
@@ -441,8 +440,8 @@ module Grape
|
|
441
440
|
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
|
442
441
|
end
|
443
442
|
|
444
|
-
def
|
445
|
-
params(parameters).respond_to?(:
|
443
|
+
def all_element_blank?(parameters)
|
444
|
+
params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
|
446
445
|
end
|
447
446
|
end
|
448
447
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative 'types/build_coercer'
|
2
2
|
require_relative 'types/custom_type_coercer'
|
3
|
+
require_relative 'types/custom_type_collection_coercer'
|
3
4
|
require_relative 'types/multiple_type_coercer'
|
4
5
|
require_relative 'types/variant_collection_coercer'
|
5
6
|
require_relative 'types/json'
|
@@ -143,7 +144,8 @@ module Grape
|
|
143
144
|
end
|
144
145
|
|
145
146
|
# A valid custom type must implement a class-level `parse` method, taking
|
146
|
-
#
|
147
|
+
# one String argument and returning the parsed value in its correct type.
|
148
|
+
#
|
147
149
|
# @param type [Class] type to check
|
148
150
|
# @return [Boolean] whether or not the type can be used as a custom type
|
149
151
|
def self.custom?(type)
|
@@ -155,6 +157,17 @@ module Grape
|
|
155
157
|
type.respond_to?(:parse) &&
|
156
158
|
type.method(:parse).arity == 1
|
157
159
|
end
|
160
|
+
|
161
|
+
# Is the declared type an +Array+ or +Set+ of a {#custom?} type?
|
162
|
+
#
|
163
|
+
# @param type [Array<Class>,Class] type to check
|
164
|
+
# @return [Boolean] true if +type+ is a collection of a type that implements
|
165
|
+
# its own +#parse+ method.
|
166
|
+
def self.collection_of_custom?(type)
|
167
|
+
(type.is_a?(Array) || type.is_a?(Set)) &&
|
168
|
+
type.length == 1 &&
|
169
|
+
custom?(type.first)
|
170
|
+
end
|
158
171
|
end
|
159
172
|
end
|
160
173
|
end
|
@@ -51,6 +51,14 @@ module Grape
|
|
51
51
|
elsif method || Types.custom?(type)
|
52
52
|
converter_options[:coercer] = Types::CustomTypeCoercer.new(type, method)
|
53
53
|
|
54
|
+
# Special coercer for collections of types that implement a parse method.
|
55
|
+
# CustomTypeCoercer (above) already handles such types when an explicit coercion
|
56
|
+
# method is supplied.
|
57
|
+
elsif Types.collection_of_custom?(type)
|
58
|
+
converter_options[:coercer] = Types::CustomTypeCollectionCoercer.new(
|
59
|
+
type.first, type.is_a?(Set)
|
60
|
+
)
|
61
|
+
|
54
62
|
# Grape swaps in its own Virtus::Attribute implementations
|
55
63
|
# for certain special types that merit first-class support
|
56
64
|
# (but not if a custom coercion method has been supplied).
|
@@ -137,7 +137,7 @@ module Grape
|
|
137
137
|
# passed, or if the type also implements a parse() method.
|
138
138
|
type
|
139
139
|
elsif type.is_a?(Enumerable)
|
140
|
-
->(value) { value.all? { |item| item.is_a? type[0] } }
|
140
|
+
->(value) { value.respond_to?(:all?) && value.all? { |item| item.is_a? type[0] } }
|
141
141
|
else
|
142
142
|
# By default, do a simple type check
|
143
143
|
->(value) { value.is_a? type }
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Grape
|
2
|
+
module Validations
|
3
|
+
module Types
|
4
|
+
# Instances of this class may be passed to
|
5
|
+
# +Virtus::Attribute.build+ as the +:coercer+
|
6
|
+
# option, to handle collections of types that
|
7
|
+
# provide their own parsing (and optionally,
|
8
|
+
# type-checking) functionality.
|
9
|
+
#
|
10
|
+
# See {CustomTypeCoercer} for details on types
|
11
|
+
# that will be supported by this by this coercer.
|
12
|
+
# This coercer works in the same way as +CustomTypeCoercer+
|
13
|
+
# except that it expects to receive an array of strings to
|
14
|
+
# coerce and will return an array (or optionally, a set)
|
15
|
+
# of coerced values.
|
16
|
+
#
|
17
|
+
# +CustomTypeCoercer+ is already capable of providing type
|
18
|
+
# checking for arrays where an independent coercion method
|
19
|
+
# is supplied. As such, +CustomTypeCollectionCoercer+ does
|
20
|
+
# not allow for such a method to be supplied independently
|
21
|
+
# of the type.
|
22
|
+
class CustomTypeCollectionCoercer < CustomTypeCoercer
|
23
|
+
# A new coercer for collections of the given type.
|
24
|
+
#
|
25
|
+
# @param type [Class,#parse]
|
26
|
+
# type to which items in the array should be coerced.
|
27
|
+
# Must implement a +parse+ method which accepts a string,
|
28
|
+
# and for the purposes of type-checking it may either be
|
29
|
+
# a class, or it may implement a +coerced?+, +parsed?+ or
|
30
|
+
# +call+ method (in that order of precedence) which
|
31
|
+
# accepts a single argument and returns true if the given
|
32
|
+
# array item has been coerced correctly.
|
33
|
+
# @param set [Boolean]
|
34
|
+
# when true, a +Set+ will be returned by {#call} instead
|
35
|
+
# of an +Array+ and duplicate items will be discarded.
|
36
|
+
def initialize(type, set = false)
|
37
|
+
super(type)
|
38
|
+
@set = set
|
39
|
+
end
|
40
|
+
|
41
|
+
# This method is called from somewhere within
|
42
|
+
# +Virtus::Attribute::coerce+ in order to coerce
|
43
|
+
# the given value.
|
44
|
+
#
|
45
|
+
# @param value [Array<String>] an array of values to be coerced
|
46
|
+
# @return [Array,Set] the coerced result. May be an +Array+ or a
|
47
|
+
# +Set+ depending on the setting given to the constructor
|
48
|
+
def call(value)
|
49
|
+
coerced = value.map { |item| super(item) }
|
50
|
+
|
51
|
+
@set ? Set.new(coerced) : coerced
|
52
|
+
end
|
53
|
+
|
54
|
+
# This method is called from somewhere within
|
55
|
+
# +Virtus::Attribute::value_coerced?+ in order to assert
|
56
|
+
# that the all of the values in the array have been coerced
|
57
|
+
# successfully.
|
58
|
+
#
|
59
|
+
# @param primitive [Axiom::Types::Type] primitive type for
|
60
|
+
# the coercion as deteced by axiom-types' inference system.
|
61
|
+
# @param value [Enumerable] a coerced result returned from {#call}
|
62
|
+
# @return [true,false] whether or not all of the coerced values in
|
63
|
+
# the collection satisfy type requirements.
|
64
|
+
def success?(primitive, value)
|
65
|
+
value.is_a?(@set ? Set : Array) &&
|
66
|
+
value.all? { |item| super(primitive, item) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -7,7 +7,7 @@ module Grape
|
|
7
7
|
value = params[attr_name]
|
8
8
|
value = value.strip if value.respond_to?(:strip)
|
9
9
|
|
10
|
-
return if
|
10
|
+
return if value == false || value.present?
|
11
11
|
|
12
12
|
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:blank)
|
13
13
|
end
|
@@ -38,6 +38,7 @@ module Grape
|
|
38
38
|
attributes = AttributesIterator.new(self, @scope, params)
|
39
39
|
array_errors = []
|
40
40
|
attributes.each do |resource_params, attr_name|
|
41
|
+
next if !@scope.required? && resource_params.empty?
|
41
42
|
next unless @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
|
42
43
|
next unless @scope.meets_dependency?(resource_params, params)
|
43
44
|
|
@@ -16,6 +16,7 @@ module Grape
|
|
16
16
|
|
17
17
|
def validate_param!(attr_name, params)
|
18
18
|
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless params.is_a? Hash
|
19
|
+
return unless requires_coercion?(params[attr_name])
|
19
20
|
new_value = coerce_value(params[attr_name])
|
20
21
|
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless valid_type?(new_value)
|
21
22
|
params[attr_name] = new_value
|
@@ -63,6 +64,11 @@ module Grape
|
|
63
64
|
def type
|
64
65
|
@option[:type].is_a?(Hash) ? @option[:type][:value] : @option[:type]
|
65
66
|
end
|
67
|
+
|
68
|
+
def requires_coercion?(value)
|
69
|
+
# JSON types do not require coercion if value is valid
|
70
|
+
!valid_type?(value) || converter.coercer.respond_to?(:method) && !converter.is_a?(Grape::Validations::Types::Json)
|
71
|
+
end
|
66
72
|
end
|
67
73
|
end
|
68
74
|
end
|
data/lib/grape/version.rb
CHANGED
data/pkg/grape-1.0.1.gem
ADDED
Binary file
|
data/spec/grape/api_spec.rb
CHANGED
@@ -209,7 +209,7 @@ describe Grape::API do
|
|
209
209
|
expect(last_response.body).to eq('outer')
|
210
210
|
end
|
211
211
|
|
212
|
-
%w
|
212
|
+
%w[group resource resources segment].each do |als|
|
213
213
|
it "`.#{als}` is an alias" do
|
214
214
|
inner_namespace = nil
|
215
215
|
subject.send(als, :awesome) do
|
@@ -318,7 +318,7 @@ describe Grape::API do
|
|
318
318
|
end
|
319
319
|
|
320
320
|
context 'when array of versions provided' do
|
321
|
-
let(:version) { %w
|
321
|
+
let(:version) { %w[v1 v2] }
|
322
322
|
|
323
323
|
it { versioned_get '/', 'v1', using: :path }
|
324
324
|
it { versioned_get '/', 'v2', using: :path }
|
@@ -333,7 +333,7 @@ describe Grape::API do
|
|
333
333
|
end
|
334
334
|
|
335
335
|
it 'header versioned APIs with multiple headers' do
|
336
|
-
subject.version %w
|
336
|
+
subject.version %w[v1 v2], using: :header, vendor: 'test'
|
337
337
|
subject.enable_root_route!
|
338
338
|
|
339
339
|
versioned_get '/', 'v1', using: :header, vendor: 'test'
|
@@ -421,7 +421,7 @@ describe Grape::API do
|
|
421
421
|
end
|
422
422
|
|
423
423
|
it 'allows for multiple verbs' do
|
424
|
-
subject.route([
|
424
|
+
subject.route(%i[get post], '/abc') do
|
425
425
|
'hiya'
|
426
426
|
end
|
427
427
|
|
@@ -435,7 +435,7 @@ describe Grape::API do
|
|
435
435
|
expect(last_response.body).to eql 'hiya'
|
436
436
|
end
|
437
437
|
|
438
|
-
[
|
438
|
+
%i[put post].each do |verb|
|
439
439
|
context verb do
|
440
440
|
['string', :symbol, 1, -1.1, {}, [], true, false, nil].each do |object|
|
441
441
|
it "allows a(n) #{object.class} json object in params" do
|
@@ -473,14 +473,14 @@ describe Grape::API do
|
|
473
473
|
end
|
474
474
|
|
475
475
|
it 'allows for multipart paths' do
|
476
|
-
subject.route([
|
476
|
+
subject.route(%i[get post], '/:id/first') do
|
477
477
|
'first'
|
478
478
|
end
|
479
479
|
|
480
|
-
subject.route([
|
480
|
+
subject.route(%i[get post], '/:id') do
|
481
481
|
'ola'
|
482
482
|
end
|
483
|
-
subject.route([
|
483
|
+
subject.route(%i[get post], '/:id/first/second') do
|
484
484
|
'second'
|
485
485
|
end
|
486
486
|
|
@@ -501,7 +501,7 @@ describe Grape::API do
|
|
501
501
|
'lol'
|
502
502
|
end
|
503
503
|
|
504
|
-
%w
|
504
|
+
%w[get post put delete options patch].each do |m|
|
505
505
|
send(m, '/abc')
|
506
506
|
expect(last_response.body).to eql 'lol'
|
507
507
|
end
|
@@ -538,7 +538,7 @@ describe Grape::API do
|
|
538
538
|
expect(last_response.body).to eql 'catch-all'
|
539
539
|
end
|
540
540
|
|
541
|
-
verbs = %w
|
541
|
+
verbs = %w[post get head delete put options patch]
|
542
542
|
verbs.each do |verb|
|
543
543
|
it "allows and properly constrain a #{verb.upcase} method" do
|
544
544
|
subject.send(verb, '/example') do
|
@@ -2393,7 +2393,7 @@ XML
|
|
2393
2393
|
expect(subject.version).to eq('v2')
|
2394
2394
|
end
|
2395
2395
|
it 'returns versions' do
|
2396
|
-
expect(subject.versions).to eq(%w
|
2396
|
+
expect(subject.versions).to eq(%w[v1 v2])
|
2397
2397
|
end
|
2398
2398
|
it 'sets route paths' do
|
2399
2399
|
expect(subject.routes.size).to be >= 2
|
@@ -3129,6 +3129,16 @@ XML
|
|
3129
3129
|
get '/two/v1/world'
|
3130
3130
|
expect(last_response.status).to eq 200
|
3131
3131
|
end
|
3132
|
+
|
3133
|
+
context 'when mounting class extends a subclass of Grape::API' do
|
3134
|
+
it 'mounts APIs with the same superclass' do
|
3135
|
+
base_api = Class.new(Grape::API)
|
3136
|
+
a = Class.new(base_api)
|
3137
|
+
b = Class.new(base_api)
|
3138
|
+
|
3139
|
+
expect { a.mount b }.to_not raise_error
|
3140
|
+
end
|
3141
|
+
end
|
3132
3142
|
end
|
3133
3143
|
end
|
3134
3144
|
|
@@ -3368,7 +3378,7 @@ XML
|
|
3368
3378
|
end
|
3369
3379
|
it 'array' do
|
3370
3380
|
subject.get '/example' do
|
3371
|
-
%w
|
3381
|
+
%w[example1 example2]
|
3372
3382
|
end
|
3373
3383
|
get '/example'
|
3374
3384
|
expect(last_response.status).to eq(200)
|