grape 1.6.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/CONTRIBUTING.md +1 -0
  4. data/README.md +9 -1
  5. data/lib/grape/api.rb +12 -0
  6. data/lib/grape/dry_types.rb +12 -0
  7. data/lib/grape/dsl/headers.rb +5 -2
  8. data/lib/grape/dsl/helpers.rb +1 -1
  9. data/lib/grape/middleware/auth/dsl.rb +7 -1
  10. data/lib/grape/middleware/base.rb +1 -1
  11. data/lib/grape/util/json.rb +2 -0
  12. data/lib/grape/util/strict_hash_configuration.rb +1 -1
  13. data/lib/grape/validations/types/array_coercer.rb +0 -2
  14. data/lib/grape/validations/types/dry_type_coercer.rb +1 -10
  15. data/lib/grape/validations/types/json.rb +0 -2
  16. data/lib/grape/validations/types/primitive_coercer.rb +5 -7
  17. data/lib/grape/validations/types/set_coercer.rb +0 -3
  18. data/lib/grape/validations/types.rb +83 -9
  19. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  20. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  21. data/lib/grape/validations/validators/as_validator.rb +14 -0
  22. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  23. data/lib/grape/validations/validators/base.rb +73 -71
  24. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  25. data/lib/grape/validations/validators/default_validator.rb +51 -0
  26. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  27. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  28. data/lib/grape/validations/validators/multiple_params_base.rb +24 -22
  29. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  30. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  31. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  32. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  33. data/lib/grape/validations/validators/values_validator.rb +88 -0
  34. data/lib/grape/version.rb +1 -1
  35. data/lib/grape.rb +59 -24
  36. data/spec/grape/api/custom_validations_spec.rb +77 -46
  37. data/spec/grape/api/deeply_included_options_spec.rb +3 -3
  38. data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
  39. data/spec/grape/api/invalid_format_spec.rb +2 -0
  40. data/spec/grape/api/recognize_path_spec.rb +1 -1
  41. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
  42. data/spec/grape/api_remount_spec.rb +16 -15
  43. data/spec/grape/api_spec.rb +317 -193
  44. data/spec/grape/dsl/callbacks_spec.rb +1 -0
  45. data/spec/grape/dsl/headers_spec.rb +39 -9
  46. data/spec/grape/dsl/helpers_spec.rb +3 -2
  47. data/spec/grape/dsl/inside_route_spec.rb +6 -4
  48. data/spec/grape/dsl/logger_spec.rb +16 -18
  49. data/spec/grape/dsl/middleware_spec.rb +1 -0
  50. data/spec/grape/dsl/parameters_spec.rb +1 -0
  51. data/spec/grape/dsl/request_response_spec.rb +1 -0
  52. data/spec/grape/dsl/routing_spec.rb +9 -6
  53. data/spec/grape/endpoint/declared_spec.rb +12 -12
  54. data/spec/grape/endpoint_spec.rb +59 -50
  55. data/spec/grape/entity_spec.rb +13 -13
  56. data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
  57. data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
  58. data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
  59. data/spec/grape/exceptions/validation_spec.rb +5 -3
  60. data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
  61. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
  62. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
  63. data/spec/grape/integration/rack_sendfile_spec.rb +1 -1
  64. data/spec/grape/loading_spec.rb +8 -8
  65. data/spec/grape/middleware/auth/dsl_spec.rb +14 -5
  66. data/spec/grape/middleware/auth/strategies_spec.rb +60 -20
  67. data/spec/grape/middleware/base_spec.rb +24 -15
  68. data/spec/grape/middleware/error_spec.rb +1 -0
  69. data/spec/grape/middleware/exception_spec.rb +111 -161
  70. data/spec/grape/middleware/formatter_spec.rb +25 -4
  71. data/spec/grape/middleware/globals_spec.rb +7 -4
  72. data/spec/grape/middleware/stack_spec.rb +11 -11
  73. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
  74. data/spec/grape/middleware/versioner/header_spec.rb +14 -13
  75. data/spec/grape/middleware/versioner/param_spec.rb +7 -1
  76. data/spec/grape/middleware/versioner/path_spec.rb +5 -1
  77. data/spec/grape/middleware/versioner_spec.rb +1 -1
  78. data/spec/grape/parser_spec.rb +4 -0
  79. data/spec/grape/path_spec.rb +52 -52
  80. data/spec/grape/presenters/presenter_spec.rb +7 -6
  81. data/spec/grape/request_spec.rb +6 -4
  82. data/spec/grape/util/inheritable_setting_spec.rb +7 -7
  83. data/spec/grape/util/inheritable_values_spec.rb +3 -2
  84. data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
  85. data/spec/grape/util/stackable_values_spec.rb +7 -5
  86. data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
  87. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -0
  88. data/spec/grape/validations/params_scope_spec.rb +9 -7
  89. data/spec/grape/validations/single_attribute_iterator_spec.rb +1 -0
  90. data/spec/grape/validations/types/primitive_coercer_spec.rb +2 -2
  91. data/spec/grape/validations/types_spec.rb +8 -8
  92. data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
  93. data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
  94. data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
  95. data/spec/grape/validations/validators/coerce_spec.rb +10 -12
  96. data/spec/grape/validations/validators/default_spec.rb +72 -78
  97. data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
  98. data/spec/grape/validations/validators/except_values_spec.rb +1 -1
  99. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
  100. data/spec/grape/validations/validators/presence_spec.rb +16 -1
  101. data/spec/grape/validations/validators/regexp_spec.rb +25 -31
  102. data/spec/grape/validations/validators/same_as_spec.rb +14 -20
  103. data/spec/grape/validations/validators/values_spec.rb +172 -171
  104. data/spec/grape/validations_spec.rb +45 -16
  105. data/spec/integration/eager_load/eager_load_spec.rb +2 -2
  106. data/spec/integration/multi_json/json_spec.rb +1 -1
  107. data/spec/integration/multi_xml/xml_spec.rb +1 -1
  108. data/spec/shared/versioning_examples.rb +10 -7
  109. data/spec/spec_helper.rb +11 -1
  110. metadata +116 -116
  111. data/lib/grape/validations/types/build_coercer.rb +0 -94
  112. data/lib/grape/validations/validators/all_or_none.rb +0 -16
  113. data/lib/grape/validations/validators/allow_blank.rb +0 -18
  114. data/lib/grape/validations/validators/as.rb +0 -12
  115. data/lib/grape/validations/validators/at_least_one_of.rb +0 -15
  116. data/lib/grape/validations/validators/coerce.rb +0 -87
  117. data/lib/grape/validations/validators/default.rb +0 -49
  118. data/lib/grape/validations/validators/exactly_one_of.rb +0 -17
  119. data/lib/grape/validations/validators/except_values.rb +0 -22
  120. data/lib/grape/validations/validators/mutual_exclusion.rb +0 -16
  121. data/lib/grape/validations/validators/presence.rb +0 -13
  122. data/lib/grape/validations/validators/regexp.rb +0 -14
  123. data/lib/grape/validations/validators/same_as.rb +0 -27
  124. data/lib/grape/validations/validators/values.rb +0 -86
