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
@@ -42,7 +42,17 @@ describe Grape::Middleware::Auth::Strategies do
42
42
  end
43
43
 
44
44
  module StrategiesSpec
45
- class Test < Grape::API
45
+ class PasswordHashed < Grape::API
46
+ http_digest(realm: { realm: 'Test Api', opaque: 'secret', passwords_hashed: true }) do |username|
47
+ { 'foo' => Digest::MD5.hexdigest(['foo', 'Test Api', 'bar'].join(':')) }[username]
48
+ end
49
+
50
+ get '/test' do
51
+ [{ hey: 'you' }, { there: 'bar' }, { foo: 'baz' }]
52
+ end
53
+ end
54
+
55
+ class PasswordIsNotHashed < Grape::API
46
56
  http_digest(realm: 'Test Api', opaque: 'secret') do |username|
47
57
  { 'foo' => 'bar' }[username]
48
58
  end
@@ -53,30 +63,60 @@ describe Grape::Middleware::Auth::Strategies do
53
63
  end
54
64
  end
55
65
 
56
- def app
57
- StrategiesSpec::Test
58
- end
66
+ context 'when password is hashed' do
67
+ def app
68
+ StrategiesSpec::PasswordHashed
69
+ end
59
70
 
60
- it 'is a digest authentication challenge' do
61
- get '/test'
62
- expect(last_response).to be_challenge
63
- end
71
+ it 'is a digest authentication challenge' do
72
+ get '/test'
73
+ expect(last_response).to be_challenge
74
+ end
64
75
 
65
- it 'throws a 401 if no auth is given' do
66
- get '/test'
67
- expect(last_response.status).to eq(401)
68
- end
76
+ it 'throws a 401 if no auth is given' do
77
+ get '/test'
78
+ expect(last_response.status).to eq(401)
79
+ end
69
80
 
70
- it 'authenticates if given valid creds' do
71
- digest_authorize 'foo', 'bar'
72
- get '/test'
73
- expect(last_response.status).to eq(200)
81
+ it 'authenticates if given valid creds' do
82
+ digest_authorize 'foo', 'bar'
83
+ get '/test'
84
+ expect(last_response.status).to eq(200)
85
+ end
86
+
87
+ it 'throws a 401 if given invalid creds' do
88
+ digest_authorize 'bar', 'foo'
89
+ get '/test'
90
+ expect(last_response.status).to eq(401)
91
+ end
74
92
  end
75
93
 
76
- it 'throws a 401 if given invalid creds' do
77
- digest_authorize 'bar', 'foo'
78
- get '/test'
79
- expect(last_response.status).to eq(401)
94
+ context 'when password is not hashed' do
95
+ def app
96
+ StrategiesSpec::PasswordIsNotHashed
97
+ end
98
+
99
+ it 'is a digest authentication challenge' do
100
+ get '/test'
101
+ expect(last_response).to be_challenge
102
+ end
103
+
104
+ it 'throws a 401 if no auth is given' do
105
+ get '/test'
106
+ expect(last_response.status).to eq(401)
107
+ end
108
+
109
+ it 'authenticates if given valid creds' do
110
+ digest_authorize 'foo', 'bar'
111
+ get '/test'
112
+ expect(last_response.status).to eq(200)
113
+ end
114
+
115
+ it 'throws a 401 if given invalid creds' do
116
+ digest_authorize 'bar', 'foo'
117
+ get '/test'
118
+ expect(last_response.status).to eq(401)
119
+ end
80
120
  end
81
121
  end
82
122
  end
@@ -3,7 +3,8 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Grape::Middleware::Base do
6
- subject { Grape::Middleware::Base.new(blank_app) }
6
+ subject { described_class.new(blank_app) }
7
+
7
8
  let(:blank_app) { ->(_) { [200, {}, 'Hi there.'] } }
8
9
 
9
10
  before do
@@ -20,6 +21,8 @@ describe Grape::Middleware::Base do
20
21
  end
21
22
 
22
23
  context 'callbacks' do
