grape 0.9.0 → 0.10.0

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.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -66
  3. data/.rubocop_todo.yml +78 -17
  4. data/.travis.yml +7 -3
  5. data/Appraisals +7 -0
  6. data/CHANGELOG.md +24 -0
  7. data/CONTRIBUTING.md +7 -0
  8. data/Gemfile +1 -7
  9. data/Guardfile +1 -1
  10. data/README.md +560 -94
  11. data/RELEASING.md +1 -1
  12. data/Rakefile +10 -11
  13. data/UPGRADING.md +211 -3
  14. data/gemfiles/rails_3.gemfile +14 -0
  15. data/gemfiles/rails_4.gemfile +14 -0
  16. data/grape.gemspec +10 -9
  17. data/lib/backports/active_support/deep_dup.rb +49 -0
  18. data/lib/backports/active_support/duplicable.rb +88 -0
  19. data/lib/grape.rb +29 -2
  20. data/lib/grape/api.rb +59 -65
  21. data/lib/grape/dsl/api.rb +19 -0
  22. data/lib/grape/dsl/callbacks.rb +6 -4
  23. data/lib/grape/dsl/configuration.rb +49 -5
  24. data/lib/grape/dsl/helpers.rb +7 -8
  25. data/lib/grape/dsl/inside_route.rb +22 -10
  26. data/lib/grape/dsl/middleware.rb +5 -5
  27. data/lib/grape/dsl/parameters.rb +6 -2
  28. data/lib/grape/dsl/request_response.rb +23 -20
  29. data/lib/grape/dsl/routing.rb +52 -49
  30. data/lib/grape/dsl/settings.rb +110 -0
  31. data/lib/grape/dsl/validations.rb +14 -6
  32. data/lib/grape/endpoint.rb +104 -88
  33. data/lib/grape/exceptions/base.rb +2 -2
  34. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  35. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  36. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  37. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
  38. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  39. data/lib/grape/exceptions/missing_option.rb +1 -1
  40. data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
  41. data/lib/grape/exceptions/unknown_options.rb +1 -1
  42. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  43. data/lib/grape/exceptions/validation.rb +1 -1
  44. data/lib/grape/exceptions/validation_errors.rb +2 -2
  45. data/lib/grape/formatter/serializable_hash.rb +1 -1
  46. data/lib/grape/formatter/xml.rb +1 -1
  47. data/lib/grape/locale/en.yml +2 -0
  48. data/lib/grape/middleware/auth/dsl.rb +26 -21
  49. data/lib/grape/middleware/auth/strategies.rb +1 -1
  50. data/lib/grape/middleware/auth/strategy_info.rb +0 -2
  51. data/lib/grape/middleware/base.rb +2 -2
  52. data/lib/grape/middleware/error.rb +1 -1
  53. data/lib/grape/middleware/formatter.rb +5 -5
  54. data/lib/grape/middleware/versioner.rb +1 -1
  55. data/lib/grape/middleware/versioner/header.rb +3 -3
  56. data/lib/grape/middleware/versioner/param.rb +2 -2
  57. data/lib/grape/middleware/versioner/path.rb +1 -1
  58. data/lib/grape/namespace.rb +1 -1
  59. data/lib/grape/path.rb +9 -3
  60. data/lib/grape/util/content_types.rb +16 -8
  61. data/lib/grape/util/inheritable_setting.rb +74 -0
  62. data/lib/grape/util/inheritable_values.rb +51 -0
  63. data/lib/grape/util/stackable_values.rb +52 -0
  64. data/lib/grape/util/strict_hash_configuration.rb +106 -0
  65. data/lib/grape/validations.rb +0 -220
  66. data/lib/grape/validations/attributes_iterator.rb +21 -0
  67. data/lib/grape/validations/params_scope.rb +176 -0
  68. data/lib/grape/validations/validators/all_or_none.rb +20 -0
  69. data/lib/grape/validations/validators/allow_blank.rb +30 -0
  70. data/lib/grape/validations/validators/at_least_one_of.rb +20 -0
  71. data/lib/grape/validations/validators/base.rb +37 -0
  72. data/lib/grape/validations/{coerce.rb → validators/coerce.rb} +3 -3
  73. data/lib/grape/validations/{default.rb → validators/default.rb} +1 -1
  74. data/lib/grape/validations/validators/exactly_one_of.rb +20 -0
  75. data/lib/grape/validations/validators/multiple_params_base.rb +26 -0
  76. data/lib/grape/validations/validators/mutual_exclusion.rb +25 -0
  77. data/lib/grape/validations/{presence.rb → validators/presence.rb} +2 -2
  78. data/lib/grape/validations/validators/regexp.rb +12 -0
  79. data/lib/grape/validations/validators/values.rb +26 -0
  80. data/lib/grape/version.rb +1 -1
  81. data/spec/grape/api_spec.rb +522 -343
  82. data/spec/grape/dsl/callbacks_spec.rb +4 -4
  83. data/spec/grape/dsl/configuration_spec.rb +48 -9
  84. data/spec/grape/dsl/helpers_spec.rb +6 -13
  85. data/spec/grape/dsl/inside_route_spec.rb +43 -4
  86. data/spec/grape/dsl/middleware_spec.rb +1 -10
  87. data/spec/grape/dsl/parameters_spec.rb +8 -1
  88. data/spec/grape/dsl/request_response_spec.rb +16 -22
  89. data/spec/grape/dsl/routing_spec.rb +21 -5
  90. data/spec/grape/dsl/settings_spec.rb +219 -0
  91. data/spec/grape/dsl/validations_spec.rb +8 -11
  92. data/spec/grape/endpoint_spec.rb +115 -86
  93. data/spec/grape/entity_spec.rb +33 -33
  94. data/spec/grape/exceptions/invalid_formatter_spec.rb +3 -5
  95. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +4 -6
  96. data/spec/grape/exceptions/missing_mime_type_spec.rb +5 -6
  97. data/spec/grape/exceptions/missing_option_spec.rb +3 -5
  98. data/spec/grape/exceptions/unknown_options_spec.rb +3 -5
  99. data/spec/grape/exceptions/unknown_validator_spec.rb +3 -5
  100. data/spec/grape/exceptions/validation_errors_spec.rb +5 -5
  101. data/spec/grape/loading_spec.rb +44 -0
  102. data/spec/grape/middleware/auth/base_spec.rb +0 -4
  103. data/spec/grape/middleware/auth/dsl_spec.rb +2 -4
  104. data/spec/grape/middleware/auth/strategies_spec.rb +5 -6
  105. data/spec/grape/middleware/exception_spec.rb +8 -10
  106. data/spec/grape/middleware/formatter_spec.rb +13 -15
  107. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +10 -10
  108. data/spec/grape/middleware/versioner/header_spec.rb +25 -25
  109. data/spec/grape/middleware/versioner/param_spec.rb +15 -17
  110. data/spec/grape/middleware/versioner/path_spec.rb +1 -2
  111. data/spec/grape/middleware/versioner_spec.rb +0 -1
  112. data/spec/grape/path_spec.rb +66 -45
  113. data/spec/grape/util/inheritable_setting_spec.rb +217 -0
  114. data/spec/grape/util/inheritable_values_spec.rb +63 -0
  115. data/spec/grape/util/stackable_values_spec.rb +115 -0
  116. data/spec/grape/util/strict_hash_configuration_spec.rb +38 -0
  117. data/spec/grape/validations/attributes_iterator_spec.rb +4 -0
  118. data/spec/grape/validations/params_scope_spec.rb +57 -0
  119. data/spec/grape/validations/validators/all_or_none_spec.rb +60 -0
  120. data/spec/grape/validations/validators/allow_blank_spec.rb +170 -0
  121. data/spec/grape/validations/{at_least_one_of_spec.rb → validators/at_least_one_of_spec.rb} +7 -3
  122. data/spec/grape/validations/{coerce_spec.rb → validators/coerce_spec.rb} +8 -11
  123. data/spec/grape/validations/{default_spec.rb → validators/default_spec.rb} +7 -9
  124. data/spec/grape/validations/{exactly_one_of_spec.rb → validators/exactly_one_of_spec.rb} +15 -11
  125. data/spec/grape/validations/{mutual_exclusion_spec.rb → validators/mutual_exclusion_spec.rb} +11 -9
  126. data/spec/grape/validations/{presence_spec.rb → validators/presence_spec.rb} +30 -30
  127. data/spec/grape/validations/{regexp_spec.rb → validators/regexp_spec.rb} +2 -4
  128. data/spec/grape/validations/{values_spec.rb → validators/values_spec.rb} +95 -23
  129. data/spec/grape/validations/{zh-CN.yml → validators/zh-CN.yml} +0 -0
  130. data/spec/grape/validations_spec.rb +335 -70
  131. data/spec/shared/versioning_examples.rb +7 -8
  132. data/spec/spec_helper.rb +2 -0
  133. data/spec/support/basic_auth_encode_helpers.rb +1 -1
  134. data/spec/support/content_type_helpers.rb +1 -1
  135. data/spec/support/versioned_helpers.rb +3 -3
  136. metadata +80 -33
  137. data/lib/grape/util/deep_merge.rb +0 -23
  138. data/lib/grape/util/hash_stack.rb +0 -120
  139. data/lib/grape/validations/at_least_one_of.rb +0 -25
  140. data/lib/grape/validations/exactly_one_of.rb +0 -26
  141. data/lib/grape/validations/mutual_exclusion.rb +0 -25
  142. data/lib/grape/validations/regexp.rb +0 -12
  143. data/lib/grape/validations/values.rb +0 -23
  144. data/spec/grape/util/hash_stack_spec.rb +0 -132
@@ -0,0 +1,20 @@
1
+ module Grape
2
+ module Validations
3
+ require 'grape/validations/validators/multiple_params_base'
4
+ class AllOrNoneOfValidator < MultipleParamsBase
5
+ def validate!(params)
6
+ super
7
+ if scope_requires_params && only_subset_present
8
+ fail Grape::Exceptions::Validation, params: all_keys, message_key: :all_or_none
9
+ end
10
+ params
11
+ end
12
+
13
+ private
14
+
15
+ def only_subset_present
16
+ scoped_params.any? { |resource_params| keys_in_common(resource_params).length > 0 && keys_in_common(resource_params).length < attrs.length }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ module Grape
2
+ module Validations
3
+ class AllowBlankValidator < Base
4
+ def validate_param!(attr_name, params)
5
+ return if @option || !params.is_a?(Hash)
6
+
7
+ value = params[attr_name]
8
+ value = value.strip if value.respond_to?(:strip)
9
+
10
+ key_exists = params.key?(attr_name)
11
+
12
+ if @scope.root?
13
+ # root scope. validate if it's a required param. if it's optional, validate only if key exists in hash
14
+ should_validate = @required || key_exists
15
+ else # nested scope
16
+ should_validate = # required param, and scope contains some values (if scoping element contains no values, treat as blank)
17
+ (@required && params.present?) ||
18
+ # optional param but key inside scoping element exists
19
+ (!@required && params.key?(attr_name))
20
+ end
21
+
22
+ return unless should_validate
23
+
24
+ unless value.present?
25
+ fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :blank
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ module Grape
2
+ module Validations
3
+ require 'grape/validations/validators/multiple_params_base'
4
+ class AtLeastOneOfValidator < MultipleParamsBase
5
+ def validate!(params)
6
+ super
7
+ if scope_requires_params && no_exclusive_params_are_present
8
+ fail Grape::Exceptions::Validation, params: all_keys, message_key: :at_least_one
9
+ end
10
+ params
11
+ end
12
+
13
+ private
14
+
15
+ def no_exclusive_params_are_present
16
+ scoped_params.any? { |resource_params| keys_in_common(resource_params).empty? }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ module Grape
2
+ module Validations
3
+ class Base
4
+ attr_reader :attrs
5
+
6
+ def initialize(attrs, options, required, scope)
7
+ @attrs = Array(attrs)
8
+ @option = options
9
+ @required = required
10
+ @scope = scope
11
+ end
12
+
13
+ def validate!(params)
14
+ attributes = AttributesIterator.new(self, @scope, params)
15
+ attributes.each do |resource_params, attr_name|
16
+ if @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
17
+ validate_param!(attr_name, resource_params)
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.convert_to_short_name(klass)
23
+ ret = klass.name.gsub(/::/, '/')
24
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
25
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
26
+ .tr('-', '_')
27
+ .downcase
28
+ File.basename(ret, '_validator')
29
+ end
30
+
31
+ def self.inherited(klass)
32
+ short_name = convert_to_short_name(klass)
33
+ Validations.register_validator(short_name, klass)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -4,14 +4,14 @@ module Grape
4
4
  end
5
5
 
6
6
  module Validations
7
- class CoerceValidator < SingleOptionValidator
7
+ class CoerceValidator < Base
8
8
  def validate_param!(attr_name, params)
9
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce unless params.is_a? Hash
9
+ fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce unless params.is_a? Hash
10
10
  new_value = coerce_value(@option, params[attr_name])
11
11
  if valid_type?(new_value)
12
12
  params[attr_name] = new_value
13
13
  else
14
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce
14
+ fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce
15
15
  end
16
16
  end
17
17
 
@@ -1,6 +1,6 @@
1
1
  module Grape
