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.

Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +8 -0
  3. data/CHANGELOG.md +40 -22
  4. data/Gemfile +1 -0
  5. data/Gemfile.lock +58 -59
  6. data/LICENSE +1 -1
  7. data/README.md +94 -49
  8. data/Rakefile +1 -0
  9. data/UPGRADING.md +89 -0
  10. data/benchmark/simple_with_type_coercer.rb +22 -0
  11. data/gemfiles/multi_json.gemfile +36 -0
  12. data/gemfiles/multi_xml.gemfile +36 -0
  13. data/gemfiles/rack_1.5.2.gemfile +1 -0
  14. data/gemfiles/rack_edge.gemfile +1 -0
  15. data/gemfiles/rails_3.gemfile +1 -0
  16. data/gemfiles/rails_4.gemfile +1 -0
  17. data/gemfiles/rails_5.gemfile +1 -0
  18. data/gemfiles/rails_edge.gemfile +1 -0
  19. data/grape.gemspec +0 -3
  20. data/lib/grape.rb +40 -17
  21. data/lib/grape/dsl/helpers.rb +32 -18
  22. data/lib/grape/dsl/inside_route.rb +2 -2
  23. data/lib/grape/dsl/parameters.rb +26 -0
  24. data/lib/grape/dsl/routing.rb +1 -1
  25. data/lib/grape/dsl/settings.rb +1 -1
  26. data/lib/grape/endpoint.rb +20 -16
  27. data/lib/grape/error_formatter/json.rb +1 -1
  28. data/lib/grape/error_formatter/txt.rb +1 -1
  29. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +26 -0
  30. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +18 -0
  31. data/lib/grape/extensions/deep_mergeable_hash.rb +19 -0
  32. data/lib/grape/extensions/deep_symbolize_hash.rb +30 -0
  33. data/lib/grape/extensions/hash.rb +23 -0
  34. data/lib/grape/extensions/hashie/mash.rb +24 -0
  35. data/lib/grape/formatter/json.rb +1 -1
  36. data/lib/grape/formatter/serializable_hash.rb +2 -2
  37. data/lib/grape/locale/en.yml +1 -1
  38. data/lib/grape/middleware/globals.rb +1 -1
  39. data/lib/grape/parser/json.rb +2 -2
  40. data/lib/grape/parser/xml.rb +2 -2
  41. data/lib/grape/request.rb +11 -10
  42. data/lib/grape/util/json.rb +8 -0
  43. data/lib/grape/util/xml.rb +8 -0
  44. data/lib/grape/validations.rb +4 -0
  45. data/lib/grape/validations/params_scope.rb +77 -39
  46. data/lib/grape/validations/types/build_coercer.rb +27 -0
  47. data/lib/grape/validations/types/custom_type_coercer.rb +18 -4
  48. data/lib/grape/validations/types/file.rb +2 -3
  49. data/lib/grape/validations/validator_factory.rb +18 -0
  50. data/lib/grape/validations/validators/base.rb +4 -5
  51. data/lib/grape/validations/validators/coerce.rb +4 -0
  52. data/lib/grape/validations/validators/except_values.rb +20 -0
  53. data/lib/grape/validations/validators/values.rb +25 -5
  54. data/lib/grape/version.rb +1 -1
  55. data/spec/grape/api/invalid_format_spec.rb +3 -3
  56. data/spec/grape/api_spec.rb +28 -16
  57. data/spec/grape/dsl/helpers_spec.rb +25 -6
  58. data/spec/grape/endpoint_spec.rb +117 -13
  59. data/spec/grape/extensions/param_builders/hash_spec.rb +83 -0
  60. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +105 -0
  61. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +79 -0
  62. data/spec/grape/middleware/formatter_spec.rb +6 -2
  63. data/spec/grape/request_spec.rb +13 -3
  64. data/spec/grape/validations/instance_behaivour_spec.rb +44 -0
  65. data/spec/grape/validations/params_scope_spec.rb +23 -0
  66. data/spec/grape/validations/types_spec.rb +19 -0
  67. data/spec/grape/validations/validators/coerce_spec.rb +117 -8
  68. data/spec/grape/validations/validators/except_values_spec.rb +191 -0
  69. data/spec/grape/validations/validators/values_spec.rb +78 -0
  70. data/spec/integration/multi_json/json_spec.rb +7 -0
  71. data/spec/integration/multi_xml/xml_spec.rb +7 -0
  72. metadata +30 -46
  73. 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