24
+ after { subject.call!({}) }
25
+
23
26
  it 'calls #before' do
24
27
  expect(subject).to receive(:before)
25
28
  end
@@ -27,8 +30,6 @@ describe Grape::Middleware::Base do
27
30
  it 'calls #after' do
28
31
  expect(subject).to receive(:after)
29
32
  end
30
-
31
- after { subject.call!({}) }
32
33
  end
33
34
 
34
35
  context 'callbacks on error' do
@@ -58,7 +59,7 @@ describe Grape::Middleware::Base do
58
59
  context 'with patched warnings' do
59
60
  before do
60
61
  @warnings = warnings = []
61
- allow_any_instance_of(Grape::Middleware::Base).to receive(:warn) { |m| warnings << m }
62
+ allow_any_instance_of(described_class).to receive(:warn) { |m| warnings << m }
62
63
  allow(subject).to receive(:after).and_raise(StandardError)
63
64
  end
64
65
 
@@ -75,49 +76,57 @@ describe Grape::Middleware::Base do
75
76
  end
76
77
 
77
78
  describe '#response' do
78
- subject { Grape::Middleware::Base.new(response) }
79
+ subject do
80
+ puts described_class
81
+ described_class.new(response)
82
+ end
79
83
 
80
- context Array do
84
+ before { subject.call({}) }
85
+
86
+ context 'when Array' do
81
87
  let(:response) { ->(_) { [204, { abc: 1 }, 'test'] } }
82
88
 
83
89
  it 'status' do
84
- subject.call({})
85
90
  expect(subject.response.status).to eq(204)
86
91
  end
87
92
 
88
93
  it 'body' do
89
- subject.call({})
90
94
  expect(subject.response.body).to eq(['test'])
91
95
  end
92
96
 
93
97
  it 'header' do
94
- subject.call({})
95
98
  expect(subject.response.header).to have_key(:abc)
96
99
  end
100
+
101
+ it 'returns the memoized Rack::Response instance' do
102
+ expect(subject.response).to be(subject.response)
103
+ end
97
104
  end
98
105
 
99
- context Rack::Response do
106
+ context 'when Rack::Response' do
100
107
  let(:response) { ->(_) { Rack::Response.new('test', 204, abc: 1) } }
101
108
 
102
109
  it 'status' do
103
- subject.call({})
104
110
  expect(subject.response.status).to eq(204)
105
111
  end
106
112
 
107
113
  it 'body' do
108
- subject.call({})
109
114
  expect(subject.response.body).to eq(['test'])
110
115
  end
111
116
 
112
117
  it 'header' do
113
- subject.call({})
114
118
  expect(subject.response.header).to have_key(:abc)
115
119
  end
120
+
121
+ it 'returns the memoized Rack::Response instance' do
122
+ expect(subject.response).to be(subject.response)
123
+ end
116
124
  end
117
125
  end
118
126
 
119
127
  describe '#context' do
120
- subject { Grape::Middleware::Base.new(blank_app) }
128
+ subject { described_class.new(blank_app) }
129
+
121
130
  it 'allows access to response context' do
122
131
  subject.call(Grape::Env::API_ENDPOINT => { header: 'some header' })
123
132
  expect(subject.context).to eq(header: 'some header')
@@ -126,7 +135,7 @@ describe Grape::Middleware::Base do
126
135
 
127
136
  context 'options' do
128
137
  it 'persists options passed at initialization' do
129
- expect(Grape::Middleware::Base.new(blank_app, abc: true).options[:abc]).to be true
138
+ expect(described_class.new(blank_app, abc: true).options[:abc]).to be true
130
139
  end
131
140
 
132
141
  context 'defaults' do
@@ -62,6 +62,7 @@ describe Grape::Middleware::Error do
62
62
 
63
63
  context 'with http code' do
64
64
  let(:options) { { default_message: 'Aww, hamburgers.' } }
65
+
65
66
  it 'adds the status code if wanted' do
66
67
  ErrorSpec::ErrApp.error = { message: { code: 200 } }
67
68
  get '/'
