grape 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (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,219 @@
1
+ require 'spec_helper'
2
+
3
+ module Grape
4
+ module DSL
5
+ module SettingsSpec
6
+ class Dummy
7
+ include Grape::DSL::Settings
8
+
9
+ def reset_validations!; end
10
+ end
11
+ end
12
+
13
+ describe Settings do
14
+ subject { SettingsSpec::Dummy.new }
15
+
16
+ describe '#unset' do
17
+ it 'deletes a key from settings' do
18
+ subject.namespace_setting :dummy, 1
19
+ expect(subject.inheritable_setting.namespace.keys).to include(:dummy)
20
+
21
+ subject.unset :namespace, :dummy
22
+ expect(subject.inheritable_setting.namespace.keys).not_to include(:dummy)
23
+ end
24
+ end
25
+
26
+ describe '#get_or_set' do
27
+ it 'sets a values' do
28
+ subject.get_or_set :namespace, :dummy, 1
29
+ expect(subject.namespace_setting(:dummy)).to eq 1
30
+ end
31
+
32
+ it 'returns a value when nil is new value is provided' do
33
+ subject.get_or_set :namespace, :dummy, 1
34
+ expect(subject.get_or_set(:namespace, :dummy, nil)).to eq 1
35
+ end
36
+ end
37
+
38
+ describe '#global_setting' do
39
+ it 'delegates to get_or_set' do
40
+ expect(subject).to receive(:get_or_set).with(:global, :dummy, 1)
41
+ subject.global_setting(:dummy, 1)
42
+ end
43
+ end
44
+
45
+ describe '#route_setting' do
46
+ it 'delegates to get_or_set' do
47
+ expect(subject).to receive(:get_or_set).with(:route, :dummy, 1)
48
+ subject.route_setting(:dummy, 1)
49
+ end
50
+
51
+ it 'sets a value until the next route' do
52
+ subject.route_setting :some_thing, :foo_bar
53
+ expect(subject.route_setting(:some_thing)).to eq :foo_bar
54
+
55
+ subject.route_end
56
+
57
+ expect(subject.route_setting(:some_thing)).to be_nil
58
+ end
59
+ end
60
+
61
+ describe '#namespace_setting' do
62
+ it 'delegates to get_or_set' do
63
+ expect(subject).to receive(:get_or_set).with(:namespace, :dummy, 1)
64
+ subject.namespace_setting(:dummy, 1)
65
+ end
66
+
67
+ it 'sets a value until the end of a namespace' do
68
+ subject.namespace_start
69
+
70
+ subject.namespace_setting :some_thing, :foo_bar
71
+ expect(subject.namespace_setting(:some_thing)).to eq :foo_bar
72
+
73
+ subject.namespace_end
74
+
75
+ expect(subject.namespace_setting(:some_thing)).to be_nil
76
+ end
77
+
78
+ it 'resets values after leaving nested namespaces' do
79
+ subject.namespace_start
80
+
81
+ subject.namespace_setting :some_thing, :foo_bar
82
+ expect(subject.namespace_setting(:some_thing)).to eq :foo_bar
83
+
84
+ subject.namespace_start
85
+
86
+ expect(subject.namespace_setting(:some_thing)).to be_nil
87
+
88
+ subject.namespace_end
89
+ expect(subject.namespace_setting(:some_thing)).to eq :foo_bar
90
+
91
+ subject.namespace_end
92
+
93
+ expect(subject.namespace_setting(:some_thing)).to be_nil
94
+ end
95
+ end
96
+
97
+ describe '#namespace_inheritable' do
98
+ it 'delegates to get_or_set' do
99
+ expect(subject).to receive(:get_or_set).with(:namespace_inheritable, :dummy, 1)
100
+ subject.namespace_inheritable(:dummy, 1)
101
+ end
102
+
103
+ it 'inherits values from surrounding namespace' do
104
+ subject.namespace_start
105
+
106
+ subject.namespace_inheritable(:some_thing, :foo_bar)
107
+ expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar
108
+
109
+ subject.namespace_start
110
+
111
+ expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar
112
+
113
+ subject.namespace_inheritable(:some_thing, :foo_bar_2)
114
+
115
+ expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar_2
116
+
117
+ subject.namespace_end
118
+ expect(subject.namespace_inheritable(:some_thing)).to eq :foo_bar
119
+ subject.namespace_end
120
+ end
121
+ end
122
+
123
+ describe '#namespace_stackable' do
124
+ it 'delegates to get_or_set' do
125
+ expect(subject).to receive(:get_or_set).with(:namespace_stackable, :dummy, 1)
126
+ subject.namespace_stackable(:dummy, 1)
127
+ end
128
+
129
+ it 'stacks values from surrounding namespace' do
130
+ subject.namespace_start
131
+
132
+ subject.namespace_stackable(:some_thing, :foo_bar)
133
+ expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar]
134
+
135
+ subject.namespace_start
136
+
137
+ expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar]
138
+
139
+ subject.namespace_stackable(:some_thing, :foo_bar_2)
140
+
141
+ expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar, :foo_bar_2]
142
+
143
+ subject.namespace_end
144
+ expect(subject.namespace_stackable(:some_thing)).to eq [:foo_bar]
145
+ subject.namespace_end
146
+ end
147
+ end
148
+
149
+ describe '#api_class_setting' do
150
+ it 'delegates to get_or_set' do
151
+ expect(subject).to receive(:get_or_set).with(:api_class, :dummy, 1)
152
+ subject.api_class_setting(:dummy, 1)
153
+ end
154
+ end
155
+
156
+ describe '#within_namespace' do
157
+ it 'calls start and end for a namespace' do
158
+ expect(subject).to receive :namespace_start
159
+ expect(subject).to receive :namespace_end
160
+
161
+ subject.within_namespace do
162
+ end
163
+ end
164
+
165
+ it 'returns the last result' do
166
+ result = subject.within_namespace do
167
+ 1
168
+ end
169
+
170
+ expect(result).to eq 1
171
+ end
172
+ end
173
+
174
+ describe 'complex scenario' do
175
+ it 'plays well' do
176
+ obj1 = SettingsSpec::Dummy.new
177
+ obj2 = SettingsSpec::Dummy.new
178
+ obj3 = SettingsSpec::Dummy.new
179
+
180
+ obj1_copy = nil
181
+ obj2_copy = nil
182
+ obj3_copy = nil
183
+
184
+ obj1.within_namespace do
185
+ obj1.namespace_stackable(:some_thing, :obj1)
186
+ expect(obj1.namespace_stackable(:some_thing)).to eq [:obj1]
187
+ obj1_copy = obj1.inheritable_setting.point_in_time_copy
188
+ end
189
+
190
+ expect(obj1.namespace_stackable(:some_thing)).to eq []
191
+ expect(obj1_copy.namespace_stackable[:some_thing]).to eq [:obj1]
192
+
193
+ obj2.within_namespace do
194
+ obj2.namespace_stackable(:some_thing, :obj2)
195
+ expect(obj2.namespace_stackable(:some_thing)).to eq [:obj2]
196
+ obj2_copy = obj2.inheritable_setting.point_in_time_copy
197
+ end
198
+
199
+ expect(obj2.namespace_stackable(:some_thing)).to eq []
200
+ expect(obj2_copy.namespace_stackable[:some_thing]).to eq [:obj2]
201
+
202
+ obj3.within_namespace do
203
+ obj3.namespace_stackable(:some_thing, :obj3)
204
+ expect(obj3.namespace_stackable(:some_thing)).to eq [:obj3]
205
+ obj3_copy = obj3.inheritable_setting.point_in_time_copy
206
+ end
207
+
208
+ expect(obj3.namespace_stackable(:some_thing)).to eq []
209
+ expect(obj3_copy.namespace_stackable[:some_thing]).to eq [:obj3]
210
+
211
+ obj1.top_level_setting.inherit_from obj2_copy.point_in_time_copy
212
+ obj2.top_level_setting.inherit_from obj3_copy.point_in_time_copy
213
+
214
+ expect(obj1_copy.namespace_stackable[:some_thing]).to eq [:obj3, :obj2, :obj1]
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
@@ -5,10 +5,6 @@ module Grape
5
5
  module ValidationsSpec
