hosts 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.
@@ -0,0 +1,290 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright Alexander E. Fischer <aef@raxys.net>, 2012
4
+
5
+ This file is part of Hosts.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ require 'spec_helper'
21
+
22
+ describe Aef::Hosts::File do
23
+ let(:hosts_file) { fixtures_dir + 'linux_hosts' }
24
+ let(:file) { described_class.new }
25
+
26
+ describe "path attribute" do
27
+ it "should exist" do
28
+ file.should respond_to(:path)
29
+ end
30
+
31
+ it "should be nil by default" do
32
+ file.path.should be_nil
33
+ end
34
+
35
+ it "should be settable" do
36
+ file.should respond_to(:path=)
37
+
38
+ file.path = Pathname.pwd
39
+
40
+ file.path.should == Pathname.pwd
41
+ end
42
+
43
+ it "should automatically convert strings to Pathname objects when set" do
44
+ file.path = Dir.pwd
45
+
46
+ file.path.should == Pathname.pwd
47
+ end
48
+
49
+ it "should be settable through the constructor" do
50
+ file = Aef::Hosts::File.new(Pathname.pwd)
51
+
52
+ file.path.should == Pathname.pwd
53
+ end
54
+ end
55
+
56
+ describe "reading from file" do
57
+ before(:each) do
58
+ file.path = hosts_file
59
+ end
60
+
61
+ it "should be possible through the .read method" do
62
+ file = Aef::Hosts::File.read(hosts_file)
63
+
64
+ file.elements.should_not be_empty
65
+ file.path.should eql Pathname.new(hosts_file)
66
+ end
67
+
68
+ it "should be possible through the #read method" do
69
+ file.should respond_to(:read)
70
+
71
+ expect {
72
+ file.read.should == file
73
+ }.to change{ file.elements.length }
74
+ end
75
+
76
+ it "should allow the path attribute to be temporarily overridden" do
77
+ file.path = nil
78
+
79
+ return_value = file.read(hosts_file)
80
+ return_value.should == file
81
+ end
82
+ end
83
+
84
+ describe "parsing" do
85
+ before(:each) do
86
+ file.path = hosts_file
87
+ end
88
+
89
+ it "should be possible through the .parse method" do
90
+ file = Aef::Hosts::File.parse(hosts_file.read)
91
+
92
+ file.elements.should_not be_empty
93
+ end
94
+
95
+ it "should be possible through the #parse method" do
96
+ file.should respond_to(:parse)
97
+
98
+ expect {
99
+ file.parse(hosts_file.read).should == file
100
+ }.to change{ file.elements.length }
101
+ end
102
+
103
+ it "should be able to parse an empty element" do
104
+ file.parse(<<-HOSTS)
105
+
106
+ HOSTS
107
+
108
+ file.elements.should have(1).item
109
+ file.elements.first.should be_a(Aef::Hosts::EmptyElement)
110
+ file.elements.first.cache_filled?.should be_true
111
+ end
112
+
113
+ it "should be able to parse a comment" do
114
+ file.parse(<<-HOSTS)
115
+ # test
116
+ HOSTS
117
+
118
+ file.elements.should have(1).item
119
+ element = file.elements.first
120
+ element.should be_a(Aef::Hosts::Comment)
121
+ element.cache_filled?.should be_true
122
+ element.comment.should eql ' test'
123
+ end
124
+
125
+ it "should be able to parse an entry" do
126
+ file.parse(<<-HOSTS)
127
+ 10.23.5.17 myhost myhost.mydomain altname # entry
128
+ HOSTS
129
+
130
+ file.elements.should have(1).item
131
+ element = file.elements.first
132
+ element.should be_a(Aef::Hosts::Entry)
133
+ element.cache_filled?.should be_true
134
+ element.address.should eql '10.23.5.17'
135
+ element.name.should eql 'myhost'
136
+ element.aliases.should eql ['myhost.mydomain', 'altname']
137
+ element.comment.should eql ' entry'
138
+ end
139
+
140
+ it "should be able to parse a section" do
141
+ file.parse(<<-HOSTS)
142
+ # -----BEGIN SECTION test-----
143
+ # -----END SECTION test-----
144
+ HOSTS
145
+
146
+ file.elements.should have(1).item
147
+ element = file.elements.first
148
+ element.should be_a(Aef::Hosts::Section)
149
+ element.cache_filled?.should be_true
150
+ element.name.should eql 'test'
151
+ end
152
+
153
+ it "should complain about nested sections" do
154
+ hosts = <<-HOSTS
155
+ # -----BEGIN SECTION outer-----
156
+ # -----BEGIN SECTION inner-----
157
+ # -----END SECTION inner-----
158
+ # -----END SECTION outer-----
159
+ HOSTS
160
+
161
+ expect {
162
+ file.parse(hosts)
163
+ }.to raise_error(Aef::Hosts::ParserError, "Invalid cascading of sections. Cannot start new section 'inner' without first closing section 'outer' on line 2.")
164
+ end
165
+
166
+ it "should complain about closing the wrong section" do
167
+ hosts = <<-HOSTS
168
+ # -----BEGIN SECTION correct-----
169
+ # -----END SECTION incorrect-----
170
+ HOSTS
171
+
172
+ expect {
173
+ file.parse(hosts)
174
+ }.to raise_error(Aef::Hosts::ParserError, "Invalid closing of section. Found attempt to close section 'incorrect' in body of section 'correct' on line 2.")
175
+ end
176
+ end
177
+
178
+ context "#invalidate_cache!" do
179
+ it "should clear the cache of the file's elements" do
180
+ child_a = 'markov'
181
+ child_b = 'chainey'
182
+
183
+ child_a.should_receive(:invalidate_cache!)
184
+ child_b.should_receive(:invalidate_cache!)
185
+
186
+ file.elements << child_a
187
+ file.elements << child_b
188
+
189
+ file.invalidate_cache!
190
+ end
191
+ end
192
+
193
+ context "#inspect" do
194
+ it "should be able to generate a debugging String" do
195
+ file.inspect.should eql %{#<Aef::Hosts::File: path=nil elements=[]>}
196
+ end
197
+
198
+ it "should be able to generate a debugging String with path" do
199
+ file.path = hosts_file
200
+ file.inspect.should eql %{#<Aef::Hosts::File: path=#<Pathname:#{hosts_file}> elements=[]>}
201
+ end
202
+
203
+ it "should be able to generate a debugging String with cascaded elements" do
204
+ section = Aef::Hosts::Section.new('new section', :elements => [
205
+ 'fnord',
206
+ 'eris'
207
+ ])
208
+
209
+ file.elements << section
210
+ file.elements << 'hagbard'
211
+
212
+ file.inspect.should eql <<-STRING.chomp
213
+ #<Aef::Hosts::File: path=nil elements=[
214
+ #<Aef::Hosts::Section: name="new section" elements=[
215
+ "fnord",
216
+ "eris"
217
+ ]>,
218
+ "hagbard"
219
+ ]>
220
+ STRING
221
+ end
222
+
223
+ it "should be able to generate a debugging String with elements" do
224
+ file.elements << 'fnord'
225
+ file.elements << 'eris'
226
+ file.inspect.should eql <<-STRING.chomp
227
+ #<Aef::Hosts::File: path=nil elements=[
228
+ "fnord",
229
+ "eris"
230
+ ]>
231
+ STRING
232
+ end
233
+ end
234
+
235
+ describe "string generation" do
236
+ it "should produce unchanged output if nothing changed" do
237
+ file.path = hosts_file
238
+ file.read
239
+
240
+ file.to_s.should == hosts_file.read
241
+ end
242
+
243
+ it "should be able to re-encode the linebreaks of generated output" do
244
+ hosts_file = fixtures_dir + 'windows_hosts'
245
+ file.path = hosts_file
246
+ file.read
247
+
248
+ original_document = hosts_file.read
249
+
250
+ file.to_s.should_not == original_document
251
+ file.to_s(:linebreak_encoding => :windows).should == original_document
252
+ end
253
+ end
254
+
255
+ describe "writing to file" do
256
+ before(:each) do
257
+ @dir = create_temp_dir
258
+ end
259
+
260
+ after(:each) do
261
+ @dir.rmtree
262
+ end
263
+
264
+ it "should be possible through the #write method" do
265
+ file.should respond_to(:write)
266
+
267
+ result_file = @dir + 'hosts'
268
+ data = hosts_file.read
269
+
270
+ file.parse(data)
271
+ file.path = result_file
272
+ file.write
273
+
274
+ result_file.read.should == data
275
+ end
276
+
277
+ it "should allow the path attribute to be temporarily overridden" do
278
+ result_file = @dir + 'hosts'
279
+ wrong_file = @dir + 'wrong_file'
280
+ data = hosts_file.read
281
+
282
+ file.parse(data)
283
+ file.path = wrong_file
284
+
285
+ file.write(:path => result_file)
286
+
287
+ result_file.read.should == data
288
+ end
289
+ end
290
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+
4
+ describe Aef::Hosts::Helpers do
5
+ include Aef::Hosts::Helpers
6
+
7
+ let(:model) {
8
+ model = OpenStruct.new(
9
+ :name => 'test',
10
+ :cache_filled => true,
11
+ :elements => ['fnord', 'eris']
12
+ )
13
+
14
+ def model.cache_filled?
15
+ cache_filled
16
+ end
17
+
18
+ model
19
+ }
20
+
21
+ context "#validate_options" do
22
+ it "should accept options with a subset of valid option keys" do
23
+ options = {:valid1 => 123, :valid2 => 456}
24
+
25
+ expect {
26
+ validate_options(options, :valid1, :valid2, :valid3)
27
+ }.not_to raise_error
28
+ end
29
+
30
+ it "should accept options with exactly the valid option keys" do
31
+ options = {:valid1 => 123, :valid2 => 456, :valid3 => 789}
32
+
33
+ expect {
34
+ validate_options(options, :valid1, :valid2, :valid3)
35
+ }.not_to raise_error
36
+ end
37
+
38
+ it "should deny options with invalid option keys" do
39
+ options = {:valid1 => 123, :invalid => 'test'}
40
+
41
+ expect {
42
+ validate_options(options, :valid1, :valid2, :valid3)
43
+ }.to raise_error(ArgumentError, 'Invalid option keys: :invalid')
44
+ end
45
+ end
46
+
47
+ context "#to_pathname" do
48
+ it "should return nil when nil is given" do
49
+ to_pathname(nil).should be_nil
50
+ end
51
+
52
+ it "should return a Pathname when a String is given" do
53
+ to_pathname('abc/def').should eql Pathname.new('abc/def')
54
+ end
55
+
56
+ it "should return a Pathname when a Pathname is given" do
57
+ to_pathname(Pathname.new('abc/def')).should eql Pathname.new('abc/def')
58
+ end
59
+ end
60
+
61
+ context "generate_inspect" do
62
+ it "should be able to render a minimal inspect output" do
63
+ generate_inspect(model).should eql %{#<OpenStruct>}
64
+ end
65
+
66
+ it "should be able to render a normal attribute in inspect output" do
67
+ generate_inspect(model, :name).should eql %{#<OpenStruct: name="test">}
68
+ end
69
+
70
+ it "should be able to render an attribute by custom String in inspect output" do
71
+ generate_inspect(model, 'abc=123').should eql %{#<OpenStruct: abc=123>}
72
+ end
73
+
74
+ it "should be able to render a special cache attribute in inspect output" do
75
+ generate_inspect(model, :cache).should eql %{#<OpenStruct: cached!>}
76
+ end
77
+
78
+ it "should not render a special cache attribute in inspect output if cache is not filled" do
79
+ model.cache_filled = false
80
+ generate_inspect(model, :cache).should eql %{#<OpenStruct>}
81
+ end
82
+
83
+ it "should be able to render elements in inspect output" do
84
+ generate_inspect(model, :elements).should eql <<-STRING.chomp
85
+ #<OpenStruct: elements=[
86
+ "fnord",
87
+ "eris"
88
+ ]>
89
+ STRING
90
+ end
91
+ end
92
+
93
+ context "generate_elements_partial" do
94
+ it "should be able to render elements in inspect output" do
95
+ generate_elements_partial(['fnord', 'eris']).should eql <<-STRING.chomp
96
+ elements=[
97
+ "fnord",
98
+ "eris"
99
+ ]
100
+ STRING
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,299 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright Alexander E. Fischer <aef@raxys.net>, 2012
4
+
5
+ This file is part of Hosts.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ require 'spec_helper'
21
+
22
+ describe Aef::Hosts::Section do
23
+ let(:element) { described_class.new('some name') }
24
+
25
+ describe "name attribute" do
26
+ it "should exist" do
27
+ element.should respond_to(:name)
28
+ end
29
+
30
+ it "should be settable" do
31
+ element.should respond_to(:name=)
32
+
33
+ element.name = 'demo section'
34
+
35
+ element.name.should == 'demo section'
36
+ end
37
+
38
+ it "should be settable through the constructor" do
39
+ element = Aef::Hosts::Section.new('demo section')
40
+
41
+ element.name.should == 'demo section'
42
+ end
43
+
44
+ it "should be a mandatory argument of the constructor" do
45
+ expect {
46
+ element = Aef::Hosts::Section.new(nil)
47
+ }.to raise_error(ArgumentError)
48
+ end
49
+
50
+ it "should flag the object as changed if modified" do
51
+ element = Aef::Hosts::Section.new('some name', :cache => {
52
+ :header => "# -----BEGIN SECTION some name----- \n",
53
+ :footer => "# -----END SECTION some name----- \n"
54
+ })
55
+
56
+ expect {
57
+ element.name = 'demo section'
58
+ }.to change{ element.cache_filled? }.from(true).to(false)
59
+ end
60
+ end
61
+
62
+ describe "elements attribute" do
63
+ it "should exist" do
64
+ element.should respond_to(:elements)
65
+ end
66
+
67
+ it "should be settable" do
68
+ element.should respond_to(:elements=)
69
+
70
+ element.elements = ['abc', 'def']
71
+
72
+ element.elements.should eql ['abc', 'def']
73
+ end
74
+
75
+ it "should be settable through the constructor" do
76
+ element = Aef::Hosts::Section.new('name', :elements => ['abc', 'def'])
77
+
78
+ element.elements.should eql ['abc', 'def']
79
+ end
80
+
81
+ it "should flag the object as changed if modified" do
82
+ child_element = 'abc'
83
+ element = Aef::Hosts::Section.new('some name', :cache => {
84
+ :header => "# -----BEGIN SECTION some name----- \n",
85
+ :footer => "# -----END SECTION some name----- \n"
86
+ }, :elements => [child_element])
87
+
88
+ child_element.should_not_receive(:invalidate_cache!)
89
+
90
+ expect {
91
+ element.elements = [child_element, 'def']
92
+ }.to change{ element.cache_filled? }.from(true).to(false)
93
+ end
94
+ end
95
+
96
+ describe "cache attribute" do
97
+ it "should exist" do
98
+ element.should respond_to(:cache)
99
+ end
100
+
101
+ it "should be nil by default" do
102
+ element.cache.should == {:footer => nil, :header => nil}
103
+ end
104
+
105
+ it "should not be allowed to be set" do
106
+ element.should_not respond_to(:cache=)
107
+ end
108
+
109
+ it "should be settable through the constructor" do
110
+ element = Aef::Hosts::Section.new('demo section', :cache => {
111
+ :header => "# -----BEGIN SECTION demo section----- \n",
112
+ :footer => "# -----END SECTION demo section-----\t\t\t\n"
113
+ })
114
+
115
+ element.cache.should == {
116
+ :header => "# -----BEGIN SECTION demo section----- \n",
117
+ :footer => "# -----END SECTION demo section-----\t\t\t\n"
118
+ }
119
+ end
120
+
121
+ it "should be correctly reported as empty by cache_filled?" do
122
+ element.should respond_to(:cache_filled?)
123
+
124
+ element.cache_filled?.should be_false
125
+ end
126
+
127
+ it "should be correctly reported as filled by cache_filled?" do
128
+ element = Aef::Hosts::Section.new('demo section', :cache => {
129
+ :header => "# -----BEGIN SECTION demo section----- \n",
130
+ :footer => "# -----END SECTION demo section-----\t\t\t\n"
131
+ })
132
+
133
+ element.should respond_to(:cache_filled?)
134
+
135
+ element.cache_filled?.should be_true
136
+ end
137
+
138
+ it "should be invalidatable" do
139
+ element = Aef::Hosts::Section.new('demo section', :cache => {
140
+ :header => "# -----BEGIN SECTION demo section----- \n",
141
+ :footer => "# -----END SECTION demo section-----\t\t\t\n"
142
+ })
143
+
144
+ element.invalidate_cache!
145
+
146
+ element.cache[:header].should be_nil
147
+ element.cache[:footer].should be_nil
148
+ end
149
+ end
150
+
151
+ context "#invalidate_cache!" do
152
+ it "should clear the cache of the section and its elements" do
153
+ child_a = 'markov'
154
+ child_b = 'chainey'
155
+
156
+ child_a.should_receive(:invalidate_cache!)
157
+ child_b.should_receive(:invalidate_cache!)
158
+
159
+ element.elements << child_a
160
+ element.elements << child_b
161
+
162
+ element.invalidate_cache!
163
+ end
164
+
165
+ it "should clear the cache of the section only if :only_section is set" do
166
+ child_a = 'markov'
167
+ child_b = 'chainey'
168
+
169
+ child_a.should_not_receive(:invalidate_cache!)
170
+ child_b.should_not_receive(:invalidate_cache!)
171
+
172
+ element.elements << child_a
173
+ element.elements << child_b
174
+
175
+ element.invalidate_cache!(:only_section => true)
176
+ end
177
+ end
178
+
179
+ context "#inspect" do
180
+ it "should be able to generate a debugging String" do
181
+ element.inspect.should eql %{#<Aef::Hosts::Section: name="some name" elements=[]>}
182
+ end
183
+
184
+ it "should be able to generate a debugging String with elements" do
185
+ element.elements << 'fnord'
186
+ element.elements << 'eris'
187
+ element.inspect.should eql <<-STRING.chomp
188
+ #<Aef::Hosts::Section: name="some name" elements=[
189
+ "fnord",
190
+ "eris"
191
+ ]>
192
+ STRING
193
+ end
194
+
195
+ it "should be able to generate a debugging String with cache filled" do
196
+ element = Aef::Hosts::Section.new('demo section', :cache => {:header => 'abc', :footer => 'xyz'})
197
+ element.inspect.should eql %{#<Aef::Hosts::Section: name="demo section" cached! elements=[]>}
198
+ end
199
+ end
200
+
201
+ describe "string generation" do
202
+ before(:all) do
203
+ class Aef::Hosts::ChildElement < Aef::Hosts::Element
204
+ def generate_string(options = {})
205
+ "uncached element representation\n"
206
+ end
207
+
208
+ def cache=(cache)
209
+ @cache = cache
210
+ end
211
+ end
212
+ end
213
+
214
+ it "should generate a new representation if no cache is available" do
215
+ element = Aef::Hosts::Section.new('demo section')
216
+
217
+ element.to_s.should == <<-EOS
218
+ # -----BEGIN SECTION demo section-----
219
+ # -----END SECTION demo section-----
220
+ EOS
221
+ end
222
+
223
+ it "should respond with a duplicate of the cached representation if cache is filled" do
224
+ element = Aef::Hosts::Section.new('demo section', :cache => {
225
+ :header => "# -----BEGIN SECTION demo section----- \n",
226
+ :footer => "# -----END SECTION demo section----- \n"
227
+ })
228
+
229
+ element.to_s.should == <<-EOS
230
+ # -----BEGIN SECTION demo section-----
231
+ # -----END SECTION demo section-----
232
+ EOS
233
+
234
+ # Should not be identical
235
+ element.to_s.should_not equal(element.cache[:footer])
236
+ element.to_s.should_not equal(element.cache[:header])
237
+ end
238
+
239
+ it "should respond with a duplicate of the cached representation if cache is filled" do
240
+ element = Aef::Hosts::Section.new('demo section', :cache => {
241
+ :header => "# -----BEGIN SECTION demo section----- \n",
242
+ :footer => "# -----END SECTION demo section----- \n"
243
+ })
244
+
245
+ element.to_s(:force_generation => true).should == <<-EOS
246
+ # -----BEGIN SECTION demo section-----
247
+ # -----END SECTION demo section-----
248
+ EOS
249
+ end
250
+
251
+ it "should correctly display including elements" do
252
+ sub_element_without_cache = Aef::Hosts::ChildElement.new
253
+ sub_element_with_cache = Aef::Hosts::ChildElement.new
254
+ sub_element_with_cache.cache = "cached element representation\n"
255
+
256
+ element = Aef::Hosts::Section.new('demo section',
257
+ :elements => [
258
+ sub_element_with_cache,
259
+ sub_element_without_cache
260
+ ],
261
+ :cache => {
262
+ :header => "# -----BEGIN SECTION demo section----- \n",
263
+ :footer => "# -----END SECTION demo section----- \n"
264
+ }
265
+ )
266
+
267
+ element.to_s.should == <<-EOS
268
+ # -----BEGIN SECTION demo section-----
269
+ cached element representation
270
+ uncached element representation
271
+ # -----END SECTION demo section-----
272
+ EOS
273
+ end
274
+
275
+ it "should correctly display including elements with forced generation" do
276
+ sub_element_without_cache = Aef::Hosts::ChildElement.new
277
+ sub_element_with_cache = Aef::Hosts::ChildElement.new
278
+ sub_element_with_cache.cache = "cached element representation\n"
279
+
280
+ element = Aef::Hosts::Section.new('demo section',
281
+ :elements => [
282
+ sub_element_with_cache,
283
+ sub_element_without_cache
284
+ ],
285
+ :cache => {
286
+ :header => "# -----BEGIN SECTION demo section----- \n",
287
+ :footer => "# -----END SECTION demo section----- \n"
288
+ }
289
+ )
290
+
291
+ element.to_s(:force_generation => true).should == <<-EOS
292
+ # -----BEGIN SECTION demo section-----
293
+ uncached element representation
294
+ uncached element representation
295
+ # -----END SECTION demo section-----
296
+ EOS
297
+ end
298
+ end
299
+ end