ghost 0.3.0 → 1.0.0.pre

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.
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