collapsium 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,22 +18,29 @@ describe ::Collapsium::EnvironmentOverride do
18
18
 
19
19
  context "environment variable name" do
20
20
  it "upcases keys" do
21
- expect(@tester.key_to_env("foo")).to eql "FOO"
21
+ expect(::Collapsium::EnvironmentOverride.key_to_env("foo")).to eql "FOO"
22
+ expect(::Collapsium::EnvironmentOverride.key_to_env("f0o")).to eql "F0O"
22
23
  end
23
24
 
24
25
  it "replaces non-alphanumeric characters with underscores" do
25
- expect(@tester.key_to_env("foo!bar")).to eql "FOO_BAR"
26
- expect(@tester.key_to_env("foo.bar")).to eql "FOO_BAR"
27
- expect(@tester.key_to_env("foo@bar")).to eql "FOO_BAR"
26
+ expect(::Collapsium::EnvironmentOverride.key_to_env("foo!bar")).to \
27
+ eql "FOO_BAR"
28
+ expect(::Collapsium::EnvironmentOverride.key_to_env("foo.bar")).to \
29
+ eql "FOO_BAR"
30
+ expect(::Collapsium::EnvironmentOverride.key_to_env("foo@bar")).to \
31
+ eql "FOO_BAR"
28
32
  end
29
33
 
30
34
  it "collapses multiple underscores into one" do
31
- expect(@tester.key_to_env("foo!_@bar")).to eql "FOO_BAR"
35
+ expect(::Collapsium::EnvironmentOverride.key_to_env("foo!_@bar")).to \
36
+ eql "FOO_BAR"
32
37
  end
33
38
 
34
39
  it "strips leading and trailing underscores" do
35
- expect(@tester.key_to_env(".foo@bar")).to eql "FOO_BAR"
36
- expect(@tester.key_to_env("foo@bar_")).to eql "FOO_BAR"
40
+ expect(::Collapsium::EnvironmentOverride.key_to_env(".foo@bar")).to \
41
+ eql "FOO_BAR"
42
+ expect(::Collapsium::EnvironmentOverride.key_to_env("foo@bar_")).to \
43
+ eql "FOO_BAR"
37
44
  end
38
45
  end
39
46
 
@@ -46,9 +53,9 @@ describe ::Collapsium::EnvironmentOverride do
46
53
  end
47
54
 
48
55
  it "inherits environment override" do
49
- expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_truthy
56
+ expect(@tester["foo"]["bar"].is_a?(Integer)).to be_truthy
50
57
  ENV["BAR"] = "test"
51
- expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_falsy
58
+ expect(@tester["foo"]["bar"].is_a?(Integer)).to be_falsy
52
59
  expect(@tester["foo"]["bar"]).to eql "test"
53
60
  end
54
61
 
@@ -74,12 +81,18 @@ describe ::Collapsium::EnvironmentOverride do
74
81
 
75
82
  context "with PathedAccess" do
76
83
  before :each do
77
- @tester = { "foo" => { "bar" => 42 } }
84
+ @tester = {
85
+ "foo" => {
86
+ "bar" => 42
87
+ },
88
+ "baz" => [{ "quux" => 123 }]
89
+ }
78
90
  @tester.extend(::Collapsium::PathedAccess)
79
91
  @tester.extend(::Collapsium::EnvironmentOverride)
80
92
  ENV.delete("FOO")
81
93
  ENV.delete("BAR")
82
94
  ENV.delete("FOO_BAR")
95
+ ENV.delete("BAZ_0_QUUX")
83
96
  end
84
97
 
85
98
  it "overrides first-order keys" do
@@ -90,9 +103,9 @@ describe ::Collapsium::EnvironmentOverride do
90
103
  end
91
104
 
92
105
  it "inherits environment override" do
93
- expect(@tester["foo.bar"].is_a?(Fixnum)).to be_truthy
106
+ expect(@tester["foo.bar"].is_a?(Integer)).to be_truthy
94
107
  ENV["BAR"] = "test"
95
- expect(@tester["foo.bar"].is_a?(Fixnum)).to be_falsy
108
+ expect(@tester["foo.bar"].is_a?(Integer)).to be_falsy
96
109
  expect(@tester["foo.bar"]).to eql "test"