6
6
  class Dummy
7
7
  include Grape::DSL::Validations
8
-
9
- def self.settings
10
- @settings ||= Grape::Util::HashStack.new
11
- end
12
8
  end
13
9
  end
14
10
 
@@ -17,17 +13,17 @@ module Grape
17
13
 
18
14
  describe '.reset_validations!' do
19
15
  before do
20
- subject.settings.peek[:declared_params] = ['dummy']
21
- subject.settings.peek[:validations] = ['dummy']
16
+ subject.namespace_stackable :declared_params, ['dummy']
17
+ subject.namespace_stackable :validations, ['dummy']
22
18
  subject.reset_validations!
23
19
  end
24
20
 
25
21
  it 'resets declared params' do
26
- expect(subject.settings.peek[:declared_params]).to be_empty
22
+ expect(subject.namespace_stackable(:declared_params)).to eq []
27
23
  end
28
24
 
29
25
  it 'resets validations' do
30
- expect(subject.settings.peek[:validations]).to be_empty
26
+ expect(subject.namespace_stackable(:validations)).to eq []
31
27
  end
32
28
  end
33
29
 
@@ -37,7 +33,7 @@ module Grape
37
33
  end
38
34
 
39
35
  it 'evaluates block' do
40
- expect { subject.params { raise 'foo' } }.to raise_error RuntimeError, 'foo'
36
+ expect { subject.params { fail 'foo' } }.to raise_error RuntimeError, 'foo'
41
37
  end
