roast 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in roast.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 blahed
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Roast [![Build Status](https://travis-ci.org/blahed/roast.png?branch=master)](https://travis-ci.org/blahed/roast)
2
+
3
+ Roast helps you manage entries in your `/etc/hosts` file. Roast allows you to group entries together in a named group and disable/enable them all at once. It's pretty useful if you work with a lot of entries in your hosts file and find it annoying to constantly be enabled/editing your hosts file manually.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ $ gem install roast
9
+ ```
10
+
11
+ ## Commands
12
+
13
+ Roast has a decent set of commands available:
14
+ ```
15
+ list list the entries in the hosts file alias: l
16
+ add adds a new entry to the hosts file alias: a
17
+ enable enables a disabled (commented out) entry alias: e
18
+ enable-group enables an entire group alias: eg
19
+ disable disables an entry (comments it out) alias: d
20
+ disable-group disables an entire group alias: dg
21
+ delete deletes an entry entirely
22
+ delete-group deletes an enitre group
23
+ ```
24
+ *a few of the commands have aliases, they are listed to the right*
25
+
26
+ ## Usage
27
+
28
+ A few examples of using roast to manage your `/etc/hosts` file:
29
+ ```bash
30
+ # list all entires
31
+ $ roast list
32
+
33
+ # add an entry to the base group
34
+ $ sudo roast add 10.0.1.1 something.dev
35
+
36
+ # add an entry to the "testing" group
37
+ $ sudo roast add testing 127.0.0.1 exampleapp.dev
38
+
39
+ # disable all entries with the ip "10.0.1.1"
40
+ $ sudo roast disable 10.0.1.1
41
+
42
+ # delete an entry entirely
43
+ $ sudo roast delete exampleapp.dev
44
+ ```
45
+
46
+ *notice that you can list entries without sudo, but editing the hosts file does require sudo*
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/testtask'
3
+ require 'bundler/gem_tasks'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/roast ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
3
+
4
+ require 'roast'
5
+
6
+ Roast::CLI.new.run
@@ -0,0 +1,125 @@
1
+ module Roast
2
+ class CLI
3
+ module Commands
4
+ ALIASES = {
5
+ 'a' => 'add',
6
+ 'e' => 'enable',
7
+ 'eg' => 'enable-group',
8
+ 'd' => 'disable',
9
+ 'dg' => 'disable-group',
10
+ 'l' => 'list'
11
+ }
12
+
13
+ def dispatch
14
+ command = ARGV.shift
15
+ command = (ALIASES[command] || command).tr('-', '_')
16
+
17
+ if respond_to? command
18
+ @hosts_file = HostsFile.new.read
19
+ send(command, *ARGV)
20
+ else
21
+ puts "`#{command}' is an unknown command, use --help to see available commands"
22
+ exit
23
+ end
24
+ end
25
+
26
+ def add(*args)
27
+ if args.length < 2
28
+ raise ArgumentError, "You must provide an ip address and a hostname to point it too"
29
+ elsif args.length == 3
30
+ group = args.shift
31
+ else
32
+ group = 'base'
33
+ end
34
+
35
+ args.reverse! if args.last =~ Host::IP_PATTERN
36
+ ip_address, hostname = args
37
+
38
+ if @hosts_file.add(group, ip_address, hostname)
39
+ @hosts_file.write
40
+ puts "added host entry for `#{ip_address} \033[4m#{hostname}\033[0m'"
41
+ end
42
+ end
43
+
44
+ def enable(*args)
45
+ entry = args.first
46
+ results = @hosts_file.enable(entry)
47
+ if results.empty?
48
+ puts "no entries found matching `#{entry}'"
49
+ else
50
+ @hosts_file.write
51
+ puts "enabled entry#{results.length > 1 ? 's' : ''} matching `#{entry}'"
52
+ end
53
+ end
54
+
55
+ def disable(*args)
56
+ entry = args.first
57
+ results = @hosts_file.disable(entry)
58
+ if results.empty?
59
+ puts "no entries found matching `#{entry}'"
60
+ else
61
+ @hosts_file.write
62
+ puts "disabled entry#{results.length > 1 ? 's' : ''} matching `#{entry}'"
63
+ end
64
+ end
65
+
66
+ def delete(*args)
67
+ entry = args.first
68
+ results = @hosts_file.delete(entry)
69
+ if results.empty?
70
+ puts "no entries found matching `#{entry}'"
71
+ else
72
+ @hosts_file.write
73
+ puts "deleted entry#{results.length > 1 ? 's' : ''} matching `#{entry}'"
74
+ end
75
+ end
76
+
77
+ def enable_group(*args)
78
+ group = args.first
79
+
80
+ if @hosts_file.enable_group(group)
81
+ @hosts_file.write
82
+ puts "enabled group `#{group}'"
83
+ else
84
+ puts "Unable to enable the group `#{group}', it doesn't exist yet."
85
+ end
86
+ end
87
+
88
+ def disable_group(*args)
89
+ group = args.first
90
+
91
+ if @hosts_file.disable_group(group)
92
+ @hosts_file.write
93
+ puts "disabled group `#{group}'"
94
+ else
95
+ puts "Unable to disable the group `#{group}', it doesn't exist yet."
96
+ end
97
+ end
98
+
99
+ def delete_group(*args)
100
+ group = args.first
101
+
102
+ if @hosts_file.delete_group(group)
103
+ @hosts_file.write
104
+ puts "deleted group `#{group}'"
105
+ else
106
+ puts "Unable to delete the group `#{group}', it doesn't exist yet."
107
+ end
108
+ end
109
+
110
+ def list(*args)
111
+ # TODO: a bit awkward, use a class var?
112
+ path, groups = @hosts_file.list
113
+
114
+ if groups.empty?
115
+ puts "there are no roast entries in `#{path}'\n"
116
+ else
117
+ entries = ''
118
+ groups.each { |group| entries << group.to_cli }
119
+ puts entries.chomp
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+ end
data/lib/roast/cli.rb ADDED
@@ -0,0 +1,69 @@
1
+ module Roast
2
+ class CLI
3
+ include Roast::CLI::Commands
4
+
5
+ BANNER = <<-BANNER
6
+ Usage: roast COMMAND [ARGS]
7
+
8
+ Description:
9
+ The roast command manages groups/entries in your /etc/hosts file. It
10
+ has a few different commands available:
11
+
12
+ list list the entries in the hosts file alias: l
13
+ add adds a new entry to the hosts file alias: a
14
+ enable enables a disabled (commented out) entry alias: e
15
+ enable-group enables an entire group alias: eg
16
+ disable disables an entry (comments it out) alias: d
17
+ disable-group disables an entire group alias: dg
18
+ delete deletes an entry entirely
19
+ delete-group deletes an enitre group
20
+
21
+ Examples:
22
+ # list all entires
23
+ > roast list
24
+
25
+ # add an entry to the base group
26
+ > roast add 10.0.1.1 something.dev
27
+
28
+ # add an entry to the "testing" group
29
+ > roast add testing 127.0.0.1 exampleapp.dev
30
+
31
+ # disable all entries with the ip "10.0.1.1"
32
+ > roast disable 10.0.1.1
33
+ BANNER
34
+
35
+ # Use OptionParser to parse options, then we remove them from ARGV
36
+ def parse_options
37
+ @opts = OptionParser.new do |opts|
38
+ opts.banner = BANNER.gsub(/^ {4}/, '')
39
+
40
+ opts.separator ''
41
+ opts.separator 'Options:'
42
+
43
+ opts.on('-v', 'Print the version') do
44
+ puts Roast::VERSION
45
+ exit
46
+ end
47
+
48
+ opts.on( '-h', '--help', 'Display this help.' ) do
49
+ puts opts
50
+ exit
51
+ end
52
+ end
53
+
54
+ @opts.parse!
55
+ end
56
+
57
+ def print_usage_and_exit!
58
+ puts @opts
59
+ exit
60
+ end
61
+
62
+ # Called from the executable. Parses the command line arguments
63
+ def run
64
+ parse_options
65
+ print_usage_and_exit! if ARGV.empty?
66
+ dispatch
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,88 @@
1
+ module Roast
2
+ class Group
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+ @hosts = {}
8
+ end
9
+
10
+ def disable!
11
+ hosts.each { |h| h.disable! }
12
+ end
13
+
14
+ def enable!
15
+ hosts.each { |h| h.enable! }
16
+ end
17
+
18
+ def disabled?
19
+ hosts.all? { |h| h.disabled? }
20
+ end
21
+
22
+ def enabled?
23
+ hosts.all? { |h| h.enabled? }
24
+ end
25
+
26
+ def hosts
27
+ @hosts.values
28
+ end
29
+
30
+ def <<(host)
31
+ @hosts[host.hostname] = host
32
+ end
33
+
34
+ def [](hostname)
35
+ @hosts[hostname]
36
+ end
37
+
38
+ def find_host(entry)
39
+ if entry =~ Host::IP_PATTERN
40
+ hosts.select { |h| h.ip_address == entry }
41
+ else
42
+ hosts.select { |h| h.hostname == entry }
43
+ end
44
+ end
45
+
46
+ def delete_host(entry)
47
+ deleted = []
48
+ if entry =~ Host::IP_PATTERN
49
+ @hosts.each { |k, h| deleted << @hosts.delete(k) if h.ip_address == entry }
50
+ else
51
+ @hosts.each { |k, h| deleted << @hosts.delete(k) if h.hostname == entry }
52
+ end
53
+
54
+ deleted
55
+ end
56
+
57
+ def to_cli
58
+ string = " - \033[4m#{name}\033[0m\n"
59
+ max = hosts.map { |h| h.hostname.size }.max
60
+
61
+ hosts.each do |host|
62
+ padding = ' ' * (max - host.hostname.size + 4)
63
+ if host.disabled?
64
+ string << " \033[31m \u00D7 "
65
+ else
66
+ string << ' '
67
+ end
68
+ string << "#{host.hostname}#{padding}#{host.ip_address}\033[0m\n"
69
+ end
70
+
71
+ string
72
+ end
73
+
74
+ def to_hosts_file
75
+ max = hosts.map { |h| h.ip_address.size }.max
76
+ section = "## [#{name}]\n"
77
+
78
+ hosts.each do |host|
79
+ padding = ' ' * (max - host.ip_address.size + 4)
80
+ section << '# ' if host.disabled?
81
+ section << "#{host.ip_address}#{padding}#{host.hostname}\n"
82
+ end
83
+
84
+ section
85
+ end
86
+
87
+ end
88
+ end
data/lib/roast/host.rb ADDED
@@ -0,0 +1,37 @@
1
+ module Roast
2
+ class Host
3
+ IP_PATTERN = /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/
4
+ HOST_PATTERN = /\A[a-z0-9\-\.]+\z/
5
+
6
+ attr_reader :ip_address
7
+ attr_reader :hostname
8
+
9
+ def initialize(ip_address, hostname)
10
+ @ip_address = ip_address.chomp
11
+ @hostname = hostname.chomp.downcase
12
+ @state = 'enabled'
13
+ validate!
14
+ end
15
+
16
+ def validate!
17
+ raise ArgumentError, "`#{ip_address}' is an invalid ip address" unless ip_address =~ IP_PATTERN
18
+ raise ArgumentError, "`#{hostname}' is an invalid hostname" unless hostname =~ HOST_PATTERN
19
+ end
20
+
21
+ def disable!
22
+ @state = 'disabled'
23
+ end
24
+
25
+ def enable!
26
+ @state = 'enabled'
27
+ end
28
+
29
+ def disabled?
30
+ @state == 'disabled'
31
+ end
32
+
33
+ def enabled?
34
+ @state == 'enabled'
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,128 @@
1
+ module Roast
2
+ class HostsFile
3
+ GROUP_PATTERN = /^## \[([\w\s-]+)\]$/
4
+ HOST_PATTERN = /^#?\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+([^\s]+)/
5
+ DISABLED_PATTERN = /^# \d+/
6
+
7
+ attr_reader :path
8
+ attr_reader :static_lines
9
+
10
+ def initialize(path = '/etc/hosts')
11
+ @path = path
12
+ @static_lines = []
13
+ @groups = {}
14
+ end
15
+
16
+ def groups
17
+ @groups.values
18
+ end
19
+
20
+ def hosts
21
+ groups.map { |g| g.hosts }.flatten
22
+ end
23
+
24
+ def [](group)
25
+ @groups[group]
26
+ end
27
+
28
+ def read
29
+ in_group = false
30
+ group = nil
31
+
32
+ File.open(path, 'r') do |file|
33
+ file.each_line do |line|
34
+ if group_match = line.match(GROUP_PATTERN)
35
+ in_group = true
36
+ group = Group.new(group_match[1])
37
+ @groups[group.name] ||= group
38
+ elsif group && host_match = line.match(HOST_PATTERN)
39
+ host = Host.new(host_match[1], host_match[2])
40
+ host.disable! if line =~ DISABLED_PATTERN
41
+ group << host
42
+ else
43
+ static_lines << line
44
+ end
45
+ end
46
+ end
47
+
48
+ self
49
+ end
50
+
51
+ def write(output_path = nil)
52
+ output_path = output_path || path
53
+ temp_file = Tempfile.new('hosts')
54
+
55
+ temp_file << static_lines.join.sub(/\n{3,}\z/, "\n\n")
56
+ temp_file << groups.map { |g| g.to_hosts_file.chomp }.join("\n\n")
57
+
58
+ File.chmod(0644, temp_file)
59
+ FileUtils.cp(path, path + '.bak') if output_path.eql?(path)
60
+ FileUtils.mv(temp_file.path, output_path, :force => true)
61
+ ensure
62
+ temp_file.close
63
+ temp_file.unlink
64
+ end
65
+
66
+ def find_host(entry)
67
+ groups.map { |g| g.find_host(entry) }.flatten
68
+ end
69
+
70
+ def delete_host(entry)
71
+ groups.map { |g| g.delete_host(entry) }.flatten
72
+ end
73
+
74
+ def add(group, ip_address, hostname)
75
+ @groups[group] ||= Group.new(group)
76
+ @groups[group] << Host.new(ip_address, hostname)
77
+ end
78
+
79
+ def enable(entry)
80
+ results = find_host(entry)
81
+
82
+ results.each { |h| h.enable! }
83
+ results
84
+ end
85
+
86
+ def disable(entry)
87
+ results = find_host(entry)
88
+
89
+ results.each { |h| h.disable! }
90
+ results
91
+ end
92
+
93
+ def delete(entry)
94
+ results = delete_host(entry)
95
+
96
+ results
97
+ end
98
+
99
+ def enable_group(group)
100
+ return false unless @groups.has_key?(group)
101
+
102
+ @groups[group].enable!
103
+
104
+ true
105
+ end
106
+
107
+ def disable_group(group)
108
+ return false unless @groups.has_key?(group)
109
+
110
+ @groups[group].disable!
111
+
112
+ true
113
+ end
114
+
115
+ def delete_group(group)
116
+ return false unless @groups.has_key?(group)
117
+
118
+ @groups.delete(group)
119
+
120
+ true
121
+ end
122
+
123
+ def list
124
+ [path, groups]
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,3 @@
1
+ module Roast
2
+ VERSION = '0.1.0'
3
+ end
data/lib/roast.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'fileutils'
2
+ require 'optparse'
3
+ require 'tempfile'
4
+
5
+ require 'roast/cli/commands'
6
+ require 'roast/cli'
7
+ require 'roast/group'
8
+ require 'roast/host'
9
+ require 'roast/hosts_file'
10
+ require 'roast/version'
11
+
12
+ module Roast
13
+
14
+ end
data/roast.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'roast/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "roast"
8
+ spec.version = Roast::VERSION
9
+ spec.authors = ["blahed"]
10
+ spec.email = ["tdunn13@gmail.com"]
11
+ spec.description = "Roast is a simple /etc/hosts entry manager"
12
+ spec.summary = "Roast helps you group and manage entries in your hosts file"
13
+ spec.homepage = "https://github.com/blahed/roast/"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
22
+ spec.add_development_dependency 'minitest', '~> 3.0.0'
23
+ spec.add_development_dependency 'rake'
24
+ end
data/test/files/one ADDED
@@ -0,0 +1,22 @@
1
+ 127.0.0.1 localhost
2
+ 255.255.255.255 broadcasthost
3
+
4
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
5
+ 10.0.1.1 example.dev
6
+
7
+ ::1 localhost
8
+ fe80::1%lo0 localhost
9
+
10
+ ## [base]
11
+ 127.0.0.1 local.dev
12
+ 10.0.1.2 something.dev
13
+ 127.0.0.1 example.org
14
+
15
+ ## [foo]
16
+ # 10.0.3.1 foo.bar
17
+ # 10.0.3.2 food.bar
18
+
19
+ ## [staging]
20
+ 10.0.20.1 staging.something.com
21
+ 10.0.20.2 staging-two.something.com
22
+ # 10.10.30.1 staging.three.something.com
@@ -0,0 +1,82 @@
1
+ require 'test_helper'
2
+
3
+ describe Roast::Group do
4
+
5
+ before do
6
+ @group = Roast::Group.new(:base)
7
+ @group << Roast::Host.new('127.0.0.1', 'foo.bar.dev')
8
+ @group << Roast::Host.new('10.0.1.1', 'example.org')
9
+ end
10
+
11
+ it "adds a host to the group" do
12
+ host = Roast::Host.new('127.0.0.1', 'something.dev')
13
+ @group << host
14
+ @group.hosts.include?(host).must_equal true
15
+ end
16
+
17
+ it "disables and enables all hosts in the group" do
18
+ @group.disable!
19
+ @group.must_be :disabled?
20
+ @group.hosts.all? { |h| h.disabled? }.must_equal true
21
+
22
+ @group.enable!
23
+ @group.must_be :enabled?
24
+ @group.hosts.all? { |h| h.enabled? }.must_equal true
25
+ end
26
+
27
+ it "finds hosts by ip address" do
28
+ results = @group.find_host('127.0.0.1')
29
+ results.wont_be :empty?
30
+ results.size.must_equal 1
31
+ results.first.must_equal @group.hosts.first
32
+ end
33
+
34
+ it "finds hosts by hostname" do
35
+ results = @group.find_host('example.org')
36
+ results.wont_be :empty?
37
+ results.size.must_equal 1
38
+ results.first.must_equal @group.hosts.last
39
+ end
40
+
41
+ it "deletes hosts by ip address" do
42
+ deleted = @group.hosts.first
43
+ results = @group.delete_host('127.0.0.1')
44
+ results.wont_be :empty?
45
+ results.size.must_equal 1
46
+ results.first.must_equal deleted
47
+ @group.hosts.size.must_equal 1
48
+ @group.hosts.include?(deleted).wont_equal true
49
+ end
50
+
51
+ it "deletes hosts by hostname" do
52
+ deleted = @group.hosts.last
53
+ results = @group.delete_host('example.org')
54
+ results.wont_be :empty?
55
+ results.size.must_equal 1
56
+ results.first.must_equal deleted
57
+ @group.hosts.size.must_equal 1
58
+ @group.hosts.include?(deleted).wont_equal true
59
+ end
60
+
61
+ it "returns an empty array if host could not be found" do
62
+ results = @group.find_host('something.else')
63
+ results.must_be :empty?
64
+ end
65
+
66
+ it "outputs host entries to cli correctly" do
67
+ @group.to_cli.must_equal <<-RESULT.gsub(/^\s{4}/, "")
68
+ - \e[4mbase\e[0m
69
+ foo.bar.dev 127.0.0.1\e[0m
70
+ example.org 10.0.1.1\e[0m
71
+ RESULT
72
+ end
73
+
74
+ it "converts the group to a string correctly" do
75
+ @group.to_hosts_file.must_equal <<-RESULT.gsub(/^\s+/, "")
76
+ ## [base]
77
+ 127.0.0.1 foo.bar.dev
78
+ 10.0.1.1 example.org
79
+ RESULT
80
+ end
81
+
82
+ end
data/test/host_test.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ describe Roast::Host do
4
+
5
+ it "validates the ip address" do
6
+ ['foo', 'example.com', '127.0.0.1.1', '1277.0.0.1'].each do |ip|
7
+ lambda { Roast::Host.new(ip, 'blah.dev') }.must_raise ArgumentError
8
+ end
9
+ end
10
+
11
+ it "validates the hostname" do
12
+ ['foo_com', '@#$asdf.com'].each do |hostname|
13
+ lambda { Roast::Host.new('127.0.0.1', hostname) }.must_raise ArgumentError
14
+ end
15
+ end
16
+
17
+
18
+ end
@@ -0,0 +1,411 @@
1
+ require 'test_helper'
2
+
3
+ describe Roast::HostsFile do
4
+ FILES_PATH = File.expand_path("../files", __FILE__)
5
+
6
+ after do
7
+ baks = Dir[File.join(FILES_PATH, '*.bak')]
8
+ news = Dir[File.join(FILES_PATH, '*.new')]
9
+ FileUtils.rm(baks)
10
+ FileUtils.rm(news)
11
+ end
12
+
13
+ it "parses a hostfile and creates groups and host entries" do
14
+ hosts = hosts_from_file('one')
15
+ hosts.groups.length.must_equal 3
16
+ [
17
+ [ '127.0.0.1', 'local.dev' ],
18
+ [ '10.0.1.2', 'something.dev' ]
19
+ ].each_with_index do |host, i|
20
+ hosts['base'].hosts[i].ip_address.must_equal host.first
21
+ hosts['base'].hosts[i].hostname.must_equal host.last
22
+ end
23
+ [
24
+ [ '10.0.20.1', 'staging.something.com' ],
25
+ [ '10.0.20.2', 'staging-two.something.com' ]
26
+ ].each_with_index do |host, i|
27
+ hosts['staging'].hosts[i].ip_address.must_equal host.first
28
+ hosts['staging'].hosts[i].hostname.must_equal host.last
29
+ end
30
+ end
31
+
32
+ it "adds entries to an existing group" do
33
+ hosts = hosts_from_file('one')
34
+ new_path = File.join(FILES_PATH, 'one.new')
35
+ hosts.groups.length.must_equal 3
36
+ hosts.add('staging', '10.0.1.1', 'added-entry.dev')
37
+ hosts.write(new_path)
38
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
39
+ 127.0.0.1 localhost
40
+ 255.255.255.255 broadcasthost
41
+
42
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
43
+ 10.0.1.1 example.dev
44
+
45
+ ::1 localhost
46
+ fe80::1%lo0 localhost
47
+
48
+ ## [base]
49
+ 127.0.0.1 local.dev
50
+ 10.0.1.2 something.dev
51
+ 127.0.0.1 example.org
52
+
53
+ ## [foo]
54
+ # 10.0.3.1 foo.bar
55
+ # 10.0.3.2 food.bar
56
+
57
+ ## [staging]
58
+ 10.0.20.1 staging.something.com
59
+ 10.0.20.2 staging-two.something.com
60
+ # 10.10.30.1 staging.three.something.com
61
+ 10.0.1.1 added-entry.dev
62
+ RESULT
63
+ end
64
+
65
+ it "adds a new entry and group if the group doesn't exist" do
66
+ hosts = hosts_from_file('one')
67
+ new_path = File.join(FILES_PATH, 'one.new')
68
+
69
+ hosts.groups.length.must_equal 3
70
+ hosts.add('testing', '10.0.30.1', 'something.testing')
71
+ hosts.write(new_path)
72
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
73
+ 127.0.0.1 localhost
74
+ 255.255.255.255 broadcasthost
75
+
76
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
77
+ 10.0.1.1 example.dev
78
+
79
+ ::1 localhost
80
+ fe80::1%lo0 localhost
81
+
82
+ ## [base]
83
+ 127.0.0.1 local.dev
84
+ 10.0.1.2 something.dev
85
+ 127.0.0.1 example.org
86
+
87
+ ## [foo]
88
+ # 10.0.3.1 foo.bar
89
+ # 10.0.3.2 food.bar
90
+
91
+ ## [staging]
92
+ 10.0.20.1 staging.something.com
93
+ 10.0.20.2 staging-two.something.com
94
+ # 10.10.30.1 staging.three.something.com
95
+
96
+ ## [testing]
97
+ 10.0.30.1 something.testing
98
+ RESULT
99
+ end
100
+
101
+ it "disables an entry via hostname" do
102
+ hosts = hosts_from_file('one')
103
+ new_path = File.join(FILES_PATH, 'one.new')
104
+
105
+ hosts.groups.length.must_equal 3
106
+ hosts.disable('local.dev')
107
+ hosts.write(new_path)
108
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
109
+ 127.0.0.1 localhost
110
+ 255.255.255.255 broadcasthost
111
+
112
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
113
+ 10.0.1.1 example.dev
114
+
115
+ ::1 localhost
116
+ fe80::1%lo0 localhost
117
+
118
+ ## [base]
119
+ # 127.0.0.1 local.dev
120
+ 10.0.1.2 something.dev
121
+ 127.0.0.1 example.org
122
+
123
+ ## [foo]
124
+ # 10.0.3.1 foo.bar
125
+ # 10.0.3.2 food.bar
126
+
127
+ ## [staging]
128
+ 10.0.20.1 staging.something.com
129
+ 10.0.20.2 staging-two.something.com
130
+ # 10.10.30.1 staging.three.something.com
131
+ RESULT
132
+ end
133
+
134
+ it "disables entries via ip address" do
135
+ hosts = hosts_from_file('one')
136
+ new_path = File.join(FILES_PATH, 'one.new')
137
+
138
+ hosts.groups.length.must_equal 3
139
+ hosts.disable('127.0.0.1')
140
+ hosts.write(new_path)
141
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
142
+ 127.0.0.1 localhost
143
+ 255.255.255.255 broadcasthost
144
+
145
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
146
+ 10.0.1.1 example.dev
147
+
148
+ ::1 localhost
149
+ fe80::1%lo0 localhost
150
+
151
+ ## [base]
152
+ # 127.0.0.1 local.dev
153
+ 10.0.1.2 something.dev
154
+ # 127.0.0.1 example.org
155
+
156
+ ## [foo]
157
+ # 10.0.3.1 foo.bar
158
+ # 10.0.3.2 food.bar
159
+
160
+ ## [staging]
161
+ 10.0.20.1 staging.something.com
162
+ 10.0.20.2 staging-two.something.com
163
+ # 10.10.30.1 staging.three.something.com
164
+ RESULT
165
+ end
166
+
167
+ it "enables an entry via hostname" do
168
+ hosts = hosts_from_file('one')
169
+ new_path = File.join(FILES_PATH, 'one.new')
170
+
171
+ hosts.groups.length.must_equal 3
172
+ hosts.enable('staging.three.something.com')
173
+ hosts.write(new_path)
174
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
175
+ 127.0.0.1 localhost
176
+ 255.255.255.255 broadcasthost
177
+
178
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
179
+ 10.0.1.1 example.dev
180
+
181
+ ::1 localhost
182
+ fe80::1%lo0 localhost
183
+
184
+ ## [base]
185
+ 127.0.0.1 local.dev
186
+ 10.0.1.2 something.dev
187
+ 127.0.0.1 example.org
188
+
189
+ ## [foo]
190
+ # 10.0.3.1 foo.bar
191
+ # 10.0.3.2 food.bar
192
+
193
+ ## [staging]
194
+ 10.0.20.1 staging.something.com
195
+ 10.0.20.2 staging-two.something.com
196
+ 10.10.30.1 staging.three.something.com
197
+ RESULT
198
+ end
199
+
200
+ it "enables an entry via ip address" do
201
+ hosts = hosts_from_file('one')
202
+ new_path = File.join(FILES_PATH, 'one.new')
203
+
204
+ hosts.groups.length.must_equal 3
205
+ hosts.enable('10.10.30.1')
206
+ hosts.write(new_path)
207
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
208
+ 127.0.0.1 localhost
209
+ 255.255.255.255 broadcasthost
210
+
211
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
212
+ 10.0.1.1 example.dev
213
+
214
+ ::1 localhost
215
+ fe80::1%lo0 localhost
216
+
217
+ ## [base]
218
+ 127.0.0.1 local.dev
219
+ 10.0.1.2 something.dev
220
+ 127.0.0.1 example.org
221
+
222
+ ## [foo]
223
+ # 10.0.3.1 foo.bar
224
+ # 10.0.3.2 food.bar
225
+
226
+ ## [staging]
227
+ 10.0.20.1 staging.something.com
228
+ 10.0.20.2 staging-two.something.com
229
+ 10.10.30.1 staging.three.something.com
230
+ RESULT
231
+ end
232
+
233
+ it "enables a group" do
234
+ hosts = hosts_from_file('one')
235
+ new_path = File.join(FILES_PATH, 'one.new')
236
+
237
+ hosts.groups.length.must_equal 3
238
+ hosts.enable_group('foo')
239
+ hosts.write(new_path)
240
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
241
+ 127.0.0.1 localhost
242
+ 255.255.255.255 broadcasthost
243
+
244
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
245
+ 10.0.1.1 example.dev
246
+
247
+ ::1 localhost
248
+ fe80::1%lo0 localhost
249
+
250
+ ## [base]
251
+ 127.0.0.1 local.dev
252
+ 10.0.1.2 something.dev
253
+ 127.0.0.1 example.org
254
+
255
+ ## [foo]
256
+ 10.0.3.1 foo.bar
257
+ 10.0.3.2 food.bar
258
+
259
+ ## [staging]
260
+ 10.0.20.1 staging.something.com
261
+ 10.0.20.2 staging-two.something.com
262
+ # 10.10.30.1 staging.three.something.com
263
+ RESULT
264
+ end
265
+
266
+ it "disables a group" do
267
+ hosts = hosts_from_file('one')
268
+ new_path = File.join(FILES_PATH, 'one.new')
269
+
270
+ hosts.groups.length.must_equal 3
271
+ hosts.disable_group('staging')
272
+ hosts.write(new_path)
273
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
274
+ 127.0.0.1 localhost
275
+ 255.255.255.255 broadcasthost
276
+
277
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
278
+ 10.0.1.1 example.dev
279
+
280
+ ::1 localhost
281
+ fe80::1%lo0 localhost
282
+
283
+ ## [base]
284
+ 127.0.0.1 local.dev
285
+ 10.0.1.2 something.dev
286
+ 127.0.0.1 example.org
287
+
288
+ ## [foo]
289
+ # 10.0.3.1 foo.bar
290
+ # 10.0.3.2 food.bar
291
+
292
+ ## [staging]
293
+ # 10.0.20.1 staging.something.com
294
+ # 10.0.20.2 staging-two.something.com
295
+ # 10.10.30.1 staging.three.something.com
296
+ RESULT
297
+ end
298
+
299
+ it "deletes an entry via hostname" do
300
+ hosts = hosts_from_file('one')
301
+ new_path = File.join(FILES_PATH, 'one.new')
302
+
303
+ hosts.groups.length.must_equal 3
304
+ hosts.delete('local.dev')
305
+ hosts.write(new_path)
306
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
307
+ 127.0.0.1 localhost
308
+ 255.255.255.255 broadcasthost
309
+
310
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
311
+ 10.0.1.1 example.dev
312
+
313
+ ::1 localhost
314
+ fe80::1%lo0 localhost
315
+
316
+ ## [base]
317
+ 10.0.1.2 something.dev
318
+ 127.0.0.1 example.org
319
+
320
+ ## [foo]
321
+ # 10.0.3.1 foo.bar
322
+ # 10.0.3.2 food.bar
323
+
324
+ ## [staging]
325
+ 10.0.20.1 staging.something.com
326
+ 10.0.20.2 staging-two.something.com
327
+ # 10.10.30.1 staging.three.something.com
328
+ RESULT
329
+ end
330
+
331
+ it "deletes entries via ip address" do
332
+ hosts = hosts_from_file('one')
333
+ new_path = File.join(FILES_PATH, 'one.new')
334
+
335
+ hosts.groups.length.must_equal 3
336
+ hosts.delete('127.0.0.1')
337
+ hosts.write(new_path)
338
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
339
+ 127.0.0.1 localhost
340
+ 255.255.255.255 broadcasthost
341
+
342
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
343
+ 10.0.1.1 example.dev
344
+
345
+ ::1 localhost
346
+ fe80::1%lo0 localhost
347
+
348
+ ## [base]
349
+ 10.0.1.2 something.dev
350
+
351
+ ## [foo]
352
+ # 10.0.3.1 foo.bar
353
+ # 10.0.3.2 food.bar
354
+
355
+ ## [staging]
356
+ 10.0.20.1 staging.something.com
357
+ 10.0.20.2 staging-two.something.com
358
+ # 10.10.30.1 staging.three.something.com
359
+ RESULT
360
+ end
361
+
362
+ it "deletes a group" do
363
+ hosts = hosts_from_file('one')
364
+ new_path = File.join(FILES_PATH, 'one.new')
365
+
366
+ hosts.groups.length.must_equal 3
367
+ hosts.delete_group('staging')
368
+ hosts.write(new_path)
369
+ File.read(new_path).must_equal <<-RESULT.gsub(/^ +/, "").chomp
370
+ 127.0.0.1 localhost
371
+ 255.255.255.255 broadcasthost
372
+
373
+ 127.0.0.1 hl2rcv.adobe.com # HEHE
374
+ 10.0.1.1 example.dev
375
+
376
+ ::1 localhost
377
+ fe80::1%lo0 localhost
378
+
379
+ ## [base]
380
+ 127.0.0.1 local.dev
381
+ 10.0.1.2 something.dev
382
+ 127.0.0.1 example.org
383
+
384
+ ## [foo]
385
+ # 10.0.3.1 foo.bar
386
+ # 10.0.3.2 food.bar
387
+ RESULT
388
+ end
389
+
390
+ it "does not create a backup file if the output file is a different name" do
391
+ hosts = hosts_from_file('one')
392
+ hosts.groups.length.must_equal 3
393
+ hosts['base'] << Roast::Host.new('127.0.0.1', 'example.org')
394
+ hosts.write(File.join(FILES_PATH, 'two.new'))
395
+ File.exist?(File.join(FILES_PATH, 'one.bak')).wont_equal true
396
+ end
397
+
398
+ it "creates a backup file if the output file is the same as the input file" do
399
+ hosts = hosts_from_file('one')
400
+ hosts.groups.length.must_equal 3
401
+ hosts['base'] << Roast::Host.new('127.0.0.1', 'example.org')
402
+ hosts.write
403
+ File.exist?(File.join(FILES_PATH, 'one.bak')).must_equal true
404
+ end
405
+
406
+ def hosts_from_file(file_name)
407
+ path = File.join(FILES_PATH, file_name)
408
+ @hosts = Roast::HostsFile.new(path).read
409
+ end
410
+
411
+ end
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../../test', __FILE__))
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ Bundler.setup
6
+
7
+ require 'minitest/autorun'
8
+ require 'minitest/pride'
9
+
10
+ require 'roast'
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: roast
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - blahed
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: minitest
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 3.0.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Roast is a simple /etc/hosts entry manager
63
+ email:
64
+ - tdunn13@gmail.com
65
+ executables:
66
+ - roast
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - .travis.yml
72
+ - Gemfile
73
+ - LICENSE.txt
74
+ - README.md
75
+ - Rakefile
76
+ - bin/roast
77
+ - lib/roast.rb
78
+ - lib/roast/cli.rb
79
+ - lib/roast/cli/commands.rb
80
+ - lib/roast/group.rb
81
+ - lib/roast/host.rb
82
+ - lib/roast/hosts_file.rb
83
+ - lib/roast/version.rb
84
+ - roast.gemspec
85
+ - test/files/one
86
+ - test/group_test.rb
87
+ - test/host_test.rb
88
+ - test/hosts_file_test.rb
89
+ - test/test_helper.rb
90
+ homepage: https://github.com/blahed/roast/
91
+ licenses:
92
+ - MIT
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.23
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: Roast helps you group and manage entries in your hosts file
115
+ test_files:
116
+ - test/files/one
117
+ - test/group_test.rb
118
+ - test/host_test.rb
119
+ - test/hosts_file_test.rb
120
+ - test/test_helper.rb