97
110
  end
98
111
 
@@ -116,27 +129,39 @@ describe ::Collapsium::EnvironmentOverride do
116
129
  end
117
130
 
118
131
  it "overrides from pathed key" do
119
- expect(@tester["foo.bar"].is_a?(Fixnum)).to be_truthy
132
+ expect(@tester["foo.bar"].is_a?(Integer)).to be_truthy
120
133
  ENV["FOO_BAR"] = "test"
121
- expect(@tester["foo.bar"].is_a?(Fixnum)).to be_falsy
134
+ expect(@tester["foo.bar"].is_a?(Integer)).to be_falsy
122
135
  expect(@tester["foo.bar"]).to eql "test"
123
136
  end
124
137
 
125
138
  it "prefers pathed key over non-pathed key" do
126
- expect(@tester["foo.bar"].is_a?(Fixnum)).to be_truthy
139
+ expect(@tester["foo.bar"].is_a?(Integer)).to be_truthy
127
140
  ENV["FOO_BAR"] = "pathed"
128
141
  ENV["BAR"] = "simple"
129
- expect(@tester["foo.bar"].is_a?(Fixnum)).to be_falsy
142
+ expect(@tester["foo.bar"].is_a?(Integer)).to be_falsy
130
143
  expect(@tester["foo.bar"]).to eql "pathed"
131
144
  end
132
145
 
133
146
  it "prefers pathed key over non-pathed key when using nested values" do
134
- expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_truthy
147
+ expect(@tester["foo"]["bar"].is_a?(Integer)).to be_truthy
135
148
  ENV["FOO_BAR"] = "pathed"
136
149
  ENV["BAR"] = "simple"
137
- expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_falsy
150
+ expect(@tester["foo"]["bar"].is_a?(Integer)).to be_falsy
138
151
  expect(@tester["foo"]["bar"]).to eql "pathed"
139
152
  end
153
+
154
+ it "can deal with componentized keys" do
155
+ expect(@tester["foo"]["foo.bar"]).to be_nil
156
+ ENV["FOO_BAR"] = "pathed"
157
+ expect(@tester["foo"]["foo.bar"]).to be_nil
158
+ end
159
+
160
+ it "works with arrays" do
161
+ expect(@tester["baz"][0]["quux"]).to eql 123
162
+ ENV["BAZ_0_QUUX"] = "override"
163
+ expect(@tester["baz"][0]["quux"]).to eql "override"
164
+ end
140
165
  end
141
166
 
142
167
  context "respects the behaviour of wrapped methods" do
@@ -5,42 +5,16 @@ class PathedHash < Hash
5
5
  prepend ::Collapsium::PathedAccess
6
6
  end
7
7
 
8
+ class IncludedPathedHash < Hash
9
+ include ::Collapsium::PathedAccess
10
+ end
11
+
8
12
  describe ::Collapsium::PathedAccess do
9
13
  before :each do
10
14
  @tester = {}
11
15
  @tester.extend(::Collapsium::PathedAccess)
12
16
  end
13
17
 
14
- describe "Path components" do
15
- it "splits a path into components" do
16
- expect(@tester.path_components("foo.bar")).to eql %w(foo bar)
17
- end
18
-
19
- it "strips empty components at the beginning" do
20
- expect(@tester.path_components("..foo.bar")).to eql %w(foo bar)
21
- end
22
-
23
- it "strips empty components at the end" do
24
- expect(@tester.path_components("foo.bar..")).to eql %w(foo bar)
25
- end
26
-
27
- it "strips empty components in the middle" do
28
- expect(@tester.path_components("foo...bar")).to eql %w(foo bar)
29
- end
30
-
31
- it "joins path components" do
32
- expect(@tester.join_path(%w(foo bar))).to eql "foo.bar"
33
- end
34
-
35
- it "joins empty components to an empty string" do
36
- expect(@tester.join_path([])).to eql ""
37
- end
38
-
39
- it "normalizes a path" do
40
- expect(@tester.normalize_path("foo..bar..baz.")).to eql ".foo.bar.baz"
41
- end
42
- end
43
-
44
18
  describe "Hash-like" do
