collapsium 0.3.0 → 0.4.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.
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+ require_relative '../lib/collapsium/environment_override'
3
+ require_relative '../lib/collapsium/pathed_access'
4
+
5
+ class EnvironmentHash < Hash
6
+ # We need only one of these; this is for coverage mostly
7
+ prepend ::Collapsium::EnvironmentOverride
8
+ include ::Collapsium::EnvironmentOverride
9
+ end
10
+
11
+ describe ::Collapsium::EnvironmentOverride do
12
+ before :each do
13
+ @tester = { "foo" => { "bar" => 42 } }
14
+ @tester.extend(::Collapsium::EnvironmentOverride)
15
+ ENV.delete("FOO")
16
+ ENV.delete("BAR")
17
+ end
18
+
19
+ context "environment variable name" do
20
+ it "upcases keys" do
21
+ expect(@tester.key_to_env("foo")).to eql "FOO"
22
+ end
23
+
24
+ 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"
28
+ end
29
+
30
+ it "collapses multiple underscores into one" do
31
+ expect(@tester.key_to_env("foo!_@bar")).to eql "FOO_BAR"
32
+ end
33
+
34
+ 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"
37
+ end
38
+ end
39
+
40
+ context "without pathed access" do
41
+ it "overrides first-order keys" do
42
+ expect(@tester["foo"].is_a?(Hash)).to be_truthy
43
+ ENV["FOO"] = "test"
44
+ expect(@tester["foo"].is_a?(Hash)).to be_falsy
45
+ expect(@tester["foo"]).to eql "test"
46
+ end
47
+
48
+ it "inherits environment override" do
49
+ expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_truthy
50
+ ENV["BAR"] = "test"
51
+ expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_falsy
52
+ expect(@tester["foo"]["bar"]).to eql "test"
53
+ end
54
+
55
+ it "write still works" do
56
+ @tester.store("foo", 42)
57
+ expect(@tester["foo"]).to eql 42
58
+ end
59
+ end
60
+
61
+ context "with pathed access" do
62
+ before :each do
63
+ @tester = { "foo" => { "bar" => 42 } }
64
+ @tester.extend(::Collapsium::EnvironmentOverride)
65
+ @tester.extend(::Collapsium::PathedAccess)
66
+ ENV["FOO"] = nil
67
+ ENV["BAR"] = nil
68
+ ENV["FOO_BAR"] = nil
69
+ end
70
+
71
+ it "overrides first-order keys" do
72
+ expect(@tester["foo"].is_a?(Hash)).to be_truthy
73
+ ENV["FOO"] = "test"
74
+ expect(@tester["foo"].is_a?(Hash)).to be_falsy
75
+ expect(@tester["foo"]).to eql "test"
76
+ end
77
+
78
+ it "inherits environment override" do
79
+ expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_truthy
80
+ ENV["BAR"] = "test"
81
+ expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_falsy
82
+ expect(@tester["foo"]["bar"]).to eql "test"
83
+ end
84
+
85
+ it "write still works" do
86
+ @tester.store("foo", 42)
87
+ expect(@tester["foo"]).to eql 42
88
+ end
89
+
90
+ it "overrides from pathed key" do
91
+ expect(@tester["foo.bar"].is_a?(Fixnum)).to be_truthy
92
+ ENV["FOO_BAR"] = "test"
93
+ expect(@tester["foo.bar"].is_a?(Fixnum)).to be_falsy
94
+ expect(@tester["foo.bar"]).to eql "test"
95
+ end
96
+
97
+ it "prefers pathed key over non-pathed key" do
98
+ expect(@tester["foo.bar"].is_a?(Fixnum)).to be_truthy
99
+ ENV["FOO_BAR"] = "pathed"
100
+ ENV["BAR"] = "simple"
101
+ expect(@tester["foo.bar"].is_a?(Fixnum)).to be_falsy
102
+ expect(@tester["foo.bar"]).to eql "pathed"
103
+ end
104
+
105
+ it "prefers pathed key over non-pathed key when using nested values" do
106
+ expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_truthy
107
+ ENV["FOO_BAR"] = "pathed"
108
+ ENV["BAR"] = "simple"
109
+ expect(@tester["foo"]["bar"].is_a?(Fixnum)).to be_falsy
110
+ expect(@tester["foo"]["bar"]).to eql "pathed"
111
+ end
112
+ end
113
+
114
+ context "respects the behaviour of wrapped methods" do
115
+ it "works with :[]" do
116
+ ENV["FOO"] = "test"
117
+ expect(@tester["foo"]).to eql "test"
118
+ end
119
+
120
+ it "works with :fetch" do
121
+ ENV["FOO"] = "test"
122
+ expect(@tester.fetch("foo", 1234)).to eql "test"
123
+ end
124
+
125
+ it "works with :key?" do
126
+ ENV["FOO"] = "test"
127
+ expect(@tester.key?("foo")).to eql true # not be_truthy
128
+ expect(@tester.key?("bar")).to eql false # not be_falsey
129
+ end
130
+ end
131
+
132
+ context EnvironmentHash do
133
+ let(:test_hash) { EnvironmentHash.new }
134
+
135
+ it "works when prepended" do
136
+ ENV["FOO"] = "test"
137
+ expect(test_hash["foo"]).to eql "test"
138
+ end
139
+ end
140
+
141
+ context "JSON" do
142
+ it "interprets JSON content in environment variables" do
143
+ ENV["FOO"] = '{ "json_key": "json_value" }'
144
+ expect(@tester["foo"].is_a?(Hash)).to be_truthy
145
+ expect(@tester["foo"]["json_key"]).to eql "json_value"
146
+ end
147
+ end
148
+
149
+ context "coverage" do
150
+ it "raises when not passing arguments" do
151
+ expect { @tester.fetch }.to raise_error
152
+ end
153
+ end
154
+ end
@@ -1,12 +1,46 @@
1
1
  require 'spec_helper'
