roast 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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