@@ -4,18 +4,6 @@ require 'spec_helper'
4
4
 
5
5
  describe Grape::Validations do
6
6
  context 'using a custom length validator' do
7
- before do
8
- module CustomValidationsSpec
9
- class DefaultLength < Grape::Validations::Base
10
- def validate_param!(attr_name, params)
11
- @option = params[:max].to_i if params.key?(:max)
12
- return if params[attr_name].length <= @option
13
-
14
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long")
15
- end
16
- end
17
- end
18
- end
19
7
  subject do
20
8
  Class.new(Grape::API) do
21
9
  params do
@@ -27,6 +15,25 @@ describe Grape::Validations do
27
15
  end
28
16
  end
29
17
 
18
+ let(:default_length_validator) do
19
+ Class.new(Grape::Validations::Validators::Base) do
20
+ def validate_param!(attr_name, params)
21
+ @option = params[:max].to_i if params.key?(:max)
22
+ return if params[attr_name].length <= @option
23
+
24
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long")
25
+ end
26
+ end
27
+ end
28
+
29
+ before do
30
+ described_class.register_validator('default_length', default_length_validator)
31
+ end
32
+
33
+ after do
34
+ described_class.deregister_validator('default_length')
35
+ end
36
+
30
37
  def app
31
38
  subject
32
39
  end
@@ -36,11 +43,13 @@ describe Grape::Validations do
36
43
  expect(last_response.status).to eq 200
37
44
  expect(last_response.body).to eq 'bacon'
38
45
  end
46
+
39
47
  it 'over 140 characters' do
40
48
  get '/', text: 'a' * 141
41
49
  expect(last_response.status).to eq 400
42
50
  expect(last_response.body).to eq 'text must be at the most 140 characters long'
43
51
  end
52
+
44
53
  it 'specified in the query string' do