42
38
  end
43
39
 
@@ -46,8 +42,9 @@ module Grape
46
42
  subject.document_attribute([full_name: 'xxx'], foo: 'bar')
47
43
  end
48
44
 
49
- it 'creates last_description' do
50
- expect(subject.instance_variable_get(:'@last_description')).to eq(params: { 'xxx' => { foo: 'bar' } })
45
+ it 'creates a param documentation' do
46
+ expect(subject.namespace_stackable(:params)).to eq(['xxx' => { foo: 'bar' }])
47
+ expect(subject.route_setting(:description)).to eq(params: { 'xxx' => { foo: 'bar' } })
51
48
  end
52
49
  end
53
50
  end
@@ -11,27 +11,27 @@ describe Grape::Endpoint do
11
11
  after { Grape::Endpoint.before_each(nil) }
12
12
 
13
13
  it 'should be settable via block' do
14
- block = lambda { |endpoint| "noop" }
14
+ block = lambda { |endpoint| 'noop' }
15
15
  Grape::Endpoint.before_each(&block)
16
16
  expect(Grape::Endpoint.before_each).to eq(block)
17
17
  end
18
18
 
19
19
  it 'should be settable via reference' do
20
- block = lambda { |endpoint| "noop" }
20
+ block = lambda { |endpoint| 'noop' }
21
21
  Grape::Endpoint.before_each block
22
22
  expect(Grape::Endpoint.before_each).to eq(block)
23
23
  end
24
24
 
25
25
  it 'should be able to override a helper' do
26
- subject.get("/") { current_user }
26
+ subject.get('/') { current_user }
27
27
  expect { get '/' }.to raise_error(NameError)
28
28
 
29
29
  Grape::Endpoint.before_each do |endpoint|
30
- allow(endpoint).to receive(:current_user).and_return("Bob")
30
+ allow(endpoint).to receive(:current_user).and_return('Bob')
31
31
  end
32
32
 
33
33
  get '/'
34
- expect(last_response.body).to eq("Bob")
34
+ expect(last_response.body).to eq('Bob')
35
35
 
36
36
  Grape::Endpoint.before_each(nil)
37
37
  expect { get '/' }.to raise_error(NameError)
@@ -41,17 +41,17 @@ describe Grape::Endpoint do
41
41
  describe '#initialize' do
42
42
  it 'takes a settings stack, options, and a block' do
43
43
  p = proc {}
