hosts 0.1.0

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