45
54
  get '/', text: 'a' * 141, max: 141
46
55
  expect(last_response.status).to eq 200
@@ -49,15 +58,6 @@ describe Grape::Validations do
49
58
  end
50
59
 
51
60
  context 'using a custom body-only validator' do
52
- before do
53
- module CustomValidationsSpec
54
- class InBody < Grape::Validations::PresenceValidator
55
- def validate(request)
56
- validate!(request.env['api.request.body'])
57
- end
58
- end
59
- end
60
- end
61
61
  subject do
62
62
  Class.new(Grape::API) do
63
63
  params do
@@ -69,6 +69,22 @@ describe Grape::Validations do
69
69
  end
70
70
  end
71
71
 
72
+ let(:in_body_validator) do
73
+ Class.new(Grape::Validations::Validators::PresenceValidator) do
74
+ def validate(request)
75
+ validate!(request.env['api.request.body'])
76
+ end
77
+ end
78
+ end
79
+
80
+ before do
81
+ described_class.register_validator('in_body', in_body_validator)
82
+ end
83
+
84
+ after do
85
+ described_class.deregister_validator('in_body')
86
+ end
87
+
72
88
  def app
73
89
  subject
74
90
  end
@@ -78,6 +94,7 @@ describe Grape::Validations do
78
94
  expect(last_response.status).to eq 200
79
95
  expect(last_response.body).to eq 'bacon'
80
96
  end
97
+
81
98
  it 'ignores field in query' do
82
99
  get '/', nil, text: 'abc'
83
100
  expect(last_response.status).to eq 400
@@ -86,15 +103,6 @@ describe Grape::Validations do
86
103
  end
87
104
 
88
105
  context 'using a custom validator with message_key' do
89
- before do
90
- module CustomValidationsSpec
91
- class WithMessageKey < Grape::Validations::PresenceValidator
92
- def validate_param!(attr_name, _params)
93
- raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: :presence)
94
- end
95
- end
96
- end
97
- end
98
106
  subject do
99
107
  Class.new(Grape::API) do
100
108
  params do
@@ -106,6 +114,22 @@ describe Grape::Validations do
106
114
  end
107
115
  end
108
116
 
117
+ let(:message_key_validator) do
118
+ Class.new(Grape::Validations::Validators::PresenceValidator) do
119
+ def validate_param!(attr_name, _params)
120
+ raise Grape::Exceptions::Validation.new(params: [@scope.full_name(attr_name)], message: :presence)
121
+ end
122
+ end
123
+ end
124
+
125
+ before do
126
+ described_class.register_validator('with_message_key', message_key_validator)
127
+ end
128
+
129
+ after do
130
+ described_class.deregister_validator('with_message_key')
131
+ end
132
+
109
133
  def app
110
134
  subject
111
135
  end
@@ -118,22 +142,6 @@ describe Grape::Validations do
118
142
  end
119
143
 
120
144
  context 'using a custom request/param validator' do
121
- before do
122
- module CustomValidationsSpec
123
- class Admin < Grape::Validations::Base
124
- def validate(request)
125
- # return if the param we are checking was not in request
126
- # @attrs is a list containing the attribute we are currently validating
127
- return unless request.params.key? @attrs.first
128
- # check if admin flag is set to true
129
- return unless @option
130
- # check if user is admin or not
131
- # as an example get a token from request and check if it's admin or not
132
- raise Grape::Exceptions::Validation.new(params: @attrs, message: 'Can not set Admin only field.') unless request.headers['X-Access-Token'] == 'admin'
133
- end
134
- end
135
- end
136
- end
137
145
  subject do
138
146
  Class.new(Grape::API) do
139
147
  params do
@@ -147,6 +155,29 @@ describe Grape::Validations do
147
155
  end
148
156
  end
149
157
 
158
+ let(:admin_validator) do
159
+ Class.new(Grape::Validations::Validators::Base) do
160
+ def validate(request)
161
+ # return if the param we are checking was not in request
162
+ # @attrs is a list containing the attribute we are currently validating
163
+ return unless request.params.key? @attrs.first
164
+ # check if admin flag is set to true
165
+ return unless @option
166
+ # check if user is admin or not
167
+ # as an example get a token from request and check if it's admin or not
168
+ raise Grape::Exceptions::Validation.new(params: @attrs, message: 'Can not set Admin only field.') unless request.headers['X-Access-Token'] == 'admin'
169
+ end
170
+ end
171
+ end
172
+
173
+ before do
174
+ described_class.register_validator('admin', admin_validator)
175
+ end
176
+
177
+ after do
178
+ described_class.deregister_validator('admin')
179
+ end
180
+
150
181
  def app