44
- expect {
45
- Grape::Endpoint.new(Grape::Util::HashStack.new, {
44
+ expect do
45
+ Grape::Endpoint.new(Grape::Util::InheritableSetting.new, {
46
46
  path: '/',
47
47
  method: :get
48
48
  }, &p)
49
- }.not_to raise_error
49
+ end.not_to raise_error
50
50
  end
51
51
  end
52
52
 
53
53
  it 'sets itself in the env upon call' do
54
- subject.get('/') { "Hello world." }
54
+ subject.get('/') { 'Hello world.' }
55
55
  get '/'
56
56
  expect(last_request.env['api.endpoint']).to be_kind_of(Grape::Endpoint)
57
57
  end
@@ -60,47 +60,46 @@ describe Grape::Endpoint do
60
60
  it 'is callable from within a block' do
61
61
  subject.get('/home') do
62
62
  status 206
63
- "Hello"
63
+ 'Hello'
64
64
  end
65
65
 
66
66
  get '/home'
67
67
  expect(last_response.status).to eq(206)
68
- expect(last_response.body).to eq("Hello")
68
+ expect(last_response.body).to eq('Hello')
69
69
  end
70
70
 
71
71
  it 'is set as default to 200 for get' do
72
72
  memoized_status = nil
73
73
  subject.get('/home') do
74
74
  memoized_status = status
75
- "Hello"
75
+ 'Hello'
76
76
  end
77
77
 
78
78
  get '/home'
79
79
  expect(last_response.status).to eq(200)
80
80
  expect(memoized_status).to eq(200)
81
- expect(last_response.body).to eq("Hello")
81
+ expect(last_response.body).to eq('Hello')
82
82
  end
83
83
 
84
84
  it 'is set as default to 201 for post' do
85
85
  memoized_status = nil
86
86
  subject.post('/home') do
87
87
  memoized_status = status
88
- "Hello"
88
+ 'Hello'
89
89
  end
90
90
 
91
91
  post '/home'
92
92
  expect(last_response.status).to eq(201)
93
93
  expect(memoized_status).to eq(201)
94
- expect(last_response.body).to eq("Hello")
94
+ expect(last_response.body).to eq('Hello')
95
95
  end
96
-
97
96
  end
98
97
 
99
98
  describe '#header' do
100
99
  it 'is callable from within a block' do
101
100
  subject.get('/hey') do
102
101
  header 'X-Awesome', 'true'
103
- "Awesome"
102
+ 'Awesome'
104
103
  end
105
104
 
106
105
  get '/hey'
@@ -117,19 +116,19 @@ describe Grape::Endpoint do
117
116
  it 'includes request headers' do
118
117
  get '/headers'
119
118
  expect(JSON.parse(last_response.body)).to eq(
120
- "Host" => "example.org",
121
- "Cookie" => ""
119
+ 'Host' => 'example.org',
120
+ 'Cookie' => ''
122
121
  )
123
122
  end
124
123
  it 'includes additional request headers' do
125
- get '/headers', nil, "HTTP_X_GRAPE_CLIENT" => "1"
126
- expect(JSON.parse(last_response.body)["X-Grape-Client"]).to eq("1")
124
+ get '/headers', nil, 'HTTP_X_GRAPE_CLIENT' => '1'
125
+ expect(JSON.parse(last_response.body)['X-Grape-Client']).to eq('1')
127
126
  end
128
127
  it 'includes headers passed as symbols' do
129
- env = Rack::MockRequest.env_for("/headers")
130
- env["HTTP_SYMBOL_HEADER".to_sym] = "Goliath passes symbols"
128
+ env = Rack::MockRequest.env_for('/headers')
129
+ env['HTTP_SYMBOL_HEADER'.to_sym] = 'Goliath passes symbols'
131
130
  body = subject.call(env)[2].body.first
132
- expect(JSON.parse(body)["Symbol-Header"]).to eq("Goliath passes symbols")
131
+ expect(JSON.parse(body)['Symbol-Header']).to eq('Goliath passes symbols')
133
132
  end
134
133
  end
135
134
 
@@ -150,10 +149,10 @@ describe Grape::Endpoint do
150
149
  get('/get/cookies')
151
150
 
152
151
  expect(last_response.headers['Set-Cookie'].split("\n").sort).to eql [
153
- "cookie3=symbol",
154
- "cookie4=secret+code+here",
155
- "my-awesome-cookie1=is+cool",
156
- "my-awesome-cookie2=is+cool+too; domain=my.example.com; path=/; secure"
152
+ 'cookie3=symbol',
153
+ 'cookie4=secret+code+here',
154
+ 'my-awesome-cookie1=is+cool',
155
+ 'my-awesome-cookie2=is+cool+too; domain=my.example.com; path=/; secure'
157
156
  ]
158
157
  end
159
158
 
@@ -170,7 +169,7 @@ describe Grape::Endpoint do
170
169
  it 'sets and update browser cookies' do
171
170
  subject.get('/username') do
172
171
  cookies[:sandbox] = true if cookies[:sandbox] == 'false'
173
- cookies[:username] += "_test"
172
+ cookies[:username] += '_test'
174
173
  end
175
174
  get('/username', {}, 'HTTP_COOKIE' => 'username=user; sandbox=false')
176
175
  expect(last_response.body).to eq('user_test')
@@ -194,10 +193,10 @@ describe Grape::Endpoint do
194
193
  [cookie.name, cookie]
195
194
  end]
196
195
  expect(cookies.size).to eq(2)
197
- ["and_this", "delete_this_cookie"].each do |cookie_name|
196
+ %w(and_this delete_this_cookie).each do |cookie_name|
198
197
  cookie = cookies[cookie_name]
199
198
  expect(cookie).not_to be_nil
200
- expect(cookie.value).to eq("deleted")
199
+ expect(cookie.value).to eq('deleted')
201
200
  expect(cookie.expired?).to be true
202
201
  end
203
202
  end
@@ -218,11 +217,11 @@ describe Grape::Endpoint do
218
217
  [cookie.name, cookie]
219
218
  end]