45
19
  it "responds to Hash functions" do
46
20
  [:invert, :delete, :fetch].each do |meth|
@@ -67,8 +41,8 @@ describe ::Collapsium::PathedAccess do
67
41
  end
68
42
 
69
43
  it "defaults to empty String" do
70
- expect(@tester.path_prefix).to be_empty
71
44
  expect(@tester.path_prefix.class).to eql String
45
+ expect(@tester.path_prefix).to eql '.' # separator
72
46
  end
73
47
 
74
48
  it "can be set" do
@@ -79,6 +53,31 @@ describe ::Collapsium::PathedAccess do
79
53
  @tester.path_prefix = "foo..bar..baz.."
80
54
  expect(@tester.path_prefix).to eql ".foo.bar.baz"
81
55
  end
56
+
57
+ it "has the correct path for each value" do
58
+ @tester.merge!(
59
+ foo: {
60
+ first: 1, second: 2,
61
+ inner: { x: 1 },
62
+ },
63
+ bar: {
64
+ baz: 42, quux: 123,
65
+ inner: { x: 1 },
66
+ },
67
+ baz: [{ inner: { x: 1 } }],
68
+ "foo.bar" => 123,
69
+ "pathed.key" => 321
70
+ )
71
+
72
+ expect(@tester.path_prefix).to eql "."
73
+ expect(@tester[:foo].path_prefix).to eql ".foo"
74
+ expect(@tester[:foo][:inner].path_prefix).to eql ".foo.inner"
75
+ expect(@tester[:bar].path_prefix).to eql ".bar"
76
+ expect(@tester[:bar][:inner].path_prefix).to eql ".bar.inner"
77
+ expect(@tester[:baz].path_prefix).to eql ".baz"
78
+ expect(@tester[:baz][0].path_prefix).to eql ".baz.0"
79
+ expect(@tester[:baz][0][:inner].path_prefix).to eql ".baz.0.inner"
80
+ end
82
81
  end
83
82
 
84
83
  it "can recursively read entries via a path" do
@@ -136,6 +135,16 @@ describe ::Collapsium::PathedAccess do
136
135
  expect(@tester[".does.not.exist"]).to be_nil
137
136
  expect(@tester.fetch(".does.not.exist", 42)).to eql 42
138
137
  end
138
+
139
+ it "ignores keys containing the path separator" do
140
+ # Exists at the top level, no "pathed" item exists at the top level,
141
+ # though.
142
+ expect(@tester["pathed.key"]).to be_nil
143
+
144
+ # "foo" exists at the top level, but it does not contain "bar".
145
+ # "foo.bar" also exists at the top level.
146
+ expect(@tester["foo.bar"]).to be_nil
147
+ end
139
148
  end
140
149
 
141
150
  describe "nested inherit capabilities" do
@@ -177,6 +186,20 @@ describe ::Collapsium::PathedAccess do
177
186
  expect(@tester['foo.bar']).to eql 123
178
187
  expect(@tester['foo.baz']).to eql 'quux'
179
188
  end
189
+
190
+ it "doesn't break #path_prefix" do
191
+ @tester[:foo] = {
192
+ bar: {
193
+ baz: 123,
194
+ }
195
+ }
196
+ @tester.default_proc = ::Collapsium::IndifferentAccess::DEFAULT_PROC
197
+
198
+ expect(@tester[:foo].path_prefix).to eql ".foo"
199
+ expect(@tester["foo"].path_prefix).to eql ".foo"
200
+ expect(@tester[:foo][:bar].path_prefix).to eql ".foo.bar"
201
+ expect(@tester["foo.bar"].path_prefix).to eql ".foo.bar"
202
+ end
180
203
  end
181
204
 
182
205
  context PathedHash do
@@ -187,4 +210,46 @@ describe ::Collapsium::PathedAccess do
187
210
  expect(test_hash["foo.bar"]).to eql 42
188
211
  end
189
212
  end