2
2
  require_relative '../lib/collapsium/pathed_access'
3
3
 
4
+ class PathedHash < Hash
5
+ prepend ::Collapsium::PathedAccess
6
+ end
7
+
4
8
  describe ::Collapsium::PathedAccess do
5
9
  before :each do
6
10
  @tester = {}
7
11
  @tester.extend(::Collapsium::PathedAccess)
8
12
  end
9
13
 
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
+
10
44
  describe "Hash-like" do
11
45
  it "responds to Hash functions" do
12
46
  [:invert, :delete, :fetch].each do |meth|
@@ -27,6 +61,26 @@ describe ::Collapsium::PathedAccess do
27
61
  end
28
62
 
29
63
  describe "pathed access" do
64
+ context ":path_prefix" do
65
+ it "can be read" do
66
+ expect { @tester.path_prefix }.not_to raise_error
67
+ end
68
+
69
+ it "defaults to empty String" do
70
+ expect(@tester.path_prefix).to be_empty
71
+ expect(@tester.path_prefix.class).to eql String
72
+ end
73
+
74
+ it "can be set" do
75
+ expect { @tester.path_prefix = "foo.bar" }.not_to raise_error
76
+ end
77
+
78
+ it "normalizes when set" do
79
+ @tester.path_prefix = "foo..bar..baz.."
80
+ expect(@tester.path_prefix).to eql ".foo.bar.baz"
81
+ end
82
+ end
83
+
30
84
  it "can recursively read entries via a path" do
31
85
  @tester["foo"] = 42
