roast 0.1.2 → 0.2.0

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.
data/README.md CHANGED
@@ -36,6 +36,9 @@ $ sudo roast add 10.0.1.1 something.dev
36
36
  # add an entry to the "testing" group
37
37
  $ sudo roast add testing 127.0.0.1 exampleapp.dev
38
38
 
39
+ # add an entry to the "testing" group via another hostname (resolve the ip)
40
+ $ sudo roast add testing example.org exampleapp.dev
41
+
39
42
  # disable all entries with the ip "10.0.1.1"
40
43
  $ sudo roast disable 10.0.1.1
41
44
 
@@ -1,6 +1,7 @@
1
1
  require 'fileutils'
2
2
  require 'optparse'
3
3
  require 'tempfile'
4
+ require 'socket'
4
5
 
5
6
  require 'roast/cli/commands'
6
7
  require 'roast/cli'
@@ -21,23 +21,30 @@ module Roast
21
21
  puts "`#{command}' is an unknown command, use --help to see available commands"
22
22
  exit
23
23
  end
24
+ rescue ArgumentError => e
25
+ puts e.message
26
+ exit 1
27
+ end
28
+
29
+ def confirm(message = 'Are you sure that you want to do that?')
30
+ puts "#{message} [\"yes\" or \"no\"]"
31
+ exit if $stdin.gets.chomp !~ /\Ay(?:es)?\Z/i
24
32
  end
25
33
 
26
34
  def add(*args)
27
35
  if args.length < 2
28
- raise ArgumentError, "You must provide an ip address and a hostname to point it too"
36
+ raise ArgumentError, "You must provide an ip address and a hostname to point it to: `roast add 127.0.0.1 something.dev'"
29
37
  elsif args.length == 3
30
38
  group = args.shift
31
39
  else
32
- group = 'base'
40
+ group = nil
33
41
  end
34
42
 
35
- args.reverse! if args.last =~ Host::IP_PATTERN
36
- ip_address, hostname = args
43
+ source, hostname = args
37
44
 
38
- if @hosts_file.add(group, ip_address, hostname)
45
+ if @hosts_file.add(group, source, hostname)
39
46
  @hosts_file.write
40
- puts "added host entry for `#{ip_address} \033[4m#{hostname}\033[0m'"
47
+ puts "added host entry for `#{source} \033[4m#{hostname}\033[0m'"
41
48
  end
42
49
  end
43
50
 
@@ -65,6 +72,7 @@ module Roast
65
72
 
66
73
  def delete(*args)
67
74
  entry = args.first
75
+ confirm("Are you sure that you want to delete entries matching `#{entry}'?")
68
76
  results = @hosts_file.delete(entry)
69
77
  if results.empty?
70
78
  puts "no entries found matching `#{entry}'"
@@ -98,7 +106,7 @@ module Roast
98
106
 
99
107
  def delete_group(*args)
100
108
  group = args.first
101
-
109
+ confirm("Are you sure that you want to delete the group `#{group}'?")
102
110
  if @hosts_file.delete_group(group)
103
111
  @hosts_file.write
104
112
  puts "deleted group `#{group}'"
@@ -115,7 +123,9 @@ module Roast
115
123
  puts "there are no roast entries in `#{path}'\n"
116
124
  else
117
125
  entries = ''
118
- groups.each { |group| entries << group.to_cli }
126
+ indent = groups.map { |g| g.hosts.map { |h| h.hostname.size }.max }.max
127
+
128
+ groups.each { |group| entries << group.to_cli(indent) }
119
129
  puts entries.chomp
120
130
  end
121
131
  end
@@ -54,9 +54,9 @@ module Roast
54
54
  deleted
55
55
  end
56
56
 
57
- def to_cli
57
+ def to_cli(max_indent = nil)
58
58
  string = " - \033[4m#{name}\033[0m\n"
59
- max = hosts.map { |h| h.hostname.size }.max
59
+ max = max_indent || hosts.map { |h| h.hostname.size }.max
60
60
 
61
61
  hosts.each do |host|
62
62
  padding = ' ' * (max - host.hostname.size + 4)
@@ -65,7 +65,9 @@ module Roast
65
65
  else
66
66
  string << ' '
67
67
  end
68
- string << "#{host.hostname}#{padding}#{host.ip_address}\033[0m\n"
68
+ string << "#{host.hostname}#{padding}#{host.ip_address}"
69
+ string << " # #{host.alias}" if host.alias
70
+ string << "\033[0m\n"
69
71
  end
