grape 0.19.2 → 1.0.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 +8 -0
- data/CHANGELOG.md +40 -22
- data/Gemfile +1 -0
- data/Gemfile.lock +58 -59
- data/LICENSE +1 -1
- data/README.md +94 -49
- data/Rakefile +1 -0
- data/UPGRADING.md +89 -0
- data/benchmark/simple_with_type_coercer.rb +22 -0
- data/gemfiles/multi_json.gemfile +36 -0
- data/gemfiles/multi_xml.gemfile +36 -0
- data/gemfiles/rack_1.5.2.gemfile +1 -0
- data/gemfiles/rack_edge.gemfile +1 -0
- data/gemfiles/rails_3.gemfile +1 -0
- data/gemfiles/rails_4.gemfile +1 -0
- data/gemfiles/rails_5.gemfile +1 -0
- data/gemfiles/rails_edge.gemfile +1 -0
- data/grape.gemspec +0 -3
- data/lib/grape.rb +40 -17
- data/lib/grape/dsl/helpers.rb +32 -18
- data/lib/grape/dsl/inside_route.rb +2 -2
- data/lib/grape/dsl/parameters.rb +26 -0
- data/lib/grape/dsl/routing.rb +1 -1
- data/lib/grape/dsl/settings.rb +1 -1
- data/lib/grape/endpoint.rb +20 -16
- data/lib/grape/error_formatter/json.rb +1 -1
- data/lib/grape/error_formatter/txt.rb +1 -1
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +26 -0
- data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +18 -0
- data/lib/grape/extensions/deep_mergeable_hash.rb +19 -0
- data/lib/grape/extensions/deep_symbolize_hash.rb +30 -0
- data/lib/grape/extensions/hash.rb +23 -0
- data/lib/grape/extensions/hashie/mash.rb +24 -0
- data/lib/grape/formatter/json.rb +1 -1
- data/lib/grape/formatter/serializable_hash.rb +2 -2
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/globals.rb +1 -1
- data/lib/grape/parser/json.rb +2 -2
- data/lib/grape/parser/xml.rb +2 -2
- data/lib/grape/request.rb +11 -10
- data/lib/grape/util/json.rb +8 -0
- data/lib/grape/util/xml.rb +8 -0
- data/lib/grape/validations.rb +4 -0
- data/lib/grape/validations/params_scope.rb +77 -39
- data/lib/grape/validations/types/build_coercer.rb +27 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +18 -4
- data/lib/grape/validations/types/file.rb +2 -3
- data/lib/grape/validations/validator_factory.rb +18 -0
- data/lib/grape/validations/validators/base.rb +4 -5
- data/lib/grape/validations/validators/coerce.rb +4 -0
- data/lib/grape/validations/validators/except_values.rb +20 -0
- data/lib/grape/validations/validators/values.rb +25 -5
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/invalid_format_spec.rb +3 -3
- data/spec/grape/api_spec.rb +28 -16
- data/spec/grape/dsl/helpers_spec.rb +25 -6
- data/spec/grape/endpoint_spec.rb +117 -13
- data/spec/grape/extensions/param_builders/hash_spec.rb +83 -0
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +105 -0
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +79 -0
- data/spec/grape/middleware/formatter_spec.rb +6 -2
- data/spec/grape/request_spec.rb +13 -3
- data/spec/grape/validations/instance_behaivour_spec.rb +44 -0
- data/spec/grape/validations/params_scope_spec.rb +23 -0
- data/spec/grape/validations/types_spec.rb +19 -0
- data/spec/grape/validations/validators/coerce_spec.rb +117 -8
- data/spec/grape/validations/validators/except_values_spec.rb +191 -0
- data/spec/grape/validations/validators/values_spec.rb +78 -0
- data/spec/integration/multi_json/json_spec.rb +7 -0
- data/spec/integration/multi_xml/xml_spec.rb +7 -0
- metadata +30 -46
- data/pkg/grape-0.18.0.gem +0 -0
@@ -20,6 +20,12 @@ module Grape
|
|
20
20
|
# @return [Virtus::Attribute] object to be used
|
21
21
|
# for coercion and type validation
|
22
22
|
def self.build_coercer(type, method = nil)
|
23
|
+
cache_instance(type, method) do
|
24
|
+
create_coercer_instance(type, method)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.create_coercer_instance(type, method = nil)
|
23
29
|
# Accept pre-rolled virtus attributes without interference
|
24
30
|
return type if type.is_a? Virtus::Attribute
|
25
31
|
|
@@ -56,6 +62,27 @@ module Grape
|
|
56
62
|
# for many common ruby types.
|
57
63
|
Virtus::Attribute.build(conversion_type, converter_options)
|
58
64
|
end
|
65
|
+
|
66
|
+
def self.cache_instance(type, method, &_block)
|
67
|
+
key = cache_key(type, method)
|
68
|
+
|
69
|
+
return @__cache[key] if @__cache.key?(key)
|
70
|
+
|
71
|
+
instance = yield
|
72
|
+
|
73
|
+
@__cache_write_lock.synchronize do
|
74
|
+
@__cache[key] = instance
|
75
|
+
end
|
76
|
+
|
77
|
+
instance
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.cache_key(type, method)
|
81
|
+
[type, method].compact.map(&:to_s).join('_')
|
82
|
+
end
|
83
|
+
|
84
|
+
instance_variable_set(:@__cache, {})
|
85
|
+
instance_variable_set(:@__cache_write_lock, Mutex.new)
|
59
86
|
end
|
60
87
|
end
|
61
88
|
end
|
@@ -147,7 +147,7 @@ module Grape
|
|
147
147
|
# Enforce symbolized keys for complex types
|
148
148
|
# by wrapping the coercion method such that
|
149
149
|
# any Hash objects in the immediate heirarchy
|
150
|
-
#
|
150
|
+
# have their keys recursively symbolized.
|
151
151
|
# This helps common libs such as JSON to work easily.
|
152
152
|
#
|
153
153
|
# @param type see #new
|
@@ -161,8 +161,8 @@ module Grape
|
|
161
161
|
if type == Array || type == Set
|
162
162
|
lambda do |val|
|
163
163
|
method.call(val).tap do |new_value|
|
164
|
-
new_value.
|
165
|
-
|
164
|
+
new_value.map do |item|
|
165
|
+
item.is_a?(Hash) ? symbolize_keys(item) : item
|
166
166
|
end
|
167
167
|
end
|
168
168
|
end
|
@@ -170,7 +170,7 @@ module Grape
|
|
170
170
|
# Hash objects are processed directly
|
171
171
|
elsif type == Hash
|
172
172
|
lambda do |val|
|
173
|
-
|
173
|
+
symbolize_keys method.call(val)
|
174
174
|
end
|
175
175
|
|
176
176
|
# Simple types are not processed.
|
@@ -179,6 +179,20 @@ module Grape
|
|
179
179
|
method
|
180
180
|
end
|
181
181
|
end
|
182
|
+
|
183
|
+
def symbolize_keys!(hash)
|
184
|
+
hash.each_key do |key|
|
185
|
+
hash[key.to_sym] = hash.delete(key) if key.respond_to?(:to_sym)
|
186
|
+
end
|
187
|
+
hash
|
188
|
+
end
|
189
|
+
|
190
|
+
def symbolize_keys(hash)
|
191
|
+
hash.inject({}) do |new_hash, (key, value)|
|
192
|
+
new_key = key.respond_to?(:to_sym) ? key.to_sym : key
|
193
|
+
new_hash.merge!(new_key => value)
|
194
|
+
end
|
195
|
+
end
|
182
196
|
end
|
183
197
|
end
|
184
198
|
end
|
@@ -17,10 +17,9 @@ module Grape
|
|
17
17
|
|
18
18
|
def value_coerced?(value)
|
19
19
|
# Rack::Request creates a Hash with filename,
|
20
|
-
# content type and an IO object.
|
21
|
-
# using hashie for convenience. Do a bit of basic
|
20
|
+
# content type and an IO object. Do a bit of basic
|
22
21
|
# duck-typing.
|
23
|
-
value.is_a?(
|
22
|
+
value.is_a?(::Hash) && value.key?(:tempfile)
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Grape
|
2
|
+
module Validations
|
3
|
+
class ValidatorFactory
|
4
|
+
def initialize(**options)
|
5
|
+
@validator_class = options.delete(:validator_class)
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_validator
|
10
|
+
@validator_class.new(@options[:attributes],
|
11
|
+
@options[:options],
|
12
|
+
@options[:required],
|
13
|
+
@options[:params_scope],
|
14
|
+
@options[:opts])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -20,7 +20,6 @@ module Grape
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# Validates a given request.
|
23
|
-
# @note This method must be thread-safe.
|
24
23
|
# @note Override #validate! unless you need to access the entire request.
|
25
24
|
# @param request [Grape::Request] the request currently being handled
|
26
25
|
# @raise [Grape::Exceptions::Validation] if validation failed
|
@@ -31,8 +30,7 @@ module Grape
|
|
31
30
|
end
|
32
31
|
|
33
32
|
# Validates a given parameter hash.
|
34
|
-
# @note
|
35
|
-
# @note Override #validate iff you need to access the entire request.
|
33
|
+
# @note Override #validate if you need to access the entire request.
|
36
34
|
# @param params [Hash] parameters to validate
|
37
35
|
# @raise [Grape::Exceptions::Validation] if validation failed
|
38
36
|
# @return [void]
|
@@ -41,6 +39,7 @@ module Grape
|
|
41
39
|
array_errors = []
|
42
40
|
attributes.each do |resource_params, attr_name|
|
43
41
|
next unless @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
|
42
|
+
next unless @scope.meets_dependency?(resource_params)
|
44
43
|
|
45
44
|
begin
|
46
45
|
validate_param!(attr_name, resource_params)
|
@@ -64,8 +63,8 @@ module Grape
|
|
64
63
|
end
|
65
64
|
|
66
65
|
def self.inherited(klass)
|
67
|
-
|
68
|
-
Validations.register_validator(
|
66
|
+
return unless klass.name.present?
|
67
|
+
Validations.register_validator(convert_to_short_name(klass), klass)
|
69
68
|
end
|
70
69
|
|
71
70
|
def message(default_key = nil)
|
@@ -10,6 +10,10 @@ module Grape
|
|
10
10
|
@converter = Types.build_coercer(type, @option[:method])
|
11
11
|
end
|
12
12
|
|
13
|
+
def validate(request)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
13
17
|
def validate_param!(attr_name, params)
|
14
18
|
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:coerce) unless params.is_a? Hash
|
15
19
|
new_value = coerce_value(params[attr_name])
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Grape
|
2
|
+
module Validations
|
3
|
+
class ExceptValuesValidator < Base
|
4
|
+
def initialize(attrs, options, required, scope, opts = {})
|
5
|
+
@except = options.is_a?(Hash) ? options[:value] : options
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate_param!(attr_name, params)
|
10
|
+
return unless params.respond_to?(:key?) && params.key?(attr_name)
|
11
|
+
|
12
|
+
excepts = @except.is_a?(Proc) ? @except.call : @except
|
13
|
+
return if excepts.nil?
|
14
|
+
|
15
|
+
param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
|
16
|
+
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:except_values) if param_array.any? { |param| excepts.include?(param) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -6,8 +6,17 @@ module Grape
|
|
6
6
|
@excepts = options[:except]
|
7
7
|
@values = options[:value]
|
8
8
|
@proc = options[:proc]
|
9
|
+
|
10
|
+
warn '[DEPRECATION] The values validator except option is deprecated. ' \
|
11
|
+
'Use the except validator instead.' if @excepts
|
12
|
+
|
9
13
|
raise ArgumentError, 'proc must be a Proc' if @proc && !@proc.is_a?(Proc)
|
14
|
+
warn '[DEPRECATION] The values validator proc option is deprecated. ' \
|
15
|
+
'The lambda expression can now be assigned directly to values.' if @proc
|
10
16
|
else
|
17
|
+
@excepts = nil
|
18
|
+
@values = nil
|
19
|
+
@proc = nil
|
11
20
|
@values = options
|
12
21
|
end
|
13
22
|
super
|
@@ -17,15 +26,13 @@ module Grape
|
|
17
26
|
return unless params.is_a?(Hash)
|
18
27
|
return unless params[attr_name] || required_for_root_scope?
|
19
28
|
|
20
|
-
values = @values.is_a?(Proc) ? @values.call : @values
|
21
|
-
excepts = @excepts.is_a?(Proc) ? @excepts.call : @excepts
|
22
29
|
param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
|
23
30
|
|
24
31
|
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: except_message \
|
25
|
-
|
32
|
+
unless check_excepts(param_array)
|
26
33
|
|
27
34
|
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values) \
|
28
|
-
|
35
|
+
unless check_values(param_array)
|
29
36
|
|
30
37
|
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values) \
|
31
38
|
if @proc && !param_array.all? { |param| @proc.call(param) }
|
@@ -33,9 +40,22 @@ module Grape
|
|
33
40
|
|
34
41
|
private
|
35
42
|
|
43
|
+
def check_values(param_array)
|
44
|
+
values = @values.is_a?(Proc) && @values.arity.zero? ? @values.call : @values
|
45
|
+
return true if values.nil?
|
46
|
+
return param_array.all? { |param| values.call(param) } if values.is_a? Proc
|
47
|
+
param_array.all? { |param| values.include?(param) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_excepts(param_array)
|
51
|
+
excepts = @excepts.is_a?(Proc) ? @excepts.call : @excepts
|
52
|
+
return true if excepts.nil?
|
53
|
+
param_array.none? { |param| excepts.include?(param) }
|
54
|
+
end
|
55
|
+
|
36
56
|
def except_message
|
37
57
|
options = instance_variable_get(:@option)
|
38
|
-
options_key?(:except_message) ? options[:except_message] : message(:
|
58
|
+
options_key?(:except_message) ? options[:except_message] : message(:except_values)
|
39
59
|
end
|
40
60
|
|
41
61
|
def required_for_root_scope?
|
data/lib/grape/version.rb
CHANGED
@@ -27,17 +27,17 @@ describe Grape::Endpoint do
|
|
27
27
|
it 'no format' do
|
28
28
|
get '/foo'
|
29
29
|
expect(last_response.status).to eq 200
|
30
|
-
expect(last_response.body).to eq(
|
30
|
+
expect(last_response.body).to eq(::Grape::Json.dump(id: 'foo', format: nil))
|
31
31
|
end
|
32
32
|
it 'json format' do
|
33
33
|
get '/foo.json'
|
34
34
|
expect(last_response.status).to eq 200
|
35
|
-
expect(last_response.body).to eq(
|
35
|
+
expect(last_response.body).to eq(::Grape::Json.dump(id: 'foo', format: 'json'))
|
36
36
|
end
|
37
37
|
it 'invalid format' do
|
38
38
|
get '/foo.invalid'
|
39
39
|
expect(last_response.status).to eq 200
|
40
|
-
expect(last_response.body).to eq(
|
40
|
+
expect(last_response.body).to eq(::Grape::Json.dump(id: 'foo', format: 'invalid'))
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
data/spec/grape/api_spec.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'shared/versioning_examples'
|
3
|
-
require 'grape-entity'
|
4
3
|
|
5
4
|
describe Grape::API do
|
6
5
|
subject { Class.new(Grape::API) }
|
@@ -444,9 +443,9 @@ describe Grape::API do
|
|
444
443
|
subject.send(verb) do
|
445
444
|
env['api.request.body']
|
446
445
|
end
|
447
|
-
send verb, '/',
|
446
|
+
send verb, '/', ::Grape::Json.dump(object), 'CONTENT_TYPE' => 'application/json'
|
448
447
|
expect(last_response.status).to eq(verb == :post ? 201 : 200)
|
449
|
-
expect(last_response.body).to eql
|
448
|
+
expect(last_response.body).to eql ::Grape::Json.dump(object)
|
450
449
|
expect(last_request.params).to eql({})
|
451
450
|
end
|
452
451
|
it 'stores input in api.request.input' do
|
@@ -454,9 +453,9 @@ describe Grape::API do
|
|
454
453
|
subject.send(verb) do
|
455
454
|
env['api.request.input']
|
456
455
|
end
|
457
|
-
send verb, '/',
|
456
|
+
send verb, '/', ::Grape::Json.dump(object), 'CONTENT_TYPE' => 'application/json'
|
458
457
|
expect(last_response.status).to eq(verb == :post ? 201 : 200)
|
459
|
-
expect(last_response.body).to eql
|
458
|
+
expect(last_response.body).to eql ::Grape::Json.dump(object).to_json
|
460
459
|
end
|
461
460
|
context 'chunked transfer encoding' do
|
462
461
|
it 'stores input in api.request.input' do
|
@@ -464,9 +463,9 @@ describe Grape::API do
|
|
464
463
|
subject.send(verb) do
|
465
464
|
env['api.request.input']
|
466
465
|
end
|
467
|
-
send verb, '/',
|
466
|
+
send verb, '/', ::Grape::Json.dump(object), 'CONTENT_TYPE' => 'application/json', 'HTTP_TRANSFER_ENCODING' => 'chunked', 'CONTENT_LENGTH' => nil
|
468
467
|
expect(last_response.status).to eq(verb == :post ? 201 : 200)
|
469
|
-
expect(last_response.body).to eql
|
468
|
+
expect(last_response.body).to eql ::Grape::Json.dump(object).to_json
|
470
469
|
end
|
471
470
|
end
|
472
471
|
end
|
@@ -2053,7 +2052,7 @@ XML
|
|
2053
2052
|
raise 'rain!'
|
2054
2053
|
end
|
2055
2054
|
get '/exception'
|
2056
|
-
json =
|
2055
|
+
json = ::Grape::Json.load(last_response.body)
|
2057
2056
|
expect(json['error']).to eql 'rain!'
|
2058
2057
|
expect(json['backtrace'].length).to be > 0
|
2059
2058
|
end
|
@@ -2225,14 +2224,27 @@ XML
|
|
2225
2224
|
expect(last_response.body).to eql 'elpmis'
|
2226
2225
|
end
|
2227
2226
|
end
|
2228
|
-
|
2229
|
-
|
2230
|
-
|
2231
|
-
|
2227
|
+
if Object.const_defined? :MultiXml
|
2228
|
+
context 'multi_xml' do
|
2229
|
+
it "doesn't parse yaml" do
|
2230
|
+
subject.put :yaml do
|
2231
|
+
params[:tag]
|
2232
|
+
end
|
2233
|
+
put '/yaml', '<tag type="symbol">a123</tag>', 'CONTENT_TYPE' => 'application/xml'
|
2234
|
+
expect(last_response.status).to eq(400)
|
2235
|
+
expect(last_response.body).to eql 'Disallowed type attribute: "symbol"'
|
2236
|
+
end
|
2237
|
+
end
|
2238
|
+
else
|
2239
|
+
context 'default xml parser' do
|
2240
|
+
it 'parses symbols' do
|
2241
|
+
subject.put :yaml do
|
2242
|
+
params[:tag]
|
2243
|
+
end
|
2244
|
+
put '/yaml', '<tag type="symbol">a123</tag>', 'CONTENT_TYPE' => 'application/xml'
|
2245
|
+
expect(last_response.status).to eq(200)
|
2246
|
+
expect(last_response.body).to eql '{"type"=>"symbol", "__content__"=>"a123"}'
|
2232
2247
|
end
|
2233
|
-
put '/yaml', '<tag type="symbol">a123</tag>', 'CONTENT_TYPE' => 'application/xml'
|
2234
|
-
expect(last_response.status).to eq(400)
|
2235
|
-
expect(last_response.body).to eql 'Disallowed type attribute: "symbol"'
|
2236
2248
|
end
|
2237
2249
|
end
|
2238
2250
|
context 'none parser class' do
|
@@ -3156,7 +3168,7 @@ XML
|
|
3156
3168
|
end
|
3157
3169
|
it 'path' do
|
3158
3170
|
get '/endpoint/options'
|
3159
|
-
options =
|
3171
|
+
options = ::Grape::Json.load(last_response.body)
|
3160
3172
|
expect(options['path']).to eq(['/endpoint/options'])
|
3161
3173
|
expect(options['source_location'][0]).to include 'api_spec.rb'
|
3162
3174
|
expect(options['source_location'][1].to_i).to be > 0
|
@@ -6,8 +6,12 @@ module Grape
|
|
6
6
|
class Dummy
|
7
7
|
include Grape::DSL::Helpers
|
8
8
|
|
9
|
-
def self.
|
10
|
-
namespace_stackable(:helpers)
|
9
|
+
def self.mods
|
10
|
+
namespace_stackable(:helpers)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.first_mod
|
14
|
+
mods.first
|
11
15
|
end
|
12
16
|
end
|
13
17
|
end
|
@@ -36,23 +40,38 @@ module Grape
|
|
36
40
|
expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original
|
37
41
|
subject.helpers(&proc)
|
38
42
|
|
39
|
-
expect(subject.
|
43
|
+
expect(subject.first_mod.instance_methods).to include(:test)
|
40
44
|
end
|
41
45
|
|
42
46
|
it 'uses provided modules' do
|
43
47
|
mod = Module.new
|
44
48
|
|
45
|
-
expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original
|
49
|
+
expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original.exactly(2).times
|
46
50
|
expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original
|
47
51
|
subject.helpers(mod, &proc)
|
48
52
|
|
49
|
-
expect(subject.
|
53
|
+
expect(subject.first_mod).to eq mod
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'uses many provided modules' do
|
57
|
+
mod = Module.new
|
58
|
+
mod2 = Module.new
|
59
|
+
mod3 = Module.new
|
60
|
+
|
61
|
+
expect(subject).to receive(:namespace_stackable).with(:helpers, kind_of(Grape::DSL::Helpers::BaseHelper)).and_call_original.exactly(4).times
|
62
|
+
expect(subject).to receive(:namespace_stackable).with(:helpers).and_call_original.exactly(3).times
|
63
|
+
|
64
|
+
subject.helpers(mod, mod2, mod3, &proc)
|
65
|
+
|
66
|
+
expect(subject.mods).to include(mod)
|
67
|
+
expect(subject.mods).to include(mod2)
|
68
|
+
expect(subject.mods).to include(mod3)
|
50
69
|
end
|
51
70
|
|
52
71
|
context 'with an external file' do
|
53
72
|
it 'sets Boolean as a Virtus::Attribute::Boolean' do
|
54
73
|
subject.helpers BooleanParam
|
55
|
-
expect(subject.
|
74
|
+
expect(subject.first_mod::Boolean).to eq Virtus::Attribute::Boolean
|
56
75
|
end
|
57
76
|
end
|
58
77
|
end
|
data/spec/grape/endpoint_spec.rb
CHANGED
@@ -249,6 +249,35 @@ describe Grape::Endpoint do
|
|
249
249
|
end
|
250
250
|
end
|
251
251
|
|
252
|
+
describe '#params' do
|
253
|
+
context 'default class' do
|
254
|
+
it 'should be a ActiveSupport::HashWithIndifferentAccess' do
|
255
|
+
subject.get '/foo' do
|
256
|
+
params.class
|
257
|
+
end
|
258
|
+
|
259
|
+
get '/foo'
|
260
|
+
expect(last_response.status).to eq(200)
|
261
|
+
expect(last_response.body).to eq('ActiveSupport::HashWithIndifferentAccess')
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
context 'sets a value to params' do
|
266
|
+
it 'params' do
|
267
|
+
subject.params do
|
268
|
+
requires :a, type: String
|
269
|
+
end
|
270
|
+
subject.get '/foo' do
|
271
|
+
params[:a] = 'bar'
|
272
|
+
end
|
273
|
+
|
274
|
+
get '/foo', a: 'foo'
|
275
|
+
expect(last_response.status).to eq(200)
|
276
|
+
expect(last_response.body).to eq('bar')
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
252
281
|
describe '#declared' do
|
253
282
|
before do
|
254
283
|
subject.format :json
|
@@ -269,6 +298,47 @@ describe Grape::Endpoint do
|
|
269
298
|
end
|
270
299
|
end
|
271
300
|
|
301
|
+
context 'when params are not built with default class' do
|
302
|
+
it 'returns an object that corresponds with the params class - hash with indifferent access' do
|
303
|
+
subject.params do
|
304
|
+
build_with Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
|
305
|
+
end
|
306
|
+
subject.get '/declared' do
|
307
|
+
d = declared(params, include_missing: true)
|
308
|
+
{ declared_class: d.class.to_s }
|
309
|
+
end
|
310
|
+
|
311
|
+
get '/declared?first=present'
|
312
|
+
expect(JSON.parse(last_response.body)['declared_class']).to eq('ActiveSupport::HashWithIndifferentAccess')
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'returns an object that corresponds with the params class - hashie mash' do
|
316
|
+
subject.params do
|
317
|
+
build_with Grape::Extensions::Hashie::Mash::ParamBuilder
|
318
|
+
end
|
319
|
+
subject.get '/declared' do
|
320
|
+
d = declared(params, include_missing: true)
|
321
|
+
{ declared_class: d.class.to_s }
|
322
|
+
end
|
323
|
+
|
324
|
+
get '/declared?first=present'
|
325
|
+
expect(JSON.parse(last_response.body)['declared_class']).to eq('Hashie::Mash')
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'returns an object that corresponds with the params class - hash' do
|
329
|
+
subject.params do
|
330
|
+
build_with Grape::Extensions::Hash::ParamBuilder
|
331
|
+
end
|
332
|
+
subject.get '/declared' do
|
333
|
+
d = declared(params, include_missing: true)
|
334
|
+
{ declared_class: d.class.to_s }
|
335
|
+
end
|
336
|
+
|
337
|
+
get '/declared?first=present'
|
338
|
+
expect(JSON.parse(last_response.body)['declared_class']).to eq('Hash')
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
272
342
|
it 'should show nil for nested params if include_missing is true' do
|
273
343
|
subject.get '/declared' do
|
274
344
|
declared(params, include_missing: true)
|
@@ -400,7 +470,7 @@ describe Grape::Endpoint do
|
|
400
470
|
''
|
401
471
|
end
|
402
472
|
|
403
|
-
post '/declared',
|
473
|
+
post '/declared', ::Grape::Json.dump(first: 'one', boolean: false), 'CONTENT_TYPE' => 'application/json'
|
404
474
|
expect(last_response.status).to eq(201)
|
405
475
|
end
|
406
476
|
|
@@ -415,7 +485,7 @@ describe Grape::Endpoint do
|
|
415
485
|
''
|
416
486
|
end
|
417
487
|
|
418
|
-
post '/declared',
|
488
|
+
post '/declared', ::Grape::Json.dump(first: 'one', second: nil), 'CONTENT_TYPE' => 'application/json'
|
419
489
|
expect(last_response.status).to eq(201)
|
420
490
|
end
|
421
491
|
|
@@ -775,6 +845,7 @@ describe Grape::Endpoint do
|
|
775
845
|
end
|
776
846
|
end
|
777
847
|
end
|
848
|
+
|
778
849
|
it 'parse email param with provided requirements for params' do
|
779
850
|
get '/outer/abc@example.com'
|
780
851
|
expect(last_response.body).to eq('abc@example.com')
|
@@ -802,7 +873,7 @@ describe Grape::Endpoint do
|
|
802
873
|
end
|
803
874
|
|
804
875
|
it 'converts JSON bodies to params' do
|
805
|
-
post '/request_body',
|
876
|
+
post '/request_body', ::Grape::Json.dump(user: 'Bobby T.'), 'CONTENT_TYPE' => 'application/json'
|
806
877
|
expect(last_response.body).to eq('Bobby T.')
|
807
878
|
end
|
808
879
|
|
@@ -811,14 +882,26 @@ describe Grape::Endpoint do
|
|
811
882
|
expect(last_response.body).to eq('')
|
812
883
|
end
|
813
884
|
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
885
|
+
if Object.const_defined? :MultiXml
|
886
|
+
it 'converts XML bodies to params' do
|
887
|
+
post '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
888
|
+
expect(last_response.body).to eq('Bobby T.')
|
889
|
+
end
|
818
890
|
|
819
|
-
|
820
|
-
|
821
|
-
|
891
|
+
it 'converts XML bodies to params' do
|
892
|
+
put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
893
|
+
expect(last_response.body).to eq('Bobby T.')
|
894
|
+
end
|
895
|
+
else
|
896
|
+
it 'converts XML bodies to params' do
|
897
|
+
post '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
898
|
+
expect(last_response.body).to eq('{"__content__"=>"Bobby T."}')
|
899
|
+
end
|
900
|
+
|
901
|
+
it 'converts XML bodies to params' do
|
902
|
+
put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
|
903
|
+
expect(last_response.body).to eq('{"__content__"=>"Bobby T."}')
|
904
|
+
end
|
822
905
|
end
|
823
906
|
|
824
907
|
it 'does not include parameters not defined by the body' do
|
@@ -826,7 +909,7 @@ describe Grape::Endpoint do
|
|
826
909
|
error! 400, 'expected nil' if params[:version]
|
827
910
|
params[:user]
|
828
911
|
end
|
829
|
-
post '/omitted_params',
|
912
|
+
post '/omitted_params', ::Grape::Json.dump(user: 'Bob'), 'CONTENT_TYPE' => 'application/json'
|
830
913
|
expect(last_response.status).to eq(201)
|
831
914
|
expect(last_response.body).to eq('Bob')
|
832
915
|
end
|
@@ -849,7 +932,7 @@ describe Grape::Endpoint do
|
|
849
932
|
subject.put '/request_body' do
|
850
933
|
params[:user]
|
851
934
|
end
|
852
|
-
put '/request_body',
|
935
|
+
put '/request_body', ::Grape::Json.dump(user: 'Bob'), 'CONTENT_TYPE' => 'text/plain'
|
853
936
|
|
854
937
|
expect(last_response.status).to eq(406)
|
855
938
|
expect(last_response.body).to eq('{"error":"The requested content-type \'text/plain\' is not supported."}')
|
@@ -863,7 +946,7 @@ describe Grape::Endpoint do
|
|
863
946
|
subject.post do
|
864
947
|
params[:data]
|
865
948
|
end
|
866
|
-
post '/',
|
949
|
+
post '/', ::Grape::Json.dump(data: { some: 'payload' }), 'CONTENT_TYPE' => 'application/json'
|
867
950
|
end
|
868
951
|
|
869
952
|
it 'should not response with 406 for same type without params' do
|
@@ -911,6 +994,21 @@ describe Grape::Endpoint do
|
|
911
994
|
expect(JSON.parse(last_response.body)['params']).to eq '123'
|
912
995
|
end
|
913
996
|
end
|
997
|
+
|
998
|
+
context 'sets a value to params' do
|
999
|
+
it 'params' do
|
1000
|
+
subject.params do
|
1001
|
+
requires :a, type: String
|
1002
|
+
end
|
1003
|
+
subject.get '/foo' do
|
1004
|
+
params[:a] = 'bar'
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
get '/foo', a: 'foo'
|
1008
|
+
expect(last_response.status).to eq(200)
|
1009
|
+
expect(last_response.body).to eq('bar')
|
1010
|
+
end
|
1011
|
+
end
|
914
1012
|
end
|
915
1013
|
|
916
1014
|
describe '#error!' do
|
@@ -1358,6 +1456,9 @@ describe Grape::Endpoint do
|
|
1358
1456
|
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
|
1359
1457
|
filters: [],
|
1360
1458
|
type: :before_validation }),
|
1459
|
+
have_attributes(name: 'endpoint_run_validators.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
|
1460
|
+
validators: [],
|
1461
|
+
request: a_kind_of(Grape::Request) }),
|
1361
1462
|
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
|
1362
1463
|
filters: [],
|
1363
1464
|
type: :after_validation }),
|
@@ -1379,6 +1480,9 @@ describe Grape::Endpoint do
|
|
1379
1480
|
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
|
1380
1481
|
filters: [],
|
1381
1482
|
type: :before_validation }),
|
1483
|
+
have_attributes(name: 'endpoint_run_validators.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
|
1484
|
+
validators: [],
|
1485
|
+
request: a_kind_of(Grape::Request) }),
|
1382
1486
|
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(Grape::Endpoint),
|
1383
1487
|
filters: [],
|
1384
1488
|
type: :after_validation }),
|