@@ -3,76 +3,82 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Grape::Middleware::Error do
6
- # raises a text exception
7
- module ExceptionSpec
8
- class ExceptionApp
6
+ let(:exception_app) do
7
+ Class.new do
9
8
  class << self
10
9
  def call(_env)
11
10
  raise 'rain!'
12
11
  end
13
12
  end
14
13
  end
14
+ end
15
15
 
16
- # raises a non-StandardError (ScriptError) exception
17
- class OtherExceptionApp
16
+ let(:other_exception_app) do
17
+ Class.new do
18
18
  class << self
19
19
  def call(_env)
20
20
  raise NotImplementedError, 'snow!'
21
21
  end
22
22
  end
23
23
  end
24
+ end
24
25
 
25
- # raises a hash error
26
- class ErrorHashApp
26
+ let(:custom_error_app) do
27
+ Class.new do
27
28
  class << self
28
- def error!(message, status)
29
- throw :error, message: { error: message, detail: 'missing widget' }, status: status
30
- end
29
+ class CustomError < Grape::Exceptions::Base; end
31
30
 
32
31
  def call(_env)
33
- error!('rain!', 401)
32
+ raise CustomError.new(status: 400, message: 'failed validation')
34
33
  end
35
34
  end
36
35
  end
36
+ end
37
37
 
38
- # raises an error!
39
- class AccessDeniedApp
38
+ let(:error_hash_app) do
39
+ Class.new do
40
40
  class << self
41
41
  def error!(message, status)
42
- throw :error, message: message, status: status
42
+ throw :error, message: { error: message, detail: 'missing widget' }, status: status
43
43
  end
44
44
 
45
45
  def call(_env)
46
- error!('Access Denied', 401)
46
+ error!('rain!', 401)
47
47
  end
48
48
  end
49
49
  end
50
+ end
50
51
 
51
- # raises a custom error
52
- class CustomError < Grape::Exceptions::Base
53
- end
54
-
55
- class CustomErrorApp
52
+ let(:access_denied_app) do
53
+ Class.new do
56
54
  class << self
55
+ def error!(message, status)
56
+ throw :error, message: message, status: status
57
+ end
58
+
57
59
  def call(_env)
58
- raise CustomError.new(status: 400, message: 'failed validation')
60
+ error!('Access Denied', 401)
59
61
  end
60
62
  end
61
63
  end
62
64
  end
63
65
 
64
- def app
65
- subject
66
+ let(:app) do
67
+ builder = Rack::Builder.new
68
+ builder.use Spec::Support::EndpointFaker
69
+ if options.any?
70
+ builder.use described_class, options
71
+ else
72
+ builder.use described_class
73
+ end
74
+ builder.run running_app
75
+ builder.to_app
66
76
  end
67
77
 
68
78
  context 'with defaults' do
69
- subject do
70
- Rack::Builder.app do
71
- use Spec::Support::EndpointFaker
72
- use Grape::Middleware::Error
73
- run ExceptionSpec::ExceptionApp
74
- end
75
- end
79
+ let(:running_app) { exception_app }
80
+ let(:options) { {} }
81
+
76
82
  it 'does not trap errors by default' do
77
83
  expect { get '/' }.to raise_error(RuntimeError, 'rain!')
78
84
  end
@@ -80,17 +86,14 @@ describe Grape::Middleware::Error do
80
86
 
81
87
  context 'with rescue_all' do
82
88
  context 'StandardError exception' do
83
- subject do
84
- Rack::Builder.app do
85
- use Spec::Support::EndpointFaker
86
- use Grape::Middleware::Error, rescue_all: true
87
- run ExceptionSpec::ExceptionApp
88
- end
89
- end
89
+ let(:running_app) { exception_app }
90
+ let(:options) { { rescue_all: true } }
91
+
90
92
  it 'sets the message appropriately' do
91
93
  get '/'
92
94
  expect(last_response.body).to eq('rain!')
93
95
  end
96
+
94
97
  it 'defaults to a 500 status' do
95
98
  get '/'