- # are passed through +Hashie.symbolize_keys!+.
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.each do |item|
165
- Hashie.symbolize_keys!(item) if item.is_a? Hash
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
- Hashie.symbolize_keys! method.call(val)
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. Grape wraps that
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?(Hashie::Mash) && value.key?(:tempfile)
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 This method must be thread-safe.
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
- short_name = convert_to_short_name(klass)
68
- Validations.register_validator(short_name, klass)
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
- if !excepts.nil? && param_array.any? { |param| excepts.include?(param) }
32
+ unless check_excepts(param_array)
26
33
 
27
34
  raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:values) \
28
- if !values.nil? && !param_array.all? { |param| values.include?(param) }
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(:except)
58
+ options_key?(:except_message) ? options[:except_message] : message(:except_values)
39
59
  end
40
60
 
41
61
  def required_for_root_scope?
@@ -1,4 +1,4 @@
1
1
  module Grape
2
2
  # The current version of Grape.
3
- VERSION = '0.19.2'.freeze
3
+ VERSION = '1.0.0'.freeze
4
4
  end
@@ -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(MultiJson.dump(id: 'foo', format: nil))
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(MultiJson.dump(id: 'foo', format: 'json'))
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(MultiJson.dump(id: 'foo', format: 'invalid'))
40
+ expect(last_response.body).to eq(::Grape::Json.dump(id: 'foo', format: 'invalid'))
41
41
  end
42
42
  end
43
43
  end
@@ -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, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json'
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 MultiJson.dump(object)
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, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json'
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 MultiJson.dump(object).to_json
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, '/', MultiJson.dump(object), 'CONTENT_TYPE' => 'application/json', 'HTTP_TRANSFER_ENCODING' => 'chunked', 'CONTENT_LENGTH' => nil
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 MultiJson.dump(object).to_json
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 = MultiJson.load(last_response.body)
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
- context 'multi_xml' do
2229
- it "doesn't parse yaml" do
2230
- subject.put :yaml do
2231
- params[:tag]
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 = MultiJson.load(last_response.body)
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.mod
10
- namespace_stackable(:helpers).first
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.mod.instance_methods).to include(:test)
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.mod).to eq mod
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.mod::Boolean).to eq Virtus::Attribute::Boolean
74
+ expect(subject.first_mod::Boolean).to eq Virtus::Attribute::Boolean
56
75
  end
57
76
  end
58
77
  end
@@ -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', MultiJson.dump(first: 'one', boolean: false), 'CONTENT_TYPE' => 'application/json'
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', MultiJson.dump(first: 'one', second: nil), 'CONTENT_TYPE' => 'application/json'
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', MultiJson.dump(user: 'Bobby T.'), 'CONTENT_TYPE' => 'application/json'
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
- it 'converts XML bodies to params' do
815
- post '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
816
- expect(last_response.body).to eq('Bobby T.')
817
- end
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
- it 'converts XML bodies to params' do
820
- put '/request_body', '<user>Bobby T.</user>', 'CONTENT_TYPE' => 'application/xml'
821
- expect(last_response.body).to eq('Bobby T.')
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', MultiJson.dump(user: 'Bob'), 'CONTENT_TYPE' => 'application/json'
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', MultiJson.dump(user: 'Bob'), 'CONTENT_TYPE' => 'text/plain'
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 '/', MultiJson.dump(data: { some: 'payload' }), 'CONTENT_TYPE' => 'application/json'
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 }),