220
219
  expect(cookies.size).to eq(2)
221
- ["and_this", "delete_this_cookie"].each do |cookie_name|
220
+ %w(and_this delete_this_cookie).each do |cookie_name|
222
221
  cookie = cookies[cookie_name]
223
222
  expect(cookie).not_to be_nil
224
- expect(cookie.value).to eq("deleted")
225
- expect(cookie.path).to eq("/test")
223
+ expect(cookie.value).to eq('deleted')
224
+ expect(cookie.path).to eq('/test')
226
225
  expect(cookie.expired?).to be true
227
226
  end
228
227
  end
@@ -244,7 +243,7 @@ describe Grape::Endpoint do
244
243
  inner_params = nil
245
244
  subject.get '/declared' do
246
245
  inner_params = declared(params).keys
247
- ""
246
+ ''
248
247
  end
249
248
  get '/declared?first=present'
250
249
  expect(last_response.status).to eq(200)
@@ -255,7 +254,7 @@ describe Grape::Endpoint do
255
254
  inner_params = nil
256
255
  subject.get '/declared' do
257
256
  inner_params = declared(params)
258
- ""
257
+ ''
259
258
  end
260
259
  get '/declared?first=one'
261
260
  expect(last_response.status).to eq(200)
@@ -266,7 +265,7 @@ describe Grape::Endpoint do
266
265
  inner_params = nil
267
266
  subject.get '/declared' do
268
267
  inner_params = declared(params)
269
- ""
268
+ ''
270
269
  end
271
270
 
272
271
  get '/declared?first=present&nested[fourth]=1'
@@ -288,7 +287,7 @@ describe Grape::Endpoint do
288
287
  inner_params = nil
289
288
  subject.get '/declared' do
290
289
  inner_params = declared(params)
291
- ""
290
+ ''
292
291
  end
293
292
 
294
293
  get '/declared?first=present&nested[][fourth]=1&nested[][fourth]=2'
@@ -300,7 +299,7 @@ describe Grape::Endpoint do
300
299
  inner_params = nil
301
300
  subject.get '/declared' do
302
301
  inner_params = declared(params)
