collapsium 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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