213
+
214
+ context IncludedPathedHash do
215
+ let(:test_hash) { IncludedPathedHash.new }
216
+
217
+ it "can write recursively" do
218
+ test_hash["foo.bar"] = 42
219
+ expect(test_hash["foo.bar"]).to eql 42
220
+ end
221
+ end
222
+
223
+ context "array entries" do
224
+ before do
225
+ @tester['foo'] = {
226
+ 'bar' => [
227
+ { 'baz1' => 'quux1' },
228
+ { 'baz2' => 'quux2' },
229
+ ]
230
+ }
231
+ end
232
+
233
+ it "resolved with pathed access" do
234
+ expect(@tester['foo.bar.0.baz1']).to eql 'quux1'
235
+ expect(@tester['foo.bar.1.baz2']).to eql 'quux2'
236
+ end
237
+ end
238
+
239
+ context "nested symbol keys" do
240
+ before do
241
+ @tester['foo'] = {
242
+ bar: { 'baz' => 'quux' },
243
+ }
244
+ end
245
+
246
+ it "resolve with pathed access & indifferent access" do
247
+ # This should be nil - we don't use indifferent access yet.
248
+ expect(@tester['foo.bar.baz']).to be_nil
249
+
250
+ # With indifferent access, pathed access must work
251
+ @tester.default_proc = ::Collapsium::IndifferentAccess::DEFAULT_PROC
252
+ expect(@tester['foo.bar.baz']).to eql 'quux'
253
+ end
254
+ end
190
255
  end
@@ -5,6 +5,10 @@ module First
5
5
  class << self
6
6
  include ::Collapsium::Support::Methods
7
7
 
8
+ def extended(base)
9
+ prepended(base)
10
+ end
11
+
8
12
  def prepended(base)
9
13
  wrap_method(base, :calling_test) do |super_method, *args, &block|
10
14
  result = super_method.call(*args, &block)
@@ -35,6 +39,48 @@ module Second
35
39
  end # class << self
36
40
  end # module First
37
41
 
42
+ module NonRaising
43
+ class << self
44
+ include ::Collapsium::Support::Methods
45
+
46
+ def extended(base)
47
+ prepended(base)
48
+ end
49
+
50
+ def prepended(base)
51
+ opts = { raise_on_missing: false }
52
+
53
+ wrap_method(base, :calling_test, opts) do |super_method, *args, &block|
54
+ result = super_method.call(*args, &block)
55
+ next "nonraising: #{result}"
56
+ end
57
+
58
+ wrap_method(base, :test, opts) do
59
+ next "nonraising"
60
+ end
61
+ end
62
+ end # class << self
63
+ end # module NonRaising
64
+
65
+ module Looping
66
+ class << self
67
+ include ::Collapsium::Support::Methods
68
+
69
+ def extended(base)
70
+ prepended(base)
71
+ end
72
+
73
+ def prepended(base)
74
+ wrap_method(base, :loop, raise_on_missing: false) do |super_method, *_, &_|
75
+ super_method.receiver.loop
76
+ end
77
+ end
78
+ end # class << self
79
+ end # module NonRaising
80
+
81
+ class EmptyClass
82
+ end
83
+
38
84
  class FirstThenSecond
39
85
  def calling_test
40
86
  return "first_then_second"
@@ -136,12 +182,18 @@ end
136
182
 
137
183
  describe ::Collapsium::Support::Methods do
138
184
  context "#wrap_method" do
139
- it "fails if there is no method to wrap" do
140
- expect do
141
- class NoMethodToWrap
142
- prepend First
185
+ it "can wrap objects" do
186
+ class TestClass
187
+ def calling_test
188
+ return "object wrapping"
143
189
  end
144
- end.to raise_error(NameError)
190
+ end
191
+
192
+ tester = nil
193
+ expect { tester = TestClass.new }.not_to raise_error
194
+ expect { tester.extend(First) }.not_to raise_error
195
+
196
+ expect(tester.calling_test).to eql "first: object wrapping"
145
197
  end
146
198
 
147
199
  context FirstThenSecond do
@@ -154,6 +206,18 @@ describe ::Collapsium::Support::Methods do
154
206
  it "wraps results appropriately for calling methods" do
155
207
  expect(tester.calling_test).to eql 'second: first: first_then_second'
156
208
  end