303
- ""
302
+ ''
304
303
  end
305
304
  get '/declared?first=one&other=two'
306
305
  expect(last_response.status).to eq(200)
@@ -311,23 +310,57 @@ describe Grape::Endpoint do
311
310
  inner_params = nil
312
311
  subject.get '/declared' do
313
312
  inner_params = declared(params, stringify: true)
314
- ""
313
+ ''
315
314
  end
316
315
 
317
316
  get '/declared?first=one&other=two'
318
317
  expect(last_response.status).to eq(200)
319
- expect(inner_params["first"]).to eq "one"
318
+ expect(inner_params['first']).to eq 'one'
320
319
  end
321
320
 
322
321
  it 'does not include missing attributes if that option is passed' do
323
322
  subject.get '/declared' do
324
- error! 400, "expected nil" if declared(params, include_missing: false)[:second]
325
- ""
323
+ error! 400, 'expected nil' if declared(params, include_missing: false)[:second]
324
+ ''
326
325
  end
327
326
 
328
327
  get '/declared?first=one&other=two'
329
328
  expect(last_response.status).to eq(200)
330
329
  end
330
+
331
+ it 'does not include missing attributes when there are nested hashes' do
332
+ subject.get '/dummy' do
333
+ end
334
+
335
+ subject.params do
336
+ requires :first
337
+ optional :second
338
+ optional :third, default: nil
339
+ optional :nested, type: Hash do
340
+ optional :fourth, default: nil
341
+ optional :fifth, default: nil
342
+ requires :nested_nested, type: Hash do
343
+ optional :sixth, default: 'sixth-default'
344
+ optional :seven, default: nil
345
+ end
346
+ end
347
+ end
348
+
349
+ inner_params = nil
350
+ subject.get '/declared' do
351
+ inner_params = declared(params, include_missing: false)
352
+ ''
353
+ end
354
+
355
+ get '/declared?first=present&nested[fourth]=&nested[nested_nested][sixth]=sixth'
356
+
357
+ expect(last_response.status).to eq(200)
358
+ expect(inner_params[:first]).to eq 'present'
359
+ expect(inner_params[:nested].keys).to eq [:fourth, :nested_nested]
360
+ expect(inner_params[:nested][:fourth]).to eq ''
361
+ expect(inner_params[:nested][:nested_nested].keys).to eq [:sixth]
362
+ expect(inner_params[:nested][:nested_nested][:sixth]).to eq 'sixth'
363
+ end
331
364
  end
332
365
 
333
366
  describe '#declared; call from child namespace' do
@@ -451,7 +484,7 @@ describe Grape::Endpoint do
451
484
  end
452
485
  end
453
486
  end
454
- it "parse email param with provided requirements for params" do
487
+ it 'parse email param with provided requirements for params' do
455
488
  get '/outer/abc@example.com'
456
489
  expect(last_response.body).to eq('abc@example.com')
457
490
  end
@@ -464,7 +497,6 @@ describe Grape::Endpoint do
464
497
  expect(last_response.status).to eq(200)
465
498
  expect(last_response.body).to eq('someone@testing.com1')
466
499
  end
467
-
468
500
  end
469
501
  end
470
502
 
@@ -500,16 +532,16 @@ describe Grape::Endpoint do
500
532
 
501
533
  it 'does not include parameters not defined by the body' do
502
534
  subject.post '/omitted_params' do
503
- error! 400, "expected nil" if params[:version]
535
+ error! 400, 'expected nil' if params[:version]
504
536
  params[:user]
505
537
  end
506
538
  post '/omitted_params', MultiJson.dump(user: 'Bob'), 'CONTENT_TYPE' => 'application/json'
507
539
  expect(last_response.status).to eq(201)
508
- expect(last_response.body).to eq("Bob")
540
+ expect(last_response.body).to eq('Bob')
509
541
  end
510
542
  end
511
543
 
512
- it "responds with a 406 for an unsupported content-type" do
544
+ it 'responds with a 406 for an unsupported content-type' do
513
545
  subject.format :json
514
546
  # subject.content_type :json, "application/json"
515
547
  subject.put '/request_body' do