151
182
  subject
152
183
  end
@@ -41,18 +41,18 @@ describe Grape::API do
41
41
 
42
42
  it 'works for unspecified format' do
43
43
  get '/users'
44
- expect(last_response.status).to eql 200
44
+ expect(last_response.status).to be 200
45
45
  expect(last_response.content_type).to eql 'application/json'
46
46
  end
47
47
 
48
48
  it 'works for specified format' do
49
49
  get '/users.json'
50
- expect(last_response.status).to eql 200
50
+ expect(last_response.status).to be 200
51
51
  expect(last_response.content_type).to eql 'application/json'
52
52
  end
53
53
 
54
54
  it "doesn't work for format different than specified" do
55
55
  get '/users.txt'
56
- expect(last_response.status).to eql 404
56
+ expect(last_response.status).to be 404
57
57
  end
58
58
  end
@@ -31,8 +31,9 @@ describe Grape::API::Instance do
31
31
 
32
32
  context 'Params endpoint type' do
33
33
  subject { DefinesBooleanInstanceSpec::API.new.router.map['POST'].first.options[:params]['message'][:type] }
34
+
34
35
  it 'params type is a boolean' do
35
- is_expected.to eq 'Grape::API::Boolean'
36
+ expect(subject).to eq 'Grape::API::Boolean'
36
37
  end
37
38
  end
38
39
  end
@@ -31,11 +31,13 @@ describe Grape::Endpoint do
31
31
  expect(last_response.status).to eq 200
32
32
  expect(last_response.body).to eq(::Grape::Json.dump(id: 'foo', format: nil))
33
33
  end
34
+
34
35
  it 'json format' do
35
36
  get '/foo.json'
36
37
  expect(last_response.status).to eq 200
37
38
  expect(last_response.body).to eq(::Grape::Json.dump(id: 'foo', format: 'json'))
38
39
  end
40
+
39
41
  it 'invalid format' do
40
42
  get '/foo.invalid'
41
43
  expect(last_response.status).to eq 200
@@ -4,7 +4,7 @@ require 'spec_helper'
4
4
 
5
5
  describe Grape::API do
6
6
  describe '.recognize_path' do
7
- subject { Class.new(Grape::API) }
7
+ subject { Class.new(described_class) }
8
8
 
9
9
  it 'fetches endpoint by given path' do
10
10
  subject.get('/foo/:id') {}
@@ -3,19 +3,17 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Grape::API::Helpers do
6
- subject do
7
- shared_params = Module.new do
8
- extend Grape::API::Helpers
6
+ let(:app) do
7
+ Class.new(Grape::API) do
8
+ helpers Module.new do
9
+ extend Grape::API::Helpers
9
10
 
10
- params :drink do
11
- optional :beer
12
- optional :wine
13
- exactly_one_of :beer, :wine
11
+ params :drink do
12
+ optional :beer
13
+ optional :wine
14
+ exactly_one_of :beer, :wine
15
+ end
14
16
  end
15
- end
16
-
17
- Class.new(Grape::API) do
18
- helpers shared_params
19
17
  format :json
20
18
 
21
19
  params do
@@ -35,10 +33,6 @@ describe Grape::API::Helpers do
35
33
  end
36
34
  end
37
35
 
38
- def app
39
- subject
40
- end
41
-
42
36
  it 'defines parameters' do
43
37
  get '/', orderType: 'food', pizza: 'mista'
44
38
  expect(last_response.status).to eq 200
@@ -4,8 +4,9 @@ require 'spec_helper'
4
4
  require 'shared/versioning_examples'
5
5
 
6
6
  describe Grape::API do
7
- subject(:a_remounted_api) { Class.new(Grape::API) }
8
- let(:root_api) { Class.new(Grape::API) }
7
+ subject(:a_remounted_api) { Class.new(described_class) }
8
+
9
+ let(:root_api) { Class.new(described_class) }
9
10
 
10
11
  def app
11
12
  root_api
@@ -68,7 +69,7 @@ describe Grape::API do
68
69
  describe 'with dynamic configuration' do
69
70
  context 'when mounting an endpoint conditional on a configuration' do
70
71
  subject(:a_remounted_api) do
71
- Class.new(Grape::API) do
72
+ Class.new(described_class) do
72
73
  get 'always' do
73
74
  'success'
74
75
  end