209
+
210
+ it "defines two wrappers for #test" do
211
+ expect(
212
+ ::Collapsium::Support::Methods.wrappers(tester, :test).size
213
+ ).to eql 2
214
+ end
215
+
216
+ it "defines two wrappers for #calling_test" do
217
+ expect(
218
+ ::Collapsium::Support::Methods.wrappers(tester, :calling_test).size
219
+ ).to eql 2
220
+ end
157
221
  end
158
222
 
159
223
  context SecondThenFirst do
@@ -166,6 +230,18 @@ describe ::Collapsium::Support::Methods do
166
230
  it "wraps results appropriately for calling methods" do
167
231
  expect(tester.calling_test).to eql 'first: second: second_then_first'
168
232
  end
233
+
234
+ it "defines two wrappers for #test" do
235
+ expect(
236
+ ::Collapsium::Support::Methods.wrappers(tester, :test).size
237
+ ).to eql 2
238
+ end
239
+
240
+ it "defines two wrappers for #calling_test" do
241
+ expect(
242
+ ::Collapsium::Support::Methods.wrappers(tester, :calling_test).size
243
+ ).to eql 2
244
+ end
169
245
  end
170
246
 
171
247
  context Included do
@@ -178,6 +254,18 @@ describe ::Collapsium::Support::Methods do
178
254
  it "wraps results appropriately for calling methods" do
179
255
  expect(tester.calling_test).to eql 'include_module: included'
180
256
  end
257
+
258
+ it "defines two wrappers for #test" do
259
+ expect(
260
+ ::Collapsium::Support::Methods.wrappers(tester, :test).size
261
+ ).to eql 1
262
+ end
263
+
264
+ it "defines two wrappers for #calling_test" do
265
+ expect(
266
+ ::Collapsium::Support::Methods.wrappers(tester, :calling_test).size
267
+ ).to eql 1
268
+ end
181
269
  end
182
270
 
183
271
  context PrependNested do
@@ -190,6 +278,18 @@ describe ::Collapsium::Support::Methods do
190
278
  it "wraps results appropriately for calling methods" do
191
279
  expect(tester.calling_test).to eql 'include_module: nest_module'
192
280
  end
281
+
282
+ it "defines two wrappers for #test" do
283
+ expect(
284
+ ::Collapsium::Support::Methods.wrappers(tester, :test).size
285
+ ).to eql 1
286
+ end
287
+
288
+ it "defines two wrappers for #calling_test" do
289
+ expect(
290
+ ::Collapsium::Support::Methods.wrappers(tester, :calling_test).size
291
+ ).to eql 1
292
+ end
193
293
  end
194
294
 
195
295
  context IncludeNested do
@@ -202,6 +302,18 @@ describe ::Collapsium::Support::Methods do
202
302
  it "wraps results appropriately for calling methods" do
203
303
  expect(tester.calling_test).to eql 'include_module: nest_module'
204
304
  end
305
+
306
+ it "defines two wrappers for #test" do
307
+ expect(
308
+ ::Collapsium::Support::Methods.wrappers(tester, :test).size
309
+ ).to eql 1
310
+ end
311
+
312
+ it "defines two wrappers for #calling_test" do
313
+ expect(
314
+ ::Collapsium::Support::Methods.wrappers(tester, :calling_test).size
315
+ ).to eql 1
316
+ end
205
317
  end
206
318
 
207
319
  context IncludeNestedWithOwn do
@@ -214,6 +326,18 @@ describe ::Collapsium::Support::Methods do
214
326
  it "uses only the own class result for calling methods" do
215
327
  expect(tester.calling_test).to eql 'include_nested_with_own'
216
328
  end
329
+
330
+ it "defines two wrappers for #test" do
331
+ expect(
332
+ ::Collapsium::Support::Methods.wrappers(tester, :test).size
333
+ ).to eql 1
334
+ end
335
+
336
+ it "defines two wrappers for #calling_test" do
337
+ expect(
338
+ ::Collapsium::Support::Methods.wrappers(tester, :calling_test).size
339
+ ).to eql 1
340
+ end
217
341
  end
218
342
 
219
343
  context PrependNestedWithOwn do