96
99
  expect(last_response.status).to eq(500)
@@ -98,13 +101,9 @@ describe Grape::Middleware::Error do
98
101
  end
99
102
 
100
103
  context 'Non-StandardError exception' do
101
- subject do
102
- Rack::Builder.app do
103
- use Spec::Support::EndpointFaker
104
- use Grape::Middleware::Error, rescue_all: true
105
- run ExceptionSpec::OtherExceptionApp
106
- end
107
- end
104
+ let(:running_app) { other_exception_app }
105
+ let(:options) { { rescue_all: true } }
106
+
108
107
  it 'does not trap errors other than StandardError' do
109
108
  expect { get '/' }.to raise_error(NotImplementedError, 'snow!')
110
109
  end
@@ -113,13 +112,9 @@ describe Grape::Middleware::Error do
113
112
 
114
113
  context 'Non-StandardError exception with a provided rescue handler' do
115
114
  context 'default error response' do
116
- subject do
117
- Rack::Builder.app do
118
- use Spec::Support::EndpointFaker
119
- use Grape::Middleware::Error, rescue_handlers: { NotImplementedError => nil }
120
- run ExceptionSpec::OtherExceptionApp
121
- end
122
- end
115
+ let(:running_app) { other_exception_app }
116
+ let(:options) { { rescue_handlers: { NotImplementedError => nil } } }
117
+
123
118
  it 'rescues the exception using the default handler' do
124
119
  get '/'
125
120
  expect(last_response.body).to eq('snow!')
@@ -127,13 +122,9 @@ describe Grape::Middleware::Error do
127
122
  end
128
123
 
129
124
  context 'custom error response' do
130
- subject do
131
- Rack::Builder.app do
132
- use Spec::Support::EndpointFaker
133
- use Grape::Middleware::Error, rescue_handlers: { NotImplementedError => -> { Rack::Response.new('rescued', 200, {}) } }
134
- run ExceptionSpec::OtherExceptionApp
135
- end
136
- end
125
+ let(:running_app) { other_exception_app }
126
+ let(:options) { { rescue_handlers: { NotImplementedError => -> { Rack::Response.new('rescued', 200, {}) } } } }
127
+
137
128
  it 'rescues the exception using the provided handler' do
138
129
  get '/'
139
130
  expect(last_response.body).to eq('rescued')
@@ -142,13 +133,9 @@ describe Grape::Middleware::Error do
142
133
  end
143
134
 
144
135
  context do
145
- subject do
146
- Rack::Builder.app do
147
- use Spec::Support::EndpointFaker
148
- use Grape::Middleware::Error, rescue_all: true, default_status: 500
149
- run ExceptionSpec::ExceptionApp
150
- end
151
- end
136
+ let(:running_app) { exception_app }
137
+ let(:options) { { rescue_all: true, default_status: 500 } }
138
+
152
139
  it 'is possible to specify a different default status code' do
153
140
  get '/'
154
141
  expect(last_response.status).to eq(500)
@@ -156,13 +143,9 @@ describe Grape::Middleware::Error do
156
143
  end
157
144
 
158
145
  context do
159
- subject do
160
- Rack::Builder.app do
161
- use Spec::Support::EndpointFaker
162
- use Grape::Middleware::Error, rescue_all: true, format: :json
163
- run ExceptionSpec::ExceptionApp
164
- end
165
- end
146
+ let(:running_app) { exception_app }
147
+ let(:options) { { rescue_all: true, format: :json } }
148
+
166
149
  it 'is possible to return errors in json format' do
167
150
  get '/'
168
151
  expect(last_response.body).to eq('{"error":"rain!"}')
@@ -170,13 +153,9 @@ describe Grape::Middleware::Error do
170
153
  end
171
154
 
172
155
  context do
173
- subject do
174
- Rack::Builder.app do
175
- use Spec::Support::EndpointFaker
176
- use Grape::Middleware::Error, rescue_all: true, format: :json
177
- run ExceptionSpec::ErrorHashApp
178
- end
179
- end
156
+ let(:running_app) { error_hash_app }
157
+ let(:options) { { rescue_all: true, format: :json } }
158
+
180
159
  it 'is possible to return hash errors in json format' do