2
2
  module Validations
3
- class DefaultValidator < Validator
3
+ class DefaultValidator < Base
4
4
  def initialize(attrs, options, required, scope)
5
5
  @default = options
6
6
  super
@@ -0,0 +1,20 @@
1
+ module Grape
2
+ module Validations
3
+ require 'grape/validations/validators/mutual_exclusion'
4
+ class ExactlyOneOfValidator < MutualExclusionValidator
5
+ def validate!(params)
6
+ super
7
+ if scope_requires_params && none_of_restricted_params_is_present
8
+ fail Grape::Exceptions::Validation, params: all_keys, message_key: :exactly_one
9
+ end
10
+ params
11
+ end
12
+
13
+ private
14
+
15
+ def none_of_restricted_params_is_present
16
+ scoped_params.any? { |resource_params| keys_in_common(resource_params).empty? }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ module Grape
2
+ module Validations
3
+ class MultipleParamsBase < Base
4
+ attr_reader :scoped_params
5
+
6
+ def validate!(params)
7
+ @scoped_params = [@scope.params(params)].flatten
8
+ params
9
+ end
10
+
11
+ private
12
+
13
+ def scope_requires_params
14
+ @scope.required? || scoped_params.any?(&:any?)
15
+ end
16
+
17
+ def keys_in_common(resource_params)
18
+ (all_keys & resource_params.stringify_keys.keys).map(&:to_s)
19
+ end
20
+
21
+ def all_keys
22
+ attrs.map(&:to_s)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ module Grape
2
+ module Validations
3
+ require 'grape/validations/validators/multiple_params_base'
4
+ class MutualExclusionValidator < MultipleParamsBase
5
+ attr_reader :processing_keys_in_common
6
+
7
+ def validate!(params)
8
+ super
9
+ if two_or_more_exclusive_params_are_present
10
+ fail Grape::Exceptions::Validation, params: processing_keys_in_common, message_key: :mutual_exclusion
11
+ end
12
+ params
13
+ end
14
+
15
+ private
16
+
17
+ def two_or_more_exclusive_params_are_present
18
+ scoped_params.any? do |resource_params|
19
+ @processing_keys_in_common = keys_in_common(resource_params)
20
+ @processing_keys_in_common.length > 1
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,6 +1,6 @@
1
1
  module Grape
2
2
  module Validations
3
- class PresenceValidator < Validator
3
+ class PresenceValidator < Base
4
4
  def validate!(params)
5
5
  return unless @scope.should_validate?(params)
6
6
  super
@@ -8,7 +8,7 @@ module Grape
8
8
 
9
9
  def validate_param!(attr_name, params)
10
10
  unless params.respond_to?(:key?) && params.key?(attr_name)
11
- raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :presence
11
+ fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :presence
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,12 @@
1
+ module Grape
2
+ module Validations
3
+ class RegexpValidator < Base
4
+ def validate_param!(attr_name, params)
5
+ if params.key?(attr_name) &&
6
+ (params[attr_name].nil? || !(params[attr_name].to_s =~ @option))
7
+ fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :regexp
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ module Grape
2
+ module Validations
3
+ class ValuesValidator < Base
4
+ def initialize(attrs, options, required, scope)
5
+ @values = options
6
+ super
7
+ end
8
+
9
+ def validate_param!(attr_name, params)
10
+ return unless params[attr_name] || required_for_root_scope?
11
+
12
+ values = @values.is_a?(Proc) ? @values.call : @values
13
+ param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
14
+ unless (param_array - values).empty?
15
+ fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :values
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def required_for_root_scope?
22
+ @required && @scope.root?
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/grape/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Grape
2
- VERSION = '0.9.0'
2
+ VERSION = '0.10.0'
3
3
  end
@@ -13,25 +13,45 @@ describe Grape::API do
13
13
  it 'routes root through with the prefix' do
14
14
  subject.prefix 'awesome/sauce'
15
15
  subject.get do
16
- "Hello there."
16
+ 'Hello there.'
17
17
  end
18
18
 
19
19
  get 'awesome/sauce/'
20
- expect(last_response.body).to eql "Hello there."
20
+ expect(last_response.status).to eql 200
21
+ expect(last_response.body).to eql 'Hello there.'
21
22
  end
22
23
 
23
24
  it 'routes through with the prefix' do
24
25
  subject.prefix 'awesome/sauce'
25
26
  subject.get :hello do
26
- "Hello there."
27
+ 'Hello there.'
27
28
  end
28
29
 
29
30
  get 'awesome/sauce/hello'
30
- expect(last_response.body).to eql "Hello there."
31
+ expect(last_response.body).to eql 'Hello there.'
31
32
 
32
33
  get '/hello'
33
34
  expect(last_response.status).to eql 404
34
35
  end
36
+
37
+ it 'supports OPTIONS' do
38
+ subject.prefix 'awesome/sauce'
39
+ subject.get do
40
+ 'Hello there.'
41
+ end
42
+
43
+ options 'awesome/sauce'
44
+ expect(last_response.status).to eql 204
45
+ expect(last_response.body).to be_blank
46
+ end
47
+
48
+ it 'disallows POST' do
49
+ subject.prefix 'awesome/sauce'
50
+ subject.get
51
+
52
+ post 'awesome/sauce'
53
+ expect(last_response.status).to eql 405
54
+ end
35
55
  end
36
56
 
37
57
  describe '.version' do
@@ -64,7 +84,7 @@ describe Grape::API do
64
84
  let(:macro_options) do
65
85
  {
66
86
  using: :param,
67
- parameter: "apiver"
87
+ parameter: 'apiver'
68
88
  }
69
89
  end
70
90
  end
@@ -116,7 +136,7 @@ describe Grape::API do
116
136
  it 'adds the association to the :representations setting' do
117
137
  klass = Class.new
118
138
  subject.represent Object, with: klass
119
- expect(subject.settings[:representations][Object]).to eq(klass)
139
+ expect(Grape::DSL::Configuration.stacked_hash_to_hash(subject.namespace_stackable(:representations))[Object]).to eq(klass)
120
140
  end
121
141
  end
122
142
 
@@ -126,7 +146,7 @@ describe Grape::API do
126
146
  subject.namespace :awesome do
127
147
  internal_namespace = namespace
128
148
  end
129
- expect(internal_namespace).to eql("/awesome")
149
+ expect(internal_namespace).to eql('/awesome')
130
150
  end
131
151
 
132
152
  it 'comes after the prefix and version' do
@@ -134,11 +154,11 @@ describe Grape::API do
134
154
  subject.version 'v1', using: :path
135
155
 
136
156
  subject.namespace :awesome do
137
- get('/hello') { "worked" }
157
+ get('/hello') { 'worked' }
138
158
  end
139
159
 
140
- get "/rad/v1/awesome/hello"
141
- expect(last_response.body).to eq("worked")
160
+ get '/rad/v1/awesome/hello'
161
+ expect(last_response.body).to eq('worked')
142
162
  end
143
163
 
144
164
  it 'cancels itself after the block is over' do
@@ -146,7 +166,7 @@ describe Grape::API do
146
166
  subject.namespace :awesome do
147
167
  internal_namespace = namespace
148
168
  end
149
- expect(subject.namespace).to eql("/")
169
+ expect(subject.namespace).to eql('/')
150
170
  end
151
171
 
152
172
  it 'is stackable' do
@@ -173,21 +193,21 @@ describe Grape::API do
173
193
  end
174
194
  end
175
195
  get '/members/23'
176
- expect(last_response.body).to eq("23")
196
+ expect(last_response.body).to eq('23')
177
197
  expect(inner_namespace).to eq('/members/:member_id')
178
198
  end
179
199
 
180
200
  it 'is callable with nil just to push onto the stack' do
181
201
  subject.namespace do
182
202
  version 'v2', using: :path
183
- get('/hello') { "inner" }
203
+ get('/hello') { 'inner' }
184
204
  end
185
- subject.get('/hello') { "outer" }
205
+ subject.get('/hello') { 'outer' }
186
206
 
187
207
  get '/v2/hello'
188
- expect(last_response.body).to eq("inner")
208
+ expect(last_response.body).to eq('inner')
189
209
  get '/hello'
190
- expect(last_response.body).to eq("outer")
210
+ expect(last_response.body).to eq('outer')
191
211
  end
192
212
 
193
213
  %w(group resource resources segment).each do |als|
@@ -196,7 +216,7 @@ describe Grape::API do
196
216
  subject.send(als, :awesome) do
197
217
  inner_namespace = namespace
198
218
  end
199
- expect(inner_namespace).to eq "/awesome"
219
+ expect(inner_namespace).to eq '/awesome'
200
220
  end
201
221
  end
202
222
  end
@@ -235,10 +255,10 @@ describe Grape::API do
235
255
  it 'allows for no path' do
236
256
  subject.namespace :votes do
237
257
  get do
238
- "Votes"
258
+ 'Votes'
239
259
  end
240
260
  post do
241
- "Created a Vote"
261
+ 'Created a Vote'
242
262
  end
243
263
  end
244
264
 
@@ -249,21 +269,23 @@ describe Grape::API do
249
269
  end
250
270
 
251
271
  it 'handles empty calls' do
252
- subject.get "/"
253
- get "/"
254
- expect(last_response.body).to eql ""
272
+ subject.get '/'
273
+ get '/'
274
+ expect(last_response.body).to eql ''
255
275
  end
256
276
 
257
277
  describe 'root routes should work with' do
258
278
  before do
259
279
  subject.format :txt
280
+ subject.content_type :json, 'application/json'
281
+ subject.formatter :json, ->(object, env) { object }
260
282
  def subject.enable_root_route!
261
- get("/") { "root" }
283
+ get('/') { 'root' }
262
284
  end
263
285
  end
264
286
 
265
287
  after do
266
- expect(last_response.body).to eql "root"
288
+ expect(last_response.body).to eql 'root'
267
289
  end
268
290
 
269
291
  describe 'path versioned APIs' do
@@ -273,11 +295,11 @@ describe Grape::API do
273
295
  end
274
296
 
275
297
  it 'without a format' do
276
- versioned_get "/", "v1", using: :path
298
+ versioned_get '/', 'v1', using: :path
277
299
  end
278
300
 
279
301
  it 'with a format' do
280
- get "/v1/.json"
302
+ get '/v1/.json'
281
303
  end
282
304
  end
283
305
 
@@ -285,41 +307,41 @@ describe Grape::API do
285
307
  subject.version 'v1', using: :header, vendor: 'test'
286
308
  subject.enable_root_route!
287
309
 
288
- versioned_get "/", "v1", using: :header, vendor: 'test'
310
+ versioned_get '/', 'v1', using: :header, vendor: 'test'
289
311
  end
290
312
 
291
313
  it 'header versioned APIs with multiple headers' do
292
- subject.version ['v1', 'v2'], using: :header, vendor: 'test'
314
+ subject.version %w(v1 v2), using: :header, vendor: 'test'
293
315
  subject.enable_root_route!
294
316
 
295
- versioned_get "/", "v1", using: :header, vendor: 'test'
296
- versioned_get "/", "v2", using: :header, vendor: 'test'
317
+ versioned_get '/', 'v1', using: :header, vendor: 'test'
318
+ versioned_get '/', 'v2', using: :header, vendor: 'test'
297
319
  end
298
320
 
299
321
  it 'param versioned APIs' do
300
322
  subject.version 'v1', using: :param
301
323
  subject.enable_root_route!
302
324
 
303
- versioned_get "/", "v1", using: :param
325
+ versioned_get '/', 'v1', using: :param
304
326
  end
305
327
 
306
328
  it 'Accept-Version header versioned APIs' do
307
329
  subject.version 'v1', using: :accept_version_header
308
330
  subject.enable_root_route!
309
331
 
310
- versioned_get "/", "v1", using: :accept_version_header
332
+ versioned_get '/', 'v1', using: :accept_version_header
311
333
  end
312
334
 
313
335
  it 'unversioned APIs' do
314
336
  subject.enable_root_route!
315
337
 
316
- get "/"
338
+ get '/'
317
339
  end
318
340
  end
319
341
 
320
342
  it 'allows for multiple paths' do
321
- subject.get(["/abc", "/def"]) do
322
- "foo"
343
+ subject.get(['/abc', '/def']) do
344
+ 'foo'
323
345
  end
324
346
 
325
347
  get '/abc'
@@ -330,10 +352,10 @@ describe Grape::API do
330
352
 
331
353
  context 'format' do
332
354
  before(:each) do
333
- allow_any_instance_of(Object).to receive(:to_json).and_return("abc")
334
- allow_any_instance_of(Object).to receive(:to_txt).and_return("def")
355
+ allow_any_instance_of(Object).to receive(:to_json).and_return('abc')
356
+ allow_any_instance_of(Object).to receive(:to_txt).and_return('def')
335
357
 
336
- subject.get("/abc") do
358
+ subject.get('/abc') do
337
359
  Object.new
338
360
  end
339
361
  end
@@ -353,7 +375,7 @@ describe Grape::API do
353
375
 
354
376
  it 'allows for format without corrupting a param' do
355
377
  subject.get('/:id') do
356
- { "id" => params[:id] }
378
+ { 'id' => params[:id] }
357
379
  end
358
380
 
