collapsium 0.4.1 → 0.5.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.
@@ -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