ghost 0.3.0 → 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/LICENSE +1 -1
  2. data/bin/ghost +2 -130
  3. data/lib/ghost.rb +3 -16
  4. data/lib/ghost/cli.rb +60 -0
  5. data/lib/ghost/cli/task.rb +71 -0
  6. data/lib/ghost/cli/task/add.rb +25 -0
  7. data/lib/ghost/cli/task/delete.rb +30 -0
  8. data/lib/ghost/cli/task/empty.rb +18 -0
  9. data/lib/ghost/cli/task/export.rb +19 -0
  10. data/lib/ghost/cli/task/help.rb +41 -0
  11. data/lib/ghost/cli/task/import.rb +25 -0
  12. data/lib/ghost/cli/task/list.rb +40 -0
  13. data/lib/ghost/host.rb +34 -0
  14. data/lib/ghost/store.rb +12 -0
  15. data/lib/ghost/store/dscl_store.rb +71 -0
  16. data/lib/ghost/store/hosts_file_store.rb +123 -0
  17. data/lib/ghost/tokenized_file.rb +65 -0
  18. data/lib/ghost/version.rb +3 -0
  19. data/spec/ghost/cli/task/add_spec.rb +80 -0
  20. data/spec/ghost/cli/task/delete_spec.rb +20 -0
  21. data/spec/ghost/cli/task/empty_spec.rb +19 -0
  22. data/spec/ghost/cli/task/export_spec.rb +16 -0
  23. data/spec/ghost/cli/task/help_spec.rb +36 -0
  24. data/spec/ghost/cli/task/import_spec.rb +56 -0
  25. data/spec/ghost/cli/task/list_spec.rb +50 -0
  26. data/spec/ghost/cli_spec.rb +22 -0
  27. data/spec/ghost/host_spec.rb +36 -0
  28. data/spec/ghost/store/dscl_store_spec.rb +153 -0
  29. data/spec/ghost/store/hosts_file_store_spec.rb +316 -0
  30. data/spec/ghost/store_spec.rb +2 -0
  31. data/spec/ghost/tokenized_file_spec.rb +131 -0
  32. data/spec/spec_helper.rb +4 -2
  33. data/spec/support/cli.rb +29 -0
  34. data/spec/support/resolv.rb +15 -0
  35. metadata +91 -27
  36. data/Rakefile +0 -28
  37. data/TODO +0 -0
  38. data/bin/ghost-ssh +0 -132
  39. data/lib/ghost/linux-host.rb +0 -158
  40. data/lib/ghost/mac-host.rb +0 -116
  41. data/lib/ghost/ssh_config.rb +0 -110
  42. data/spec/etc_hosts_spec.rb +0 -190
  43. data/spec/ghost_spec.rb +0 -151
  44. data/spec/spec.opts +0 -1
  45. data/spec/ssh_config_spec.rb +0 -80
  46. data/spec/ssh_config_template +0 -11