359
381
  get '/awesome.json'
@@ -363,7 +385,7 @@ describe Grape::API do
363
385
  it 'allows for format in namespace with no path' do
364
386
  subject.namespace :abc do
365
387
  get do
366
- ["json"]
388
+ ['json']
367
389
  end
368
390
  end
369
391
 
@@ -373,7 +395,7 @@ describe Grape::API do
373
395
 
374
396
  it 'allows for multiple verbs' do
375
397
  subject.route([:get, :post], '/abc') do
376
- "hiya"
398
+ 'hiya'
377
399
  end
378
400
 
379
401
  subject.endpoints.first.routes.each do |route|
@@ -399,7 +421,7 @@ describe Grape::API do
399
421
  expect(last_response.body).to eql MultiJson.dump(object)
400
422
  expect(last_request.params).to eql Hash.new
401
423
  end
402
- it "stores input in api.request.input" do
424
+ it 'stores input in api.request.input' do
403
425
  subject.format :json
404
426
  subject.send(verb) do
405
427
  env['api.request.input']
@@ -408,8 +430,8 @@ describe Grape::API do
408
430
  expect(last_response.status).to eq(verb == :post ? 201 : 200)
409
431
  expect(last_response.body).to eql MultiJson.dump(object).to_json
410
432
  end
411
- context "chunked transfer encoding" do
412
- it "stores input in api.request.input" do
433
+ context 'chunked transfer encoding' do
434
+ it 'stores input in api.request.input' do
413
435
  subject.format :json
414
436
  subject.send(verb) do
415
437
  env['api.request.input']
@@ -424,16 +446,15 @@ describe Grape::API do
424
446
  end
425
447
 
426
448
  it 'allows for multipart paths' do
427
-
428
449
  subject.route([:get, :post], '/:id/first') do
429
- "first"
450
+ 'first'
430
451
  end
431
452
 
432
453
  subject.route([:get, :post], '/:id') do
433
- "ola"
454
+ 'ola'
434
455
  end
435
456
  subject.route([:get, :post], '/:id/first/second') do
436
- "second"
457
+ 'second'
437
458
  end
438
459
 
439
460
  get '/1'
@@ -446,12 +467,11 @@ describe Grape::API do
446
467
  expect(last_response.body).to eql 'first'
447
468
  get '/1/first/second'
448
469
  expect(last_response.body).to eql 'second'
449
-
450
470
  end
451
471
 
452
472
  it 'allows for :any as a verb' do
453
473
  subject.route(:any, '/abc') do
454
- "lol"
474
+ 'lol'
455
475
  end
456
476
 
457
477
  %w(get post put delete options patch).each do |m|
@@ -476,7 +496,7 @@ describe Grape::API do
476
496
 
477
497
  it 'returns a 201 response code for POST by default' do
478
498
  subject.post('example') do
479
- "Created"
499
+ 'Created'
480
500
  end
481
501
 
482
502
  post '/example'
@@ -487,7 +507,7 @@ describe Grape::API do
487
507
  it 'returns a 405 for an unsupported method with an X-Custom-Header' do
488
508
  subject.before { header 'X-Custom-Header', 'foo' }
489
509
  subject.get 'example' do
490
- "example"
510
+ 'example'
491
511
  end
492
512
  put '/example'
493
513
  expect(last_response.status).to eql 405
@@ -497,10 +517,10 @@ describe Grape::API do
497
517
 
498
518
  specify '405 responses includes an Allow header specifying supported methods' do
499
519
  subject.get 'example' do
500
- "example"
520
+ 'example'
501
521
  end
502
522
  subject.post 'example' do
503
- "example"
523
+ 'example'
504
524
  end
505
525
  put '/example'
506
526
  expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, POST, HEAD'
@@ -508,10 +528,10 @@ describe Grape::API do
508
528
 
509
529
  specify '405 responses includes an Content-Type header' do
510
530
  subject.get 'example' do
511
- "example"
531
+ 'example'
512
532
  end
513
533
  subject.post 'example' do
514
- "example"
534
+ 'example'
515
535
  end
516
536
  put '/example'
517
537
  expect(last_response.headers['Content-Type']).to eql 'text/plain'
@@ -520,7 +540,7 @@ describe Grape::API do
520
540
  it 'adds an OPTIONS route that returns a 204, an Allow header and a X-Custom-Header' do
521
541
  subject.before { header 'X-Custom-Header', 'foo' }
522
542
  subject.get 'example' do
523
- "example"
543
+ 'example'
524
544
  end
525
545
  options '/example'
526
546
  expect(last_response.status).to eql 204
@@ -531,7 +551,7 @@ describe Grape::API do
531
551
 
532
552
  it 'allows HEAD on a GET request' do
533
553
  subject.get 'example' do
534
- "example"
554
+ 'example'
535
555
  end
536
556
  head '/example'
537
557
  expect(last_response.status).to eql 200
@@ -543,18 +563,18 @@ describe Grape::API do
543
563
  error! 'nothing to see here', 400
544
564
  end
545
565
  subject.get 'example' do
546
- "example"
566
+ 'example'
547
567
  end
548
568
  head '/example'
549
569
  expect(last_response.status).to eql 400
550
570
  end
551
571
  end
552
572
 
553
- context "do_not_route_head!" do
573
+ context 'do_not_route_head!' do
554
574
  before :each do
555
575
  subject.do_not_route_head!
556
576
  subject.get 'example' do
557
- "example"
577
+ 'example'
558
578
  end
559
579
  end
560
580
  it 'options does not contain HEAD' do
@@ -569,11 +589,11 @@ describe Grape::API do
569
589
  end
570
590
  end
571
591
 
572
- context "do_not_route_options!" do
592
+ context 'do_not_route_options!' do
573
593
  before :each do
574
594
  subject.do_not_route_options!
575
595
  subject.get 'example' do
576
- "example"
596
+ 'example'
577
597
  end
578
598
  end
579
599
  it 'options does not exist' do
@@ -629,7 +649,7 @@ describe Grape::API do
629
649
  "#{@foo} #{@bar}"
630
650
  end
631
651
 
632
- get '/', id: "32"
652
+ get '/', id: '32'
633
653
  expect(last_response.body).to eql 'first 32:Fixnum second'
634
654
  end
635
655
 
@@ -737,17 +757,42 @@ describe Grape::API do
737
757
 
738
758
  context 'format' do
739
759
  before do
740
- subject.get("/foo") { "bar" }
760
+ subject.get('/foo') { 'bar' }
741
761
  end
742
762
 
743
763
  it 'sets content type for txt format' do
744
764
  get '/foo'
745
- expect(last_response.headers['Content-Type']).to eql 'text/plain'
765
+ expect(last_response.headers['Content-Type']).to eq('text/plain')
766
+ end
767
+
768
+ it 'sets content type for xml' do
769
+ get '/foo.xml'
770
+ expect(last_response.headers['Content-Type']).to eq('application/xml')
746
771
  end
747
772
 
748
773
  it 'sets content type for json' do
749
774
  get '/foo.json'
750
- expect(last_response.headers['Content-Type']).to eql 'application/json'
775
+ expect(last_response.headers['Content-Type']).to eq('application/json')
776
+ end
777
+
778
+ it 'sets content type for serializable hash format' do
779
+ get '/foo.serializable_hash'
780
+ expect(last_response.headers['Content-Type']).to eq('application/json')
781
+ end
782
+
783
+ it 'sets content type for binary format' do
784
+ get '/foo.binary'
785
+ expect(last_response.headers['Content-Type']).to eq('application/octet-stream')
786
+ end
787
+
788
+ it 'returns raw data when content type binary' do
789
+ image_filename = 'grape.png'
790
+ file = File.open(image_filename, 'rb') { |io| io.read }
791
+ subject.format :binary
792
+ subject.get('/binary_file') { File.binread(image_filename) }
793
+ get '/binary_file'
794
+ expect(last_response.headers['Content-Type']).to eq('application/octet-stream')
795
+ expect(last_response.body).to eq(file)
751
796
  end
752
797
 
753
798
  it 'sets content type for error' do
@@ -756,24 +801,24 @@ describe Grape::API do
756
801
  expect(last_response.headers['Content-Type']).to eql 'text/plain'
757
802
  end
758
803
 
759
- it 'sets content type for error' do
804
+ it 'sets content type for json error' do
760
805
  subject.format :json
761
806
  subject.get('/error') { error!('error in json', 500) }
762
- get '/error.json'
807
+ get '/error'
763
808
  expect(last_response.headers['Content-Type']).to eql 'application/json'
764
809
  end
765
810
 
766
- it 'sets content type for xml' do
811
+ it 'sets content type for xml error' do
767
812
  subject.format :xml
768
813
  subject.get('/error') { error!('error in xml', 500) }
769
- get '/error.xml'
814
+ get '/error'
770
815
  expect(last_response.headers['Content-Type']).to eql 'application/xml'
771
816
  end
772
817
 
773
818
  context 'with a custom content_type' do
774
819
  before do
775
820
  subject.content_type :custom, 'application/custom'
776
- subject.formatter :custom, lambda { |object, env| "custom" }
821
+ subject.formatter :custom, ->(object, env) { 'custom' }
777
822
 
778
823
  subject.get('/custom') { 'bar' }
779
824
  subject.get('/error') { error!('error in custom', 500) }
@@ -792,21 +837,21 @@ describe Grape::API do
792
837
 
793
838
  context 'env["api.format"]' do
794
839
  before do
795
- subject.post "attachment" do
840
+ subject.post 'attachment' do
796
841
  filename = params[:file][:filename]
797
842
  content_type MIME::Types.type_for(filename)[0].to_s
798
843
  env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
799
- header "Content-Disposition", "attachment; filename*=UTF-8''#{URI.escape(filename)}"
844
+ header 'Content-Disposition', "attachment; filename*=UTF-8''#{URI.escape(filename)}"
800
845
  params[:file][:tempfile].read
801
846
  end
802
847
  end
803
848
 
804
849
  ['/attachment.png', 'attachment'].each do |url|
805
850
  it "uploads and downloads a PNG file via #{url}" do
806
- image_filename = "grape.png"
851
+ image_filename = 'grape.png'
807
852
  post url, file: Rack::Test::UploadedFile.new(image_filename, 'image/png', true)
808
853
  expect(last_response.status).to eq(201)
809
- expect(last_response.headers['Content-Type']).to eq("image/png")
854
+ expect(last_response.headers['Content-Type']).to eq('image/png')
810
855
  expect(last_response.headers['Content-Disposition']).to eq("attachment; filename*=UTF-8''grape.png")
811
856
  File.open(image_filename, 'rb') do |io|
812
857
  expect(last_response.body).to eq io.read
@@ -814,11 +859,11 @@ describe Grape::API do
814
859
  end
815
860
  end
816
861
 
817
- it "uploads and downloads a Ruby file" do
862
+ it 'uploads and downloads a Ruby file' do
818
863
  filename = __FILE__
819
- post "/attachment.rb", file: Rack::Test::UploadedFile.new(filename, 'application/x-ruby', true)
864
+ post '/attachment.rb', file: Rack::Test::UploadedFile.new(filename, 'application/x-ruby', true)
820
865
  expect(last_response.status).to eq(201)
821
- expect(last_response.headers['Content-Type']).to eq("application/x-ruby")
866
+ expect(last_response.headers['Content-Type']).to eq('application/x-ruby')
822
867
  expect(last_response.headers['Content-Disposition']).to eq("attachment; filename*=UTF-8''api_spec.rb")
823
868
  File.open(filename, 'rb') do |io|
824
869
  expect(last_response.body).to eq io.read
@@ -847,20 +892,14 @@ describe Grape::API do
847
892
 
848
893
  describe '.middleware' do
849
894
  it 'includes middleware arguments from settings' do
850
- settings = Grape::Util::HashStack.new
851
- allow(settings).to receive(:stack).and_return([{ middleware: [[ApiSpec::PhonyMiddleware, 'abc', 123]] }])
852
- allow(subject).to receive(:settings).and_return(settings)
895
+ subject.use ApiSpec::PhonyMiddleware, 'abc', 123
853
896
  expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, 'abc', 123]]
854
897
  end
855
898
 
856
899
  it 'includes all middleware from stacked settings' do
857
- settings = Grape::Util::HashStack.new
858
- allow(settings).to receive(:stack).and_return [
859
- { middleware: [[ApiSpec::PhonyMiddleware, 123], [ApiSpec::PhonyMiddleware, 'abc']] },
860
- { middleware: [[ApiSpec::PhonyMiddleware, 'foo']] }
861
- ]
862
-
863
- allow(subject).to receive(:settings).and_return(settings)
900
+ subject.use ApiSpec::PhonyMiddleware, 123
901
+ subject.use ApiSpec::PhonyMiddleware, 'abc'
902
+ subject.use ApiSpec::PhonyMiddleware, 'foo'
864
903
 