32
86
  @tester["bar"] = {
@@ -84,6 +138,26 @@ describe ::Collapsium::PathedAccess do
84
138
  end
85
139
  end
86
140
 
141
+ describe "nested inherit capabilities" do
142
+ before do
143
+ @tester['foo'] = {
144
+ 'bar' => {
145
+ 'baz' => 42,
146
+ },
147
+ }
148
+ end
149
+
150
+ it "can still perform pathed access" do
151
+ foo = @tester['foo']
152
+ expect(foo['bar.baz']).to eql 42
153
+ end
154
+
155
+ it "knows its path prefix" do
156
+ bar = @tester['foo.bar']
157
+ expect(bar.path_prefix).to eql '.foo.bar'
158
+ end
159
+ end
160
+
87
161
  describe "with indifferent access" do
88
162
  before do
89
163
  require_relative '../lib/collapsium/indifferent_access'
@@ -104,4 +178,13 @@ describe ::Collapsium::PathedAccess do
104
178
  expect(@tester['foo.baz']).to eql 'quux'
105
179
  end
106
180
  end
181
+
182
+ context PathedHash do
183
+ let(:test_hash) { PathedHash.new }
184
+
185
+ it "can write recursively" do
186
+ test_hash["foo.bar"] = 42
187
+ expect(test_hash["foo.bar"]).to eql 42
188
+ end
189
+ end
107
190
  end
@@ -67,4 +67,21 @@ describe ::Collapsium::RecursiveMerge do
67
67
 
68
68
  expect(x[:bar].length).to eql 2
69
69
  end
70
+
71
+ it "makes nested hashes able to merge recursively" do
72
+ @tester[:foo] = {
73
+ bar: true,
74
+ }
75
+ expect(@tester[:foo].respond_to?(:recursive_merge)).to be_truthy
76
+
77
+ to_merge = {
78
+ foo: {
79
+ bar: false,
80
+ },
81
+ }
82
+ x = @tester.recursive_merge(to_merge)
83
+
84
+ expect(@tester[:foo].respond_to?(:recursive_merge)).to be_truthy
85
+ expect(x[:foo].respond_to?(:recursive_merge)).to be_truthy
86
+ end
70
87
  end
@@ -0,0 +1,231 @@
1
+ require 'spec_helper'
2
+ require_relative '../lib/collapsium/support/methods'
3
+
4
+ module First
5
+ class << self
6
+ include ::Collapsium::Support::Methods
7
+
8
+ def prepended(base)
9
+ wrap_method(base, :calling_test) do |super_method, *args, &block|
10
+ result = super_method.call(*args, &block)
11
+ next "first: #{result}"
12
+ end
13
+
14
+ wrap_method(base, :test) do
15
+ next "first"
16
+ end
17
+ end
18
+ end # class << self
19
+ end # module First
20
+
21
+ module Second
22
+ class << self
23
+ include ::Collapsium::Support::Methods
24
+
25
+ def prepended(base)
26
+ wrap_method(base, :calling_test) do |super_method, *args, &block|
27
+ result = super_method.call(*args, &block)
28
+ next "second: #{result}"
29
+ end
30
+
31
+ wrap_method(base, :test) do
32
+ next "second"
33
+ end
34
+ end
35
+ end # class << self
36
+ end # module First
37
+
38
+ class FirstThenSecond
39
+ def calling_test
40
+ return "first_then_second"
41
+ end
42
+
43
+ def test
44
+ return "first_then_second"
45
+ end
46
+
47
+ prepend First
48
+ prepend Second
49
+ end
50
+
51
+ class SecondThenFirst
52
+ def calling_test
53
+ return "second_then_first"
54
+ end
55
+
56
+ def test
57
+ return "second_then_first"
58
+ end
59
+
60
+ prepend Second
61
+ prepend First
62
+ end
63
+
64
+ module IncludeModule
65
+ class << self
66
+ include ::Collapsium::Support::Methods
67
+
68
+ def included(base)
69
+ wrap_method(base, :calling_test) do |super_method, *args, &block|
70
+ result = super_method.call(*args, &block)
71
+ next "include_module: #{result}"
72
+ end
73
+
74
+ wrap_method(base, :test) do
75
+ next "include_module"
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ class Included
82
+ def calling_test
83
+ return "included"
84
+ end
85
+
86
+ def test
87
+ return "included"
88
+ end
89
+
90
+ include IncludeModule
91
+ end
92
+
93
+ module NestModule
94
+ def calling_test
95
+ return "nest_module"
96
+ end
97
+
98
+ def test
99
+ return "nest_module"
100
+ end
101
+
102
+ include IncludeModule
103
+ end
104
+
105
+ class PrependNested
106
+ prepend NestModule
107
+ end
108
+
109
+ class IncludeNested
110
+ include NestModule
111
+ end
112
+
113
+ class IncludeNestedWithOwn
114
+ def calling_test
115
+ return "include_nested_with_own"
116
+ end
117
+
118
+ def test
119
+ return "include_nested_with_own"
120
+ end
121
+
122
+ include NestModule
123
+ end
124
+
125
+ class PrependNestedWithOwn
126
+ def calling_test
127
+ return "prepend_nested_with_own"
128
+ end
129
+
130
+ def test
131
+ return "prepend_nested_with_own"
132
+ end
133
+
134
+ prepend NestModule
135
+ end
136
+
137
+ describe ::Collapsium::Support::Methods do
138
+ context "#wrap_method" do
139
+ it "fails if there is no method to wrap" do
140
+ expect do
141
+ class NoMethodToWrap
142
+ prepend First
143
+ end
144
+ end.to raise_error(NameError)
145
+ end
146
+
147
+ context FirstThenSecond do
148
+ let(:tester) { FirstThenSecond.new }
149
+
150
+ it "uses only the second module's return value for non-calling methods" do
151
+ expect(tester.test).to eql 'second'
152
+ end
153
+
154
+ it "wraps results appropriately for calling methods" do
155
+ expect(tester.calling_test).to eql 'second: first: first_then_second'
156
+ end
157
+ end
158
+
159
+ context SecondThenFirst do
160
+ let(:tester) { SecondThenFirst.new }
161
+
162
+ it "uses only the second module's return value for non-calling methods" do
163
+ expect(tester.test).to eql 'first'
164
+ end
165
+
166
+ it "wraps results appropriately for calling methods" do
167
+ expect(tester.calling_test).to eql 'first: second: second_then_first'
168
+ end
169
+ end
170
+
171
+ context Included do
172
+ let(:tester) { Included.new }
173
+
174
+ it "uses only the included module's return value for non-calling methods" do
175
+ expect(tester.test).to eql 'include_module'
176
+ end
177
+
178
+ it "wraps results appropriately for calling methods" do
179
+ expect(tester.calling_test).to eql 'include_module: included'
180
+ end
181
+ end
182
+
183
+ context PrependNested do
184
+ let(:tester) { PrependNested.new }
185
+
186
+ it "uses only the included module's return value for non-calling methods" do
187
+ expect(tester.test).to eql 'include_module'
188
+ end
189
+
190
+ it "wraps results appropriately for calling methods" do
191
+ expect(tester.calling_test).to eql 'include_module: nest_module'
192
+ end
193
+ end
194
+
195
+ context IncludeNested do
196
+ let(:tester) { IncludeNested.new }
197
+
198
+ it "uses only the included module's return value for non-calling methods" do
199
+ expect(tester.test).to eql 'include_module'
200
+ end
201
+
202
+ it "wraps results appropriately for calling methods" do
203
+ expect(tester.calling_test).to eql 'include_module: nest_module'
204
+ end
205
+ end
206
+
207
+ context IncludeNestedWithOwn do
208
+ let(:tester) { IncludeNestedWithOwn.new }
209
+
210
+ it "uses only the own class result for non-calling methods" do
211
+ expect(tester.test).to eql 'include_nested_with_own'
212
+ end
213
+
214
+ it "uses only the own class result for calling methods" do
215
+ expect(tester.calling_test).to eql 'include_nested_with_own'
216
+ end
217
+ end
218
+
219
+ context PrependNestedWithOwn do
220
+ let(:tester) { PrependNestedWithOwn.new }
221
+
222
+ it "ignores class methods for non-calling methods" do
223
+ expect(tester.test).to eql 'include_module'
224
+ end
225
+
226
+ it "ignores class methods for calling methods" do
227
+ expect(tester.calling_test).to eql 'include_module: nest_module'
228
+ end
229
+ end
230
+ end
231
+ end