181
160
  get '/'
182
161
  expect(['{"error":"rain!","detail":"missing widget"}',
@@ -185,13 +164,9 @@ describe Grape::Middleware::Error do
185
164
  end
186
165
 
187
166
  context do
188
- subject do
189
- Rack::Builder.app do
190
- use Spec::Support::EndpointFaker
191
- use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
192
- run ExceptionSpec::ExceptionApp
193
- end
194
- end
167
+ let(:running_app) { exception_app }
168
+ let(:options) { { rescue_all: true, format: :jsonapi } }
169
+
195
170
  it 'is possible to return errors in jsonapi format' do
196
171
  get '/'
197
172
  expect(last_response.body).to eq('{&quot;error&quot;:&quot;rain!&quot;}')
@@ -199,13 +174,8 @@ describe Grape::Middleware::Error do
199
174
  end
200
175
 
201
176
  context do
202
- subject do
203
- Rack::Builder.app do
204
- use Spec::Support::EndpointFaker
205
- use Grape::Middleware::Error, rescue_all: true, format: :jsonapi
206
- run ExceptionSpec::ErrorHashApp
207
- end
208
- end
177
+ let(:running_app) { error_hash_app }
178
+ let(:options) { { rescue_all: true, format: :jsonapi } }
209
179
 
210
180
  it 'is possible to return hash errors in jsonapi format' do
211
181
  get '/'
@@ -215,13 +185,9 @@ describe Grape::Middleware::Error do
215
185
  end
216
186
 
217
187
  context do
218
- subject do
219
- Rack::Builder.app do
220
- use Spec::Support::EndpointFaker
221
- use Grape::Middleware::Error, rescue_all: true, format: :xml
222
- run ExceptionSpec::ExceptionApp
223
- end
224
- end
188
+ let(:running_app) { exception_app }
189
+ let(:options) { { rescue_all: true, format: :xml } }
190
+
225
191
  it 'is possible to return errors in xml format' do
226
192
  get '/'
227
193
  expect(last_response.body).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <message>rain!</message>\n</error>\n")
@@ -229,13 +195,9 @@ describe Grape::Middleware::Error do
229
195
  end
230
196
 
231
197
  context do
232
- subject do
233
- Rack::Builder.app do
234
- use Spec::Support::EndpointFaker
235
- use Grape::Middleware::Error, rescue_all: true, format: :xml
236
- run ExceptionSpec::ErrorHashApp
237
- end
238
- end
198
+ let(:running_app) { error_hash_app }
199
+ let(:options) { { rescue_all: true, format: :xml } }
200
+
239
201
  it 'is possible to return hash errors in xml format' do
240
202
  get '/'
241
203
  expect(["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<error>\n <detail>missing widget</detail>\n <error>rain!</error>\n</error>\n",
@@ -244,20 +206,19 @@ describe Grape::Middleware::Error do
244
206
  end
245
207
 
246
208
  context do
247
- subject do
248
- Rack::Builder.app do
249
- use Spec::Support::EndpointFaker
250
- use Grape::Middleware::Error,
251
- rescue_all: true,
252
- format: :custom,
253
- error_formatters: {
254
- custom: lambda do |message, _backtrace, _options, _env, _original_exception|
255
- { custom_formatter: message }.inspect
256
- end
257
- }
258
- run ExceptionSpec::ExceptionApp
259
- end
209
+ let(:running_app) { exception_app }
210
+ let(:options) do
211
+ {
212
+ rescue_all: true,
213
+ format: :custom,
214
+ error_formatters: {
215
+ custom: lambda do |message, _backtrace, _options, _env, _original_exception|
216
+ { custom_formatter: message }.inspect
217
+ end
218
+ }
219
+ }
260
220
  end
221
+
261
222
  it 'is possible to specify a custom formatter' do
262
223
  get '/'
263
224
  expect(last_response.body).to eq('{:custom_formatter=&gt;&quot;rain!&quot;}')
@@ -265,13 +226,9 @@ describe Grape::Middleware::Error do
265
226
  end
266
227
 
267
228
  context do
268
- subject do
269
- Rack::Builder.app do
270
- use Spec::Support::EndpointFaker
271
- use Grape::Middleware::Error
272
- run ExceptionSpec::AccessDeniedApp
273
- end
274
- end
229
+ let(:running_app) { access_denied_app }
230
+ let(:options) { {} }
231
+
275
232
  it 'does not trap regular error! codes' do
276
233
  get '/'
277
234
  expect(last_response.status).to eq(401)
@@ -279,13 +236,9 @@ describe Grape::Middleware::Error do
279
236
  end
280
237
 
281
238
  context do
282
- subject do
283
- Rack::Builder.app do
284
- use Spec::Support::EndpointFaker
285
- use Grape::Middleware::Error, rescue_all: false
286
- run ExceptionSpec::CustomErrorApp
287
- end
288
- end
239
+ let(:running_app) { custom_error_app }
240
+ let(:options) { { rescue_all: false } }
241
+
289
242
  it 'responds to custom Grape exceptions appropriately' do
290
243
  get '/'
291
244
  expect(last_response.status).to eq(400)
@@ -294,16 +247,15 @@ describe Grape::Middleware::Error do
294
247
  end
295
248
 
296
249
  context 'with rescue_options :backtrace and :exception set to true' do
297
- subject do
298
- Rack::Builder.app do
299
- use Spec::Support::EndpointFaker
300
- use Grape::Middleware::Error,
301
- rescue_all: true,
302
- format: :json,
303
- rescue_options: { backtrace: true, original_exception: true }
304
- run ExceptionSpec::ExceptionApp
305
- end
250
+ let(:running_app) { exception_app }
251
+ let(:options) do
252
+ {
253
+ rescue_all: true,
254
+ format: :json,
255
+ rescue_options: { backtrace: true, original_exception: true }
256
+ }
306
257
  end
258
+
307
259
  it 'is possible to return the backtrace and the original exception in json format' do
308
260
  get '/'
309
261
  expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original_exception', 'RuntimeError')
@@ -311,16 +263,15 @@ describe Grape::Middleware::Error do
311
263
  end
312
264
 
313
265
  context do
314
- subject do
315
- Rack::Builder.app do
316
- use Spec::Support::EndpointFaker
317
- use Grape::Middleware::Error,
318
- rescue_all: true,
319
- format: :xml,
320
- rescue_options: { backtrace: true, original_exception: true }
321
- run ExceptionSpec::ExceptionApp
322
- end
266
+ let(:running_app) { exception_app }
267
+ let(:options) do
268
+ {
269
+ rescue_all: true,
270
+ format: :xml,
271
+ rescue_options: { backtrace: true, original_exception: true }
272
+ }
323
273
  end
274
+
324
275
  it 'is possible to return the backtrace and the original exception in xml format' do
325
276
  get '/'
326
277
  expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original-exception', 'RuntimeError')
@@ -328,16 +279,15 @@ describe Grape::Middleware::Error do
328
279
  end
329
280
 
330
281
  context do
331
- subject do
332
- Rack::Builder.app do
333
- use Spec::Support::EndpointFaker
334
- use Grape::Middleware::Error,
335
- rescue_all: true,
336
- format: :txt,
337
- rescue_options: { backtrace: true, original_exception: true }
338
- run ExceptionSpec::ExceptionApp
339
- end
282
+ let(:running_app) { exception_app }
283
+ let(:options) do
284
+ {
285
+ rescue_all: true,
286
+ format: :txt,
287
+ rescue_options: { backtrace: true, original_exception: true }
288
+ }
340
289
  end
290
+
341
291
  it 'is possible to return the backtrace and the original exception in txt format' do
342
292
  get '/'
343
293
  expect(last_response.body).to include('error', 'rain!', 'backtrace', 'original exception', 'RuntimeError')