865
904
  expect(subject.middleware).to eql [
866
905
  [ApiSpec::PhonyMiddleware, 123],
@@ -899,13 +938,13 @@ describe Grape::API do
899
938
  end
900
939
 
901
940
  it 'adds a block if one is given' do
902
- block = lambda {}
941
+ block = -> {}
903
942
  subject.use ApiSpec::PhonyMiddleware, &block
904
943
  expect(subject.middleware).to eql [[ApiSpec::PhonyMiddleware, block]]
905
944
  end
906
945
 
907
946
  it 'uses a block if one is given' do
908
- block = lambda {}
947
+ block = -> {}
909
948
  subject.use ApiSpec::PhonyMiddleware, &block
910
949
  subject.get '/' do
911
950
  env['phony.block'].inspect
@@ -916,7 +955,7 @@ describe Grape::API do
916
955
  end
917
956
 
918
957
  it 'does not destroy the middleware settings on multiple runs' do
919
- block = lambda {}
958
+ block = -> {}
920
959
  subject.use ApiSpec::PhonyMiddleware, &block
921
960
  subject.get '/' do
922
961
  env['phony.block'].inspect
@@ -931,15 +970,15 @@ describe Grape::API do
931
970
  it 'mounts behind error middleware' do
932
971
  m = Class.new(Grape::Middleware::Base) do
933
972
  def before
934
- throw :error, message: "Caught in the Net", status: 400
973
+ throw :error, message: 'Caught in the Net', status: 400
935
974
  end
936
975
  end
937
976
  subject.use m
938
- subject.get "/" do
977
+ subject.get '/' do
939
978
  end
940
- get "/"
979
+ get '/'
941
980
  expect(last_response.status).to eq(400)
942
- expect(last_response.body).to eq("Caught in the Net")
981
+ expect(last_response.body).to eq('Caught in the Net')
943
982
  end
944
983
  end
945
984
  end
@@ -948,7 +987,7 @@ describe Grape::API do
948
987
  subject.http_basic do |u, p|
949
988
  u == 'allow'
950
989
  end
951
- subject.get(:hello) { "Hello, world." }
990
+ subject.get(:hello) { 'Hello, world.' }
952
991
  get '/hello'
953
992
  expect(last_response.status).to eql 401
954
993
  get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
@@ -956,13 +995,13 @@ describe Grape::API do
956
995
  end
957
996
 
958
997
  it 'is scopable' do
959
- subject.get(:hello) { "Hello, world." }
998
+ subject.get(:hello) { 'Hello, world.' }
960
999
  subject.namespace :admin do
961
1000
  http_basic do |u, p|
962
1001
  u == 'allow'
963
1002
  end
964
1003
 
965
- get(:hello) { "Hello, world." }
1004
+ get(:hello) { 'Hello, world.' }
966
1005
  end
967
1006
 
968
1007
  get '/hello'
@@ -976,7 +1015,7 @@ describe Grape::API do
976
1015
  u == 'allow'
977
1016
  end
978
1017
 
979
- subject.get(:hello) { "Hello, world." }
1018
+ subject.get(:hello) { 'Hello, world.' }
980
1019
  get '/hello'
981
1020
  expect(last_response.status).to eql 401
982
1021
  get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
@@ -992,7 +1031,7 @@ describe Grape::API do
992
1031
  u == 'allow'
993
1032
  end
994
1033
 
995
- subject.get(:hello) { "Hello, world." }
1034
+ subject.get(:hello) { 'Hello, world.' }
996
1035
  get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
997
1036
  expect(basic_auth_context).to be_an_instance_of(Grape::Endpoint)
998
1037
  end
@@ -1008,7 +1047,7 @@ describe Grape::API do
1008
1047
  authorize(u, p)
1009
1048
  end
1010
1049
 
1011
- subject.get(:hello) { "Hello, world." }
1050
+ subject.get(:hello) { 'Hello, world.' }
1012
1051
  get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
1013
1052
  expect(last_response.status).to eql 200
1014
1053
  get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('disallow', 'whatever')
@@ -1017,7 +1056,7 @@ describe Grape::API do
1017
1056
 
1018
1057
  it 'can set instance variables accessible to routes' do
1019
1058
  subject.http_basic do |u, p|
1020
- @hello = "Hello, world."
1059
+ @hello = 'Hello, world.'
1021
1060
 
1022
1061
  u == 'allow'
1023
1062
  end
@@ -1025,11 +1064,20 @@ describe Grape::API do
1025
1064
  subject.get(:hello) { @hello }
1026
1065
  get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
1027
1066
  expect(last_response.status).to eql 200
1028
- expect(last_response.body).to eql "Hello, world."
1067
+ expect(last_response.body).to eql 'Hello, world.'
1029
1068
  end
1030
1069
  end
1031
1070
 
1032
1071
  describe '.logger' do
1072
+ subject do
1073
+ Class.new(Grape::API) do
1074
+ def self.io
1075
+ @io ||= StringIO.new
1076
+ end
1077
+ logger ::Logger.new(io)
1078
+ end
1079
+ end
1080
+
1033
1081
  it 'returns an instance of Logger class by default' do
1034
1082
  expect(subject.logger.class).to eql Logger
1035
1083
  end
@@ -1038,14 +1086,18 @@ describe Grape::API do
1038
1086
  mylogger = Class.new
1039
1087
  subject.logger mylogger
1040
1088
  expect(mylogger).to receive(:info).exactly(1).times
1041
- subject.logger.info "this will be logged"
1089
+ subject.logger.info 'this will be logged'
1042
1090
  end
1043
1091
 
1044
- it "defaults to a standard logger log format" do
1092
+ it 'defaults to a standard logger log format' do
1045
1093
  t = Time.at(100)
1046
1094
  allow(Time).to receive(:now).and_return(t)
1047
- expect(STDOUT).to receive(:write).with("I, [#{Logger::Formatter.new.send(:format_datetime, t)}\##{Process.pid}] INFO -- : this will be logged\n")
1048
- subject.logger.info "this will be logged"
1095
+ if ActiveSupport::VERSION::MAJOR >= 4
1096
+ expect(subject.io).to receive(:write).with("I, [#{Logger::Formatter.new.send(:format_datetime, t)}\##{Process.pid}] INFO -- : this will be logged\n")
1097
+ else
1098
+ expect(subject.io).to receive(:write).with("this will be logged\n")
1099
+ end
1100
+ subject.logger.info 'this will be logged'
1049
1101
  end
1050
1102
  end
1051
1103
 
@@ -1053,7 +1105,7 @@ describe Grape::API do
1053
1105
  it 'is accessible from the endpoint' do
1054
1106
  subject.helpers do
1055
1107
  def hello
1056
- "Hello, world."
1108
+ 'Hello, world.'
1057
1109
  end
1058
1110
  end
1059
1111
 
@@ -1117,7 +1169,7 @@ describe Grape::API do
1117
1169
  it 'allows for modules' do
1118
1170
  mod = Module.new do
1119
1171
  def hello
1120
- "Hello, world."
1172
+ 'Hello, world.'
1121
1173
  end
1122
1174
  end
1123
1175
  subject.helpers mod
@@ -1184,7 +1236,7 @@ describe Grape::API do
1184
1236
  describe '.rescue_from' do
1185
1237
  it 'does not rescue errors when rescue_from is not set' do
1186
1238
  subject.get '/exception' do
1187
- raise "rain!"
1239
+ fail 'rain!'
1188
1240
  end
1189
1241
  expect { get '/exception' }.to raise_error
1190
1242
  end
@@ -1192,16 +1244,29 @@ describe Grape::API do
1192
1244
  it 'rescues all errors if rescue_from :all is called' do
1193
1245
  subject.rescue_from :all
1194
1246
  subject.get '/exception' do
1195
- raise "rain!"
1247
+ fail 'rain!'
1248
+ end
1249
+ get '/exception'
1250
+ expect(last_response.status).to eql 500
1251
+ expect(last_response.body).to eq 'rain!'
1252
+ end
1253
+
1254
+ it 'rescues all errors with a json formatter' do
1255
+ subject.format :json
1256
+ subject.default_format :json
1257
+ subject.rescue_from :all
1258
+ subject.get '/exception' do
1259
+ fail 'rain!'
1196
1260
  end
1197
1261
  get '/exception'
1198
1262
  expect(last_response.status).to eql 500
1263
+ expect(last_response.body).to eq({ error: 'rain!' }.to_json)
1199
1264
  end
1200
1265
 
1201
1266
  it 'rescues only certain errors if rescue_from is called with specific errors' do
1202
1267
  subject.rescue_from ArgumentError
1203
- subject.get('/rescued') { raise ArgumentError }
1204
- subject.get('/unrescued') { raise "beefcake" }
1268
+ subject.get('/rescued') { fail ArgumentError }
1269
+ subject.get('/unrescued') { fail 'beefcake' }
1205
1270
 
1206
1271
  get '/rescued'
1207
1272
  expect(last_response.status).to eql 500
@@ -1215,7 +1280,7 @@ describe Grape::API do
1215
1280
  end
1216
1281
 
1217
1282
  it 'does not re-raise exceptions of type Grape::Exceptions::Base' do
1218
- subject.get('/custom_exception') { raise CustomError }
1283
+ subject.get('/custom_exception') { fail CustomError }
1219
1284
 
1220
1285
  expect { get '/custom_exception' }.not_to raise_error
1221
1286
  end
@@ -1225,7 +1290,7 @@ describe Grape::API do
1225
1290
  rack_response('New Error', e.status)
1226
1291
  end
1227
1292
  subject.get '/custom_error' do
1228
- raise CustomError, status: 400, message: 'Custom Error'
1293
+ fail CustomError, status: 400, message: 'Custom Error'
1229
1294
  end
1230
1295
 
1231
1296
  get '/custom_error'
@@ -1236,7 +1301,7 @@ describe Grape::API do
1236
1301
 
1237
1302
  it 'can rescue exceptions raised in the formatter' do
1238
1303
  formatter = double(:formatter)
1239
- allow(formatter).to receive(:call) { raise StandardError }
1304
+ allow(formatter).to receive(:call) { fail StandardError }
1240
1305
  allow(Grape::Formatter::Base).to receive(:formatter_for) { formatter }
1241
1306
 
1242
1307
  subject.rescue_from :all do |e|
@@ -1256,7 +1321,7 @@ describe Grape::API do
1256
1321
  rack_response("rescued from #{e.message}", 202)
1257
1322
  end
1258
1323
  subject.get '/exception' do
1259
- raise "rain!"
1324
+ fail 'rain!'
1260
1325
  end
1261
1326
  get '/exception'
1262
1327
  expect(last_response.status).to eql 202
@@ -1275,7 +1340,7 @@ describe Grape::API do
1275
1340
  rack_response("rescued from #{e.class.name}", 500)
1276
1341
  end
1277
1342
  subject.get '/exception' do
1278
- raise ConnectionError
1343
+ fail ConnectionError
1279
1344
  end
1280
1345
  get '/exception'
1281
1346
  expect(last_response.status).to eql 500
@@ -1286,7 +1351,7 @@ describe Grape::API do
1286
1351
  rack_response("rescued from #{e.class.name}", 500)
1287
1352
  end
1288
1353
  subject.get '/exception' do
1289
- raise ConnectionError
1354
+ fail ConnectionError
1290
1355
  end
1291
1356
  get '/exception'
1292
1357
  expect(last_response.status).to eql 500
@@ -1297,7 +1362,7 @@ describe Grape::API do
1297
1362
  rack_response("rescued from #{e.class.name}", 500)
1298
1363
  end
1299
1364
  subject.get '/exception' do
1300
- raise ConnectionError
1365
+ fail ConnectionError
1301
1366
  end
1302
1367
  get '/exception'
1303
1368
  expect(last_response.status).to eql 500
@@ -1311,10 +1376,10 @@ describe Grape::API do
1311
1376
  rack_response("rescued from #{e.class.name}", 500)
1312
1377
  end
1313
1378
  subject.get '/connection' do
1314
- raise ConnectionError
1379
+ fail ConnectionError
1315
1380
  end
1316
1381
  subject.get '/database' do
1317
- raise DatabaseError
1382
+ fail DatabaseError
1318
1383
  end
1319
1384
  get '/connection'
1320
1385
  expect(last_response.status).to eql 500
@@ -1328,7 +1393,7 @@ describe Grape::API do
1328
1393
  rack_response("rescued from #{e.class.name}", 500)
1329
1394
  end
1330
1395
  subject.get '/uncaught' do
1331
- raise CommunicationError
1396
+ fail CommunicationError
1332
1397
  end
1333
1398
  expect { get '/uncaught' }.to raise_error(CommunicationError)
1334
1399
  end
@@ -1337,21 +1402,21 @@ describe Grape::API do
1337
1402
 
1338
1403
  describe '.rescue_from klass, lambda' do
1339
1404
  it 'rescues an error with the lambda' do
1340
- subject.rescue_from ArgumentError, lambda {
1341
- rack_response("rescued with a lambda", 400)
1405
+ subject.rescue_from ArgumentError, -> {
1406
+ rack_response('rescued with a lambda', 400)
1342
1407
  }
1343
- subject.get('/rescue_lambda') { raise ArgumentError }
1408
+ subject.get('/rescue_lambda') { fail ArgumentError }
1344
1409
 
1345
1410
  get '/rescue_lambda'
1346
1411
  expect(last_response.status).to eq(400)
1347
- expect(last_response.body).to eq("rescued with a lambda")
1412
+ expect(last_response.body).to eq('rescued with a lambda')
1348
1413
  end
1349
1414
 
1350
1415
  it 'can execute the lambda with an argument' do
1351
- subject.rescue_from ArgumentError, lambda { |e|
1416
+ subject.rescue_from ArgumentError, ->(e) {
1352
1417
  rack_response(e.message, 400)
1353
1418
  }
1354
- subject.get('/rescue_lambda') { raise ArgumentError, 'lambda takes an argument' }
1419
+ subject.get('/rescue_lambda') { fail ArgumentError, 'lambda takes an argument' }
1355
1420
 
1356
1421
  get '/rescue_lambda'
1357
1422
  expect(last_response.status).to eq(400)
@@ -1366,7 +1431,7 @@ describe Grape::API do
1366
1431
  end
1367
1432
 
1368
1433
  subject.rescue_from ArgumentError, with: rescue_arg_error
1369
- subject.get('/rescue_method') { raise ArgumentError }
1434
+ subject.get('/rescue_method') { fail ArgumentError }
1370
1435
 
1371
1436
  get '/rescue_method'
1372
1437
  expect(last_response.status).to eq(400)
@@ -1387,13 +1452,13 @@ describe Grape::API do
1387
1452
  rack_response("rescued from #{e.class.name}", 500)
1388
1453
  end
1389
1454
  subject.get '/caught_child' do
1390
- raise APIErrors::ChildError
1455
+ fail APIErrors::ChildError
1391
1456
  end
1392
1457
  subject.get '/caught_parent' do
1393
- raise APIErrors::ParentError
1458
+ fail APIErrors::ParentError
1394
1459
  end
1395
1460
  subject.get '/uncaught_parent' do
1396
- raise StandardError
1461
+ fail StandardError
1397
1462
  end
1398
1463
 
1399
1464
  get '/caught_child'
@@ -1408,7 +1473,7 @@ describe Grape::API do
1408
1473
  rack_response("rescued from #{e.class.name}", 500)
1409
1474
  end
1410
1475
  subject.get '/caught_child' do
1411
- raise APIErrors::ChildError
1476
+ fail APIErrors::ChildError
1412
1477
  end
1413
1478
 
1414
1479
  get '/caught_child'
@@ -1420,7 +1485,7 @@ describe Grape::API do
1420
1485
  rack_response("rescued from #{e.class.name}", 500)
1421
1486
  end
1422
1487
  subject.get '/uncaught' do
1423
- raise APIErrors::ChildError
1488
+ fail APIErrors::ChildError
1424
1489
  end
1425
1490
  expect { get '/uncaught' }.to raise_error(APIErrors::ChildError)
1426
1491
  end
@@ -1431,17 +1496,17 @@ describe Grape::API do
1431
1496
  subject.rescue_from :all
1432
1497
  subject.format :txt
1433
1498
  subject.get '/exception' do
1434
- raise "rain!"
1499
+ fail 'rain!'
1435
1500
  end
1436
1501
  get '/exception'
1437
- expect(last_response.body).to eql "rain!"
1502
+ expect(last_response.body).to eql 'rain!'
1438
1503
  end
1439
1504
 
1440
1505
  it 'rescues all errors and return :txt with backtrace' do
1441
1506
  subject.rescue_from :all, backtrace: true
1442
1507
  subject.format :txt
1443
1508
  subject.get '/exception' do
1444
- raise "rain!"
1509
+ fail 'rain!'
1445
1510
  end
1446
1511
  get '/exception'
1447
1512
  expect(last_response.body.start_with?("rain!\r\n")).to be true
@@ -1449,22 +1514,22 @@ describe Grape::API do
1449
1514
 
1450
1515
  it 'rescues all errors with a default formatter' do
1451
1516
  subject.default_format :foo
1452
- subject.content_type :foo, "text/foo"
1517
+ subject.content_type :foo, 'text/foo'
1453
1518
  subject.rescue_from :all
1454
1519
  subject.get '/exception' do
1455
- raise "rain!"
1520
+ fail 'rain!'
1456
1521
  end
1457
1522
  get '/exception.foo'
1458
- expect(last_response.body).to start_with "rain!"
1523
+ expect(last_response.body).to start_with 'rain!'
1459
1524
  end
1460
1525
 
1461
1526
  it 'defaults the error formatter to format' do
1462
1527
  subject.format :json
1463
1528
  subject.rescue_from :all
1464
- subject.content_type :json, "application/json"
1465
- subject.content_type :foo, "text/foo"
1529
+ subject.content_type :json, 'application/json'
1530
+ subject.content_type :foo, 'text/foo'
1466
1531
  subject.get '/exception' do
1467
- raise "rain!"
1532
+ fail 'rain!'
1468
1533
  end
1469
1534
  get '/exception.json'
1470
1535
  expect(last_response.body).to eq('{"error":"rain!"}')
@@ -1484,10 +1549,10 @@ describe Grape::API do
1484
1549
  subject.rescue_from :all, backtrace: true
1485
1550
  subject.error_formatter :txt, CustomErrorFormatter
1486
1551
  subject.get '/exception' do
1487
- raise "rain!"
1552
+ fail 'rain!'
1488
1553
  end
1489
1554
  get '/exception'
1490
- expect(last_response.body).to eq("message: rain! @backtrace")
1555
+ expect(last_response.body).to eq('message: rain! @backtrace')
1491
1556
  end
1492
1557
  end
1493
1558
 
@@ -1504,7 +1569,7 @@ describe Grape::API do
1504
1569
  it 'returns a custom error format' do
1505
1570
  subject.rescue_from :all, backtrace: true
1506
1571
  subject.error_formatter :txt, with: CustomErrorFormatter
1507
- subject.get('/exception') { raise "rain!" }
1572
+ subject.get('/exception') { fail 'rain!' }
1508
1573
 
1509
1574
  get '/exception'
1510
1575
  expect(last_response.body).to eq('message: rain! @backtrace')
@@ -1516,7 +1581,7 @@ describe Grape::API do
1516
1581
  subject.rescue_from :all
1517
1582
  subject.format :json
1518
1583
  subject.get '/exception' do
1519
- raise "rain!"
1584
+ fail 'rain!'
1520
1585
  end
1521
1586
  get '/exception'
1522
1587
  expect(last_response.body).to eql '{"error":"rain!"}'
@@ -1525,25 +1590,25 @@ describe Grape::API do
1525
1590
  subject.rescue_from :all, backtrace: true
1526
1591
  subject.format :json
1527
1592
  subject.get '/exception' do
1528
- raise "rain!"
1593
+ fail 'rain!'
1529
1594
  end
1530
1595
  get '/exception'
1531
1596
  json = MultiJson.load(last_response.body)
1532
- expect(json["error"]).to eql 'rain!'
1533
- expect(json["backtrace"].length).to be > 0
1597
+ expect(json['error']).to eql 'rain!'
1598
+ expect(json['backtrace'].length).to be > 0
1534
1599
  end
1535
1600
  it 'rescues error! and return txt' do
1536
1601
  subject.format :txt
1537
1602
  subject.get '/error' do
1538
- error!("Access Denied", 401)
1603
+ error!('Access Denied', 401)
1539
1604
  end
1540
1605
  get '/error'
1541
- expect(last_response.body).to eql "Access Denied"
1606
+ expect(last_response.body).to eql 'Access Denied'
1542
1607
  end
1543
1608
  it 'rescues error! and return json' do
1544
1609
  subject.format :json
1545
1610
  subject.get '/error' do
1546
- error!("Access Denied", 401)
1611
+ error!('Access Denied', 401)
1547
1612
  end
1548
1613
  get '/error'
1549
1614
  expect(last_response.body).to eql '{"error":"Access Denied"}'
@@ -1552,25 +1617,25 @@ describe Grape::API do
1552
1617
 
1553
1618
  describe '.content_type' do
1554
1619
  it 'sets additional content-type' do
1555
- subject.content_type :xls, "application/vnd.ms-excel"
1620
+ subject.content_type :xls, 'application/vnd.ms-excel'
1556
1621
  subject.get :excel do
1557
- "some binary content"
1622
+ 'some binary content'
1558
1623
  end
1559
1624
  get '/excel.xls'
1560
- expect(last_response.content_type).to eq("application/vnd.ms-excel")
1625
+ expect(last_response.content_type).to eq('application/vnd.ms-excel')
1561
1626
  end
1562
1627
  it 'allows to override content-type' do
1563
1628
  subject.get :content do
1564
- content_type "text/javascript"
1565
- "var x = 1;"
1629
+ content_type 'text/javascript'
1630
+ 'var x = 1;'
1566
1631
  end
1567
1632
  get '/content'
1568
- expect(last_response.content_type).to eq("text/javascript")
1633
+ expect(last_response.content_type).to eq('text/javascript')
1569
1634
  end
1570
1635
  it 'removes existing content types' do
1571
- subject.content_type :xls, "application/vnd.ms-excel"
1636
+ subject.content_type :xls, 'application/vnd.ms-excel'
1572
1637
  subject.get :excel do
1573
- "some binary content"
1638
+ 'some binary content'
1574
1639
  end
1575
1640
  get '/excel.json'
1576
1641
  expect(last_response.status).to eq(406)
@@ -1581,8 +1646,8 @@ describe Grape::API do
1581
1646
  describe '.formatter' do
1582
1647
  context 'multiple formatters' do
1583
1648
  before :each do
1584
- subject.formatter :json, lambda { |object, env| "{\"custom_formatter\":\"#{object[:some] }\"}" }
1585
- subject.formatter :txt, lambda { |object, env| "custom_formatter: #{object[:some] }" }
1649
+ subject.formatter :json, ->(object, env) { "{\"custom_formatter\":\"#{object[:some] }\"}" }
1650
+ subject.formatter :txt, ->(object, env) { "custom_formatter: #{object[:some] }" }
1586
1651
  subject.get :simple do
1587
1652
  { some: 'hash' }
1588
1653
  end
@@ -1600,7 +1665,7 @@ describe Grape::API do
1600
1665
  before :each do
1601
1666
  subject.content_type :json, 'application/json'
1602
1667
  subject.content_type :custom, 'application/custom'
1603
- subject.formatter :custom, lambda { |object, env| "{\"custom_formatter\":\"#{object[:some] }\"}" }
1668
+ subject.formatter :custom, ->(object, env) { "{\"custom_formatter\":\"#{object[:some] }\"}" }
1604
1669
  subject.get :simple do
1605
1670
  { some: 'hash' }
1606
1671
  end
@@ -1645,24 +1710,24 @@ describe Grape::API do
1645
1710
  subject.post '/data' do
1646
1711
  { x: params[:x] }
1647
1712
  end
1648
- post "/data", '{"x":42}', 'CONTENT_TYPE' => 'application/json'
1713
+ post '/data', '{"x":42}', 'CONTENT_TYPE' => 'application/json'
1649
1714
  expect(last_response.status).to eq(201)
1650
1715
  expect(last_response.body).to eq('{"x":42}')
1651
1716
  end
1652
1717
  context 'lambda parser' do
1653
1718
  before :each do
1654
- subject.content_type :txt, "text/plain"
1655
- subject.content_type :custom, "text/custom"
1656
- subject.parser :custom, lambda { |object, env| { object.to_sym => object.to_s.reverse } }
1719
+ subject.content_type :txt, 'text/plain'
1720
+ subject.content_type :custom, 'text/custom'
1721
+ subject.parser :custom, ->(object, env) { { object.to_sym => object.to_s.reverse } }
1657
1722
  subject.put :simple do
1658
1723
  params[:simple]
1659
1724
  end
1660
1725
  end
1661
- ["text/custom", "text/custom; charset=UTF-8"].each do |content_type|
1726
+ ['text/custom', 'text/custom; charset=UTF-8'].each do |content_type|
1662
1727
  it "uses parser for #{content_type}" do
1663
- put '/simple', "simple", "CONTENT_TYPE" => content_type
1728
+ put '/simple', 'simple', 'CONTENT_TYPE' => content_type
1664
1729
  expect(last_response.status).to eq(200)
1665
- expect(last_response.body).to eql "elpmis"
1730
+ expect(last_response.body).to eql 'elpmis'
1666
1731
  end
1667
1732
  end
1668
1733
  end
@@ -1673,40 +1738,40 @@ describe Grape::API do
1673
1738
  end
1674
1739
  end
1675
1740
  before :each do
1676
- subject.content_type :txt, "text/plain"
1677
- subject.content_type :custom, "text/custom"
1741
+ subject.content_type :txt, 'text/plain'
1742
+ subject.content_type :custom, 'text/custom'
1678
1743
  subject.parser :custom, CustomParser
1679
1744
  subject.put :simple do
1680
1745
  params[:simple]
1681
1746
  end
1682
1747
  end
1683
1748
  it 'uses custom parser' do
1684
- put '/simple', "simple", "CONTENT_TYPE" => "text/custom"
1749
+ put '/simple', 'simple', 'CONTENT_TYPE' => 'text/custom'
1685
1750
  expect(last_response.status).to eq(200)
1686
- expect(last_response.body).to eql "elpmis"
1751
+ expect(last_response.body).to eql 'elpmis'
1687
1752
  end
1688
1753
  end
1689
- context "multi_xml" do
1754
+ context 'multi_xml' do
1690
1755
  it "doesn't parse yaml" do
1691
1756
  subject.put :yaml do
1692
1757
  params[:tag]
1693
1758
  end
1694
- put '/yaml', '<tag type="symbol">a123</tag>', "CONTENT_TYPE" => "application/xml"
1759
+ put '/yaml', '<tag type="symbol">a123</tag>', 'CONTENT_TYPE' => 'application/xml'
1695
1760
  expect(last_response.status).to eq(400)
1696
1761
  expect(last_response.body).to eql 'Disallowed type attribute: "symbol"'
1697
1762
  end
1698
1763
  end
1699
- context "none parser class" do
1764
+ context 'none parser class' do
1700
1765
  before :each do
1701
1766
  subject.parser :json, nil
1702
- subject.put "data" do
1767
+ subject.put 'data' do
1703
1768
  "body: #{env['api.request.body'] }"
1704
1769
  end
1705
1770
  end
1706
- it "does not parse data" do
1707
- put '/data', 'not valid json', "CONTENT_TYPE" => "application/json"
1771
+ it 'does not parse data' do
1772
+ put '/data', 'not valid json', 'CONTENT_TYPE' => 'application/json'
1708
1773
  expect(last_response.status).to eq(200)
1709
- expect(last_response.body).to eq("body: not valid json")
1774
+ expect(last_response.body).to eq('body: not valid json')
1710
1775
  end
1711
1776
  end
1712
1777
  end
@@ -1720,7 +1785,7 @@ describe Grape::API do
1720
1785
  subject.get '/data' do
1721
1786
  { x: 42 }
1722
1787
  end
1723
- get "/data"
1788
+ get '/data'
1724
1789
  expect(last_response.status).to eq(200)
1725
1790
  expect(last_response.body).to eq('{"x":42}')
1726
1791
  end
@@ -1728,7 +1793,7 @@ describe Grape::API do
1728
1793
  subject.post '/data' do
1729
1794
  { x: params[:x] }
1730
1795
  end
1731
- post "/data", '{"x":42}', "CONTENT_TYPE" => ""
1796
+ post '/data', '{"x":42}', 'CONTENT_TYPE' => ''
1732
1797
  expect(last_response.status).to eq(201)
1733
1798
  expect(last_response.body).to eq('{"x":42}')
1734
1799
  end
@@ -1739,7 +1804,7 @@ describe Grape::API do
1739
1804
  subject.rescue_from :all
1740
1805
  subject.default_error_status 200
1741
1806
  subject.get '/exception' do
1742
- raise "rain!"
1807
+ fail 'rain!'
1743
1808
  end
1744
1809
  get '/exception'
1745
1810
  expect(last_response.status).to eql 200
@@ -1747,7 +1812,7 @@ describe Grape::API do
1747
1812
  it 'has a default error status' do
1748
1813
  subject.rescue_from :all
1749
1814
  subject.get '/exception' do
1750
- raise "rain!"
1815
+ fail 'rain!'
1751
1816
  end
1752
1817
  get '/exception'
1753
1818
  expect(last_response.status).to eql 500
@@ -1756,7 +1821,7 @@ describe Grape::API do
1756
1821
  subject.rescue_from :all
1757
1822
  subject.default_error_status 400
1758
1823
  subject.get '/exception' do
1759
- error! "rain!"
1824
+ error! 'rain!'
1760
1825
  end
1761
1826
  get '/exception'
1762
1827
  expect(last_response.status).to eql 400
@@ -1817,8 +1882,8 @@ describe Grape::API do
1817
1882
  expect(subject.routes.size).to eq(1)
1818
1883
  route = subject.routes[0]
1819
1884
  expect(route.route_version).to be_nil
1820
- expect(route.route_path).to eq("/ping(.:format)")
1821
- expect(route.route_method).to eq("GET")
1885
+ expect(route.route_path).to eq('/ping(.:format)')
1886
+ expect(route.route_method).to eq('GET')
1822
1887
  end
1823
1888
  end
1824
1889
  describe 'api structure with two versions and a namespace' do
@@ -1842,19 +1907,19 @@ describe Grape::API do
1842
1907
  expect(subject.version).to eq('v2')
1843
1908
  end
1844
1909
  it 'returns versions' do
1845
- expect(subject.versions).to eq(['v1', 'v2'])
1910
+ expect(subject.versions).to eq(%w(v1 v2))
1846
1911
  end
1847
1912
  it 'sets route paths' do
1848
1913
  expect(subject.routes.size).to be >= 2
1849
- expect(subject.routes[0].route_path).to eq("/:version/version(.:format)")
1850
- expect(subject.routes[1].route_path).to eq("/p/:version/n1/n2/version(.:format)")
1914
+ expect(subject.routes[0].route_path).to eq('/:version/version(.:format)')
1915
+ expect(subject.routes[1].route_path).to eq('/p/:version/n1/n2/version(.:format)')
1851
1916
  end
1852
1917
  it 'sets route versions' do
1853
1918
  expect(subject.routes[0].route_version).to eq('v1')
1854
1919
  expect(subject.routes[1].route_version).to eq('v2')
1855
1920
  end
1856
1921
  it 'sets a nested namespace' do
1857
- expect(subject.routes[1].route_namespace).to eq("/n1/n2")
1922
+ expect(subject.routes[1].route_namespace).to eq('/n1/n2')
1858
1923
  end
1859
1924
  it 'sets prefix' do
1860
1925
  expect(subject.routes[1].route_prefix).to eq('p')
@@ -1862,24 +1927,108 @@ describe Grape::API do
1862
1927
  end
1863
1928
  describe 'api structure with additional parameters' do
1864
1929
  before(:each) do
1865
- subject.get 'split/:string', params: { "token" => "a token" }, optional_params: { "limit" => "the limit" } do
1930
+ subject.params do
1931
+ requires :token, desc: 'a token'
1932
+ optional :limit, desc: 'the limit'
1933
+ end
1934
+ subject.get 'split/:string' do
1866
1935
  params[:string].split(params[:token], (params[:limit] || 0).to_i)
1867
1936
  end
1868
1937
  end
1869
1938
  it 'splits a string' do
1870
- get "/split/a,b,c.json", token: ','
1939
+ get '/split/a,b,c.json', token: ','
1871
1940
  expect(last_response.body).to eq('["a","b","c"]')
1872
1941
  end
1873
1942
  it 'splits a string with limit' do
1874
- get "/split/a,b,c.json", token: ',', limit: '2'
1943
+ get '/split/a,b,c.json', token: ',', limit: '2'
1875
1944
  expect(last_response.body).to eq('["a","b,c"]')
1876
1945
  end
1877
1946
  it 'sets route_params' do
1878
1947
  expect(subject.routes.map { |route|
1879
- { params: route.route_params, optional_params: route.route_optional_params }
1948
+ { params: route.route_params }
1880
1949
  }).to eq [
1881
- { params: { "string" => "", "token" => "a token" }, optional_params: { "limit" => "the limit" } }
1882
- ]
1950
+ {
1951
+ params: {
1952
+ 'string' => '',
1953
+ 'token' => { required: true, desc: 'a token' },
1954
+ 'limit' => { required: false, desc: 'the limit' }
1955
+ }
1956
+ }
1957
+ ]
1958
+ end
1959
+ end
1960
+ describe 'api structure with multiple apis' do
1961
+ before(:each) do
1962
+ subject.params do
1963
+ requires :one, desc: 'a token'
1964
+ optional :two, desc: 'the limit'
1965
+ end
1966
+ subject.get 'one' do
1967
+ end
1968
+
1969
+ subject.params do
1970
+ requires :three, desc: 'a token'
1971
+ optional :four, desc: 'the limit'
1972
+ end
1973
+ subject.get 'two' do
1974
+ end
1975
+ end
1976
+ it 'sets route_params' do
1977
+ expect(subject.routes.map { |route|
1978
+ { params: route.route_params }
1979
+ }).to eq [
1980
+ {
1981
+ params: {
1982
+ 'one' => { required: true, desc: 'a token' },
1983
+ 'two' => { required: false, desc: 'the limit' }
1984
+ }
1985
+ },
1986
+ {
1987
+ params: {
1988
+ 'three' => { required: true, desc: 'a token' },
1989
+ 'four' => { required: false, desc: 'the limit' }
1990
+ }
1991
+ }
1992
+ ]
1993
+ end
1994
+ end
1995
+ describe 'api structure with an api without params' do
1996
+ before(:each) do
1997
+ subject.params do
1998
+ requires :one, desc: 'a token'
1999
+ optional :two, desc: 'the limit'
2000
+ end
2001
+ subject.get 'one' do
2002
+ end
2003
+
2004
+ subject.get 'two' do
2005
+ end
2006
+ end
2007
+ it 'sets route_params' do
2008
+ expect(subject.routes.map { |route|
2009
+ { params: route.route_params }
2010
+ }).to eq [
2011
+ {
2012
+ params: {
2013
+ 'one' => { required: true, desc: 'a token' },
2014
+ 'two' => { required: false, desc: 'the limit' }
2015
+ }
2016
+ },
2017
+ {
2018
+ params: {}
2019
+ }
2020
+ ]
2021
+ end
2022
+ end
2023
+ describe 'api with a custom route setting' do
2024
+ before(:each) do
2025
+ subject.route_setting :custom, key: 'value'
2026
+ subject.get 'one'
2027
+ end
2028
+ it 'exposed' do
2029
+ expect(subject.routes.count).to eq 1
2030
+ route = subject.routes.first
2031
+ expect(route.route_settings[:custom]).to eq(key: 'value')
1883
2032
  end
1884
2033
  end
1885
2034
  end
@@ -1889,112 +2038,114 @@ describe Grape::API do
1889
2038
  expect(subject.routes).to eq([])
1890
2039
  end
1891
2040
  it 'empty array of routes' do
1892
- subject.desc "grape api"
2041
+ subject.desc 'grape api'
1893
2042
  expect(subject.routes).to eq([])
1894
2043
  end
1895
2044
  it 'describes a method' do
1896
- subject.desc "first method"
2045
+ subject.desc 'first method'
1897
2046
  subject.get :first do ; end
1898
2047
  expect(subject.routes.length).to eq(1)
1899
2048
  route = subject.routes.first
1900
- expect(route.route_description).to eq("first method")
2049
+ expect(route.route_description).to eq('first method')
1901
2050
  expect(route.route_foo).to be_nil
1902
2051
  expect(route.route_params).to eq({})
1903
2052
  end
1904
2053
  it 'describes methods separately' do
1905
- subject.desc "first method"
2054
+ subject.desc 'first method'
1906
2055
  subject.get :first do ; end
1907
- subject.desc "second method"
2056
+ subject.desc 'second method'
1908
2057
  subject.get :second do ; end
1909
2058
  expect(subject.routes.count).to eq(2)
1910
2059
  expect(subject.routes.map { |route|
1911
2060
  { description: route.route_description, params: route.route_params }
1912
2061
  }).to eq [
1913
- { description: "first method", params: {} },
1914
- { description: "second method", params: {} }
2062
+ { description: 'first method', params: {} },
2063
+ { description: 'second method', params: {} }
1915
2064
  ]
1916
2065
  end
1917
2066
  it 'resets desc' do
1918
- subject.desc "first method"
2067
+ subject.desc 'first method'
1919
2068
  subject.get :first do ; end
1920
2069
  subject.get :second do ; end
1921
2070
  expect(subject.routes.map { |route|
1922
2071
  { description: route.route_description, params: route.route_params }
1923
2072
  }).to eq [
1924
- { description: "first method", params: {} },
2073
+ { description: 'first method', params: {} },
1925
2074
  { description: nil, params: {} }
1926
2075
  ]
1927
2076
  end
1928
2077
  it 'namespaces and describe arbitrary parameters' do
1929
2078
  subject.namespace 'ns' do
1930
- desc "ns second", foo: "bar"
2079
+ desc 'ns second', foo: 'bar'
1931
2080
  get 'second' do ; end
1932
2081
  end
1933
2082
  expect(subject.routes.map { |route|
1934
2083
  { description: route.route_description, foo: route.route_foo, params: route.route_params }
1935
2084
  }).to eq [
1936
- { description: "ns second", foo: "bar", params: {} }
2085
+ { description: 'ns second', foo: 'bar', params: {} }
1937
2086
  ]
1938
2087
  end
1939
2088
  it 'includes details' do
1940
- subject.desc "method", details: "method details"
2089
+ subject.desc 'method', details: 'method details'
1941
2090
  subject.get 'method' do ; end
1942
2091
  expect(subject.routes.map { |route|
1943
2092
  { description: route.route_description, details: route.route_details, params: route.route_params }
1944
2093
  }).to eq [
1945
- { description: "method", details: "method details", params: {} }
2094
+ { description: 'method', details: 'method details', params: {} }
1946
2095
  ]
1947
2096
  end
1948
2097
  it 'describes a method with parameters' do
1949
- subject.desc "Reverses a string.", params: { "s" => { desc: "string to reverse", type: "string" } }
2098
+ subject.desc 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } }
1950
2099
  subject.get 'reverse' do
