right_cloud_api_base 0.1.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.
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