right_cloud_api_base 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY +2 -0
  3. data/LICENSE +19 -0
  4. data/README.md +14 -0
  5. data/Rakefile +37 -0
  6. data/lib/base/api_manager.rb +707 -0
  7. data/lib/base/helpers/cloud_api_logger.rb +214 -0
  8. data/lib/base/helpers/http_headers.rb +239 -0
  9. data/lib/base/helpers/http_parent.rb +103 -0
  10. data/lib/base/helpers/http_request.rb +173 -0
  11. data/lib/base/helpers/http_response.rb +122 -0
  12. data/lib/base/helpers/net_http_patch.rb +31 -0
  13. data/lib/base/helpers/query_api_patterns.rb +862 -0
  14. data/lib/base/helpers/support.rb +270 -0
  15. data/lib/base/helpers/support.xml.rb +306 -0
  16. data/lib/base/helpers/utils.rb +380 -0
  17. data/lib/base/manager.rb +122 -0
  18. data/lib/base/parsers/json.rb +38 -0
  19. data/lib/base/parsers/plain.rb +36 -0
  20. data/lib/base/parsers/rexml.rb +83 -0
  21. data/lib/base/parsers/sax.rb +200 -0
  22. data/lib/base/routines/cache_validator.rb +184 -0
  23. data/lib/base/routines/connection_proxies/net_http_persistent_proxy.rb +194 -0
  24. data/lib/base/routines/connection_proxies/right_http_connection_proxy.rb +224 -0
  25. data/lib/base/routines/connection_proxy.rb +66 -0
  26. data/lib/base/routines/request_analyzer.rb +122 -0
  27. data/lib/base/routines/request_generator.rb +48 -0
  28. data/lib/base/routines/request_initializer.rb +52 -0
  29. data/lib/base/routines/response_analyzer.rb +152 -0
  30. data/lib/base/routines/response_parser.rb +79 -0
  31. data/lib/base/routines/result_wrapper.rb +75 -0
  32. data/lib/base/routines/retry_manager.rb +106 -0
  33. data/lib/base/routines/routine.rb +98 -0
  34. data/lib/right_cloud_api_base.rb +72 -0
  35. data/lib/right_cloud_api_base_version.rb +37 -0
  36. data/right_cloud_api_base.gemspec +63 -0
  37. data/spec/helpers/query_api_pattern_spec.rb +312 -0
  38. data/spec/helpers/support_spec.rb +211 -0
  39. data/spec/helpers/support_xml_spec.rb +207 -0
  40. data/spec/helpers/utils_spec.rb +179 -0
  41. data/spec/routines/connection_proxies/test_net_http_persistent_proxy_spec.rb +143 -0
  42. data/spec/routines/test_cache_validator_spec.rb +152 -0
  43. data/spec/routines/test_connection_proxy_spec.rb +44 -0
  44. data/spec/routines/test_request_analyzer_spec.rb +106 -0
  45. data/spec/routines/test_response_analyzer_spec.rb +132 -0
  46. data/spec/routines/test_response_parser_spec.rb +228 -0
  47. data/spec/routines/test_result_wrapper_spec.rb +63 -0
  48. data/spec/routines/test_retry_manager_spec.rb +84 -0
  49. data/spec/spec_helper.rb +15 -0
  50. metadata +215 -0