1951
2100
  params[:s].reverse
1952
2101
  end
1953
2102
  expect(subject.routes.map { |route|
1954
2103
  { description: route.route_description, params: route.route_params }
1955
2104
  }).to eq [
1956
- { description: "Reverses a string.", params: { "s" => { desc: "string to reverse", type: "string" } } }
2105
+ { description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
1957
2106
  ]
1958
2107
  end
1959
2108
  it 'merges the parameters of the namespace with the parameters of the method' do
1960
- subject.desc "namespace"
2109
+ subject.desc 'namespace'
1961
2110
  subject.params do
1962
- requires :ns_param, desc: "namespace parameter"
2111
+ requires :ns_param, desc: 'namespace parameter'
1963
2112
  end
1964
2113
  subject.namespace 'ns' do
1965
- desc "method"
2114
+ desc 'method'
1966
2115
  params do
1967
- optional :method_param, desc: "method parameter"
2116
+ optional :method_param, desc: 'method parameter'
1968
2117
  end
1969
2118
  get 'method' do ; end
1970
2119
  end
1971
- expect(subject.routes.map { |route|
2120
+
2121
+ routes_doc = subject.routes.map { |route|
1972
2122
  { description: route.route_description, params: route.route_params }
1973
- }).to eq [
1974
- { description: "method",
2123
+ }
2124
+ expect(routes_doc).to eq [
2125
+ { description: 'method',
1975
2126
  params: {
1976
- "ns_param" => { required: true, desc: "namespace parameter" },
1977
- "method_param" => { required: false, desc: "method parameter" }
2127
+ 'ns_param' => { required: true, desc: 'namespace parameter' },
2128
+ 'method_param' => { required: false, desc: 'method parameter' }
1978
2129
  }
1979
2130
  }
1980
2131
  ]
1981
2132
  end
1982
2133
  it 'merges the parameters of nested namespaces' do
1983
- subject.desc "ns1"
2134
+ subject.desc 'ns1'
1984
2135
  subject.params do
1985
- optional :ns_param, desc: "ns param 1"
1986
- requires :ns1_param, desc: "ns1 param"
2136
+ optional :ns_param, desc: 'ns param 1'
2137
+ requires :ns1_param, desc: 'ns1 param'
1987
2138
  end
1988
2139
  subject.namespace 'ns1' do
1989
- desc "ns2"
2140
+ desc 'ns2'
1990
2141
  params do
1991
- requires :ns_param, desc: "ns param 2"
1992
- requires :ns2_param, desc: "ns2 param"
2142
+ requires :ns_param, desc: 'ns param 2'
2143
+ requires :ns2_param, desc: 'ns2 param'
1993
2144
  end
1994
2145
  namespace 'ns2' do
1995
- desc "method"
2146
+ desc 'method'
1996
2147
  params do
1997
- optional :method_param, desc: "method param"
2148
+ optional :method_param, desc: 'method param'
1998
2149
  end
1999
2150
  get 'method' do ; end
2000
2151
  end
@@ -2002,58 +2153,56 @@ describe Grape::API do
2002
2153
  expect(subject.routes.map { |route|
2003
2154
  { description: route.route_description, params: route.route_params }
2004
2155
  }).to eq [
2005
- { description: "method",
2156
+ { description: 'method',
2006
2157
  params: {
2007
- "ns_param" => { required: true, desc: "ns param 2" },
2008
- "ns1_param" => { required: true, desc: "ns1 param" },
2009
- "ns2_param" => { required: true, desc: "ns2 param" },
2010
- "method_param" => { required: false, desc: "method param" }
2158
+ 'ns_param' => { required: true, desc: 'ns param 2' },
2159
+ 'ns1_param' => { required: true, desc: 'ns1 param' },
2160
+ 'ns2_param' => { required: true, desc: 'ns2 param' },
2161
+ 'method_param' => { required: false, desc: 'method param' }
2011
2162
  }
2012
2163
  }
2013
2164
  ]
2014
2165
  end
2015
- it "groups nested params and prevents overwriting of params with same name in different groups" do
2016
- subject.desc "method"
2166
+ it 'groups nested params and prevents overwriting of params with same name in different groups' do
2167
+ subject.desc 'method'
2017
2168
  subject.params do
2018
2169
  group :group1 do
2019
- optional :param1, desc: "group1 param1 desc"
2020
- requires :param2, desc: "group1 param2 desc"
2170
+ optional :param1, desc: 'group1 param1 desc'
2171
+ requires :param2, desc: 'group1 param2 desc'
2021
2172
  end
2022
2173
  group :group2 do
2023
- optional :param1, desc: "group2 param1 desc"
2024
- requires :param2, desc: "group2 param2 desc"
2174
+ optional :param1, desc: 'group2 param1 desc'
2175
+ requires :param2, desc: 'group2 param2 desc'
2025
2176
  end
2026
2177
  end
2027
- subject.get "method" do ; end
2178
+ subject.get 'method' do ; end
2028
2179
 
2029
- expect(subject.routes.map { |route|
2030
- route.route_params
2031
- }).to eq [{
2032
- "group1" => { required: true, type: "Array" },
2033
- "group1[param1]" => { required: false, desc: "group1 param1 desc" },
2034
- "group1[param2]" => { required: true, desc: "group1 param2 desc" },
2035
- "group2" => { required: true, type: "Array" },
2036
- "group2[param1]" => { required: false, desc: "group2 param1 desc" },
2037
- "group2[param2]" => { required: true, desc: "group2 param2 desc" }
2180
+ expect(subject.routes.map(&:route_params)).to eq [{
2181
+ 'group1' => { required: true, type: 'Array' },
2182
+ 'group1[param1]' => { required: false, desc: 'group1 param1 desc' },
2183
+ 'group1[param2]' => { required: true, desc: 'group1 param2 desc' },
2184
+ 'group2' => { required: true, type: 'Array' },
2185
+ 'group2[param1]' => { required: false, desc: 'group2 param1 desc' },
2186
+ 'group2[param2]' => { required: true, desc: 'group2 param2 desc' }
2038
2187
  }]
2039
2188
  end
2040
2189
  it 'uses full name of parameters in nested groups' do
2041
- subject.desc "nesting"
2190
+ subject.desc 'nesting'
2042
2191
  subject.params do
2043
- requires :root_param, desc: "root param"
2192
+ requires :root_param, desc: 'root param'
2044
2193
  group :nested do
2045
- requires :nested_param, desc: "nested param"
2194
+ requires :nested_param, desc: 'nested param'
2046
2195
  end
2047
2196
  end
2048
2197
  subject.get 'method' do ; end
2049
2198
  expect(subject.routes.map { |route|
2050
2199
  { description: route.route_description, params: route.route_params }
2051
2200
  }).to eq [
2052
- { description: "nesting",
2201
+ { description: 'nesting',
2053
2202
  params: {
2054
- "root_param" => { required: true, desc: "root param" },
2055
- "nested" => { required: true, type: "Array" },
2056
- "nested[nested_param]" => { required: true, desc: "nested param" }
2203
+ 'root_param' => { required: true, desc: 'root param' },
2204
+ 'nested' => { required: true, type: 'Array' },
2205
+ 'nested[nested_param]' => { required: true, desc: 'nested param' }
2057
2206
  }
2058
2207
  }
2059
2208
  ]
@@ -2067,30 +2216,30 @@ describe Grape::API do
2067
2216
  end
2068
2217
  it 'parses parameters when no description is given' do
2069
2218
  subject.params do
2070
- requires :one_param, desc: "one param"
2219
+ requires :one_param, desc: 'one param'
2071
2220
  end
2072
2221
  subject.get 'method' do ; end
2073
2222
  expect(subject.routes.map { |route|
2074
2223
  { description: route.route_description, params: route.route_params }
2075
2224
  }).to eq [
2076
- { description: nil, params: { "one_param" => { required: true, desc: "one param" } } }
2225
+ { description: nil, params: { 'one_param' => { required: true, desc: 'one param' } } }
2077
2226
  ]
2078
2227
  end
2079
2228
  it 'does not symbolize params' do
2080
- subject.desc "Reverses a string.", params: { "s" => { desc: "string to reverse", type: "string" } }
2229
+ subject.desc 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } }
2081
2230
  subject.get 'reverse/:s' do
2082
2231
  params[:s].reverse
2083
2232
  end
2084
2233
  expect(subject.routes.map { |route|
2085
2234
  { description: route.route_description, params: route.route_params }
2086
2235
  }).to eq [
2087
- { description: "Reverses a string.", params: { "s" => { desc: "string to reverse", type: "string" } } }
2236
+ { description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
2088
2237
  ]
2089
2238
  end
2090
2239
  end
2091
2240
 
2092
2241
  describe '.mount' do
2093
- let(:mounted_app) { lambda { |env| [200, {}, ["MOUNTED"]] } }
2242
+ let(:mounted_app) { ->(env) { [200, {}, ['MOUNTED']] } }
2094
2243
 
2095
2244
  context 'with a bare rack app' do
2096
2245
  before do
@@ -2111,7 +2260,7 @@ describe Grape::API do
2111
2260
  subject.mount lambda { |env|
2112
2261
  headers = {}
2113
2262
  headers['X-Cascade'] == 'pass' unless env['PATH_INFO'].include?('boo')
2114
- [200, headers, ["Farfegnugen"]]
2263
+ [200, headers, ['Farfegnugen']]
2115
2264
  } => '/'
2116
2265
 
2117
2266
  get '/boo'
@@ -2136,7 +2285,7 @@ describe Grape::API do
2136
2285
  subject.namespace :cool do
2137
2286
  app = Class.new(Grape::API)
2138
2287
  app.get('/awesome') do
2139
- "yo"
2288
+ 'yo'
2140
2289
  end
2141
2290
 
2142
2291
  mount app
@@ -2152,7 +2301,7 @@ describe Grape::API do
2152
2301
  subject.namespace :cool do
2153
2302
  inner_app = Class.new(Grape::API)
2154
2303
  inner_app.get('/awesome') do
2155
- "yo"
2304
+ 'yo'
2156
2305
  end
2157
2306
 
2158
2307
  app = Class.new(Grape::API)
@@ -2168,12 +2317,15 @@ describe Grape::API do
2168
2317
  subject.rescue_from :all do |e|
2169
2318
  rack_response("rescued from #{e.message}", 202)
2170
2319
  end
2320
+
2321
+ app = Class.new(Grape::API)
2322
+
2171
2323
  subject.namespace :mounted do
2172
- app = Class.new(Grape::API)
2173
2324
  app.rescue_from ArgumentError
2174
- app.get('/fail') { raise "doh!" }
2325
+ app.get('/fail') { fail 'doh!' }
2175
2326
  mount app
2176
2327
  end
2328
+
2177
2329
  get '/mounted/fail'
2178
2330
  expect(last_response.status).to eql 202
2179
2331
  expect(last_response.body).to eq('rescued from doh!')
@@ -2195,28 +2347,29 @@ describe Grape::API do
2195
2347
  subject.namespace :cool do
2196
2348
  app = Class.new(Grape::API)
2197
2349
  app.get '/awesome' do
2198
- "sauce"
2350
+ 'sauce'
2199
2351
  end
2200
2352
  mount app => '/mounted'
2201
2353
  end
2202
- get "/mounted/cool/awesome"
2354
+ get '/mounted/cool/awesome'
2203
2355
  expect(last_response.status).to eq(200)
2204
- expect(last_response.body).to eq("sauce")
2356
+ expect(last_response.body).to eq('sauce')
2205
2357
  end
2206
2358
 
2207
2359
  it 'mounts on a nested path' do
2208
- app1 = Class.new(Grape::API)
2209
- app2 = Class.new(Grape::API)
2210
- app2.get '/nice' do
2211
- "play"
2360
+ APP1 = Class.new(Grape::API)
2361
+ APP2 = Class.new(Grape::API)
2362
+ APP2.get '/nice' do
2363
+ 'play'
2212
2364
  end
2213
2365
  # note that the reverse won't work, mount from outside-in
2214
- subject.mount app1 => '/app1'
2215
- app1.mount app2 => '/app2'
2216
- get "/app1/app2/nice"
2366
+ APP3 = subject
2367
+ APP3.mount APP1 => '/app1'
2368
+ APP1.mount APP2 => '/app2'
2369
+ get '/app1/app2/nice'
2217
2370
  expect(last_response.status).to eq(200)
2218
- expect(last_response.body).to eq("play")
2219
- options "/app1/app2/nice"
2371
+ expect(last_response.body).to eq('play')
2372
+ options '/app1/app2/nice'
2220
2373
  expect(last_response.status).to eq(204)
2221
2374
  end
2222
2375
 
@@ -2233,6 +2386,7 @@ describe Grape::API do
2233
2386
  subject.namespace :apples do
2234
2387
  mount app
2235
2388
  end
2389
+
2236
2390
  get '/apples/colour'
2237
2391
  expect(last_response.status).to eql 200
2238
2392
  expect(last_response.body).to eq('red')
@@ -2250,7 +2404,7 @@ describe Grape::API do
2250
2404
  subject.namespace :apples do
2251
2405
  app = Class.new(Grape::API)
2252
2406
  app.get('/colour') do
2253
- "red"
2407
+ 'red'
2254
2408
  end
2255
2409
  mount app
2256
2410
  end
@@ -2261,7 +2415,6 @@ describe Grape::API do
2261
2415
  options '/v1/apples/colour'
2262
2416
  expect(last_response.status).to eql 204
2263
2417
  end
2264
-
2265
2418
  end
2266
2419
  end
2267
2420
 
@@ -2289,7 +2442,7 @@ describe Grape::API do
2289
2442
  end
2290
2443
  end
2291
2444
 
2292
- describe ".endpoint" do
2445
+ describe '.endpoint' do
2293
2446
  before(:each) do
2294
2447
  subject.format :json
2295
2448
  subject.get '/endpoint/options' do
@@ -2302,9 +2455,9 @@ describe Grape::API do
2302
2455
  it 'path' do
2303
2456
  get '/endpoint/options'
2304
2457
  options = MultiJson.load(last_response.body)
2305
- expect(options["path"]).to eq(["/endpoint/options"])
2306
- expect(options["source_location"][0]).to include "api_spec.rb"
2307
- expect(options["source_location"][1].to_i).to be > 0
2458
+ expect(options['path']).to eq(['/endpoint/options'])
2459
+ expect(options['source_location'][0]).to include 'api_spec.rb'
2460
+ expect(options['source_location'][1].to_i).to be > 0
2308
2461
  end
2309
2462
  end
2310
2463
 
@@ -2320,9 +2473,9 @@ describe Grape::API do
2320
2473
  end
2321
2474
  it 'provides access to route info' do
2322
2475
  get '/'
2323
- expect(last_response.body).to eq("/(.:format)")
2476
+ expect(last_response.body).to eq('/(.:format)')
2324
2477
  get '/path'
2325
- expect(last_response.body).to eq("/path(.:format)")
2478
+ expect(last_response.body).to eq('/path(.:format)')
2326
2479
  end
2327
2480
  end
2328
2481
  context 'with desc' do
@@ -2331,18 +2484,18 @@ describe Grape::API do
2331
2484
  subject.get '/description' do
2332
2485
  route.route_description
2333
2486
  end
2334
- subject.desc 'returns parameters', params: { "x" => "y" }
2487
+ subject.desc 'returns parameters', params: { 'x' => 'y' }
2335
2488
  subject.get '/params/:id' do
2336
2489
  route.route_params[params[:id]]
2337
2490
  end
2338
2491
  end
2339
2492
  it 'returns route description' do
2340
2493
  get '/description'
2341
- expect(last_response.body).to eq("returns description")
2494
+ expect(last_response.body).to eq('returns description')
2342
2495
  end
2343
2496
  it 'returns route parameters' do
2344
2497
  get '/params/x'
2345
- expect(last_response.body).to eq("y")
2498
+ expect(last_response.body).to eq('y')
2346
2499
  end
2347
2500
  end
2348
2501
  end
@@ -2350,7 +2503,7 @@ describe Grape::API do
2350
2503
  context ':txt' do
2351
2504
  before(:each) do
2352
2505
  subject.format :txt
2353
- subject.content_type :json, "application/json"
2506
+ subject.content_type :json, 'application/json'
2354
2507
  subject.get '/meaning_of_life' do
2355
2508
  { meaning_of_life: 42 }
2356
2509
  end
@@ -2379,9 +2532,9 @@ describe Grape::API do
2379
2532
  get '/meaning_of_life'
2380
2533
  expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
2381
2534
  end
2382
- it 'forces txt with the wrong extension' do
2535
+ it 'does not accept any extensions' do
2383
2536
  get '/meaning_of_life.json'
2384
- expect(last_response.body).to eq({ meaning_of_life: 42 }.to_s)
2537
+ expect(last_response.status).to eq(404)
2385
2538
  end
2386
2539
  it 'forces txt from a non-accepting header' do
2387
2540
  get '/meaning_of_life', {}, 'HTTP_ACCEPT' => 'application/json'
@@ -2391,7 +2544,7 @@ describe Grape::API do
2391
2544
  context ':json' do
2392
2545
  before(:each) do
2393
2546
  subject.format :json
2394
- subject.content_type :txt, "text/plain"
2547
+ subject.content_type :txt, 'text/plain'
2395
2548
  subject.get '/meaning_of_life' do
2396
2549
  { meaning_of_life: 42 }
2397
2550
  end
@@ -2410,7 +2563,7 @@ describe Grape::API do
2410
2563
  end
2411
2564
  it 'can be overwritten with an explicit content type' do
2412
2565
  subject.get '/meaning_of_life_with_content_type' do
2413
- content_type "text/plain"
2566
+ content_type 'text/plain'
2414
2567
  { meaning_of_life: 42 }.to_s
2415
2568
  end
2416
2569
  get '/meaning_of_life_with_content_type'
@@ -2419,18 +2572,16 @@ describe Grape::API do
2419
2572
  it 'raised :error from middleware' do
2420
2573
  middleware = Class.new(Grape::Middleware::Base) do
2421
2574
  def before
2422
- throw :error, message: "Unauthorized", status: 42
2575
+ throw :error, message: 'Unauthorized', status: 42
2423
2576
  end
2424
2577
  end
2425
2578
  subject.use middleware
2426
2579
  subject.get do
2427
-
2428
2580
  end
2429
- get "/"
2581
+ get '/'
2430
2582
  expect(last_response.status).to eq(42)
2431
- expect(last_response.body).to eq({ error: "Unauthorized" }.to_json)
2583
+ expect(last_response.body).to eq({ error: 'Unauthorized' }.to_json)
2432
2584
  end
2433
-
2434
2585
  end
2435
2586
  context ':serializable_hash' do
2436
2587
  before(:each) do
@@ -2450,7 +2601,7 @@ describe Grape::API do
2450
2601
  end
2451
2602
  it 'root' do
2452
2603
  subject.get '/example' do
2453
- { "root" => SerializableHashExample.new }
2604
+ { 'root' => SerializableHashExample.new }
2454
2605
  end
2455
2606
  get '/example'
2456
2607
  expect(last_response.body).to eq('{"root":{"abc":"def"}}')
@@ -2463,13 +2614,13 @@ describe Grape::API do
2463
2614
  expect(last_response.body).to eq('[{"abc":"def"},{"abc":"def"}]')
2464
2615
  end
2465
2616
  end
2466
- context ":xml" do
2617
+ context ':xml' do
2467
2618
  before(:each) do
2468
2619
  subject.format :xml
2469
2620
  end
2470
2621
  it 'string' do
2471
- subject.get "/example" do
2472
- "example"
2622
+ subject.get '/example' do
2623
+ 'example'
2473
2624
  end
2474
2625
  get '/example'
2475
2626
  expect(last_response.status).to eq(500)
@@ -2481,10 +2632,10 @@ describe Grape::API do
2481
2632
  XML
2482
2633
  end
2483
2634
  it 'hash' do
2484
- subject.get "/example" do
2635
+ subject.get '/example' do
2485
2636
  ActiveSupport::OrderedHash[
2486
- :example1, "example1",
2487
- :example2, "example2"
2637
+ :example1, 'example1',
2638
+ :example2, 'example2'
2488
2639
  ]
2489
2640
  end
2490
2641
  get '/example'
@@ -2498,8 +2649,8 @@ XML
2498
2649
  XML
2499
2650
  end
2500
2651
  it 'array' do
2501
- subject.get "/example" do
2502
- ["example1", "example2"]
2652
+ subject.get '/example' do
2653
+ %w(example1 example2)
2503
2654
  end
2504
2655
  get '/example'
2505
2656
  expect(last_response.status).to eq(200)
@@ -2514,14 +2665,13 @@ XML
2514
2665
  it 'raised :error from middleware' do
2515
2666
  middleware = Class.new(Grape::Middleware::Base) do
2516
2667
  def before
2517
- throw :error, message: "Unauthorized", status: 42
2668
+ throw :error, message: 'Unauthorized', status: 42
2518
2669
  end
2519
2670
  end
2520
2671
  subject.use middleware
2521
2672
  subject.get do
2522
-
2523
2673
  end
2524
- get "/"
2674
+ get '/'
2525
2675
  expect(last_response.status).to eq(42)
2526
2676
  expect(last_response.body).to eq <<-XML
2527
2677
  <?xml version="1.0" encoding="UTF-8"?>
@@ -2533,17 +2683,17 @@ XML
2533
2683
  end
2534
2684
  end
2535
2685
 
2536
- context "catch-all" do
2686
+ context 'catch-all' do
2537
2687
  before do
2538
2688
  api1 = Class.new(Grape::API)
2539
2689
  api1.version 'v1', using: :path
2540
- api1.get "hello" do
2541
- "v1"
2690
+ api1.get 'hello' do
2691
+ 'v1'
2542
2692
  end
2543
2693
  api2 = Class.new(Grape::API)
2544
2694
  api2.version 'v2', using: :path
2545
- api2.get "hello" do
2546
- "v2"
2695
+ api2.get 'hello' do
2696
+ 'v2'
2547
2697
  end
2548
2698
  subject.mount api1
2549
2699
  subject.mount api2
@@ -2553,53 +2703,53 @@ XML
2553
2703
  subject.route :any, '*path', anchor: anchor do
2554
2704
  error!("Unrecognized request path: #{params[:path] } - #{env['PATH_INFO'] }#{env['SCRIPT_NAME'] }", 404)
2555
2705
  end
2556
- get "/v1/hello"
2706
+ get '/v1/hello'
2557
2707
  expect(last_response.status).to eq(200)
2558
- expect(last_response.body).to eq("v1")
2559
- get "/v2/hello"
2708
+ expect(last_response.body).to eq('v1')
2709
+ get '/v2/hello'
2560
2710
  expect(last_response.status).to eq(200)
2561
- expect(last_response.body).to eq("v2")
2562
- get "/foobar"
2711
+ expect(last_response.body).to eq('v2')
2712
+ get '/foobar'
2563
2713
  expect(last_response.status).to eq(404)
2564
- expect(last_response.body).to eq("Unrecognized request path: foobar - /foobar")
2714
+ expect(last_response.body).to eq('Unrecognized request path: foobar - /foobar')
2565
2715
  end
2566
2716
  end
2567
2717
  end
2568
2718
 
2569
- context "cascading" do
2570
- context "via version" do
2571
- it "cascades" do
2719
+ context 'cascading' do
2720
+ context 'via version' do
2721
+ it 'cascades' do
2572
2722
  subject.version 'v1', using: :path, cascade: true
2573
- get "/v1/hello"
2723
+ get '/v1/hello'
2574
2724
  expect(last_response.status).to eq(404)
2575
- expect(last_response.headers["X-Cascade"]).to eq("pass")
2725
+ expect(last_response.headers['X-Cascade']).to eq('pass')
2576
2726
  end
2577
- it "does not cascade" do
2727
+ it 'does not cascade' do
2578
2728
  subject.version 'v2', using: :path, cascade: false
2579
- get "/v2/hello"
2729
+ get '/v2/hello'
2580
2730
  expect(last_response.status).to eq(404)
2581
- expect(last_response.headers.keys).not_to include "X-Cascade"
2731
+ expect(last_response.headers.keys).not_to include 'X-Cascade'
2582
2732
  end
2583
2733
  end
2584
- context "via endpoint" do
2585
- it "cascades" do
2734
+ context 'via endpoint' do
2735
+ it 'cascades' do
2586
2736
  subject.cascade true
2587
- get "/hello"
2737
+ get '/hello'
2588
2738
  expect(last_response.status).to eq(404)
2589
- expect(last_response.headers["X-Cascade"]).to eq("pass")
2739
+ expect(last_response.headers['X-Cascade']).to eq('pass')
2590
2740
  end
2591
- it "does not cascade" do
2741
+ it 'does not cascade' do
2592
2742
  subject.cascade false
2593
- get "/hello"
2743
+ get '/hello'
2594
2744
  expect(last_response.status).to eq(404)
2595
- expect(last_response.headers.keys).not_to include "X-Cascade"
2745
+ expect(last_response.headers.keys).not_to include 'X-Cascade'
2596
2746
  end
2597
2747
  end
2598
2748
  end
2599
2749
 
2600
2750
  context 'with json default_error_formatter' do
2601
2751
  it 'returns json error' do
2602
- subject.content_type :json, "application/json"
2752
+ subject.content_type :json, 'application/json'
2603
2753
  subject.default_error_formatter :json
2604
2754
  subject.get '/something' do
2605
2755
  'foo'
@@ -2609,4 +2759,33 @@ XML
2609
2759
  expect(last_response.body).to eq("{\"error\":\"The requested format 'txt' is not supported.\"}")
2610
2760
  end
2611
2761
  end
2762
+
2763
+ context 'body' do
2764
+ context 'false' do
2765
+ before do
2766
+ subject.get '/blank' do
2767
+ body false
2768
+ end
2769
+ end
2770
+ it 'returns blank body' do
2771
+ get '/blank'
2772
+ expect(last_response.status).to eq(204)
2773
+ expect(last_response.body).to be_blank
2774
+ end
2775
+ end
2776
+ context 'plain text' do
2777
+ before do
2778
+ subject.get '/text' do
2779
+ content_type 'text/plain'
2780
+ body 'Hello World'
2781
+ 'ignored'
2782
+ end
2783
+ end
2784
+ it 'returns blank body' do
2785
+ get '/text'
2786
+ expect(last_response.status).to eq(200)
2787
+ expect(last_response.body).to eq 'Hello World'
2788
+ end
2789
+ end
2790
+ end
2612
2791
  end