@@ -0,0 +1,19 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../../spec_helper.rb")
2
+ require 'ghost/cli'
3
+
4
+ describe Ghost::Cli, :type => :cli do
5
+ describe "empty" do
6
+ before do
7
+ store.add(Ghost::Host.new("xyz", "127.0.0.1"))
8
+ end
9
+
10
+ it 'empties the list of hosts' do
11
+ ghost("empty")
12
+ store.all.should be_empty
13
+ end
14
+
15
+ it 'outputs a summary of the operation' do
16
+ ghost("empty").should == "[Emptying] Done.\n"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../../spec_helper.rb")
2
+ require 'ghost/cli'
3
+
4
+ describe Ghost::Cli, :type => :cli do
5
+ describe "export" do
6
+ it "outputs all hosts one-per-line in hosts file format" do
7
+ store.add Ghost::Host.new("gist.github.com", "10.0.0.1")
8
+ store.add Ghost::Host.new("google.com", "192.168.1.10")
9
+
10
+ ghost("export").should == <<-EOE.unindent
11
+ 10.0.0.1 gist.github.com
12
+ 192.168.1.10 google.com
13
+ EOE
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../../spec_helper.rb")
2
+ require 'ghost/cli'
3
+
4
+ describe Ghost::Cli, :type => :cli do
5
+ describe "help" do
6
+ let(:overview) do
7
+ <<-EOF.unindent
8
+ USAGE: ghost <task> [<args>]
9
+
10
+ The ghost tasks are:
11
+ add Add a host
12
+ delete Remove a ghost-managed host
13
+ empty Clear all ghost-managed hosts
14
+ export Export all hosts in /etc/hosts format
15
+ import Import hosts in /etc/hosts format
16
+ list Show all (or a filtered) list of hosts
17
+
18
+ See 'ghost help <task>' for more information on a specific task.
19
+ EOF
20
+ end
21
+
22
+ it 'displays help overview when called with no args' do
23
+ ghost("").should == overview
24
+ end
25
+
26
+ it 'displays help overview when help task is called with no arguments' do
27
+ ghost("help").should == overview
28
+ end
29
+
30
+ context "when no help text for a given topic is available" do
31
+ it "prints out a message" do
32
+ ghost("help missing").should == "No help for task 'missing'\n"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,56 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../../spec_helper.rb")
2
+ require 'ghost/cli'
3
+
4
+ describe Ghost::Cli, :type => :cli do
5
+ describe "import" do
6
+ context "with current export format" do
7
+ let(:import) do
8
+ <<-EOI.unindent
9
+ 1.2.3.4 foo.com
10
+ 2.3.4.5 bar.com
11
+ EOI
12
+ end
13
+
14
+ let(:foo_com) { Ghost::Host.new('foo.com', '1.2.3.4') }
15
+ let(:bar_com) { Ghost::Host.new('bar.com', '2.3.4.5') }
16
+
17
+ context 'with no file name'
18
+ context 'with STDIN pseudo file name (-)'
19
+
20
+ context 'with a file name' do
21
+ it 'adds each entry' do
22
+ file = Tempfile.new('import')
23
+ file.write(import)
24
+ file.close
25
+
26
+ ghost("import #{file.path}")
27
+
28
+ store.all.should include(foo_com)
29
+ store.all.should include(bar_com)
30
+ end
31
+ end
32
+
33
+ context 'with multiple file names'
34
+
35
+ context 'when an entry is already present' do
36
+ context 'without the -f flag' do
37
+ it 'skips the existing entries'
38
+ it 'prints a warning about skipped entries'
39
+ end
40
+
41
+ context 'with the -f flag' do
42
+ it 'overwrites the existing entries'
43
+ end
44
+ end
45
+
46
+ context 'with multiple hosts per line' do
47
+ let(:import) do
48
+ <<-EOI.unindent
49
+ 1.2.3.4 foo.com
50
+ 2.3.4.5 bar.com subdomain.bar.com
51
+ EOI
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../../spec_helper.rb")
2
+ require 'ghost/cli'
3
+
4
+ describe Ghost::Cli, :type => :cli do
5
+ describe "list" do
6
+ before do
7
+ store.add Ghost::Host.new("gist.github.com", "10.0.0.1")
8
+ store.add Ghost::Host.new("google.com", "192.168.1.10")
9
+ end
10
+
11
+ context "with no filtering parameter" do
12
+ it "outputs all hostnames" do
13
+ ghost("list").should == <<-EOF.unindent
14
+ Listing 2 host(s):
15
+ gist.github.com -> 10.0.0.1
16
+ google.com -> 192.168.1.10
17
+ EOF
18
+ end
19
+ end
20
+
21
+ context "with a filtering parameter" do
22
+ before do
23
+ store.empty
24
+ store.add Ghost::Host.new("google.com", "10.0.0.1")
25
+ store.add Ghost::Host.new("google.co.uk", "192.168.1.10")
26
+ store.add Ghost::Host.new("gmail.com")
27
+ end
28
+
29
+ context 'that is a regex' do
30
+ it "outputs entries whose hostname or IP match the filter" do
31
+ ghost("list /\.com$/").should == <<-EOF.unindent
32
+ Listing 2 host(s):
33
+ google.com -> 10.0.0.1
34
+ gmail.com -> 127.0.0.1
35
+ EOF
36
+ end
37
+ end
38
+
39
+ context 'that is a string' do
40
+ it "outputs entries whose hostname or IP match the filter" do
41
+ ghost("list google").should == <<-EOF.unindent
42
+ Listing 2 host(s):
43
+ google.com -> 10.0.0.1
44
+ google.co.uk -> 192.168.1.10
45
+ EOF
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper.rb")
2
+ require 'ghost/cli'
3
+
4
+ # TODO: test exit statuses
5
+ describe Ghost::Cli, :type => :cli do
6
+ context 'writable operations with out proper permissions' do
7
+ it 'outputs a message telling user to escalate permissions' do
8
+ store.stub(:add).and_raise(Errno::EACCES)
9
+
10
+ ghost("add domain.com").should == "Insufficient privileges. Try using `sudo` or running as root.\n"
11
+ end
12
+ end
13
+
14
+ describe "environment configuration" # via GHOST_OPTS (see OptionParser#environment)
15
+
16
+ describe "--version" do
17
+ it "outputs the gem name and version" do
18
+ ghost("--version").should == "ghost #{Ghost::VERSION}\n"
19
+ ghost("-v").should == "ghost #{Ghost::VERSION}\n"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper.rb")
2
+ require 'ghost/host'
3
+
4
+ describe Ghost::Host do
5
+ describe 'attributes' do
6
+ subject { Ghost::Host.new('google.com', '74.125.225.102') }
7
+
8
+ its(:name) { should == 'google.com' }
9
+ its(:to_s) { should == 'google.com' }
10
+ its(:host) { should == 'google.com' }
11
+ its(:hostname) { should == 'google.com' }
12
+ its(:ip) { should == '74.125.225.102'}
13
+ its(:ip_address) { should == '74.125.225.102'}
14
+ end
15
+
16
+ it 'has a default IP of 127.0.0.1' do
17
+ Ghost::Host.new('xyz.com').ip.should == '127.0.0.1'
18
+ end
19
+
20
+ describe 'equality' do
21
+ it 'is equal to a host with the same hostname and IP' do
22
+ Ghost::Host.new('google.com', '123.123.123.123').should ==
23
+ Ghost::Host.new('google.com', '123.123.123.123')
24
+ end
25
+
26
+ it 'is not equal to a host with a different host name' do
27
+ Ghost::Host.new('google.com', '123.123.123.123').should_not ==
28
+ Ghost::Host.new('gmail.com', '123.123.123.123')
29
+ end
30
+
31
+ it 'is not equal to a host with a different IP' do
32
+ Ghost::Host.new('google.com', '123.123.123.123').should_not ==
33
+ Ghost::Host.new('google.com', '222.222.222.222')
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,153 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper.rb")
2
+ require 'ghost/store/dscl_store'
3
+
4
+ # TODO: Raise exception error when `dscl` doesn't have sufficient privileges
5
+ describe Ghost::Store::DsclStore do
6
+ let(:store) { Ghost::Store::DsclStore.new }
7
+ let(:cmd) { Ghost::Store::DsclStore::Dscl }
8
+
9
+ before do
10
+ cmd.stub(:list => [])
11
+ cmd.stub(:read => nil)
12
+ cmd.stub(:create => true)
13
+ cmd.stub(:delete => true)
14
+ end
15
+
16
+ let(:dscl_foo_com) do
17
+ <<-EOF.unindent
18
+ AppleMetaNodeLocation: /Local/Default
19
+ GeneratedUID: 7FE20A09-21B5-42C5-9E8C-E64999BD20E2
20
+ IPAddress: 123.123.123.123
21
+ RecordName: foo.com
22
+ RecordType: dsRecTypeStandard:Hosts
23
+ EOF
24
+ end
25
+
26
+ let(:dscl_bar_com) do
27
+ <<-EOF.unindent
28
+ AppleMetaNodeLocation: /Local/Default
29
+ GeneratedUID: 15286594-C5F1-4508-BAB0-98B96D424657
30
+ IPAddress: 127.0.0.1
31
+ RecordName: bar.com
32
+ RecordType: dsRecTypeStandard:Hosts
33
+ EOF
34
+ end
35
+
36
+ describe "#all" do
37
+ it 'returns an empty array when no hosts are in the store' do
38
+ store.all.should == []
39
+ end
40
+
41
+ it 'returns an array of Ghost::Host entries for each host in the store' do
42
+ cmd.stub(:list).and_return([dscl_foo_com, dscl_bar_com])
43
+ store.all.should == [
44
+ Ghost::Host.new('foo.com', '123.123.123.123'),
45
+ Ghost::Host.new('bar.com', '127.0.0.1')
46
+ ]
47
+ end
48
+ end
49
+
50
+ describe "#find" do
51
+ it 'finds hosts matching a regex' do
52
+ cmd.stub(:list).and_return([dscl_foo_com, dscl_bar_com])
53
+ store.find(/.*/).should == store.all
54
+ store.find(/f/).should == [Ghost::Host.new('foo.com', '123.123.123.123')]
55
+ end
56
+ end
57
+
58
+ describe "#add" do
59
+ let(:host) { Ghost::Host.new('foo.com', '123.123.123.123') }
60
+
61
+ it 'returns true' do
62
+ store.add(host).should be_true
63
+ end
64
+
65
+ # In order to make this run off OS X and without root, have to use an
66
+ # expectation... I think?
67
+ it 'adds the host' do
68
+ cmd.should_receive(:create).with('localhost', 'foo.com', '123.123.123.123')
69
+ store.add(host)
70
+ end
71
+ end
72
+
73
+ describe "#delete" do
74
+ before do
75
+ store.stub(:all).and_return [
76
+ Ghost::Host.new('foo.com', '127.0.0.1'),
77
+ Ghost::Host.new('fo.com', '127.0.0.1'),
78
+ Ghost::Host.new('fooo.com', '127.0.0.2')
79
+ ]
80
+ end
81
+
82
+ context 'using a Ghost::Host to identify host' do
83
+ context 'and the IP does not match an entry' do
84
+ let(:host) { Ghost::Host.new("foo.com", "127.0.0.2") }
85
+
86
+ it 'returns empty array' do
87
+ store.delete(host).should == []
88
+ end
89
+
90
+ it 'has no effect' do
91
+ store.delete(host)
92
+ cmd.should_not_receive(:delete)
93
+ end
94
+ end
95
+
96
+ context 'and the IP matches an entry' do
97
+ let(:host) { Ghost::Host.new("foo.com", "127.0.0.1") }
98
+
99
+ it 'returns array of deleted hosts' do
100
+ store.delete(host).should == [host]
101
+ end
102
+
103
+ # In order to make this run off OS X and without root, have to use an
104
+ # expectation... I think?
105
+ it 'deletes the host' do
106
+ cmd.should_receive(:delete).with('localhost', 'foo.com')
107
+ store.delete(host)
108
+ end
109
+ end
110
+ end
111
+
112
+ context 'using a regex to identify hosts' do
113
+ let(:host) { /fo*\.com/ }
114
+
115
+ it 'returns array of removed hosts' do
116
+ store.delete(host).should == [
117
+ Ghost::Host.new('fo.com', '127.0.0.1'),
118
+ Ghost::Host.new('foo.com', '127.0.0.1'),
119
+ Ghost::Host.new('fooo.com', '127.0.0.2')
120
+ ]
121
+ end
122
+
123
+ it 'deletes the hosts' do
124
+ cmd.should_receive(:delete).with('localhost', 'fo.com')
125
+ cmd.should_receive(:delete).with('localhost', 'foo.com')
126
+ cmd.should_receive(:delete).with('localhost', 'fooo.com')
127
+ store.delete(host)
128
+ end
129
+ end
130
+
131
+ context 'using a string to identify host' do
132
+ let(:host) { "foo.com" }
133
+
134
+ it 'returns array of removed hosts' do
135
+ store.delete(host).should == [Ghost::Host.new('foo.com', '127.0.0.1')]
136
+ end
137
+
138
+ it 'removes the host from the file' do
139
+ cmd.should_receive(:delete).with('localhost', 'foo.com')
140
+ store.delete(host)
141
+ end
142
+ end
143
+ end
144
+
145
+ describe "#empty" do
146
+ it 'deletes all the entries' do
147
+ store.stub(:all => [Ghost::Host.new('foo'), Ghost::Host.new('bar')])
148
+ cmd.should_receive(:delete).with('localhost', 'foo')
149
+ cmd.should_receive(:delete).with('localhost', 'bar')
150
+ store.empty
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,316 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
2
+ require 'ghost/store/hosts_file_store'
3
+
4
+ require 'tmpdir'
5
+
6
+ describe Ghost::Store::HostsFileStore do
7
+ def write(content)
8
+ File.open(file_path, 'w') { |f| f.write(content) }
9
+ end
10
+
11
+ def read
12
+ File.read(file_path)
13
+ end
14
+
15
+ def no_write
16
+ File.any_instance.stub(:reopen).with(anything, /[aw]/).and_raise("no writing!")
17
+ File.any_instance.stub(:puts).and_raise("no writing!")
18
+ File.any_instance.stub(:print).and_raise("no writing!")
19
+ File.any_instance.stub(:write).and_raise("no writing!")
20
+ File.any_instance.stub(:<<).and_raise("no writing!")
21
+ File.any_instance.stub(:flush).and_raise("no writing!")
22
+ end
23
+
24
+ subject { store }
25
+
26
+ let(:file_path) { File.join(Dir.tmpdir, "etc_hosts.#{Process.pid}.#{rand(9999)}") }
27
+ let(:store) { described_class.new(file_path) }
28
+ let(:contents) do
29
+ <<-EOF.unindent
30
+ 127.0.0.1 localhost localhost.localdomain
31
+ EOF
32
+ end
33
+
34
+ before { write(contents) }
35
+
36
+ it 'manages the default file of /etc/hosts when no file path is provided' do
37
+ described_class.new.path.should == "/etc/hosts"
38
+ end
39
+
40
+ it 'manages the file at the provided path when given' do
41
+ described_class.new('xyz').path.should == 'xyz'
42
+ end
43
+
44
+ describe "#all" do
45
+ context 'with no ghost-managed hosts in the file' do
46
+ it 'returns no hosts' do
47
+ store.all.should == []
48
+ end
49
+ end
50
+
51
+ context 'with some ghost-managed hosts in the file' do
52
+ let(:contents) do
53
+ <<-EOF.gsub(/^\s+/,'')
54
+ 127.0.0.1 localhost localhost.localdomain
55
+ # ghost start
56
+ 1.2.3.4 bjeanes.com
57
+ 2.3.4.5 my-app.com subdomain.my-app.com
58
+ # ghost end
59
+ EOF
60
+ end
61
+
62
+ it 'returns an array with one Ghost::Host per ghost-managed host in the hosts file' do
63
+ store.all.should == [
64
+ Ghost::Host.new('bjeanes.com', '1.2.3.4'),
65
+ Ghost::Host.new('my-app.com', '2.3.4.5'),
66
+ Ghost::Host.new('subdomain.my-app.com', '2.3.4.5')
67
+ ]
68
+ end
69
+
70
+ it "shouldn't write to the file" do
71
+ no_write
72
+ store.all
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "#find" do
78
+ let(:contents) do
79
+ <<-EOF.gsub(/^\s+/,'')
80
+ # ghost start
81
+ 1.2.3.4 bjeanes.com
82
+ 2.3.4.5 my-app.com subdomain.my-app.com
83
+ # ghost end
84
+ EOF
85
+ end
86
+
87
+ it "finds hosts matching a regex" do
88
+ store.find(/.*/).should == store.all
89
+ store.find(/my-app\.com$/i).should == [
90
+ Ghost::Host.new('my-app.com', '2.3.4.5'),
91
+ Ghost::Host.new('subdomain.my-app.com', '2.3.4.5')
92
+ ]
93
+ end
94
+ end
95
+
96
+ describe "#add" do
97
+ let(:host) { Ghost::Host.new("google.com", "127.0.0.1") }
98
+
99
+ context 'with no ghost-managed hosts in the file' do
100
+ it 'returns true' do
101
+ store.add(host).should be_true
102
+ end
103
+
104
+ it 'adds the new host between delimeters' do
105
+ store.add(host)
106
+ read.should == <<-EOF.gsub(/^\s+/,'')
107
+ 127.0.0.1 localhost localhost.localdomain
108
+ # ghost start
109
+ 127.0.0.1 google.com
110
+ # ghost end
111
+ EOF
112
+ end
113
+ end
114
+
115
+ context 'with existing ghost-managed hosts in the file' do
116
+ let(:contents) do
117
+ <<-EOF.gsub(/^\s+/,'')
118
+ 127.0.0.1 localhost localhost.localdomain
119
+ # ghost start
120
+ 192.168.1.1 github.com
121
+ # ghost end
122
+ EOF
123
+ end
124
+
125
+ context 'when adding to an existing IP' do
126
+ before { host.ip = '192.168.1.1' }
127
+
128
+ it 'adds to existing entry between tokens, listing host names in alphabetical order' do
129
+ store.add(host)
130
+ read.should == <<-EOF.gsub(/^\s+/,'')
131
+ 127.0.0.1 localhost localhost.localdomain
132
+ # ghost start
133
+ 192.168.1.1 github.com google.com
134
+ # ghost end
135
+ EOF
136
+ end
137
+
138
+ it 'returns true' do
139
+ store.add(host).should be_true
140
+ end
141
+ end
142
+
143
+ context 'when adding a new IP' do
144
+ it 'adds new entry between tokens, in numerical order' do
145
+ store.add(host)
146
+ read.should == <<-EOF.gsub(/^\s+/,'')
147
+ 127.0.0.1 localhost localhost.localdomain
148
+ # ghost start
149
+ 127.0.0.1 google.com
150
+ 192.168.1.1 github.com
151
+ # ghost end
152
+ EOF
153
+ end
154
+
155
+ it 'returns true' do
156
+ store.add(host).should be_true
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ describe "#delete" do
163
+ context 'with no ghost-managed hosts in the file' do
164
+ let(:host) { Ghost::Host.new("localhost", "127.0.0.1") }
165
+
166
+ it 'returns empty array' do
167
+ store.delete(host).should == []
168
+ end
169
+
170
+ it 'has no effect' do
171
+ store.delete(host)
172
+ read.should == contents
173
+ end
174
+ end
175
+
176
+ context 'with existing ghost-managed hosts in the file' do
177
+ let(:contents) do
178
+ <<-EOF.gsub(/^\s+/,'')
179
+ 127.0.0.1 localhost localhost.localdomain
180
+ # ghost start
181
+ 127.0.0.1 google.com
182
+ 127.0.0.2 gooogle.com
183
+ 192.168.1.1 github.com
184
+ # ghost end
185
+ EOF
186
+ end
187
+
188
+ context 'when deleting one of the ghost entries' do
189
+ context 'using a Ghost::Host to identify host' do
190
+ context 'and the IP does not match an entry' do
191
+ let(:host) { Ghost::Host.new("google.com", "127.0.0.2") }
192
+
193
+ it 'returns empty array' do
194
+ store.delete(host).should == []
195
+ end
196
+
197
+ it 'has no effect' do
198
+ store.delete(host)
199
+ read.should == contents
200
+ end
201
+ end
202
+
203
+ context 'and the IP matches an entry' do
204
+ let(:host) { Ghost::Host.new("google.com", "127.0.0.1") }
205
+
206
+ it 'returns array of removed hosts' do
207
+ store.delete(host).should == [Ghost::Host.new('google.com', '127.0.0.1')]
208
+ end
209
+
210
+ it 'removes the host from the file' do
211
+ store.delete(host)
212
+ read.should == <<-EOF.gsub(/^\s+/,'')
213
+ 127.0.0.1 localhost localhost.localdomain
214
+ # ghost start
215
+ 127.0.0.2 gooogle.com
216
+ 192.168.1.1 github.com
217
+ # ghost end
218
+ EOF
219
+ end
220
+ end
221
+ end
222
+
223
+ context 'using a regex to identify hosts' do
224
+ let(:host) { /go*gle\.com/ }
225
+
226
+ it 'returns array of removed hosts' do
227
+ store.delete(host).should == [
228
+ Ghost::Host.new('google.com', '127.0.0.1'),
229
+ Ghost::Host.new('gooogle.com', '127.0.0.2')
230
+ ]
231
+ end
232
+
233
+ it 'removes the host from the file' do
234
+ store.delete(host)
235
+ read.should == <<-EOF.gsub(/^\s+/,'')
236
+ 127.0.0.1 localhost localhost.localdomain
237
+ # ghost start
238
+ 192.168.1.1 github.com
239
+ # ghost end
240
+ EOF
241
+ end
242
+ end
243
+
244
+ context 'using a string to identify host' do
245
+ let(:host) { "google.com" }
246
+
247
+ it 'returns array of removed hosts' do
248
+ store.delete(host).should == [Ghost::Host.new('google.com', '127.0.0.1')]
249
+ end
250
+
251
+ it 'removes the host from the file' do
252
+ store.delete(host)
253
+ read.should == <<-EOF.gsub(/^\s+/,'')
254
+ 127.0.0.1 localhost localhost.localdomain
255
+ # ghost start
256
+ 127.0.0.2 gooogle.com
257
+ 192.168.1.1 github.com
258
+ # ghost end
259
+ EOF
260
+ end
261
+ end
262
+ end
263
+
264
+ context 'when trying to delete a non-ghost entry' do
265
+ let(:host) { Ghost::Host.new("localhost") }
266
+
267
+ it 'returns false' do
268
+ store.delete(host).should == []
269
+ end
270
+
271
+ it 'has no effect' do
272
+ store.delete(host)
273
+ read.should == contents
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ describe "#empty" do
280
+ context 'with no ghost-managed hosts in the file' do
281
+ it 'returns false' do
282
+ store.empty.should be_false
283
+ end
284
+
285
+ it 'has no effect' do
286
+ store.empty
287
+ read.should == contents
288
+ end
289
+ end
290
+
291
+ context 'with existing ghost-managed hosts in the file' do
292
+ let(:contents) do
293
+ <<-EOF.gsub(/^\s+/,'')
294
+ 127.0.0.1 localhost localhost.localdomain
295
+ # ghost start
296
+ 127.0.0.1 google.com
297
+ 192.168.1.1 github.com
298
+ # ghost end
299
+ EOF
300
+ end
301
+
302
+ context 'when deleting one of the ghost entries' do
303
+ it 'returns true' do
304
+ store.empty.should be_true
305
+ end
306
+
307
+ it 'removes the host from the file' do
308
+ store.empty
309
+ read.should == <<-EOF.gsub(/^\s+/,'')
310
+ 127.0.0.1 localhost localhost.localdomain
311
+ EOF
312
+ end
313
+ end
314
+ end
315
+ end
316
+ end