@@ -0,0 +1,312 @@
1
+ #--
2
+ # Copyright (c) 2013 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require File.expand_path(File.dirname(__FILE__)) + "/../spec_helper"
25
+ require "base/helpers/query_api_patterns"
26
+
27
+ module RightScale
28
+ module CloudApi
29
+ module Test
30
+
31
+ class FakeRoutine < Routine
32
+ def process
33
+ @data[:result] = @data.dup
34
+ end
35
+ end
36
+
37
+ class ApiManager < RightScale::CloudApi::ApiManager
38
+ include RightScale::CloudApi::Mixin::QueryApiPatterns
39
+
40
+ set_routine FakeRoutine
41
+
42
+ query_api_pattern :GetService, :get
43
+
44
+ query_api_pattern :GetResource, :get, 'resource/1'
45
+
46
+ query_api_pattern :GetResourceWithHardcodedData, :get, 'resource/1',
47
+ :params => { 'p1' => 1, 'p2' => 2 },
48
+ :headers => { 'x-text-header' => 'my-test-value' },
49
+ :body => 'MyTestStringBody'
50
+
51
+ query_api_pattern :GetResourceWithVars, :get, 'resource/{:ResourceId}',
52
+ :params => { 'p1' => :Param1, 'p2' => :Param2 },
53
+ :headers => { 'x-text-header' => :MyHeader },
54
+ :body => :MyBody
55
+
56
+ query_api_pattern :GetResourceWithFlexibleVars, :get,'resource/{:ResourceId}/subresource/{:SubresourceId}',
57
+ :headers => { 'x-text-header' => "{:MyHeaderSource}/{:MyHeaderKey}" },
58
+ :body => "text-{:BodyParam1}-text-again-{:BodyParam2}"
59
+
60
+ query_api_pattern :GetResourceWithFlexibleVarsAndDefaults, :get,'resource/{:ResourceId}/subresource/{:SubresourceId}',
61
+ :headers => { 'x-text-header' => "{:MyHeaderSource}/{:MyHeaderKey}" },
62
+ :body => "text-{:BodyParam1}-text-again-{:BodyParam2}",
63
+ :defaults => {
64
+ :BodyParam2 => Utils::NONE,
65
+ :SubresourceId => 2,
66
+ :MyHeaderSource => 'something'
67
+ }
68
+
69
+ query_api_pattern :GetResourceWithFlexibleVarsAndDefaultsV2, :get, '',
70
+ :body => {
71
+ 'Key1' => 'Value1',
72
+ 'Key2' => :Value2,
73
+ 'Key3' => {
74
+ 'Key4' => :Value4
75
+ }
76
+ },
77
+ :defaults => {
78
+ :Value4 => Utils::NONE,
79
+ }
80
+
81
+ query_api_pattern :GetResourceWithFlexibleVarsAndCollection, :get, '',
82
+ :body => {
83
+ 'Key1' => :Value1,
84
+ 'Collection[{:Items}]' => {
85
+ 'Name' => :Name,
86
+ 'Value' => :Value
87
+ },
88
+ 'Collection2[]' => {
89
+ 'Name2' => :Name2,
90
+ 'Value2' => :Value2
91
+ }
92
+ },
93
+ :defaults => {
94
+ :Value => 13,
95
+ }
96
+
97
+ query_api_pattern :GetResourceWithSubCollectionReplacement, :get, '',
98
+ :body => {
99
+ 'Key1' => :Value1,
100
+ 'Collections{:Collections}' => {
101
+ 'Collection[{:Items}]' => {
102
+ 'Name' => :Name,
103
+ 'Value' => :Value
104
+ }
105
+ }
106
+ },
107
+ :defaults => {
108
+ :Value => 13
109
+ }
110
+
111
+ query_api_pattern :GetResourceWithSubCollectionReplacementAndDefaults, :get, '',
112
+ :body => {
113
+ 'Key1' => :Value1,
114
+ 'Collections{:Collections}' => {
115
+ 'Collection[{:Items}]' => {
116
+ 'Name' => :Name,
117
+ 'Value' => :Value
118
+ }
119
+ }
120
+ },
121
+ :defaults => {
122
+ :Value => 13,
123
+ :Collections => Utils::NONE
124
+ }
125
+
126
+ query_api_pattern(:GetResourceWithBlock, :get, 'resource/1') do |result|
127
+ result[:path] = 'my-new-path'
128
+ result[:verb] = :post
129
+ result[:params] = {'p1' => 'v1'}
130
+ result[:headers] = {'x-my-header' => 'value'}
131
+ result[:body] = 'MyBody'
132
+ end
133
+
134
+ query_api_pattern :GetResourceWithProc, :get, 'resource/1',
135
+ :after => Proc.new { |result| result[:path] = 'my-new-path'
136
+ result[:verb] = :post
137
+ result[:params] = {'p1' => 'v1'}
138
+ result[:headers] = {'x-my-header' => 'value'}
139
+ result[:body] = 'MyBody' }
140
+ end
141
+
142
+ end
143
+ end
144
+ end
145
+
146
+ describe "QueryApiPattern" do
147
+ before(:each) do
148
+ unless @initialized
149
+ logger = Logger.new(STDOUT)
150
+ logger.level = Logger::INFO
151
+ @api_manager = RightScale::CloudApi::Test::ApiManager.new({'x' => 'y'},'endpoint', {:logger => logger})
152
+ end
153
+ end
154
+
155
+ context "query_api_pattern" do
156
+ it "fails when there is an unexpected parameter" do
157
+ expect {
158
+ @api_manager.class.query_api_pattern(:GetService, :get, '', :unknown_something => 'blah-blah')
159
+ }.to raise_error(RightScale::CloudApi::Error)
160
+ end
161
+ end
162
+
163
+ it "works when there are no issues and generates request data properly" do
164
+ data = @api_manager.GetService
165
+ request = data[:request]
166
+ expect(request[:verb]).to eq(:get)
167
+ expect(request[:relative_path]._blank?).to be(true)
168
+ expect(request[:headers].to_hash._blank?).to be(true)
169
+ expect(request[:params]._blank?).to be(true)
170
+ expect(request[:body].nil?).to be(true)
171
+ end
172
+
173
+ it "moves all unused variables into URL params" do
174
+ data = @api_manager.GetService('Param1' => 'value1', 'Param2' => 'value2')
175
+ request = data[:request]
176
+ expect(request[:verb]).to eq(:get)
177
+ expect(request[:relative_path]._blank?).to be(true)
178
+ expect(request[:headers].to_hash._blank?).to be(true)
179
+ expect(request[:params]).to eq('Param1' => 'value1', 'Param2' => 'value2')
180
+ expect(request[:body]).to be(nil)
181
+ end
182
+
183
+ it "works for GetResource Query API definition" do
184
+ data = @api_manager.GetResource
185
+ request = data[:request]
186
+ expect(request[:verb]).to eq :get
187
+ expect(request[:relative_path]).to eq 'resource/1'
188
+ expect(request[:headers].to_hash._blank?).to be(true)
189
+ expect(request[:params]._blank?).to be(true)
190
+ expect(request[:body]).to be(nil)
191
+ end
192
+
193
+ it "works for GetResourceWithHardcodedData Query API definition" do
194
+ data = @api_manager.GetResourceWithHardcodedData
195
+ request = data[:request]
196
+ expect(request[:verb]).to eq :get
197
+ expect(request[:relative_path]).to eq 'resource/1'
198
+ expect(request[:headers].to_hash).to eq('x-text-header' => ['my-test-value'])
199
+ expect(request[:params]).to eq('p1' => 1, 'p2' => 2 )
200
+ expect(request[:body]).to eq 'MyTestStringBody'
201
+ end
202
+
203
+ it "works for GetResourceWithVars Query API definition" do
204
+ data = @api_manager.GetResourceWithVars( 'ResourceId' => 123, 'Param1' => 11, 'Param2' => 12, 'MyHeader' => 'x-test-something', 'MyBody' => 'MyTestStringBody')
205
+ request = data[:request]
206
+ expect(request[:verb]).to eq :get
207
+ expect(request[:relative_path]).to eq 'resource/123'
208
+ expect(request[:headers].to_hash).to eq('x-text-header' => ['x-test-something'])
209
+ expect(request[:params]).to eq( 'p1' => 11, 'p2' => 12 )
210
+ expect(request[:body]).to eq 'MyTestStringBody'
211
+ end
212
+
213
+ it "fails when a mandatory variable is missing" do
214
+ expect{ @api_manager.GetResourceWithVars }.to raise_error(RightScale::CloudApi::Error)
215
+ end
216
+
217
+ it "works for GetResourceWithFlexibleVars Query API definition" do
218
+ data = @api_manager.GetResourceWithFlexibleVars( 'ResourceId' => 1, 'SubresourceId' => 2, 'MyHeaderSource' => 3, 'MyHeaderKey' => 4, 'BodyParam1' => 5, 'BodyParam2' => 6)
219
+ request = data[:request]
220
+ expect(request[:verb]).to eq :get
221
+ expect(request[:relative_path]).to eq 'resource/1/subresource/2'
222
+ expect(request[:headers].to_hash).to eq('x-text-header' => ['3/4'])
223
+ expect(request[:body]).to eq 'text-5-text-again-6'
224
+ end
225
+
226
+ it "works for GetResourceWithFlexibleVarsAndDefaults Query API definition" do
227
+ data = @api_manager.GetResourceWithFlexibleVarsAndDefaults('ResourceId' => 1, 'MyHeaderKey' => 3, 'BodyParam1' => 5)
228
+ request = data[:request]
229
+ expect(request[:verb]).to eq :get
230
+ expect(request[:relative_path]).to eq 'resource/1/subresource/2'
231
+ expect(request[:headers].to_hash).to eq('x-text-header' => ['something/3'])
232
+ expect(request[:body]).to eq 'text-5-text-again-'
233
+ end
234
+
235
+ it "works for GetResourceWithFlexibleVarsAndDefaultsV2 Query API definition" do
236
+ data = @api_manager.GetResourceWithFlexibleVarsAndDefaultsV2('Value2' => 1)
237
+ request = data[:request]
238
+ expect(request[:verb]).to eq :get
239
+ expect(request[:relative_path]._blank?).to be(true)
240
+ expect(request[:headers].to_hash._blank?).to be(true)
241
+ expect(request[:params]._blank?).to be(true)
242
+ expect(request[:body]).to eq('Key1' => 'Value1', 'Key2' => 1, 'Key3' => {})
243
+ end
244
+
245
+ it "works for GetResourceWithFlexibleVarsAndCollection Query API definition" do
246
+ data = @api_manager.GetResourceWithFlexibleVarsAndCollection('Value1' => 1,
247
+ 'Items' => [{'Name' => 'x1', 'Value' => 'xv1'}, {'Name' => 'x2'}],
248
+ 'Collection2' => [{'Name2' => 'x21', 'Value2' => 'xv21'}, {'Name2' => 'x22', 'Value2' => 'v22'}]
249
+ )
250
+ request = data[:request]
251
+ expect(request[:verb]).to eq :get
252
+ expect(request[:relative_path]._blank?).to be(true)
253
+ expect(request[:headers].to_hash._blank?).to be(true)
254
+ expect(request[:params]._blank?).to be(true)
255
+ expect(request[:body]).to eq('Key1' => 1,
256
+ 'Collection' => [{'Name' => 'x1', 'Value' => 'xv1'}, {'Name' => 'x2', 'Value' => 13}],
257
+ 'Collection2' => [{'Name2' => 'x21', 'Value2' => 'xv21'}, {'Name2' => 'x22', 'Value2' => 'v22'}])
258
+ end
259
+
260
+ it "works for GetResourceWithSubCollectionReplacement Query API definition" do
261
+ # Nothing is pased - should complain
262
+ expect {
263
+ @api_manager.GetResourceWithSubCollectionReplacement('Value1' => 1)
264
+ }.to raise_error(RightScale::CloudApi::Error)
265
+ # A replacement is passed - must replace
266
+ data = @api_manager.GetResourceWithSubCollectionReplacement('Value1' => 1, 'Collections' => 'blah-blah')
267
+ request = data[:request]
268
+ expect(request[:verb]).to eq :get
269
+ expect(request[:relative_path]._blank?).to be(true)
270
+ expect(request[:headers].to_hash._blank?).to be(true)
271
+ expect(request[:params]._blank?).to be(true)
272
+ expect(request[:body]).to eq('Key1' => 1, 'Collections' => 'blah-blah')
273
+
274
+ data = @api_manager.GetResourceWithSubCollectionReplacement('Value1' => 1, 'Items' => [{'Name' => 'x1', 'Value' => 'xv1'}, {'Name' => 'x2'}])
275
+ request = data[:request]
276
+ expect(request[:verb]).to eq :get
277
+ expect(request[:relative_path]._blank?).to be(true)
278
+ expect(request[:headers].to_hash._blank?).to be(true)
279
+ expect(request[:params]._blank?).to be(true)
280
+ expect(request[:body]).to eq('Key1' => 1, 'Collections' => {'Collection' => [{'Name' => 'x1', 'Value' => 'xv1'}, {'Name' => 'x2', 'Value' => 13}]})
281
+ end
282
+
283
+ it "works for GetResourceWithSubCollectionReplacementAndDefaults Query API definition" do
284
+ data = @api_manager.GetResourceWithSubCollectionReplacementAndDefaults('Value1' => 1)
285
+ request = data[:request]
286
+ expect(request[:verb]).to eq :get
287
+ expect(request[:relative_path]._blank?).to be(true)
288
+ expect(request[:headers].to_hash._blank?).to be(true)
289
+ expect(request[:params]._blank?).to be(true)
290
+ expect(request[:body]).to eq('Key1' => 1 )
291
+ end
292
+
293
+ it "works for GetResourceWithBlock Query API definition" do
294
+ data = @api_manager.GetResourceWithBlock
295
+ request = data[:request]
296
+ expect(request[:verb]).to eq :post
297
+ expect(request[:relative_path]).to eq 'my-new-path'
298
+ expect(request[:headers].to_hash).to eq('x-my-header' => ['value'])
299
+ expect(request[:params]).to eq( 'p1' => 'v1')
300
+ expect(request[:body]).to eq 'MyBody'
301
+ end
302
+
303
+ it "works for GetResourceWithProc Query API definition" do
304
+ data = @api_manager.GetResourceWithProc
305
+ request = data[:request]
306
+ expect(request[:verb]).to eq :post
307
+ expect(request[:relative_path]).to eq 'my-new-path'
308
+ expect(request[:headers].to_hash).to eq('x-my-header' => ['value'])
309
+ expect(request[:params]).to eq( 'p1' => 'v1')
310
+ expect(request[:body]).to eq 'MyBody'
311
+ end
312
+ end
@@ -0,0 +1,211 @@
1
+ #--
2
+ # Copyright (c) 2013 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require File.expand_path(File.dirname(__FILE__)) + "/../spec_helper"
25
+
26
+ describe "support.rb" do
27
+
28
+ # --- String ---
29
+
30
+ context "String#_constantize" do
31
+ it "constantizes when a string points to an existing class/module name" do
32
+ class MyCoolTestConstantizeClass; end
33
+ expect('MyCoolTestConstantizeClass'._constantize).to be MyCoolTestConstantizeClass
34
+ end
35
+ it "fails when a string points to a non-existing class/module name" do
36
+ expect {
37
+ 'MyBadTestConstantizeClass'._constantize
38
+ }.to raise_error(::NameError)
39
+ end
40
+ end
41
+
42
+ context "String#_camelize" do
43
+ it "camelizes a string" do
44
+ expect('my_test_string'._camelize).to eq'MyTestString'
45
+ expect('MyTestString'._camelize).to eq 'MyTestString'
46
+ expect('my_test_string'._camelize(:lower_case)).to eq 'myTestString'
47
+ expect('MyTestString'._camelize(:lower_case)).to eq 'myTestString'
48
+ expect('Privet, how are you_doing, los_Amigos?'._camelize).to eq 'Privet, How Are YouDoing, LosAmigos?'
49
+ end
50
+ end
51
+
52
+ context "String#_snake_case" do
53
+ it "underscorizes a string" do
54
+ expect('MyTestString'._snake_case).to eq 'my_test_string'
55
+ expect('my_test_string'._snake_case).to eq 'my_test_string'
56
+ expect('Privet, How Are YouDoing, LosAmigos?'._snake_case).to eq 'privet, how are you_doing, los_amigos?'
57
+ end
58
+ end
59
+
60
+ context "String#_arrayify" do
61
+ it "arrayifies into array" do
62
+ expect(''._arrayify).to eq ['']
63
+ expect('something'._arrayify).to eq ['something']
64
+ end
65
+ end
66
+
67
+ context "String#_blank?" do
68
+ it "returns true when it has zero size" do
69
+ expect(''._blank?).to be true
70
+ end
71
+ it "returns true when it contains spaces only" do
72
+ expect(" \n\n\n "._blank?).to be true
73
+ end
74
+ it "returns false when it has anything valueble" do
75
+ expect("something"._blank?).to be false
76
+ end
77
+ end
78
+
79
+ # --- Object ---
80
+
81
+ context "Object#_blank?" do
82
+ it "checks if an object responds blank?" do
83
+ object = Object.new
84
+ expect(object).to receive(:blank?).once.and_return(true)
85
+ expect(object).to receive(:respond_to?).with(:blank?).once.and_return(true)
86
+ expect(object._blank?).to be true
87
+ end
88
+ it "checks if an object responds empty? unles it responds to blank?" do
89
+ object = Object.new
90
+ expect(object).to receive(:empty?).once.and_return(true)
91
+ expect(object).to receive(:respond_to?).with(:blank?).once.and_return(false)
92
+ expect(object).to receive(:respond_to?).with(:empty?).once.and_return(true)
93
+ expect(object._blank?).to be true
94
+ end
95
+ it "returns !self unless it responds to blank? and empty?" do
96
+ object = Object.new
97
+ expect(object).to receive(:respond_to?).with(:blank?).once.and_return(false)
98
+ expect(object).to receive(:respond_to?).with(:empty?).once.and_return(false)
99
+ expect(object._blank?).to eq !object
100
+ end
101
+ end
102
+
103
+ context "Object#_arrayify" do
104
+ it "feeds self to Array()" do
105
+ [nil, 1, :symbol].each do |object|
106
+ expect(object._arrayify).to eq Array(object)
107
+ end
108
+ end
109
+ end
110
+
111
+ # --- NilClass ---
112
+
113
+ context "NilClass" do
114
+ it "always return true" do
115
+ expect(nil._blank?).to be true
116
+ end
117
+ end
118
+
119
+ # --- FalseClass ---
120
+
121
+ context "FalseClass" do
122
+ it "always return true" do
123
+ expect(false._blank?).to be true
124
+ end
125
+ end
126
+
127
+ # --- TrueClass ---
128
+
129
+ context "FalseClass" do
130
+ it "always return false" do
131
+ expect(true._blank?).to be false
132
+ end
133
+ end
134
+
135
+ # --- Array ---
136
+
137
+ context "Array#_blank?" do
138
+ it "behaves accordingly to array's emptyness status" do
139
+ expect([]._blank?).to be true
140
+ expect([1]._blank?).to be false
141
+ end
142
+ end
143
+
144
+ context "Array#_stringify_keys" do
145
+ it "stringifies all the keys for all its hash items" do
146
+ expect([[{:x=>{:y=>[:z => 13]}}]]._stringify_keys).to eq [[{"x"=>{"y"=>[{"z"=>13}]}}]]
147
+ end
148
+ end
149
+
150
+ context "Array#_stringify_keys" do
151
+ it "symbolizes all the keys for all its hash items" do
152
+ expect([[{"x"=>{"y"=>[{"z"=>13}]}}]]._symbolize_keys).to eq [[{:x=>{:y=>[:z => 13]}}]]
153
+ end
154
+ end
155
+
156
+ # --- Hash ---
157
+
158
+ context "Hash#_blank?" do
159
+ it "behaves accordingly to hash's emptyness status" do
160
+ expect({}._blank?).to be true
161
+ expect({:foo => :bar}._blank?).to be false
162
+ end
163
+ end
164
+
165
+ context "Hash#_stringify_keys" do
166
+ it "stringifies all the keys" do
167
+ expect({"1"=>2, :x=>[[{:y=>{:z=>13}}], 2]}._stringify_keys).to eq("1"=>2, "x"=>[[{"y"=>{"z"=>13}}], 2])
168
+ end
169
+ end
170
+
171
+ context "Hash#_symbolize_keys" do
172
+ it "symbolizes all keys" do
173
+ expect({"1"=>2, "x"=>[[{"y"=>{"z"=>13}}], 2]}._symbolize_keys).to eq(:"1"=>2, :x=>[[{:y=>{:z=>13}}], 2])
174
+ end
175
+ end
176
+
177
+ context "Hash#_at" do
178
+ it "fails if the given path does not not exist" do
179
+ expect { {}._at('x','y') }.to raise_error(StandardError)
180
+ end
181
+ it "does not fail if the given path does not exist but a default value is provided" do
182
+ expect({}._at('x', :default => 'defval')).to eq 'defval'
183
+ end
184
+ it "calls a block if the given path does not exist and a default value is not provided" do
185
+ expect({}._at('x'){ 'defval' }).to eq 'defval'
186
+ expect{ {}._at('x'){ fail "NotFound.MyCoolError" }}.to raise_error(RuntimeError, "NotFound.MyCoolError")
187
+ end
188
+ it "returns the requested value by the given path when the path exists" do
189
+ expect({'x' => nil}._at('x')).to be nil
190
+ expect({'x' => 4}._at('x')).to eq 4
191
+ expect({'x' => { 'y' => { 'z' => 'value'} }}._at('x', 'y', 'z')).to eq 'value'
192
+ end
193
+ it "arrayifies the result when :arrayify => true option is set" do
194
+ expect({'x' => { 'y' => { 'z' => 'value'} }}._at('x', 'y', 'z', :arrayify => true)).to eq ['value']
195
+ end
196
+ end
197
+
198
+ context "Hash#_arrayify_at" do
199
+ it "extracts a value by the given path and arrayifies it" do
200
+ expect({'x' => { 'y' => 'z' }}._arrayify_at('x', 'y')).to eq ['z']
201
+ expect({'x' => { 'y' => ['z'] }}._arrayify_at('x', 'y')).to eq ['z']
202
+ end
203
+ end
204
+
205
+ context "Hash#_arrayify" do
206
+ it "wraps self into array" do
207
+ hash = { 1 => 2}
208
+ expect(hash._arrayify).to eq [hash]
209
+ end
210
+ end
211
+ end