@@ -226,6 +350,124 @@ describe ::Collapsium::Support::Methods do
226
350
  it "ignores class methods for calling methods" do
227
351
  expect(tester.calling_test).to eql 'include_module: nest_module'
228
352
  end
353
+
354
+ it "defines two wrappers for #test" do
355
+ expect(
356
+ ::Collapsium::Support::Methods.wrappers(tester, :test).size
357
+ ).to eql 1
358
+ end
359
+
360
+ it "defines two wrappers for #calling_test" do
361
+ expect(
362
+ ::Collapsium::Support::Methods.wrappers(tester, :calling_test).size
363
+ ).to eql 1
364
+ end
365
+ end
366
+
367
+ context "failing" do
368
+ context "classes" do
369
+ it "fails if there is no method to wrap" do
370
+ expect do
371
+ class NoMethodToWrap1
372
+ prepend First
373
+ end
374
+ end.to raise_error(NameError)
375
+ end
376
+
377
+ it "fails silently if asked not to raise" do
378
+ expect do
379
+ class NoMethodToWrap2
380
+ prepend NonRaising
381
+ end
382
+ end.not_to raise_error
383
+ end
384
+ end
385
+
386
+ context "objects" do
387
+ it "fails if there is no method to wrap" do
388
+ expect do
389
+ tester = EmptyClass.new
390
+ tester.extend(First)
391
+ end.to raise_error(NameError)
392
+ end
393
+
394
+ it "fails silently if asked not to raise" do
395
+ expect do
396
+ tester = EmptyClass.new
397
+ tester.extend(NonRaising)
398
+ end.not_to raise_error
399
+ end
400
+ end
401
+ end
402
+ end
403
+
404
+ context "#wrappers" do
405
+ it "finds wrappers in FirstThenSecond" do
406
+ expect(
407
+ ::Collapsium::Support::Methods.wrappers(FirstThenSecond, :test).size
408
+ ).to eql 2
409
+ end
410
+
411
+ it "finds wrappers in SecondThenFirst" do
412
+ expect(
413
+ ::Collapsium::Support::Methods.wrappers(SecondThenFirst, :test).size
414
+ ).to eql 2
415
+ end
416
+
417
+ it "finds wrappers in Included" do
418
+ expect(
419
+ ::Collapsium::Support::Methods.wrappers(Included, :test).size
420
+ ).to eql 1
421
+ end
422
+
423
+ it "finds wrappers in NestModule" do
424
+ expect(
425
+ ::Collapsium::Support::Methods.wrappers(NestModule, :test).size
426
+ ).to eql 1
427
+ end
428
+
429
+ it "finds wrappers in PrependNested" do
430
+ expect(
431
+ ::Collapsium::Support::Methods.wrappers(PrependNested, :test).size
432
+ ).to eql 1
433
+ end
434
+
435
+ it "finds wrappers in IncludeNested" do
436
+ expect(
437
+ ::Collapsium::Support::Methods.wrappers(IncludeNested, :test).size
438
+ ).to eql 1
439
+ end
440
+
441
+ it "finds wrappers in PrependNestedWithOwn" do
442
+ expect(
443
+ ::Collapsium::Support::Methods.wrappers(PrependNestedWithOwn, :test).size
444
+ ).to eql 1
445
+ end
446
+
447
+ it "finds wrappers in IncludeNestedWithOwn" do
448
+ expect(
449
+ ::Collapsium::Support::Methods.wrappers(IncludeNestedWithOwn, :test).size
450
+ ).to eql 1
451
+ end
452
+
453
+ it "does not find wrappers on undecorated Hashes" do
454
+ expect(::Collapsium::Support::Methods.wrappers({}, :test)).to be_empty
455
+ end
456
+ end
457
+
458
+ context "loop detection" do
459
+ class LoopClass
460
+ def loop
461
+ return "loop"
462
+ end
463
+ end
464
+
465
+ it "prevents loops" do
466
+ tester = LoopClass.new
467
+ tester.extend(Looping)
468
+
469
+ expect { tester.loop }.not_to raise_error
470
+ expect(tester.loop).to eql "loop"
229
471
  end
230
472
  end
231
473
  end