@@ -531,18 +563,16 @@ describe Grape::Endpoint do
531
563
  post '/', MultiJson.dump(data: { some: 'payload' }), 'CONTENT_TYPE' => 'application/json'
532
564
  end
533
565
 
534
- it "should not response with 406 for same type without params" do
566
+ it 'should not response with 406 for same type without params' do
535
567
  expect(last_response.status).not_to be 406
536
568
  end
537
569
 
538
- it "should response with given content type in headers" do
570
+ it 'should response with given content type in headers' do
539
571
  expect(last_response.headers['Content-Type']).to eq 'application/json; charset=utf-8'
540
572
  end
541
-
542
573
  end
543
574
 
544
575
  context 'precedence' do
545
-
546
576
  before do
547
577
  subject.format :json
548
578
  subject.namespace '/:id' do
@@ -578,29 +608,28 @@ describe Grape::Endpoint do
578
608
  expect(JSON.parse(last_response.body)['params']).to eq '123'
579
609
  end
580
610
  end
581
-
582
611
  end
583
612
 
584
613
  describe '#error!' do
585
614
  it 'accepts a message' do
586
615
  subject.get('/hey') do
587
- error! "This is not valid."
588
- "This is valid."
616
+ error! 'This is not valid.'
617
+ 'This is valid.'
589
618
  end
590
619
 
591
620
  get '/hey'
592
621
  expect(last_response.status).to eq(500)
593
- expect(last_response.body).to eq("This is not valid.")
622
+ expect(last_response.body).to eq('This is not valid.')
594
623
  end
595
624
 
596
625
  it 'accepts a code' do
597
626
  subject.get('/hey') do
598
- error! "Unauthorized.", 401
627
+ error! 'Unauthorized.', 401
599
628
  end
600
629
 
601
630
  get '/hey'
602
631
  expect(last_response.status).to eq(401)
603
- expect(last_response.body).to eq("Unauthorized.")
632
+ expect(last_response.body).to eq('Unauthorized.')
604
633
  end
605
634
 
606
635
  it 'accepts an object and render it in format' do
@@ -640,31 +669,31 @@ describe Grape::Endpoint do
640
669
  describe '#redirect' do
641
670
  it 'redirects to a url with status 302' do
642
671
  subject.get('/hey') do
643
- redirect "/ha"
672
+ redirect '/ha'
644
673
  end
645
674
  get '/hey'
646
675
  expect(last_response.status).to eq 302
647
- expect(last_response.headers['Location']).to eq "/ha"
648
- expect(last_response.body).to eq ""
676
+ expect(last_response.headers['Location']).to eq '/ha'
677
+ expect(last_response.body).to eq ''
649
678
  end
650
679
 
651
680
  it 'has status code 303 if it is not get request and it is http 1.1' do
652
681
  subject.post('/hey') do
653
- redirect "/ha"
682
+ redirect '/ha'
654
683
  end
655
684
  post '/hey', {}, 'HTTP_VERSION' => 'HTTP/1.1'
656
685
  expect(last_response.status).to eq 303
657
- expect(last_response.headers['Location']).to eq "/ha"
686
+ expect(last_response.headers['Location']).to eq '/ha'
658
687
  end
659
688
 
660
689
  it 'support permanent redirect' do
661
690
  subject.get('/hey') do
662
- redirect "/ha", permanent: true
691
+ redirect '/ha', permanent: true
663
692
  end
664
693
  get '/hey'
665
694
  expect(last_response.status).to eq 301
666
- expect(last_response.headers['Location']).to eq "/ha"
667
- expect(last_response.body).to eq ""
695
+ expect(last_response.headers['Location']).to eq '/ha'
696
+ expect(last_response.body).to eq ''
668
697
  end
669
698
  end
670
699
 
@@ -699,54 +728,54 @@ describe Grape::Endpoint do
699
728
 
700
729
  it 'allows explicit return calls' do
701
730
  subject.get('/home') do
702
- return "Hello"
731
+ return 'Hello'
703
732
  end
704
733
 
705
734
  get '/home'
706
735
  expect(last_response.status).to eq(200)
