grape 1.5.3 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +67 -0
- data/CONTRIBUTING.md +2 -1
- data/README.md +150 -21
- data/UPGRADING.md +61 -4
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +14 -18
- data/lib/grape/api.rb +17 -12
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dry_types.rb +12 -0
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/callbacks.rb +0 -2
- data/lib/grape/dsl/configuration.rb +0 -2
- data/lib/grape/dsl/desc.rb +2 -19
- data/lib/grape/dsl/headers.rb +5 -2
- data/lib/grape/dsl/helpers.rb +7 -7
- data/lib/grape/dsl/inside_route.rb +43 -30
- data/lib/grape/dsl/middleware.rb +4 -6
- data/lib/grape/dsl/parameters.rb +8 -10
- data/lib/grape/dsl/request_response.rb +9 -8
- data/lib/grape/dsl/routing.rb +6 -4
- data/lib/grape/dsl/settings.rb +5 -7
- data/lib/grape/dsl/validations.rb +0 -15
- data/lib/grape/endpoint.rb +20 -35
- data/lib/grape/error_formatter/json.rb +9 -7
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/base.rb +2 -2
- data/lib/grape/exceptions/missing_group_type.rb +8 -1
- data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
- data/lib/grape/exceptions/unsupported_group_type.rb +8 -1
- data/lib/grape/exceptions/validation.rb +1 -6
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/locale/en.yml +9 -8
- data/lib/grape/middleware/auth/dsl.rb +7 -2
- data/lib/grape/middleware/base.rb +3 -1
- data/lib/grape/middleware/error.rb +2 -2
- data/lib/grape/middleware/formatter.rb +4 -4
- data/lib/grape/middleware/stack.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +3 -0
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +6 -0
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/json.rb +2 -0
- data/lib/grape/util/lazy_value.rb +3 -2
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/attributes_doc.rb +58 -0
- data/lib/grape/validations/params_scope.rb +137 -78
- data/lib/grape/validations/types/array_coercer.rb +0 -2
- data/lib/grape/validations/types/custom_type_coercer.rb +1 -0
- data/lib/grape/validations/types/dry_type_coercer.rb +4 -8
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +16 -8
- data/lib/grape/validations/types/set_coercer.rb +0 -2
- data/lib/grape/validations/types.rb +98 -30
- data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
- data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
- data/lib/grape/validations/validators/as_validator.rb +14 -0
- data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
- data/lib/grape/validations/validators/base.rb +82 -70
- data/lib/grape/validations/validators/coerce_validator.rb +75 -0
- data/lib/grape/validations/validators/default_validator.rb +51 -0
- data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
- data/lib/grape/validations/validators/except_values_validator.rb +24 -0
- data/lib/grape/validations/validators/multiple_params_base.rb +24 -20
- data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
- data/lib/grape/validations/validators/presence_validator.rb +15 -0
- data/lib/grape/validations/validators/regexp_validator.rb +16 -0
- data/lib/grape/validations/validators/same_as_validator.rb +29 -0
- data/lib/grape/validations/validators/values_validator.rb +88 -0
- data/lib/grape/validations.rb +16 -6
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +69 -29
- data/spec/grape/api/custom_validations_spec.rb +116 -45
- data/spec/grape/api/deeply_included_options_spec.rb +3 -5
- data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -3
- data/spec/grape/api/documentation_spec.rb +59 -0
- data/spec/grape/api/inherited_helpers_spec.rb +0 -2
- data/spec/grape/api/instance_spec.rb +0 -1
- data/spec/grape/api/invalid_format_spec.rb +2 -2
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -2
- data/spec/grape/api/nested_helpers_spec.rb +0 -2
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -2
- data/spec/grape/api/parameters_modification_spec.rb +0 -2
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -2
- data/spec/grape/api/recognize_path_spec.rb +1 -3
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -2
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -2
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -10
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -17
- data/spec/grape/api/shared_helpers_spec.rb +0 -2
- data/spec/grape/api_remount_spec.rb +16 -16
- data/spec/grape/api_spec.rb +457 -231
- data/spec/grape/config_spec.rb +0 -2
- data/spec/grape/dsl/callbacks_spec.rb +2 -3
- data/spec/grape/dsl/configuration_spec.rb +0 -2
- data/spec/grape/dsl/desc_spec.rb +0 -2
- data/spec/grape/dsl/headers_spec.rb +39 -11
- data/spec/grape/dsl/helpers_spec.rb +3 -4
- data/spec/grape/dsl/inside_route_spec.rb +16 -16
- data/spec/grape/dsl/logger_spec.rb +15 -19
- data/spec/grape/dsl/middleware_spec.rb +2 -3
- data/spec/grape/dsl/parameters_spec.rb +2 -2
- data/spec/grape/dsl/request_response_spec.rb +7 -8
- data/spec/grape/dsl/routing_spec.rb +11 -10
- data/spec/grape/dsl/settings_spec.rb +0 -2
- data/spec/grape/dsl/validations_spec.rb +0 -17
- data/spec/grape/endpoint/declared_spec.rb +261 -16
- data/spec/grape/endpoint_spec.rb +86 -58
- data/spec/grape/entity_spec.rb +22 -23
- data/spec/grape/exceptions/base_spec.rb +16 -2
- data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -2
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -24
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -2
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -2
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +1 -3
- data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -2
- data/spec/grape/exceptions/missing_option_spec.rb +1 -3
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -2
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -2
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +13 -11
- data/spec/grape/exceptions/validation_spec.rb +5 -5
- data/spec/grape/extensions/param_builders/hash_spec.rb +7 -9
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -10
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -10
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -2
- data/spec/grape/integration/rack_sendfile_spec.rb +1 -3
- data/spec/grape/integration/rack_spec.rb +0 -2
- data/spec/grape/loading_spec.rb +8 -10
- data/spec/grape/middleware/auth/base_spec.rb +0 -1
- data/spec/grape/middleware/auth/dsl_spec.rb +15 -8
- data/spec/grape/middleware/auth/strategies_spec.rb +60 -22
- data/spec/grape/middleware/base_spec.rb +24 -17
- data/spec/grape/middleware/error_spec.rb +8 -3
- data/spec/grape/middleware/exception_spec.rb +111 -163
- data/spec/grape/middleware/formatter_spec.rb +27 -8
- data/spec/grape/middleware/globals_spec.rb +7 -6
- data/spec/grape/middleware/stack_spec.rb +14 -14
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -3
- data/spec/grape/middleware/versioner/header_spec.rb +30 -15
- data/spec/grape/middleware/versioner/param_spec.rb +7 -3
- data/spec/grape/middleware/versioner/path_spec.rb +5 -3
- data/spec/grape/middleware/versioner_spec.rb +1 -3
- data/spec/grape/named_api_spec.rb +0 -2
- data/spec/grape/parser_spec.rb +4 -2
- data/spec/grape/path_spec.rb +52 -54
- data/spec/grape/presenters/presenter_spec.rb +7 -8
- data/spec/grape/request_spec.rb +6 -6
- data/spec/grape/util/inheritable_setting_spec.rb +7 -8
- data/spec/grape/util/inheritable_values_spec.rb +3 -3
- data/spec/grape/util/reverse_stackable_values_spec.rb +3 -2
- data/spec/grape/util/stackable_values_spec.rb +7 -6
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -1
- data/spec/grape/validations/attributes_doc_spec.rb +153 -0
- data/spec/grape/validations/attributes_iterator_spec.rb +0 -2
- data/spec/grape/validations/instance_behaivour_spec.rb +9 -12
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +1 -2
- data/spec/grape/validations/params_scope_spec.rb +361 -96
- data/spec/grape/validations/single_attribute_iterator_spec.rb +2 -3
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -2
- data/spec/grape/validations/types/primitive_coercer_spec.rb +24 -9
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -2
- data/spec/grape/validations/types_spec.rb +36 -10
- data/spec/grape/validations/validators/all_or_none_spec.rb +50 -58
- data/spec/grape/validations/validators/allow_blank_spec.rb +135 -141
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -58
- data/spec/grape/validations/validators/coerce_spec.rb +23 -24
- data/spec/grape/validations/validators/default_spec.rb +72 -80
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -79
- data/spec/grape/validations/validators/except_values_spec.rb +3 -5
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -79
- data/spec/grape/validations/validators/presence_spec.rb +16 -3
- data/spec/grape/validations/validators/regexp_spec.rb +25 -33
- data/spec/grape/validations/validators/same_as_spec.rb +14 -22
- data/spec/grape/validations/validators/values_spec.rb +182 -179
- data/spec/grape/validations_spec.rb +149 -80
- data/spec/integration/eager_load/eager_load_spec.rb +2 -2
- data/spec/integration/multi_json/json_spec.rb +1 -3
- data/spec/integration/multi_xml/xml_spec.rb +1 -3
- data/spec/shared/versioning_examples.rb +12 -9
- data/spec/spec_helper.rb +21 -6
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- metadata +126 -117
- data/lib/grape/validations/validators/all_or_none.rb +0 -15
- data/lib/grape/validations/validators/allow_blank.rb +0 -18
- data/lib/grape/validations/validators/as.rb +0 -16
- data/lib/grape/validations/validators/at_least_one_of.rb +0 -14
- data/lib/grape/validations/validators/coerce.rb +0 -91
- data/lib/grape/validations/validators/default.rb +0 -48
- data/lib/grape/validations/validators/exactly_one_of.rb +0 -16
- data/lib/grape/validations/validators/except_values.rb +0 -22
- data/lib/grape/validations/validators/mutual_exclusion.rb +0 -15
- data/lib/grape/validations/validators/presence.rb +0 -12
- data/lib/grape/validations/validators/regexp.rb +0 -13
- data/lib/grape/validations/validators/same_as.rb +0 -26
- data/lib/grape/validations/validators/values.rb +0 -83
- data/spec/support/eager_load.rb +0 -19
data/spec/grape/endpoint_spec.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
3
|
describe Grape::Endpoint do
|
6
4
|
subject { Class.new(Grape::API) }
|
7
5
|
|
@@ -10,32 +8,32 @@ describe Grape::Endpoint do
|
|
10
8
|
end
|
11
9
|
|
12
10
|
describe '.before_each' do
|
13
|
-
after {
|
11
|
+
after { described_class.before_each.clear }
|
14
12
|
|
15
13
|
it 'is settable via block' do
|
16
14
|
block = ->(_endpoint) { 'noop' }
|
17
|
-
|
18
|
-
expect(
|
15
|
+
described_class.before_each(&block)
|
16
|
+
expect(described_class.before_each.first).to eq(block)
|
19
17
|
end
|
20
18
|
|
21
19
|
it 'is settable via reference' do
|
22
20
|
block = ->(_endpoint) { 'noop' }
|
23
|
-
|
24
|
-
expect(
|
21
|
+
described_class.before_each block
|
22
|
+
expect(described_class.before_each.first).to eq(block)
|
25
23
|
end
|
26
24
|
|
27
25
|
it 'is able to override a helper' do
|
28
26
|
subject.get('/') { current_user }
|
29
27
|
expect { get '/' }.to raise_error(NameError)
|
30
28
|
|
31
|
-
|
29
|
+
described_class.before_each do |endpoint|
|
32
30
|
allow(endpoint).to receive(:current_user).and_return('Bob')
|
33
31
|
end
|
34
32
|
|
35
33
|
get '/'
|
36
34
|
expect(last_response.body).to eq('Bob')
|
37
35
|
|
38
|
-
|
36
|
+
described_class.before_each(nil)
|
39
37
|
expect { get '/' }.to raise_error(NameError)
|
40
38
|
end
|
41
39
|
|
@@ -46,18 +44,18 @@ describe Grape::Endpoint do
|
|
46
44
|
end
|
47
45
|
expect { get '/' }.to raise_error(NameError)
|
48
46
|
|
49
|
-
|
47
|
+
described_class.before_each do |endpoint|
|
50
48
|
allow(endpoint).to receive(:current_user).and_return('Bob')
|
51
49
|
end
|
52
50
|
|
53
|
-
|
51
|
+
described_class.before_each do |endpoint|
|
54
52
|
allow(endpoint).to receive(:authenticate_user!).and_return(true)
|
55
53
|
end
|
56
54
|
|
57
55
|
get '/'
|
58
56
|
expect(last_response.body).to eq('Bob')
|
59
57
|
|
60
|
-
|
58
|
+
described_class.before_each(nil)
|
61
59
|
expect { get '/' }.to raise_error(NameError)
|
62
60
|
end
|
63
61
|
end
|
@@ -66,7 +64,7 @@ describe Grape::Endpoint do
|
|
66
64
|
it 'takes a settings stack, options, and a block' do
|
67
65
|
p = proc {}
|
68
66
|
expect do
|
69
|
-
|
67
|
+
described_class.new(Grape::Util::InheritableSetting.new, {
|
70
68
|
path: '/',
|
71
69
|
method: :get
|
72
70
|
}, &p)
|
@@ -77,7 +75,7 @@ describe Grape::Endpoint do
|
|
77
75
|
it 'sets itself in the env upon call' do
|
78
76
|
subject.get('/') { 'Hello world.' }
|
79
77
|
get '/'
|
80
|
-
expect(last_request.env['api.endpoint']).to be_kind_of(
|
78
|
+
expect(last_request.env['api.endpoint']).to be_kind_of(described_class)
|
81
79
|
end
|
82
80
|
|
83
81
|
describe '#status' do
|
@@ -137,6 +135,7 @@ describe Grape::Endpoint do
|
|
137
135
|
headers.to_json
|
138
136
|
end
|
139
137
|
end
|
138
|
+
|
140
139
|
it 'includes request headers' do
|
141
140
|
get '/headers'
|
142
141
|
expect(JSON.parse(last_response.body)).to eq(
|
@@ -144,13 +143,15 @@ describe Grape::Endpoint do
|
|
144
143
|
'Cookie' => ''
|
145
144
|
)
|
146
145
|
end
|
146
|
+
|
147
147
|
it 'includes additional request headers' do
|
148
148
|
get '/headers', nil, 'HTTP_X_GRAPE_CLIENT' => '1'
|
149
149
|
expect(JSON.parse(last_response.body)['X-Grape-Client']).to eq('1')
|
150
150
|
end
|
151
|
+
|
151
152
|
it 'includes headers passed as symbols' do
|
152
153
|
env = Rack::MockRequest.env_for('/headers')
|
153
|
-
env[
|
154
|
+
env[:HTTP_SYMBOL_HEADER] = 'Goliath passes symbols'
|
154
155
|
body = read_chunks(subject.call(env)[2]).join
|
155
156
|
expect(JSON.parse(body)['Symbol-Header']).to eq('Goliath passes symbols')
|
156
157
|
end
|
@@ -212,10 +213,10 @@ describe Grape::Endpoint do
|
|
212
213
|
end
|
213
214
|
get '/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2'
|
214
215
|
expect(last_response.body).to eq('3')
|
215
|
-
cookies =
|
216
|
+
cookies = last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
|
216
217
|
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
217
218
|
[cookie.name, cookie]
|
218
|
-
end
|
219
|
+
end.to_h
|
219
220
|
expect(cookies.size).to eq(2)
|
220
221
|
%w[and_this delete_this_cookie].each do |cookie_name|
|
221
222
|
cookie = cookies[cookie_name]
|
@@ -236,10 +237,10 @@ describe Grape::Endpoint do
|
|
236
237
|
end
|
237
238
|
get('/test', {}, 'HTTP_COOKIE' => 'delete_this_cookie=1; and_this=2')
|
238
239
|
expect(last_response.body).to eq('3')
|
239
|
-
cookies =
|
240
|
+
cookies = last_response.headers['Set-Cookie'].split("\n").map do |set_cookie|
|
240
241
|
cookie = CookieJar::Cookie.from_set_cookie 'http://localhost/test', set_cookie
|
241
242
|
[cookie.name, cookie]
|
242
|
-
end
|
243
|
+
end.to_h
|
243
244
|
expect(cookies.size).to eq(2)
|
244
245
|
%w[and_this delete_this_cookie].each do |cookie_name|
|
245
246
|
cookie = cookies[cookie_name]
|
@@ -253,7 +254,7 @@ describe Grape::Endpoint do
|
|
253
254
|
|
254
255
|
describe '#params' do
|
255
256
|
context 'default class' do
|
256
|
-
it '
|
257
|
+
it 'is a ActiveSupport::HashWithIndifferentAccess' do
|
257
258
|
subject.get '/foo' do
|
258
259
|
params.class
|
259
260
|
end
|
@@ -339,7 +340,7 @@ describe Grape::Endpoint do
|
|
339
340
|
end
|
340
341
|
|
341
342
|
context 'namespace requirements' do
|
342
|
-
before
|
343
|
+
before do
|
343
344
|
subject.namespace :outer, requirements: { person_email: /abc@(.*).com/ } do
|
344
345
|
get('/:person_email') do
|
345
346
|
params[:person_email]
|
@@ -358,7 +359,7 @@ describe Grape::Endpoint do
|
|
358
359
|
expect(last_response.body).to eq('abc@example.com')
|
359
360
|
end
|
360
361
|
|
361
|
-
it "
|
362
|
+
it "overrides outer namespace's requirements" do
|
362
363
|
get '/outer/inner/someone@testing.wrong/test/1'
|
363
364
|
expect(last_response.status).to eq(404)
|
364
365
|
|
@@ -370,7 +371,7 @@ describe Grape::Endpoint do
|
|
370
371
|
end
|
371
372
|
|
372
373
|
context 'from body parameters' do
|
373
|
-
before
|
374
|
+
before do
|
374
375
|
subject.post '/request_body' do
|
375
376
|
params[:user]
|
376
377
|
end
|
@@ -431,7 +432,28 @@ describe Grape::Endpoint do
|
|
431
432
|
end
|
432
433
|
post '/upload', { file: '' }, 'CONTENT_TYPE' => 'multipart/form-data; boundary=foobar'
|
433
434
|
expect(last_response.status).to eq(400)
|
434
|
-
expect(last_response.body).to eq('
|
435
|
+
expect(last_response.body).to eq('empty message body supplied with multipart/form-data; boundary=foobar content-type')
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
context 'when the limit on multipart files is exceeded' do
|
440
|
+
around do |example|
|
441
|
+
limit = Rack::Utils.multipart_part_limit
|
442
|
+
Rack::Utils.multipart_part_limit = 1
|
443
|
+
example.run
|
444
|
+
Rack::Utils.multipart_part_limit = limit
|
445
|
+
end
|
446
|
+
|
447
|
+
it 'returns a 413 if given too many multipart files' do
|
448
|
+
subject.params do
|
449
|
+
requires :file, type: Rack::Multipart::UploadedFile
|
450
|
+
end
|
451
|
+
subject.post '/upload' do
|
452
|
+
params[:file][:filename]
|
453
|
+
end
|
454
|
+
post '/upload', { file: Rack::Test::UploadedFile.new(__FILE__, 'text/plain'), extra: Rack::Test::UploadedFile.new(__FILE__, 'text/plain') }
|
455
|
+
expect(last_response.status).to eq(413)
|
456
|
+
expect(last_response.body).to eq("the number of uploaded files exceeded the system's configured limit (1)")
|
435
457
|
end
|
436
458
|
end
|
437
459
|
|
@@ -469,11 +491,11 @@ describe Grape::Endpoint do
|
|
469
491
|
post '/', ::Grape::Json.dump(data: { some: 'payload' }), 'CONTENT_TYPE' => 'application/json'
|
470
492
|
end
|
471
493
|
|
472
|
-
it '
|
494
|
+
it 'does not response with 406 for same type without params' do
|
473
495
|
expect(last_response.status).not_to be 406
|
474
496
|
end
|
475
497
|
|
476
|
-
it '
|
498
|
+
it 'responses with given content type in headers' do
|
477
499
|
expect(last_response.headers['Content-Type']).to eq 'application/json; charset=utf-8'
|
478
500
|
end
|
479
501
|
end
|
@@ -709,16 +731,18 @@ describe Grape::Endpoint do
|
|
709
731
|
describe '.generate_api_method' do
|
710
732
|
it 'raises NameError if the method name is already in use' do
|
711
733
|
expect do
|
712
|
-
|
734
|
+
described_class.generate_api_method('version', &proc {})
|
713
735
|
end.to raise_error(NameError)
|
714
736
|
end
|
737
|
+
|
715
738
|
it 'raises ArgumentError if a block is not given' do
|
716
739
|
expect do
|
717
|
-
|
740
|
+
described_class.generate_api_method('GET without a block method')
|
718
741
|
end.to raise_error(ArgumentError)
|
719
742
|
end
|
743
|
+
|
720
744
|
it 'returns a Proc' do
|
721
|
-
expect(
|
745
|
+
expect(described_class.generate_api_method('GET test for a proc', &proc {})).to be_a Proc
|
722
746
|
end
|
723
747
|
end
|
724
748
|
|
@@ -777,7 +801,7 @@ describe Grape::Endpoint do
|
|
777
801
|
end
|
778
802
|
|
779
803
|
get '/error_filters'
|
780
|
-
expect(last_response.status).to
|
804
|
+
expect(last_response.status).to be 500
|
781
805
|
expect(called).to match_array %w[before before_validation]
|
782
806
|
end
|
783
807
|
|
@@ -786,8 +810,11 @@ describe Grape::Endpoint do
|
|
786
810
|
subject.before { called << 'parent' }
|
787
811
|
subject.namespace :parent do
|
788
812
|
before { called << 'prior' }
|
813
|
+
|
789
814
|
before { error! :oops, 500 }
|
815
|
+
|
790
816
|
before { called << 'subsequent' }
|
817
|
+
|
791
818
|
get :hello do
|
792
819
|
called << :endpoint
|
793
820
|
'Hello!'
|
@@ -795,7 +822,7 @@ describe Grape::Endpoint do
|
|
795
822
|
end
|
796
823
|
|
797
824
|
get '/parent/hello'
|
798
|
-
expect(last_response.status).to
|
825
|
+
expect(last_response.status).to be 500
|
799
826
|
expect(called).to match_array %w[parent prior]
|
800
827
|
end
|
801
828
|
end
|
@@ -806,19 +833,19 @@ describe Grape::Endpoint do
|
|
806
833
|
it 'allows for the anchoring option with a delete method' do
|
807
834
|
subject.send(:delete, '/example', anchor: true) {}
|
808
835
|
send(:delete, '/example/and/some/more')
|
809
|
-
expect(last_response.status).to
|
836
|
+
expect(last_response.status).to be 404
|
810
837
|
end
|
811
838
|
|
812
839
|
it 'anchors paths by default for the delete method' do
|
813
840
|
subject.send(:delete, '/example') {}
|
814
841
|
send(:delete, '/example/and/some/more')
|
815
|
-
expect(last_response.status).to
|
842
|
+
expect(last_response.status).to be 404
|
816
843
|
end
|
817
844
|
|
818
845
|
it 'responds to /example/and/some/more for the non-anchored delete method' do
|
819
846
|
subject.send(:delete, '/example', anchor: false) {}
|
820
847
|
send(:delete, '/example/and/some/more')
|
821
|
-
expect(last_response.status).to
|
848
|
+
expect(last_response.status).to be 204
|
822
849
|
expect(last_response.body).to be_empty
|
823
850
|
end
|
824
851
|
end
|
@@ -830,7 +857,7 @@ describe Grape::Endpoint do
|
|
830
857
|
body 'deleted'
|
831
858
|
end
|
832
859
|
send(:delete, '/example/and/some/more')
|
833
|
-
expect(last_response.status).to
|
860
|
+
expect(last_response.status).to be 200
|
834
861
|
expect(last_response.body).not_to be_empty
|
835
862
|
end
|
836
863
|
end
|
@@ -839,7 +866,7 @@ describe Grape::Endpoint do
|
|
839
866
|
it 'responds to /example delete method' do
|
840
867
|
subject.delete(:example) { 'deleted' }
|
841
868
|
delete '/example'
|
842
|
-
expect(last_response.status).to
|
869
|
+
expect(last_response.status).to be 200
|
843
870
|
expect(last_response.body).not_to be_empty
|
844
871
|
end
|
845
872
|
end
|
@@ -848,7 +875,7 @@ describe Grape::Endpoint do
|
|
848
875
|
it 'responds to /example delete method' do
|
849
876
|
subject.delete(:example) { nil }
|
850
877
|
delete '/example'
|
851
|
-
expect(last_response.status).to
|
878
|
+
expect(last_response.status).to be 204
|
852
879
|
expect(last_response.body).to be_empty
|
853
880
|
end
|
854
881
|
end
|
@@ -857,7 +884,7 @@ describe Grape::Endpoint do
|
|
857
884
|
it 'responds to /example delete method' do
|
858
885
|
subject.delete(:example) { '' }
|
859
886
|
delete '/example'
|
860
|
-
expect(last_response.status).to
|
887
|
+
expect(last_response.status).to be 204
|
861
888
|
expect(last_response.body).to be_empty
|
862
889
|
end
|
863
890
|
end
|
@@ -869,7 +896,7 @@ describe Grape::Endpoint do
|
|
869
896
|
verb
|
870
897
|
end
|
871
898
|
send(verb, '/example/and/some/more')
|
872
|
-
expect(last_response.status).to
|
899
|
+
expect(last_response.status).to be 404
|
873
900
|
end
|
874
901
|
|
875
902
|
it "anchors paths by default for the #{verb.upcase} method" do
|
@@ -877,7 +904,7 @@ describe Grape::Endpoint do
|
|
877
904
|
verb
|
878
905
|
end
|
879
906
|
send(verb, '/example/and/some/more')
|
880
|
-
expect(last_response.status).to
|
907
|
+
expect(last_response.status).to be 404
|
881
908
|
end
|
882
909
|
|
883
910
|
it "responds to /example/and/some/more for the non-anchored #{verb.upcase} method" do
|
@@ -900,8 +927,9 @@ describe Grape::Endpoint do
|
|
900
927
|
get '/url'
|
901
928
|
expect(last_response.body).to eq('http://example.org/url')
|
902
929
|
end
|
930
|
+
|
903
931
|
['v1', :v1].each do |version|
|
904
|
-
it "
|
932
|
+
it "includes version #{version}" do
|
905
933
|
subject.version version, using: :path
|
906
934
|
subject.get('/url') do
|
907
935
|
request.url
|
@@ -910,7 +938,7 @@ describe Grape::Endpoint do
|
|
910
938
|
expect(last_response.body).to eq("http://example.org/#{version}/url")
|
911
939
|
end
|
912
940
|
end
|
913
|
-
it '
|
941
|
+
it 'includes prefix' do
|
914
942
|
subject.version 'v1', using: :path
|
915
943
|
subject.prefix 'api'
|
916
944
|
subject.get('/url') do
|
@@ -1000,26 +1028,26 @@ describe Grape::Endpoint do
|
|
1000
1028
|
|
1001
1029
|
# In order that the events finalized (time each block ended)
|
1002
1030
|
expect(@events).to contain_exactly(
|
1003
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1031
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1004
1032
|
filters: a_collection_containing_exactly(an_instance_of(Proc)),
|
1005
1033
|
type: :before }),
|
1006
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1034
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1007
1035
|
filters: [],
|
1008
1036
|
type: :before_validation }),
|
1009
|
-
have_attributes(name: 'endpoint_run_validators.grape', payload: { endpoint: a_kind_of(
|
1037
|
+
have_attributes(name: 'endpoint_run_validators.grape', payload: { endpoint: a_kind_of(described_class),
|
1010
1038
|
validators: [],
|
1011
1039
|
request: a_kind_of(Grape::Request) }),
|
1012
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1040
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1013
1041
|
filters: [],
|
1014
1042
|
type: :after_validation }),
|
1015
|
-
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: a_kind_of(
|
1016
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1043
|
+
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: a_kind_of(described_class) }),
|
1044
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1017
1045
|
filters: [],
|
1018
1046
|
type: :after }),
|
1019
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1047
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1020
1048
|
filters: [],
|
1021
1049
|
type: :finally }),
|
1022
|
-
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: a_kind_of(
|
1050
|
+
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: a_kind_of(described_class),
|
1023
1051
|
env: an_instance_of(Hash) }),
|
1024
1052
|
have_attributes(name: 'format_response.grape', payload: { env: an_instance_of(Hash),
|
1025
1053
|
formatter: a_kind_of(Module) })
|
@@ -1027,25 +1055,25 @@ describe Grape::Endpoint do
|
|
1027
1055
|
|
1028
1056
|
# In order that events were initialized
|
1029
1057
|
expect(@events.sort_by(&:time)).to contain_exactly(
|
1030
|
-
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: a_kind_of(
|
1058
|
+
have_attributes(name: 'endpoint_run.grape', payload: { endpoint: a_kind_of(described_class),
|
1031
1059
|
env: an_instance_of(Hash) }),
|
1032
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1060
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1033
1061
|
filters: a_collection_containing_exactly(an_instance_of(Proc)),
|
1034
1062
|
type: :before }),
|
1035
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1063
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1036
1064
|
filters: [],
|
1037
1065
|
type: :before_validation }),
|
1038
|
-
have_attributes(name: 'endpoint_run_validators.grape', payload: { endpoint: a_kind_of(
|
1066
|
+
have_attributes(name: 'endpoint_run_validators.grape', payload: { endpoint: a_kind_of(described_class),
|
1039
1067
|
validators: [],
|
1040
1068
|
request: a_kind_of(Grape::Request) }),
|
1041
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1069
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1042
1070
|
filters: [],
|
1043
1071
|
type: :after_validation }),
|
1044
|
-
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: a_kind_of(
|
1045
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1072
|
+
have_attributes(name: 'endpoint_render.grape', payload: { endpoint: a_kind_of(described_class) }),
|
1073
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1046
1074
|
filters: [],
|
1047
1075
|
type: :after }),
|
1048
|
-
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(
|
1076
|
+
have_attributes(name: 'endpoint_run_filters.grape', payload: { endpoint: a_kind_of(described_class),
|
1049
1077
|
filters: [],
|
1050
1078
|
type: :finally }),
|
1051
1079
|
have_attributes(name: 'format_response.grape', payload: { env: an_instance_of(Hash),
|
data/spec/grape/entity_spec.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'spec_helper'
|
4
3
|
require 'grape_entity'
|
5
4
|
|
6
5
|
describe Grape::Entity do
|
@@ -32,7 +31,7 @@ describe Grape::Entity do
|
|
32
31
|
end
|
33
32
|
|
34
33
|
it 'pulls a representation from the class options if it exists' do
|
35
|
-
entity = Class.new(
|
34
|
+
entity = Class.new(described_class)
|
36
35
|
allow(entity).to receive(:represent).and_return('Hiya')
|
37
36
|
|
38
37
|
subject.represent Object, with: entity
|
@@ -44,7 +43,7 @@ describe Grape::Entity do
|
|
44
43
|
end
|
45
44
|
|
46
45
|
it 'pulls a representation from the class options if the presented object is a collection of objects' do
|
47
|
-
entity = Class.new(
|
46
|
+
entity = Class.new(described_class)
|
48
47
|
allow(entity).to receive(:represent).and_return('Hiya')
|
49
48
|
|
50
49
|
module EntitySpec
|
@@ -75,7 +74,7 @@ describe Grape::Entity do
|
|
75
74
|
end
|
76
75
|
|
77
76
|
it 'pulls a representation from the class ancestor if it exists' do
|
78
|
-
entity = Class.new(
|
77
|
+
entity = Class.new(described_class)
|
79
78
|
allow(entity).to receive(:represent).and_return('Hiya')
|
80
79
|
|
81
80
|
subclass = Class.new(Object)
|
@@ -90,7 +89,7 @@ describe Grape::Entity do
|
|
90
89
|
|
91
90
|
it 'automatically uses Klass::Entity if that exists' do
|
92
91
|
some_model = Class.new
|
93
|
-
entity = Class.new(
|
92
|
+
entity = Class.new(described_class)
|
94
93
|
allow(entity).to receive(:represent).and_return('Auto-detect!')
|
95
94
|
|
96
95
|
some_model.const_set :Entity, entity
|
@@ -104,7 +103,7 @@ describe Grape::Entity do
|
|
104
103
|
|
105
104
|
it 'automatically uses Klass::Entity based on the first object in the collection being presented' do
|
106
105
|
some_model = Class.new
|
107
|
-
entity = Class.new(
|
106
|
+
entity = Class.new(described_class)
|
108
107
|
allow(entity).to receive(:represent).and_return('Auto-detect!')
|
109
108
|
|
110
109
|
some_model.const_set :Entity, entity
|
@@ -117,7 +116,7 @@ describe Grape::Entity do
|
|
117
116
|
end
|
118
117
|
|
119
118
|
it 'does not run autodetection for Entity when explicitly provided' do
|
120
|
-
entity = Class.new(
|
119
|
+
entity = Class.new(described_class)
|
121
120
|
some_array = []
|
122
121
|
|
123
122
|
subject.get '/example' do
|
@@ -129,7 +128,7 @@ describe Grape::Entity do
|
|
129
128
|
end
|
130
129
|
|
131
130
|
it 'does not use #first method on ActiveRecord::Relation to prevent needless sql query' do
|
132
|
-
entity = Class.new(
|
131
|
+
entity = Class.new(described_class)
|
133
132
|
some_relation = Class.new
|
134
133
|
some_model = Class.new
|
135
134
|
|
@@ -173,7 +172,7 @@ describe Grape::Entity do
|
|
173
172
|
|
174
173
|
%i[json serializable_hash].each do |format|
|
175
174
|
it "presents with #{format}" do
|
176
|
-
entity = Class.new(
|
175
|
+
entity = Class.new(described_class)
|
177
176
|
entity.root 'examples', 'example'
|
178
177
|
entity.expose :id
|
179
178
|
|
@@ -195,7 +194,7 @@ describe Grape::Entity do
|
|
195
194
|
end
|
196
195
|
|
197
196
|
it "presents with #{format} collection" do
|
198
|
-
entity = Class.new(
|
197
|
+
entity = Class.new(described_class)
|
199
198
|
entity.root 'examples', 'example'
|
200
199
|
entity.expose :id
|
201
200
|
|
@@ -219,7 +218,7 @@ describe Grape::Entity do
|
|
219
218
|
end
|
220
219
|
|
221
220
|
it 'presents with xml' do
|
222
|
-
entity = Class.new(
|
221
|
+
entity = Class.new(described_class)
|
223
222
|
entity.root 'examples', 'example'
|
224
223
|
entity.expose :name
|
225
224
|
|
@@ -238,18 +237,18 @@ describe Grape::Entity do
|
|
238
237
|
get '/example'
|
239
238
|
expect(last_response.status).to eq(200)
|
240
239
|
expect(last_response.headers['Content-type']).to eq('application/xml')
|
241
|
-
expect(last_response.body).to eq
|
242
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
243
|
-
<hash>
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
</hash>
|
248
|
-
XML
|
240
|
+
expect(last_response.body).to eq <<~XML
|
241
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
242
|
+
<hash>
|
243
|
+
<example>
|
244
|
+
<name>johnnyiller</name>
|
245
|
+
</example>
|
246
|
+
</hash>
|
247
|
+
XML
|
249
248
|
end
|
250
249
|
|
251
250
|
it 'presents with json' do
|
252
|
-
entity = Class.new(
|
251
|
+
entity = Class.new(described_class)
|
253
252
|
entity.root 'examples', 'example'
|
254
253
|
entity.expose :name
|
255
254
|
|
@@ -275,7 +274,7 @@ XML
|
|
275
274
|
# Include JSONP middleware
|
276
275
|
subject.use Rack::JSONP
|
277
276
|
|
278
|
-
entity = Class.new(
|
277
|
+
entity = Class.new(described_class)
|
279
278
|
entity.root 'examples', 'example'
|
280
279
|
entity.expose :name
|
281
280
|
|
@@ -315,7 +314,7 @@ XML
|
|
315
314
|
user1 = user.new(name: 'user1')
|
316
315
|
user2 = user.new(name: 'user2')
|
317
316
|
|
318
|
-
entity = Class.new(
|
317
|
+
entity = Class.new(described_class)
|
319
318
|
entity.expose :name
|
320
319
|
|
321
320
|
subject.format :json
|
@@ -326,7 +325,7 @@ XML
|
|
326
325
|
end
|
327
326
|
get '/example'
|
328
327
|
expect_response_json = {
|
329
|
-
'page'
|
328
|
+
'page' => 1,
|
330
329
|
'user1' => { 'name' => 'user1' },
|
331
330
|
'user2' => { 'name' => 'user2' }
|
332
331
|
}
|
@@ -1,8 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
3
|
describe Grape::Exceptions::Base do
|
4
|
+
describe '#to_s' do
|
5
|
+
subject { described_class.new(message: message).to_s }
|
6
|
+
|
7
|
+
let(:message) { 'a_message' }
|
8
|
+
|
9
|
+
it { is_expected.to eq(message) }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#message' do
|
13
|
+
subject { described_class.new(message: message).message }
|
14
|
+
|
15
|
+
let(:message) { 'a_message' }
|
16
|
+
|
17
|
+
it { is_expected.to eq(message) }
|
18
|
+
end
|
19
|
+
|
6
20
|
describe '#compose_message' do
|
7
21
|
subject { described_class.new.send(:compose_message, key, **attributes) }
|
8
22
|
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
3
|
describe Grape::Exceptions::ValidationErrors do
|
6
4
|
context 'api with rescue_from :all handler' do
|
7
5
|
subject { Class.new(Grape::API) }
|
6
|
+
|
8
7
|
before do
|
9
8
|
subject.rescue_from :all do |_e|
|
10
9
|
rack_response 'message was processed', 400
|
@@ -56,6 +55,7 @@ describe Grape::Exceptions::ValidationErrors do
|
|
56
55
|
|
57
56
|
context 'api with rescue_from :grape_exceptions handler' do
|
58
57
|
subject { Class.new(Grape::API) }
|
58
|
+
|
59
59
|
before do
|
60
60
|
subject.rescue_from :all do |_e|
|
61
61
|
rack_response 'message was processed', 400
|
@@ -93,6 +93,7 @@ describe Grape::Exceptions::ValidationErrors do
|
|
93
93
|
|
94
94
|
context 'api without a rescue handler' do
|
95
95
|
subject { Class.new(Grape::API) }
|
96
|
+
|
96
97
|
before do
|
97
98
|
subject.params do
|
98
99
|
requires :beer
|