70
72
 
71
73
  string
@@ -78,7 +80,9 @@ module Roast
78
80
  hosts.each do |host|
79
81
  padding = ' ' * (max - host.ip_address.size + 4)
80
82
  section << '# ' if host.disabled?
81
- section << "#{host.ip_address}#{padding}#{host.hostname}\n"
83
+ section << "#{host.ip_address}#{padding}#{host.hostname}"
84
+ section << " # #{host.alias}" if host.alias
85
+ section << "\n"
82
86
  end
83
87
 
84
88
  section
@@ -1,23 +1,35 @@
1
1
  module Roast
2
2
  class Host
3
- IP_PATTERN = /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/
3
+ IP_PATTERN = /\A([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}\z/
4
4
  HOST_PATTERN = /\A[a-z0-9\-\.]+\z/
5
5
 
6
- attr_reader :ip_address
7
- attr_reader :hostname
6
+ attr_reader :ip_address
7
+ attr_reader :hostname
8
+ attr_reader :state
9
+ attr_accessor :alias
8
10
 
9
- def initialize(ip_address, hostname)
10
- @ip_address = ip_address.chomp
11
+ def initialize(source, hostname)
12
+ if source !~ IP_PATTERN
13
+ @alias = source.chomp
14
+ resolve_source
15
+ else
16
+ @ip_address = source.chomp
17
+ end
11
18
  @hostname = hostname.chomp.downcase
12
19
  @state = 'enabled'
13
20
  validate!
14
21
  end
15
22
 
16
23
  def validate!
17
- raise ArgumentError, "`#{ip_address}' is an invalid ip address" unless ip_address =~ IP_PATTERN
18
24
  raise ArgumentError, "`#{hostname}' is an invalid hostname" unless hostname =~ HOST_PATTERN
19
25
  end
20
26
 
27
+ def resolve_source
28
+ @ip_address = IPSocket.getaddress(@alias)
29
+ rescue SocketError
30
+ raise ArgumentError, "unable to determine the ip address of `#{@alias}'"
31
+ end
32
+
21
33
  def disable!
22
34
  @state = 'disabled'
23
35
  end
@@ -1,7 +1,7 @@
1
1
  module Roast
2
2
  class HostsFile
3
3
  GROUP_PATTERN = /^## \[([\w\s-]+)\]$/
4
- HOST_PATTERN = /^#?\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+([^\s]+)/
4
+ HOST_PATTERN = /^#?\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+([a-z0-9\.\-]+)\s*(?:#\s*(\S+))?$/
5
5
  DISABLED_PATTERN = /^# \d+/
6
6
 
7
7
  attr_reader :path
@@ -37,6 +37,7 @@ module Roast
37
37
  @groups[group.name] ||= group
38
38
  elsif group && host_match = line.match(HOST_PATTERN)
39
39
  host = Host.new(host_match[1], host_match[2])
40
+ host.alias = host_match[3]
40
41
  host.disable! if line =~ DISABLED_PATTERN
41
42
  group << host
42
43
  else
@@ -46,6 +47,9 @@ module Roast
46
47
  end
47
48
 
48
49
  self
50
+ rescue Errno::EACCES
51
+ puts "Unable to read hosts file: `#{@path}', you might need to `sudo roast'"
52
+ exit 1
49
53
  end
50
54
 
51
55
  def write(output_path = nil)
@@ -54,10 +58,14 @@ module Roast
54
58
 
55
59
  temp_file << static_lines.join.sub(/\n{3,}\z/, "\n\n")
56
60
  temp_file << groups.map { |g| g.to_hosts_file.chomp }.join("\n\n")
57
-
58
61
  File.chmod(0644, temp_file.path)
59
- FileUtils.cp(path, path + '.roast.bak') if output_path.eql?(path)
60
- FileUtils.mv(temp_file.path, output_path, :force => true)
62
+ begin
63
+ FileUtils.cp(path, path + '.roast.bak') if output_path.eql?(path)
64
+ FileUtils.mv(temp_file.path, output_path, :force => true)
65
+ rescue Errno::EACCES
66
+ puts "Unable to write to hosts file: `#{@path}', you might need to `sudo roast'"
67
+ exit 1
68
+ end
61
69
  ensure
62
70
  temp_file.close
63
71
  temp_file.unlink
@@ -71,9 +79,10 @@ module Roast
71
79
  groups.map { |g| g.delete_host(entry) }.flatten
72
80
  end
73
81
 
74
- def add(group, ip_address, hostname)
82
+ def add(group, source, hostname)
83
+ group ||= 'base'
75
84
  @groups[group] ||= Group.new(group)
76
- @groups[group] << Host.new(ip_address, hostname)
85
+ @groups[group] << Host.new(source, hostname)
77
86
  end
78
87
 
79
88
  def enable(entry)
@@ -1,3 +1,3 @@
1
1
  module Roast
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -20,5 +20,6 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency 'bundler', '~> 1.3'
22
22
  spec.add_development_dependency 'minitest', '~> 3.0.0'
23
+ spec.add_development_dependency 'mocha', '~> 0.13.3'
23
24
  spec.add_development_dependency 'rake'
24
25
  end
@@ -10,7 +10,7 @@ fe80::1%lo0 localhost
10
10
  ## [base]
11
11
  127.0.0.1 local.dev
12
12
  10.0.1.2 something.dev
13
- 127.0.0.1 example.org
13
+ 127.0.0.1 example.org # whatever.org
14
14
 
15
15
  ## [foo]
16
16
  # 10.0.3.1 foo.bar
@@ -2,10 +2,33 @@ require 'test_helper'
2
2
 
3
3
  describe Roast::Host do
4
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
5
+ it "initializes a Host" do
6
+ host = Roast::Host.new('127.0.0.1', 'blah.dev')
7
+ host.hostname.must_equal 'blah.dev'
8
+ host.ip_address.must_equal '127.0.0.1'
9
+ host.state.must_equal 'enabled'
10
+ host.must_be :enabled?
11
+ end
12
+
13
+ it "disables a host" do
14
+ host = Roast::Host.new('127.0.0.1', 'blah.dev')
15
+ host.state.must_equal 'enabled'
16
+ host.disable!
17
+ host.state.must_equal 'disabled'
18
+ host.must_be :disabled?
19
+ end
20
+
21
+ it "enables a host" do
22
+ host = Roast::Host.new('127.0.0.1', 'blah.dev')
23
+ host.disable!
24
+ host.must_be :disabled?
25
+ host.enable!
26
+ host.must_be :enabled?
27
+ end
28
+
29
+ it "attempts to resolve a source hostname if given" do
30
+ Roast::Host.any_instance.expects(:resolve_source)
31
+ Roast::Host.new('google.com', 'blah.dev')
9
32
  end
10
33
 
11
34
  it "validates the hostname" do
@@ -15,10 +15,12 @@ describe Roast::HostsFile do
15
15
  hosts.groups.length.must_equal 3
16
16
  [
17
17
  [ '127.0.0.1', 'local.dev' ],
18
- [ '10.0.1.2', 'something.dev' ]
18
+ [ '10.0.1.2', 'something.dev' ],
19
+ [ '127.0.0.1', 'example.org', 'whatever.org']
19
20
  ].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
21
+ hosts['base'].hosts[i].ip_address.must_equal host[0]
22
+ hosts['base'].hosts[i].hostname.must_equal host[1]
23
+ hosts['base'].hosts[i].alias.must_equal host[2] if host[2]
22
24
  end
23
25
  [
24
26
  [ '10.0.20.1', 'staging.something.com' ],
@@ -48,7 +50,7 @@ describe Roast::HostsFile do
48
50
  ## [base]
49
51
  127.0.0.1 local.dev
50
52
  10.0.1.2 something.dev
51
- 127.0.0.1 example.org
53
+ 127.0.0.1 example.org # whatever.org
52
54
 
53
55
  ## [foo]
54
56
  # 10.0.3.1 foo.bar
@@ -82,7 +84,7 @@ describe Roast::HostsFile do
82
84
  ## [base]
83
85
  127.0.0.1 local.dev
84
86
  10.0.1.2 something.dev
85
- 127.0.0.1 example.org
87
+ 127.0.0.1 example.org # whatever.org
86
88
 
87
89
  ## [foo]
88
90
  # 10.0.3.1 foo.bar
@@ -118,7 +120,7 @@ describe Roast::HostsFile do
118
120
  ## [base]
119
121
  # 127.0.0.1 local.dev
120
122
  10.0.1.2 something.dev
121
- 127.0.0.1 example.org
123
+ 127.0.0.1 example.org # whatever.org
122
124
 
123
125
  ## [foo]
124
126
  # 10.0.3.1 foo.bar
@@ -151,7 +153,7 @@ describe Roast::HostsFile do
151
153
  ## [base]
152
154
  # 127.0.0.1 local.dev
153
155
  10.0.1.2 something.dev
154
- # 127.0.0.1 example.org
156
+ # 127.0.0.1 example.org # whatever.org
155
157
 
156
158
  ## [foo]
157
159
  # 10.0.3.1 foo.bar
@@ -184,7 +186,7 @@ describe Roast::HostsFile do
184
186
  ## [base]
185
187
  127.0.0.1 local.dev
186
188
  10.0.1.2 something.dev
187
- 127.0.0.1 example.org
189
+ 127.0.0.1 example.org # whatever.org
188
190
 
189
191
  ## [foo]
190
192
  # 10.0.3.1 foo.bar
@@ -217,7 +219,7 @@ describe Roast::HostsFile do
217
219
  ## [base]
218
220
  127.0.0.1 local.dev
219
221
  10.0.1.2 something.dev
220
- 127.0.0.1 example.org
222
+ 127.0.0.1 example.org # whatever.org
221
223
 
222
224
  ## [foo]
223
225
  # 10.0.3.1 foo.bar
@@ -250,7 +252,7 @@ describe Roast::HostsFile do
250
252
  ## [base]
251
253
  127.0.0.1 local.dev
252
254
  10.0.1.2 something.dev
253
- 127.0.0.1 example.org
255
+ 127.0.0.1 example.org # whatever.org
254
256
 
255
257
  ## [foo]
256
258
  10.0.3.1 foo.bar
@@ -283,7 +285,7 @@ describe Roast::HostsFile do
283
285
  ## [base]
284
286
  127.0.0.1 local.dev
285
287
  10.0.1.2 something.dev
286
- 127.0.0.1 example.org
288
+ 127.0.0.1 example.org # whatever.org
287
289
 
288
290
  ## [foo]
289
291
  # 10.0.3.1 foo.bar
@@ -315,7 +317,7 @@ describe Roast::HostsFile do
315
317
 
316
318
  ## [base]
317
319
  10.0.1.2 something.dev
318
- 127.0.0.1 example.org
320
+ 127.0.0.1 example.org # whatever.org
319
321
 
320
322
  ## [foo]
321
323
  # 10.0.3.1 foo.bar
@@ -379,7 +381,7 @@ describe Roast::HostsFile do
379
381
  ## [base]
380
382
  127.0.0.1 local.dev
381
383
  10.0.1.2 something.dev
382
- 127.0.0.1 example.org
384
+ 127.0.0.1 example.org # whatever.org
383
385
 
384
386
  ## [foo]
385
387
  # 10.0.3.1 foo.bar
@@ -392,15 +394,14 @@ describe Roast::HostsFile do
392
394
  hosts.groups.length.must_equal 3
393
395
  hosts['base'] << Roast::Host.new('127.0.0.1', 'example.org')
394
396
  hosts.write(File.join(FILES_PATH, 'two.new'))
395
- File.exist?(File.join(FILES_PATH, 'one.bak')).wont_equal true
397
+ File.exist?(File.join(FILES_PATH, 'one.roast.bak')).wont_equal true
396
398
  end
397
399
 
398
400
  it "creates a backup file if the output file is the same as the input file" do
399
401
  hosts = hosts_from_file('one')
400
402
  hosts.groups.length.must_equal 3
401
- hosts['base'] << Roast::Host.new('127.0.0.1', 'example.org')
402
403
  hosts.write
403
- File.exist?(File.join(FILES_PATH, 'one.bak')).must_equal true
404
+ File.exist?(File.join(FILES_PATH, 'one.roast.bak')).must_equal true
404
405
  end
405
406
 
406
407
  def hosts_from_file(file_name)
@@ -7,4 +7,6 @@ Bundler.setup
7
7
  require 'minitest/autorun'
8
8
  require 'minitest/pride'
9
9
 
10
+ require 'mocha/setup'
11
+
10
12
  require 'roast'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roast
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-13 00:00:00.000000000 Z
12
+ date: 2013-03-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -43,6 +43,22 @@ dependencies:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
45
  version: 3.0.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: mocha
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.13.3
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.13.3
46
62
  - !ruby/object:Gem::Dependency
47
63
  name: rake
48
64
  requirement: !ruby/object:Gem::Requirement