hostsfile 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/Guardfile +1 -1
- data/README.md +17 -13
- data/hostsfile.gemspec +12 -4
- data/lib/hostsfile/entry.rb +57 -60
- data/lib/hostsfile/manipulator.rb +144 -112
- data/lib/hostsfile/version.rb +1 -1
- data/spec/entry_spec.rb +43 -0
- data/spec/fixtures/sample_hosts +10 -0
- data/spec/hostsfile_spec.rb +3 -69
- data/spec/manipulator_spec.rb +202 -0
- data/spec/spec_helper.rb +34 -0
- metadata +80 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0df9590599c111715cca1d3e873ccf494a58b6ba
|
4
|
+
data.tar.gz: ce025bcf57210c92e589356129cc775dbdcb55dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d3bbefaae49d7797e16e1b9361e301447c0b1e591d0fcc87e387acc131b26dc64fc17377088899adc3d22f95b7aa352f7d3bd45224fc0521588be78883fb4c0
|
7
|
+
data.tar.gz: 4c12713032af45faffd8f0cdb3c5940aee31e053938830cced802856455fef8b22bddc6f13f155c11d784d04beef3c5f49bdf82d622543d7fdb2a652b2c22127
|
data/Guardfile
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
# * 'just' rspec: 'rspec'
|
12
12
|
guard :rspec, cmd: 'bundle exec rspec' do
|
13
13
|
watch(%r{^spec/.+_spec\.rb$})
|
14
|
-
watch(%r{^lib/(
|
14
|
+
watch(%r{^lib/([^/]+).*\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
15
15
|
watch('spec/spec_helper.rb') { "spec" }
|
16
16
|
end
|
17
17
|
|
data/README.md
CHANGED
@@ -1,24 +1,22 @@
|
|
1
1
|
# Hostsfile
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/tnarik/hostsfile)
|
4
|
+
[](https://codeclimate.com/github/tnarik/hostsfile)
|
5
|
+
[](https://coveralls.io/r/tnarik/hostsfile)
|
6
|
+
[](http://rubygems.org/gems/hostsfile)
|
7
|
+
[](https://gemnasium.com/tnarik/hostsfile)
|
4
8
|
|
5
9
|
## Installation
|
6
10
|
|
7
|
-
|
11
|
+
Install via Rubygems or Gemfile
|
8
12
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
$ bundle
|
14
|
-
|
15
|
-
Or install it yourself as:
|
16
|
-
|
17
|
-
$ gem install hostsfile
|
13
|
+
```zsh
|
14
|
+
$ gem install hostsfile
|
15
|
+
```
|
18
16
|
|
19
17
|
## Usage
|
20
18
|
|
21
|
-
|
19
|
+
This gem has been extracted from [customink-webops/hostsfile code](https://github.com/customink-webops/hostsfile), to decouple it from Chef, so that it can be used independently.
|
22
20
|
|
23
21
|
## Contributing
|
24
22
|
|
@@ -26,4 +24,10 @@ TODO: Write usage instructions here
|
|
26
24
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
25
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
26
|
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
-
5. Create a new Pull Request
|
27
|
+
5. Create a new Pull Request
|
28
|
+
|
29
|
+
## Authors
|
30
|
+
|
31
|
+
- Tnarik Innael (@tnarik) : gem packaging, tests
|
32
|
+
- Seth Vargo (@sethvargo) : original code as part of [the 'hostsfile' cookbook](https://github.com/customink-webops/hostsfile)
|
33
|
+
- CustomInk, LCC : original code as part of [the 'hostsfile' cookbook](https://github.com/customink-webops/hostsfile)
|
data/hostsfile.gemspec
CHANGED
@@ -6,11 +6,11 @@ require 'hostsfile/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "hostsfile"
|
8
8
|
spec.version = Hostsfile::VERSION
|
9
|
-
spec.authors = ["
|
9
|
+
spec.authors = ["Tnarik Innael"]
|
10
10
|
spec.email = ["tnarik@lecafeautomatique.co.uk"]
|
11
11
|
spec.summary = %q{code from the hostsfile cookbook to allow reusability}
|
12
12
|
spec.description = %q{code from the hostsfile cookbook to allow reusability}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/tnarik/hostsfile"
|
14
14
|
spec.license = "Apache-2.0"
|
15
15
|
|
16
16
|
spec.files = `git ls-files`.split($/)
|
@@ -18,14 +18,22 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
# development dependencies
|
21
22
|
spec.add_development_dependency "bundler", "~> 1.6"
|
22
23
|
spec.add_development_dependency "rake"
|
23
24
|
|
25
|
+
# development dependencies (testing)
|
24
26
|
spec.add_development_dependency "rspec", "~> 3.0"
|
25
27
|
spec.add_development_dependency "guard"
|
26
28
|
spec.add_development_dependency "guard-rspec"
|
29
|
+
spec.add_development_dependency "fakefs"
|
30
|
+
|
31
|
+
# development dependencies (coverage)
|
32
|
+
spec.add_development_dependency 'coveralls'
|
33
|
+
spec.add_development_dependency 'simplecov'
|
34
|
+
spec.add_development_dependency 'simplecov-console'
|
27
35
|
|
36
|
+
# development dependencies (notifier), controlled by .guard.rb
|
28
37
|
spec.add_development_dependency "terminal-notifier-guard"
|
29
|
-
# Pre OS X 10.8
|
30
|
-
# spec.add_development_dependency "ruby_gntp"
|
38
|
+
spec.add_development_dependency "ruby_gntp" # Pre OS X 10.8
|
31
39
|
end
|
data/lib/hostsfile/entry.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# Copyright 2013-14, Tnarik Innael
|
2
|
+
#
|
3
|
+
# Heavily based on:
|
1
4
|
# Copyright 2012-2013, Seth Vargo (from customink-webops/hostsfile/libraries/entry.rb)
|
2
5
|
# Copyright 2012, CustomInk, LCC
|
3
6
|
#
|
@@ -28,6 +31,14 @@ class Entry
|
|
28
31
|
# Return nil if the line is empty
|
29
32
|
return nil if entries.nil? || entries.empty?
|
30
33
|
|
34
|
+
# If the hostsfile has broken content:
|
35
|
+
#if entries[0].nil?
|
36
|
+
# raise ArgumentError, "Hostsfile has a line without IP address: #{line}"
|
37
|
+
#end
|
38
|
+
if entries[1].nil?
|
39
|
+
raise ArgumentError, "Hostsfile has a line without hostname: #{line}"
|
40
|
+
end
|
41
|
+
|
31
42
|
return self.new(
|
32
43
|
ip_address: entries[0],
|
33
44
|
hostname: entries[1],
|
@@ -38,31 +49,31 @@ class Entry
|
|
38
49
|
end
|
39
50
|
|
40
51
|
private
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
52
|
+
def extract_comment(line)
|
53
|
+
return nil if presence(line).nil?
|
54
|
+
line.split('#', 2).collect { |part| presence(part) }
|
55
|
+
end
|
45
56
|
|
46
|
-
|
47
|
-
|
57
|
+
def extract_priority(comment)
|
58
|
+
return nil if comment.nil?
|
48
59
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
60
|
+
if comment.include?('@')
|
61
|
+
comment.split('@', 2).collect { |part| presence(part) }
|
62
|
+
else
|
63
|
+
[comment, nil]
|
54
64
|
end
|
65
|
+
end
|
55
66
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
67
|
+
def extract_entries(entry)
|
68
|
+
return nil if entry.nil?
|
69
|
+
entry.split(/\s+/).collect { |entry| presence(entry) }.compact
|
70
|
+
end
|
60
71
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
72
|
+
def presence(string)
|
73
|
+
return nil if string.nil?
|
74
|
+
return nil if string.strip.empty?
|
75
|
+
string.strip
|
76
|
+
end
|
66
77
|
end
|
67
78
|
|
68
79
|
# @return [String]
|
@@ -90,7 +101,7 @@ class Entry
|
|
90
101
|
raise ArgumentError, ':ip_address and :hostname are both required options'
|
91
102
|
end
|
92
103
|
|
93
|
-
@ip_address = IPAddr.new(options[:ip_address]
|
104
|
+
@ip_address = IPAddr.new(remove_ip_scope(options[:ip_address]))
|
94
105
|
@hostname = options[:hostname]
|
95
106
|
@aliases = [options[:aliases]].flatten.compact
|
96
107
|
@comment = options[:comment]
|
@@ -121,32 +132,6 @@ class Entry
|
|
121
132
|
[ip_address, hosts, comments].compact.join("\t").strip
|
122
133
|
end
|
123
134
|
|
124
|
-
# The string representation of this Entry
|
125
|
-
#
|
126
|
-
# @return [String]
|
127
|
-
# the string representation of this entry
|
128
|
-
def to_s
|
129
|
-
"#<#{self.class.to_s} " + [
|
130
|
-
"ip_address: '#{ip_address}'",
|
131
|
-
"hostname: '#{hostname}'",
|
132
|
-
].join(', ') + '>'
|
133
|
-
end
|
134
|
-
|
135
|
-
# The object representation of this Entry
|
136
|
-
#
|
137
|
-
# @return [String]
|
138
|
-
# the object representation of this entry
|
139
|
-
def inspect
|
140
|
-
"#<#{self.class.to_s} " + [
|
141
|
-
"ip_address: '#{ip_address}'",
|
142
|
-
"hostname: '#{hostname}'",
|
143
|
-
"aliases: #{aliases.inspect}",
|
144
|
-
"comment: '#{comment}'",
|
145
|
-
"priority: #{priority}",
|
146
|
-
"calculated_priority?: #{@calculated_priority}",
|
147
|
-
].join(', ') + '>'
|
148
|
-
end
|
149
|
-
|
150
135
|
# Returns true if priority is calculated
|
151
136
|
#
|
152
137
|
# @return [Boolean]
|
@@ -156,20 +141,32 @@ class Entry
|
|
156
141
|
end
|
157
142
|
|
158
143
|
private
|
144
|
+
# Calculates the relative priority of this entry.
|
145
|
+
#
|
146
|
+
# @return [Fixnum]
|
147
|
+
# the relative priority of this item
|
148
|
+
def calculated_priority
|
149
|
+
@calculated_priority = true
|
159
150
|
|
160
|
-
|
161
|
-
#
|
162
|
-
#
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
151
|
+
priority ||= 81 if ip_address == IPAddr.new('127.0.0.1')
|
152
|
+
priority ||= 80 if IPAddr.new('127.0.0.0/8').include?(ip_address) # local
|
153
|
+
priority ||= 60 if ip_address.ipv4? # ipv4
|
154
|
+
priority ||= 20 if ip_address.ipv6? # ipv6
|
155
|
+
|
156
|
+
return priority || 00
|
157
|
+
end
|
158
|
+
|
159
|
+
# Removes the scopes pieces of the address, because of the below reasons.
|
160
|
+
#
|
161
|
+
# @see https://bugs.ruby-lang.org/issues/8464
|
162
|
+
# @see https://github.com/customink-webops/hostsfile/issues/51
|
163
|
+
#
|
164
|
+
# @return [String, nil]
|
165
|
+
#
|
166
|
+
def remove_ip_scope(address)
|
167
|
+
return nil if address.nil?
|
168
|
+
address.to_s.sub(/%.*/, '')
|
169
|
+
end
|
173
170
|
end
|
174
171
|
|
175
172
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# Copyright 2013-14, Tnarik Innael
|
2
|
+
#
|
3
|
+
# Heavily based on:
|
2
4
|
# Copyright 2012-2013, Seth Vargo (from customink-webops/hostsfile/libraries/manipulator.rb)
|
3
5
|
# Copyright 2012, CustomInk, LCC
|
4
6
|
#
|
@@ -9,18 +11,21 @@ module Hostsfile
|
|
9
11
|
|
10
12
|
class Manipulator
|
11
13
|
attr_reader :entries
|
12
|
-
|
13
14
|
# Create a new Manipulator object (aka an /etc/hosts manipulator). If a
|
14
|
-
# hostsfile is not found, a Exception is risen
|
15
|
-
#
|
15
|
+
# hostsfile is not found, a Exception is risen.
|
16
|
+
# Parameters are optional (see #hostsfile_path)
|
16
17
|
#
|
17
|
-
# @param [
|
18
|
-
# the
|
18
|
+
# @param [String] path
|
19
|
+
# the file path for the host file
|
20
|
+
# @param [String] family
|
21
|
+
# the OS family ('windows' or anything else for POSIX support)
|
22
|
+
# @param [String] system_directory
|
23
|
+
# System directory for the 'windows' family (like C:\\Windows\\system32)
|
19
24
|
# @return [Manipulator]
|
20
|
-
# a class designed to manipulate the
|
25
|
+
# a class designed to manipulate the /etc/hosts file
|
21
26
|
def initialize(path = nil, family = nil, system_directory = nil)
|
22
27
|
# Fail if no hostsfile is found
|
23
|
-
unless ::File.exists?(hostsfile_path)
|
28
|
+
unless ::File.exists?(hostsfile_path(path, family, system_directory))
|
24
29
|
raise "No hostsfile exists at '#{hostsfile_path}'!"
|
25
30
|
end
|
26
31
|
|
@@ -113,28 +118,19 @@ class Manipulator
|
|
113
118
|
# Save the new hostsfile to the target machine. This method will only write the
|
114
119
|
# hostsfile if the current version has changed. In other words, it is convergent.
|
115
120
|
def save
|
116
|
-
entries = []
|
117
|
-
entries << '#'
|
118
|
-
entries << '# This file is managed by Chef, using the hostsfile cookbook.'
|
119
|
-
entries << '# Editing this file by hand is highly discouraged!'
|
120
|
-
entries << '#'
|
121
|
-
entries << '# Comments containing an @ sign should not be modified or else'
|
122
|
-
entries << '# hostsfile will be unable to guarantee relative priority in'
|
123
|
-
entries << '# future Chef runs!'
|
124
|
-
entries << '#'
|
125
|
-
entries << ''
|
126
|
-
entries += unique_entries.map(&:to_line)
|
127
|
-
entries << ''
|
128
|
-
|
129
|
-
contents = entries.join("\n")
|
130
|
-
contents_sha = Digest::SHA512.hexdigest(contents)
|
131
|
-
|
132
121
|
# Only write out the file if the contents have changed...
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
122
|
+
::File.open(hostsfile_path, 'w') do |f|
|
123
|
+
f.write(new_content)
|
124
|
+
end if content_changed?
|
125
|
+
end
|
126
|
+
|
127
|
+
# Determine if the content of the hostfile has changed by comparing sha
|
128
|
+
# values of existing file and new content
|
129
|
+
#
|
130
|
+
# @return [Boolean]
|
131
|
+
def content_changed?
|
132
|
+
new_sha = Digest::SHA512.hexdigest(new_content)
|
133
|
+
new_sha != current_sha
|
138
134
|
end
|
139
135
|
|
140
136
|
# Find an entry by the given IP Address.
|
@@ -152,108 +148,144 @@ class Manipulator
|
|
152
148
|
# Determine if the current hostsfile contains the given resource. This
|
153
149
|
# is really just a proxy to {find_resource_by_ip_address} /
|
154
150
|
#
|
155
|
-
# @param [
|
156
|
-
#
|
151
|
+
# @param [String] ip_address
|
152
|
+
# the IP Address of the entry to check
|
157
153
|
# @return [Boolean]
|
158
|
-
def contains?(
|
159
|
-
!!find_entry_by_ip_address(
|
154
|
+
def contains?(ip_address)
|
155
|
+
!!find_entry_by_ip_address(ip_address)
|
160
156
|
end
|
161
157
|
|
162
158
|
private
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
159
|
+
# The path to the current hostsfile.
|
160
|
+
# If not path is provided, a default is guessed based on 'family' and 'system_directory'
|
161
|
+
# If path is provided, it takes priority
|
162
|
+
#
|
163
|
+
# @param [String] path
|
164
|
+
# the file path for the host file
|
165
|
+
# @param [String] family
|
166
|
+
# the OS family ('windows' or anything else for POSIX support)
|
167
|
+
# @param [String] system_directory
|
168
|
+
# System directory for the 'windows' family (default C:\\Windows\\system32)
|
169
|
+
# @return [String]
|
170
|
+
# the full path to the hostsfile, depending on the operating system
|
171
|
+
def hostsfile_path (path = nil, family = nil, system_directory = nil)
|
172
|
+
return @hostsfile_path if @hostsfile_path
|
173
|
+
@hostsfile_path = path || case family
|
174
|
+
when 'windows'
|
175
|
+
system_directory ||= File.join('C:','Windows','system32')
|
176
|
+
File.join("#{system_directory}", 'drivers', 'etc', 'hosts')
|
177
|
+
else
|
178
|
+
'/etc/hosts'
|
179
|
+
end
|
180
|
+
end
|
177
181
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
182
|
+
# The header of the new hostsfile
|
183
|
+
#
|
184
|
+
# @return [Array]
|
185
|
+
# an array of header comments
|
186
|
+
def hostsfile_header
|
187
|
+
lines = []
|
188
|
+
lines << '#'
|
189
|
+
lines << '# This file is managed by the hostsfile gem.'
|
190
|
+
lines << '# Editing this file by hand is highly discouraged!'
|
191
|
+
lines << '#'
|
192
|
+
lines << '# Comments containing an @ sign should not be modified or else'
|
193
|
+
lines << '# hostsfile will be unable to guarantee relative priority in'
|
194
|
+
lines << '# future runs!'
|
195
|
+
lines << '#'
|
196
|
+
lines << ''
|
197
|
+
end
|
185
198
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
199
|
+
# The content that will be written to the hostfile
|
200
|
+
#
|
201
|
+
# @return [String]
|
202
|
+
# the full contents of the hostfile to be written
|
203
|
+
def new_content
|
204
|
+
lines = hostsfile_header
|
205
|
+
lines += unique_entries.map(&:to_line)
|
206
|
+
lines << ''
|
207
|
+
lines.join("\n")
|
208
|
+
end
|
196
209
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
210
|
+
# The current sha of the system hostsfile.
|
211
|
+
#
|
212
|
+
# @return [String]
|
213
|
+
# the sha of the current hostsfile
|
214
|
+
def current_sha
|
215
|
+
@current_sha ||= Digest::SHA512.hexdigest(File.read(hostsfile_path))
|
216
|
+
end
|
217
|
+
|
218
|
+
# Normalize the given list of elements into a single array with no nil
|
219
|
+
# values and no duplicate values.
|
220
|
+
#
|
221
|
+
# @param [Object] things
|
222
|
+
#
|
223
|
+
# @return [Array]
|
224
|
+
# a normalized array of things
|
225
|
+
def normalize(*things)
|
226
|
+
things.flatten.compact.uniq
|
227
|
+
end
|
205
228
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
entry = ::Hostsfile::Entry.parse(line)
|
215
|
-
next if entry.nil?
|
229
|
+
# This is a crazy way of ensuring unique objects in an array using a Hash.
|
230
|
+
#
|
231
|
+
# @return [Array]
|
232
|
+
# the sorted list of entires that are unique
|
233
|
+
def unique_entries
|
234
|
+
entries = Hash[*@entries.map { |entry| [entry.ip_address, entry] }.flatten].values
|
235
|
+
entries.sort_by { |e| [-e.priority.to_i, e.hostname.to_s] }
|
236
|
+
end
|
216
237
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
238
|
+
# Takes /etc/hosts file contents and builds a flattened entries
|
239
|
+
# array so that each IP address has only one line and multiple hostnames
|
240
|
+
# are flattened into a list of aliases.
|
241
|
+
#
|
242
|
+
# @param [Array] contents
|
243
|
+
# Array of lines from /etc/hosts file
|
244
|
+
def collect_and_flatten(contents)
|
245
|
+
contents.each do |line|
|
246
|
+
entry = ::Hostsfile::Entry.parse(line)
|
247
|
+
next if entry.nil?
|
248
|
+
|
249
|
+
append(
|
250
|
+
ip_address: entry.ip_address,
|
251
|
+
hostname: entry.hostname,
|
252
|
+
aliases: entry.aliases,
|
253
|
+
comment: entry.comment,
|
254
|
+
priority: !entry.calculated_priority? && entry.priority,
|
255
|
+
)
|
225
256
|
end
|
257
|
+
end
|
226
258
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
259
|
+
# Removes duplicate hostnames in other files ensuring they are unique
|
260
|
+
#
|
261
|
+
# @param [Entry] entry
|
262
|
+
# the entry to keep the hostname and aliases from
|
263
|
+
#
|
264
|
+
# @return [nil]
|
265
|
+
def remove_existing_hostnames(entry)
|
266
|
+
@entries.delete(entry)
|
267
|
+
changed_hostnames = [entry.hostname, entry.aliases].flatten.uniq
|
236
268
|
|
237
|
-
|
238
|
-
|
239
|
-
|
269
|
+
@entries = @entries.collect do |entry|
|
270
|
+
entry.hostname = nil if changed_hostnames.include?(entry.hostname)
|
271
|
+
entry.aliases = entry.aliases - changed_hostnames
|
240
272
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
else
|
245
|
-
entry.hostname = entry.aliases.shift
|
246
|
-
entry
|
247
|
-
end
|
273
|
+
if entry.hostname.nil?
|
274
|
+
if entry.aliases.empty?
|
275
|
+
nil
|
248
276
|
else
|
277
|
+
entry.hostname = entry.aliases.shift
|
249
278
|
entry
|
250
279
|
end
|
251
|
-
|
280
|
+
else
|
281
|
+
entry
|
282
|
+
end
|
283
|
+
end.compact
|
252
284
|
|
253
|
-
|
285
|
+
@entries << entry
|
254
286
|
|
255
|
-
|
256
|
-
|
287
|
+
nil
|
288
|
+
end
|
257
289
|
end
|
258
290
|
|
259
291
|
end
|
data/lib/hostsfile/version.rb
CHANGED
data/spec/entry_spec.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'fakefs/spec_helpers'
|
2
|
+
|
3
|
+
describe Hostsfile do
|
4
|
+
describe "::Entry" do
|
5
|
+
let(:entry) { Hostsfile::Entry.new ip_address: "0.0.0.0", hostname: "test" }
|
6
|
+
|
7
|
+
context "#parse" do
|
8
|
+
it "raises a fatal error if the hostname is missing (considers the first field the IP" do
|
9
|
+
expect { Hostsfile::Entry.parse "0.0.0.0"}.to raise_error(ArgumentError, /Hostsfile has a line without hostname/)
|
10
|
+
expect { Hostsfile::Entry.parse " hostname"}.to raise_error(ArgumentError, /Hostsfile has a line without hostname/)
|
11
|
+
expect { Hostsfile::Entry.parse " \thostname"}.to raise_error(ArgumentError, /Hostsfile has a line without hostname/)
|
12
|
+
expect { Hostsfile::Entry.parse "\thostname"}.to raise_error(ArgumentError, /Hostsfile has a line without hostname/)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "#to_line" do
|
17
|
+
it "exists" do
|
18
|
+
expect(entry.respond_to? :to_line).to eq(true)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "generates a proper line" do
|
22
|
+
expect(entry.to_line).to eq("0.0.0.0\ttest")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
## Entry
|
29
|
+
# def parse(line)
|
30
|
+
# private
|
31
|
+
# def extract_comment(line)
|
32
|
+
# def extract_priority(comment)
|
33
|
+
# def extract_entries(entry)
|
34
|
+
# def presence(string)
|
35
|
+
#
|
36
|
+
# def initialize(options = {})
|
37
|
+
# def priority=(new_priority)
|
38
|
+
# def to_line
|
39
|
+
# def to_s
|
40
|
+
# def inspect
|
41
|
+
# def calculated_priority?
|
42
|
+
# private
|
43
|
+
# def calculated_priority
|
@@ -0,0 +1,10 @@
|
|
1
|
+
##
|
2
|
+
# Host Database
|
3
|
+
#
|
4
|
+
# localhost is used to configure the loopback interface
|
5
|
+
# when the system is booting. Do not change this entry.
|
6
|
+
##
|
7
|
+
127.0.0.1 localhost
|
8
|
+
192.0.2.1 awesome.example.com
|
9
|
+
192.0.2.2 fine.example.com refined.example.com
|
10
|
+
2001:db8::1 ipv6.example.com
|
data/spec/hostsfile_spec.rb
CHANGED
@@ -1,75 +1,9 @@
|
|
1
|
+
require 'fakefs/spec_helpers'
|
2
|
+
|
1
3
|
describe Hostsfile do
|
2
4
|
describe "::VERSION" do
|
3
|
-
it "
|
5
|
+
it "is defined" do
|
4
6
|
expect(Hostsfile::VERSION).not_to be_empty
|
5
7
|
end
|
6
8
|
end
|
7
|
-
|
8
|
-
describe "::Entry" do
|
9
|
-
it "exists" do
|
10
|
-
expect(Hostsfile::Entry::respond_to? :new).to eq(true)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "#to_line" do
|
14
|
-
h = Hostsfile::Entry.new ip_address: "0.0.0.0", hostname: "test"
|
15
|
-
expect(h.respond_to? :to_line).to eq(true)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "#to_line_response" do
|
19
|
-
h = Hostsfile::Entry.new ip_address: "0.0.0.0", hostname: "test"
|
20
|
-
expect(h.to_line).to eq("0.0.0.0\ttest")
|
21
|
-
end
|
22
|
-
|
23
|
-
it "#to_s" do
|
24
|
-
h = Hostsfile::Entry.new ip_address: "0.0.0.0", hostname: "test"
|
25
|
-
expect(h.respond_to? :to_line).to eq(true)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "::Manipulator" do
|
30
|
-
it "exists" do
|
31
|
-
expect(Hostsfile::Manipulator::respond_to? :new).to eq(true)
|
32
|
-
end
|
33
|
-
|
34
|
-
#it "#append" do
|
35
|
-
# h = Hostsfile::Manipulator.new
|
36
|
-
# expect(h.respond_to? :append).to eq(true)
|
37
|
-
#end
|
38
|
-
end
|
39
9
|
end
|
40
|
-
|
41
|
-
## Manipulator
|
42
|
-
# def initialize(path = nil, family = nil, system_directory = nil)
|
43
|
-
# def ip_addresses
|
44
|
-
# def add(options = {})
|
45
|
-
# def update(options = {})
|
46
|
-
# def append(options = {})
|
47
|
-
# def remove(ip_address)
|
48
|
-
# def save
|
49
|
-
# def find_entry_by_ip_address(ip_address)
|
50
|
-
# def contains?(resource)
|
51
|
-
# private
|
52
|
-
# def hostsfile_path (path = nil, family = nil, system_directory = nil)
|
53
|
-
# def current_sha
|
54
|
-
# def normalize(*things)
|
55
|
-
# def unique_entries
|
56
|
-
# def collect_and_flatten(contents)
|
57
|
-
# def remove_existing_hostnames(entry)
|
58
|
-
|
59
|
-
|
60
|
-
## Entry
|
61
|
-
# def parse(line)
|
62
|
-
# private
|
63
|
-
# def extract_comment(line)
|
64
|
-
# def extract_priority(comment)
|
65
|
-
# def extract_entries(entry)
|
66
|
-
# def presence(string)
|
67
|
-
#
|
68
|
-
# def initialize(options = {})
|
69
|
-
# def priority=(new_priority)
|
70
|
-
# def to_line
|
71
|
-
# def to_s
|
72
|
-
# def inspect
|
73
|
-
# def calculated_priority?
|
74
|
-
# private
|
75
|
-
# def calculated_priority
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'fakefs/spec_helpers'
|
2
|
+
|
3
|
+
describe Hostsfile do
|
4
|
+
describe "::Manipulator" do
|
5
|
+
include FakeFS::SpecHelpers
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
fixture_to_fakefs("sample_hosts", "/etc/hosts")
|
9
|
+
fixture_to_fakefs("sample_hosts", "/windows/drivers/etc/hosts")
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:manipulator) { Hostsfile::Manipulator.new }
|
13
|
+
|
14
|
+
context "#initialize" do
|
15
|
+
it "raises a fatal error if the hostfile does not exist" do
|
16
|
+
expect { Hostsfile::Manipulator.new "/etc/hosts_does_not_exist"}.to raise_error(RuntimeError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "reads the default /etc/hosts file if none is specified" do
|
20
|
+
expect { Hostsfile::Manipulator.new }.to_not raise_error
|
21
|
+
expect(Hostsfile::Manipulator.new.ip_addresses.size).to be(4)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "reads the default drivers/etc/hosts file for windows with a system directory" do
|
25
|
+
expect { Hostsfile::Manipulator.new nil, 'windows', '/windows'}.to_not raise_error
|
26
|
+
expect(Hostsfile::Manipulator.new(nil, 'windows', '/windows').ip_addresses.size).to be(4)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "#ip_addresses" do
|
31
|
+
it "reads ip addresses correctly" do
|
32
|
+
expect(manipulator.ip_addresses.size).to be(4)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context '#find_entry_by_ip_address' do
|
37
|
+
it 'finds the associated entry' do
|
38
|
+
expect( manipulator.find_entry_by_ip_address('127.0.0.1') ).not_to be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns nil if the entry does not exist' do
|
42
|
+
expect( manipulator.find_entry_by_ip_address('192.0.2.0') ).to be_nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context '#contains?' do
|
47
|
+
it 'detects an existing entry' do
|
48
|
+
expect( manipulator.contains?('127.0.0.1') ).to be_truthy
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'detects the non existing entry' do
|
52
|
+
expect( manipulator.contains?('192.0.2.0') ).to be_falsey
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "#add" do
|
57
|
+
let(:options) { { ip_address: '192.0.2.0', hostname: 'example.com', aliases: nil, comment: 'Some comment', priority: 5 } }
|
58
|
+
let(:minimal_options) { { ip_address: '192.0.2.0', hostname: 'example.com' } }
|
59
|
+
|
60
|
+
it "requires at least IP Address and Hostname" do
|
61
|
+
expect { manipulator.add(ip_address: options[:ip_address]) }.to raise_error(ArgumentError)
|
62
|
+
expect { manipulator.add(hostname: options[:hostname]) }.to raise_error(ArgumentError)
|
63
|
+
expect { manipulator.add(aliases: options[:aliases]) }.to raise_error(ArgumentError)
|
64
|
+
expect { manipulator.add(comment: options[:comment]) }.to raise_error(ArgumentError)
|
65
|
+
|
66
|
+
expect { manipulator.add(options.reject {|k| k == :ip_address}) }.to raise_error(ArgumentError)
|
67
|
+
expect { manipulator.add(options.reject {|k| k == :hostname}) }.to raise_error(ArgumentError)
|
68
|
+
|
69
|
+
expect { manipulator.add(minimal_options) }.to change{manipulator.ip_addresses.size}.from(4).to(5)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "add entries in memory until save" do # Includes debugging
|
73
|
+
expect { manipulator.add(options) }.to change{manipulator.ip_addresses.size}.from(4).to(5)
|
74
|
+
refreshed_manipulator = Hostsfile::Manipulator.new
|
75
|
+
expect(refreshed_manipulator.ip_addresses.size).to be(4)
|
76
|
+
manipulator.save
|
77
|
+
refreshed_manipulator = Hostsfile::Manipulator.new
|
78
|
+
expect(refreshed_manipulator.ip_addresses.size).to be(5)
|
79
|
+
puts File.read("/etc/hosts")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "#update" do
|
84
|
+
let(:options) { { ip_address: '127.0.0.1', hostname: 'new.example.com' } }
|
85
|
+
let(:not_existing_options) { { ip_address: '192.0.2.0', hostname: 'new.example.com' } }
|
86
|
+
context "when the entry exists" do
|
87
|
+
it "does not add a new entry" do
|
88
|
+
expect { manipulator.update(options) }.not_to change{manipulator.ip_addresses.size}
|
89
|
+
end
|
90
|
+
|
91
|
+
it "updates the entry" do # Includes debugging
|
92
|
+
p manipulator.find_entry_by_ip_address(options[:ip_address])
|
93
|
+
expect { manipulator.update(options) }.to change{manipulator.find_entry_by_ip_address(options[:ip_address]).hostname}
|
94
|
+
expect( manipulator.find_entry_by_ip_address(options[:ip_address]).hostname ).to eq(options[:hostname])
|
95
|
+
p manipulator.find_entry_by_ip_address(options[:ip_address])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
context "when the entry does not exist" do
|
99
|
+
it "does nothing" do
|
100
|
+
expect { manipulator.update(not_existing_options) }.not_to change{manipulator.ip_addresses.size}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "#append" do
|
106
|
+
let(:options) { { ip_address: '127.0.0.1', hostname: 'new.example.com', aliases: "alias.example.com", comment: 'Some comment', priority: 5 } }
|
107
|
+
let(:not_existing_options) { { ip_address: '192.0.2.0', hostname: 'example.com', aliases: nil, comment: 'Some comment', priority: 5 } }
|
108
|
+
let(:unique_options) { { ip_address: '192.0.2.3', hostname: 'awesome.example.com', aliases: nil, comment: 'Was previously the single hostname of 192.0.2.1', unique: true } }
|
109
|
+
let(:unique_options_fine) { { ip_address: '192.0.2.3', hostname: 'fine.example.com', aliases: nil, comment: 'Was previously the hostname of 192.0.2.2', unique: true } }
|
110
|
+
let(:unique_options_refined) { { ip_address: '192.0.2.3', hostname: 'refined.example.com', aliases: nil, comment: 'Was previously an alias of 192.0.2.2', unique: true } }
|
111
|
+
|
112
|
+
context "when the entry exists by IP" do
|
113
|
+
it "does not update hostname, instead adds the new one as an alias" do
|
114
|
+
original_entry = manipulator.find_entry_by_ip_address(options[:ip_address])
|
115
|
+
expect { manipulator.append(options) }.not_to change{manipulator.ip_addresses.size}
|
116
|
+
expect( manipulator.find_entry_by_ip_address(options[:ip_address]).hostname ).to eq(original_entry.hostname)
|
117
|
+
expect( manipulator.find_entry_by_ip_address(options[:ip_address]).aliases ).to include("new.example.com")
|
118
|
+
end
|
119
|
+
it "updates aliases" do
|
120
|
+
expect { manipulator.append(options) }.not_to change{manipulator.ip_addresses.size}
|
121
|
+
expect( manipulator.find_entry_by_ip_address(options[:ip_address]).aliases ).to include("alias.example.com")
|
122
|
+
end
|
123
|
+
it "updates comment" do
|
124
|
+
expect { manipulator.append(options) }.not_to change{manipulator.ip_addresses.size}
|
125
|
+
expect( manipulator.find_entry_by_ip_address(options[:ip_address]).comment ).to eq(options[:comment])
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when the entry exists by hostname" do
|
130
|
+
context "when tagged as unique" do
|
131
|
+
it "replaces it when original entry contains only name" do
|
132
|
+
original_entry = manipulator.find_entry_by_ip_address('192.0.2.1').dup
|
133
|
+
expect( original_entry.hostname ).to eq(unique_options[:hostname])
|
134
|
+
expect { manipulator.append(unique_options) }.not_to change{manipulator.ip_addresses.size}
|
135
|
+
expect( manipulator.find_entry_by_ip_address(unique_options[:ip_address]).hostname ).to eq(unique_options[:hostname])
|
136
|
+
expect( manipulator.find_entry_by_ip_address(original_entry.ip_address) ).to be_nil
|
137
|
+
end
|
138
|
+
|
139
|
+
it "adds it when original entry has alises (and hostnames match)" do
|
140
|
+
original_entry = manipulator.find_entry_by_ip_address('192.0.2.2').dup
|
141
|
+
expect( original_entry.hostname ).to eq(unique_options_fine[:hostname])
|
142
|
+
|
143
|
+
expect { manipulator.append(unique_options_fine) }.to change{manipulator.ip_addresses.size}.from(4).to(5)
|
144
|
+
expect( manipulator.find_entry_by_ip_address(unique_options_fine[:ip_address]).hostname ).to eq(unique_options_fine[:hostname])
|
145
|
+
refreshed_original_entry = manipulator.find_entry_by_ip_address(original_entry.ip_address)
|
146
|
+
expect( refreshed_original_entry ).not_to be_nil
|
147
|
+
expect( original_entry.aliases ).to include(refreshed_original_entry.hostname )
|
148
|
+
expect( original_entry.aliases.size - refreshed_original_entry.aliases.size ).to eq(1)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "adds it when original entry has aliases (one being the hostname)" do
|
152
|
+
original_entry = manipulator.find_entry_by_ip_address('192.0.2.2').dup
|
153
|
+
expect( original_entry.aliases ).to include(unique_options_refined[:hostname])
|
154
|
+
|
155
|
+
expect { manipulator.append(unique_options_refined) }.to change{manipulator.ip_addresses.size}.from(4).to(5)
|
156
|
+
expect( manipulator.find_entry_by_ip_address(unique_options_refined[:ip_address]).hostname ).to eq(unique_options_refined[:hostname])
|
157
|
+
expect( manipulator.find_entry_by_ip_address(original_entry.ip_address) ).not_to be_nil
|
158
|
+
expect( manipulator.find_entry_by_ip_address(original_entry.ip_address).aliases ).not_to include(unique_options_refined[:hostname])
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
it "adds a new entry if not unique" do
|
164
|
+
original_entry = manipulator.find_entry_by_ip_address('192.0.2.1').dup
|
165
|
+
expect( original_entry.hostname ).to eq(unique_options[:hostname])
|
166
|
+
expect { manipulator.append(unique_options.reject {|k| k == :unique}) }.to change{manipulator.ip_addresses.size}.from(4).to(5)
|
167
|
+
expect( manipulator.find_entry_by_ip_address(unique_options[:ip_address]).hostname ).to eq(unique_options[:hostname])
|
168
|
+
expect( manipulator.find_entry_by_ip_address(original_entry.ip_address) ).not_to be_nil
|
169
|
+
end
|
170
|
+
end
|
171
|
+
context "when the entry does not exist" do
|
172
|
+
it "adds the new entry" do
|
173
|
+
expect { manipulator.append(not_existing_options) }.to change{manipulator.ip_addresses.size}.from(4).to(5)
|
174
|
+
expect( manipulator.find_entry_by_ip_address(not_existing_options[:ip_address]).hostname ).to eq('example.com')
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context "#remove" do
|
180
|
+
context "when the entry exists" do
|
181
|
+
it "is removed" do
|
182
|
+
expect { manipulator.remove('127.0.0.1') }.to change{manipulator.ip_addresses.size}.from(4).to(3)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
context "when the entry does not exist" do
|
186
|
+
it "does nothing" do
|
187
|
+
expect { manipulator.remove('192.0.2.0') }.not_to change{manipulator.ip_addresses.size}
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Contains will probably dissapear, as it links to entries specifically
|
193
|
+
|
194
|
+
it "ip addresses are removed works on memory" do
|
195
|
+
expect(manipulator.ip_addresses.size).to be(4)
|
196
|
+
manipulator.add(ip_address: '192.0.2.0', hostname: 'test')
|
197
|
+
expect(manipulator.ip_addresses.size).to be(5)
|
198
|
+
expect { manipulator.remove('192.0.2.0') }.to change{manipulator.ip_addresses.size}.from(5).to(4)
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,38 @@
|
|
1
1
|
require 'bundler/setup'
|
2
2
|
Bundler.setup
|
3
3
|
|
4
|
+
require 'simplecov'
|
5
|
+
require 'simplecov-console'
|
6
|
+
require 'coveralls'
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
8
|
+
SimpleCov::Formatter::HTMLFormatter,
|
9
|
+
SimpleCov::Formatter::Console,
|
10
|
+
Coveralls::SimpleCov::Formatter
|
11
|
+
]
|
12
|
+
|
13
|
+
SimpleCov.start do
|
14
|
+
add_filter "/spec/"
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def fixture_path
|
19
|
+
File.expand_path("../fixtures", __FILE__)
|
20
|
+
end
|
21
|
+
|
22
|
+
def fixture(*segments)
|
23
|
+
fakefs_status = (defined? FakeFS).nil? ? false : FakeFS.activated?
|
24
|
+
FakeFS.deactivate! if fakefs_status
|
25
|
+
fixture = File.read(File.join(fixture_path, *segments))
|
26
|
+
FakeFS.activate! if fakefs_status
|
27
|
+
fixture
|
28
|
+
end
|
29
|
+
|
30
|
+
def fixture_to_fakefs(name, filepath)
|
31
|
+
raise "FakeFS required but not installed or activated" unless !(defined? FakeFS).nil? && FakeFS.activated?
|
32
|
+
|
33
|
+
fixture_content = fixture(name)
|
34
|
+
FileUtils.mkdir_p(File.dirname(filepath))
|
35
|
+
File.open(filepath, "w") { |f| f.write(fixture_content) }
|
36
|
+
end
|
37
|
+
|
4
38
|
require 'hostsfile'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hostsfile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Tnarik Innael
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,6 +80,62 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: fakefs
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: coveralls
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov-console
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
83
139
|
- !ruby/object:Gem::Dependency
|
84
140
|
name: terminal-notifier-guard
|
85
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +150,20 @@ dependencies:
|
|
94
150
|
- - ">="
|
95
151
|
- !ruby/object:Gem::Version
|
96
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: ruby_gntp
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
97
167
|
description: code from the hostsfile cookbook to allow reusability
|
98
168
|
email:
|
99
169
|
- tnarik@lecafeautomatique.co.uk
|
@@ -114,9 +184,12 @@ files:
|
|
114
184
|
- lib/hostsfile/entry.rb
|
115
185
|
- lib/hostsfile/manipulator.rb
|
116
186
|
- lib/hostsfile/version.rb
|
187
|
+
- spec/entry_spec.rb
|
188
|
+
- spec/fixtures/sample_hosts
|
117
189
|
- spec/hostsfile_spec.rb
|
190
|
+
- spec/manipulator_spec.rb
|
118
191
|
- spec/spec_helper.rb
|
119
|
-
homepage:
|
192
|
+
homepage: https://github.com/tnarik/hostsfile
|
120
193
|
licenses:
|
121
194
|
- Apache-2.0
|
122
195
|
metadata: {}
|
@@ -141,5 +214,8 @@ signing_key:
|
|
141
214
|
specification_version: 4
|
142
215
|
summary: code from the hostsfile cookbook to allow reusability
|
143
216
|
test_files:
|
217
|
+
- spec/entry_spec.rb
|
218
|
+
- spec/fixtures/sample_hosts
|
144
219
|
- spec/hostsfile_spec.rb
|
220
|
+
- spec/manipulator_spec.rb
|
145
221
|
- spec/spec_helper.rb
|