@@ -101,7 +102,7 @@ describe Grape::API do
101
102
 
102
103
  context 'when using an expression derived from a configuration' do
103
104
  subject(:a_remounted_api) do
104
- Class.new(Grape::API) do
105
+ Class.new(described_class) do
105
106
  get(mounted { "api_name_#{configuration[:api_name]}" }) do
106
107
  'success'
107
108
  end
@@ -126,7 +127,7 @@ describe Grape::API do
126
127
 
127
128
  context 'when the expression lives in a namespace' do
128
129
  subject(:a_remounted_api) do
129
- Class.new(Grape::API) do
130
+ Class.new(described_class) do
130
131
  namespace :base do
131
132
  get(mounted { "api_name_#{configuration[:api_name]}" }) do
132
133
  'success'
@@ -149,7 +150,7 @@ describe Grape::API do
149
150
 
150
151
  context 'when executing a standard block within a `mounted` block with all dynamic params' do
151
152
  subject(:a_remounted_api) do
152
- Class.new(Grape::API) do
153
+ Class.new(described_class) do
153
154
  mounted do
154
155
  desc configuration[:description] do
155
156
  headers configuration[:headers]
@@ -191,7 +192,7 @@ describe Grape::API do
191
192
 
192
193
  context 'when executing a custom block on mount' do
193
194
  subject(:a_remounted_api) do
194
- Class.new(Grape::API) do
195
+ Class.new(described_class) do
195
196
  get 'always' do
196
197
  'success'
197
198
  end
@@ -215,7 +216,7 @@ describe Grape::API do
215
216
 
216
217
  context 'when the configuration is part of the arguments of a method' do
217
218
  subject(:a_remounted_api) do
218
- Class.new(Grape::API) do
219
+ Class.new(described_class) do
219
220
  get configuration[:endpoint_name] do
220
221
  'success'
221
222
  end
@@ -237,7 +238,7 @@ describe Grape::API do
237
238
 
238
239
  context 'when the configuration is the value in a key-arg pair' do
239
240
  subject(:a_remounted_api) do
240
- Class.new(Grape::API) do
241
+ Class.new(described_class) do
241
242
  version 'v1', using: :param, parameter: configuration[:version_param]
242
243
  get 'endpoint' do
243
244
  'version 1'
@@ -267,7 +268,7 @@ describe Grape::API do
267
268
 
268
269
  context 'on the DescSCope' do
269
270
  subject(:a_remounted_api) do
270
- Class.new(Grape::API) do
271
+ Class.new(described_class) do
271
272
  desc 'The description of this' do
272
273
  tags ['not_configurable_tag', configuration[:a_configurable_tag]]
273
274
  end
@@ -284,7 +285,7 @@ describe Grape::API do
284
285
 
285
286
  context 'on the ParamScope' do
286
287
  subject(:a_remounted_api) do
287
- Class.new(Grape::API) do
288
+ Class.new(described_class) do
288
289
  params do
289
290
  requires configuration[:required_param], type: configuration[:required_type]
290
291
  end
@@ -314,7 +315,7 @@ describe Grape::API do
314
315
 
315
316
  context 'on dynamic checks' do
316
317
  subject(:a_remounted_api) do
317
- Class.new(Grape::API) do
318
+ Class.new(described_class) do
318
319
  params do
319
320
  optional :restricted_values, values: -> { [configuration[:allowed_value], 'always'] }
320
321
  end
@@ -363,7 +364,7 @@ describe Grape::API do
363
364
 
364
365
  context 'a very complex configuration example' do
365
366
  before do
366
- top_level_api = Class.new(Grape::API) do
367
+ top_level_api = Class.new(described_class) do
367
368
  remounted_api = Class.new(Grape::API) do
368
369
  get configuration[:endpoint_name] do
369
370
  configuration[:response]
@@ -431,7 +432,7 @@ describe Grape::API do
431
432
 
432
433
  context 'when the configuration is read in a helper' do
433
434
  subject(:a_remounted_api) do
434
- Class.new(Grape::API) do
435
+ Class.new(described_class) do
435
436
  helpers do
436
437
  def printed_response
437
438
  configuration[:some_value]
@@ -454,7 +455,7 @@ describe Grape::API do
454
455
 
455
456
  context 'when the configuration is read within the response block' do
456
457
  subject(:a_remounted_api) do
457
- Class.new(Grape::API) do
458
+ Class.new(described_class) do
458
459
  get 'location' do
459
460
  configuration[:some_value]
460
461
  end