707
- expect(last_response.body).to eq("Hello")
736
+ expect(last_response.body).to eq('Hello')
708
737
  end
709
738
 
710
739
  describe '.generate_api_method' do
711
740
  it 'raises NameError if the method name is already in use' do
712
- expect {
713
- Grape::Endpoint.generate_api_method("version", &proc {})
714
- }.to raise_error(NameError)
741
+ expect do
742
+ Grape::Endpoint.generate_api_method('version', &proc {})
743
+ end.to raise_error(NameError)
715
744
  end
716
745
  it 'raises ArgumentError if a block is not given' do
717
- expect {
718
- Grape::Endpoint.generate_api_method("GET without a block method")
719
- }.to raise_error(ArgumentError)
746
+ expect do
747
+ Grape::Endpoint.generate_api_method('GET without a block method')
748
+ end.to raise_error(ArgumentError)
720
749
  end
721
750
  it 'returns a Proc' do
722
- expect(Grape::Endpoint.generate_api_method("GET test for a proc", &proc {})).to be_a Proc
751
+ expect(Grape::Endpoint.generate_api_method('GET test for a proc', &proc {})).to be_a Proc
723
752
  end
724
753
  end
725
754
 
726
755
  context 'filters' do
727
756
  describe 'before filters' do
728
757
  it 'runs the before filter if set' do
729
- subject.before { env['before_test'] = "OK" }
758
+ subject.before { env['before_test'] = 'OK' }
730
759
  subject.get('/before_test') { env['before_test'] }
731
760
 
732
761
  get '/before_test'
733
- expect(last_response.body).to eq("OK")
762
+ expect(last_response.body).to eq('OK')
734
763
  end
735
764
  end
736
765
 
737
766
  describe 'after filters' do
738
767
  it 'overrides the response body if it sets it' do
739
- subject.after { body "after" }
740
- subject.get('/after_test') { "during" }
768
+ subject.after { body 'after' }
769
+ subject.get('/after_test') { 'during' }
741
770
  get '/after_test'
742
771
  expect(last_response.body).to eq('after')
743
772
  end
744
773
 
745
774
  it 'does not override the response body with its return' do
746
- subject.after { "after" }
747
- subject.get('/after_test') { "body" }
775
+ subject.after { 'after' }
776
+ subject.get('/after_test') { 'body' }
748
777
  get '/after_test'
749
- expect(last_response.body).to eq("body")
778
+ expect(last_response.body).to eq('body')
750
779
  end
751
780
  end
752
781
  end
@@ -776,7 +805,7 @@ describe Grape::Endpoint do
776
805
  verb
777
806
  end
778
807
  send(verb, '/example/and/some/more')
779
- expect(last_response.status).to eql verb == "post" ? 201 : 200
808
+ expect(last_response.status).to eql verb == 'post' ? 201 : 200
780
809
  expect(last_response.body).to eql verb == 'head' ? '' : verb
781
810
  end
782
811
  end
@@ -788,7 +817,7 @@ describe Grape::Endpoint do
788
817
  request.url
789
818
  end
790
819
  get '/url'
791
- expect(last_response.body).to eq("http://example.org/url")
820
+ expect(last_response.body).to eq('http://example.org/url')
792
821
  end
793
822
  ['v1', :v1].each do |version|
794
823
  it 'should include version #{version}' do
@@ -807,7 +836,7 @@ describe Grape::Endpoint do
807
836
  request.url
808
837
  end
809
838
  get '/api/v1/url'
810
- expect(last_response.body).to eq("http://example.org/api/v1/url")
839
+ expect(last_response.body).to eq('http://example.org/api/v1/url')
811
840
  end
812
841
  end
813
842
 
@@ -816,7 +845,7 @@ describe Grape::Endpoint do
816
845
  # NOTE: a 404 is returned instead of the 406 if cascade: false is not set.
817
846
  subject.version 'v1', using: :header, vendor: 'ohanapi', cascade: false
818
847
  subject.get '/test' do
819
- "Hello!"
848
+ 'Hello!'
820
849
  end
821
850
  end
822
851