ghost 0.1.0-universal-darwin-9

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Bodaniel Jeanes
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,80 @@
1
+ Ghost 0.0.1
2
+ ===========
3
+
4
+ A gem that allows you to create, list, and modify local hostnames in 10.5 with ease...
5
+
6
+ Requirements
7
+ ============
8
+
9
+ This command is designed for users of Mac OS X Leopard and relies on a tool which I am pretty
10
+ sure was not in earlier versions of OS X and definitely isn't in other OSes. This uses the `dscl`
11
+ command to intelligently add hostnames to the database. An adapter to use the hosts file in other OSes is definitely a possibility, but not a priority at this time. Feel free to add it yourself and I'll merge it.
12
+
13
+ Intended Usage
14
+ ==============
15
+
16
+ This gem is designed primarily for web developers who need to add and modify hostnames to their system for virtual hosts on their local/remote web server. However, it could be of use to other people who would otherwise modify their `/etc/hosts` file and flush the cache.
17
+
18
+ Command
19
+ -------
20
+
21
+ $ ghost add mydevsite.local
22
+ $ ghost add staging-server.local 67.207.136.164
23
+ $ ghost list
24
+ Listing 2 host(s):
25
+ mydevsite.local -> 127.0.0.1
26
+ staging-server.local -> 67.207.136.164
27
+ $ ghost delete mydevsite.local
28
+ $ ghost list
29
+ Listing 1 host(s):
30
+ staging-server -> 67.207.136.164
31
+ $ ghost empty
32
+ Emptied host list.
33
+ $ ghost list
34
+ Listing 0 host(s):
35
+
36
+ Library
37
+ -------
38
+
39
+ There is also a library that can be used in Ruby scripts. The `ghost` command is a wrapper for
40
+ the library. View the source of `bin/ghost` to see how to use the library.
41
+
42
+ Sake Task
43
+ ---------
44
+
45
+ I also want to make this available as a Sake task to cater those who use it. It too will be a wrapper for the library. I just have to figure out how I can provide an easy way to install the sake tasks via the gem
46
+
47
+ Installation
48
+ ============
49
+
50
+ sudo gem install bjeanes-ghost --source http://gems.github.com/
51
+
52
+ Notes
53
+ =====
54
+
55
+ This library is not fully implemented yet. I am just putting this README up so that you can
56
+ see what the goals are.
57
+
58
+ Legal Stuff
59
+ ===========
60
+
61
+ Copyright (c) 2008 Bodaniel Jeanes
62
+
63
+ Permission is hereby granted, free of charge, to any person obtaining
64
+ a copy of this software and associated documentation files (the
65
+ "Software"), to deal in the Software without restriction, including
66
+ without limitation the rights to use, copy, modify, merge, publish,
67
+ distribute, sublicense, and/or sell copies of the Software, and to
68
+ permit persons to whom the Software is furnished to do so, subject to
69
+ the following conditions:
70
+
71
+ The above copyright notice and this permission notice shall be
72
+ included in all copies or substantial portions of the Software.
73
+
74
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
75
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
76
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
77
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
78
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
79
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
80
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+
6
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
7
+
8
+
9
+ #### MISC TASKS ####
10
+
11
+ desc "list tasks"
12
+ task :default do
13
+ puts `rake -T`.grep(/^[^(].*$/)
14
+ end
15
+
16
+ desc "Outstanding TODO's"
17
+ task :todo do
18
+ files = ["**/*.{rb,rake}" "bin/*", "README.mkdn"]
19
+
20
+ File.open('TODO','w') do |f|
21
+ FileList[*files].egrep(/TODO|FIXME/) do |file, line, text|
22
+ output = "#{file}:#{line} - #{text.chomp.gsub(/^\s+|\s+$/ , "")}"
23
+
24
+ puts output
25
+ f.puts output
26
+ end
27
+ end
28
+ end
data/TODO ADDED
File without changes
data/bin/ghost ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Bodaniel Jeanes on 2008-8-19.
4
+ # Copyright (c) 2008. All rights reserved.
5
+
6
+ begin
7
+ require 'rubygems'
8
+ require 'ghost'
9
+ rescue LoadError
10
+ # no rubygems to load, so we fail silently
11
+ end
12
+
13
+ def help_text(exit_code = 0)
14
+ script_name = File.basename $0
15
+ puts """USAGE: #{script_name} add <hostname> [<ip=127.0.1.0>]
16
+ #{script_name} delete <hostname>
17
+ #{script_name} list
18
+ #{script_name} empty
19
+ """
20
+ exit(exit_code)
21
+ end
22
+
23
+ if ARGV.size.zero? || ['-h', '--help', 'help'].include?(ARGV.first)
24
+ help_text
25
+ else
26
+ case ARGV[0]
27
+ when 'add':
28
+ if [2,3].include?(ARGV.size)
29
+ ARGV.shift
30
+ Host.add(*ARGV)
31
+ else
32
+ $stderr.puts "The add subcommand requires at least a hostname.\n\n"
33
+ help_text 2
34
+ end
35
+ when 'delete':
36
+ if ARGV.size == 2
37
+ Host.delete(ARGV[1])
38
+ else
39
+ $stderr.puts "The delete subcommand requires a hostname.\n\n"
40
+ help_text 2
41
+ end
42
+ when 'list':
43
+ hosts = Host.list
44
+ pad = hosts.max{|a,b| a.to_s.length <=> b.to_s.length }.to_s.length
45
+
46
+ puts "Listing #{hosts.size} host(s):"
47
+
48
+ hosts.each do |host|
49
+ puts "#{host.name.rjust(pad+2)} -> #{host.ip}"
50
+ end
51
+ exit 0
52
+ when 'empty':
53
+ Host.empty!
54
+ puts "Emptied host list."
55
+ exit 0
56
+ else
57
+ $stderr.puts "Invalid option: #{ARGV[0]}"
58
+ help_text 1
59
+ end
60
+ end
data/lib/ghost/host.rb ADDED
@@ -0,0 +1,97 @@
1
+ class Host
2
+ ListCmd = "dscl localhost -list /Local/Default/Hosts 2>&1"
3
+ ReadCmd = "dscl localhost -read /Local/Default/Hosts/%s 2>&1"
4
+ CreateCmd = "sudo dscl localhost -create /Local/Default/Hosts/%s IPAddress %s 2>&1"
5
+ DeleteCmd = "sudo dscl localhost -delete /Local/Default/Hosts/%s 2>&1"
6
+
7
+ class << self
8
+ protected :new
9
+
10
+ def list
11
+ list = `#{ListCmd}`
12
+ list.collect { |host| Host.new(host.chomp) }
13
+ end
14
+
15
+ def add(host, ip = "127.0.0.1", force = false)
16
+ if find_by_host(host).nil? || force
17
+ `#{CreateCmd % [host, ip]}`
18
+ flush!
19
+ find_by_host(host)
20
+ else
21
+ raise "Can not overwrite existing record"
22
+ end
23
+ end
24
+
25
+ def find_by_host(host)
26
+ @hosts ||= {}
27
+ @hosts[host] ||= begin
28
+ output = `#{ReadCmd % host}`
29
+
30
+ if output =~ /eDSRecordNotFound/
31
+ nil
32
+ else
33
+ host = parse_host(output)
34
+ ip = parse_ip(output)
35
+
36
+ Host.new(host, ip)
37
+ end
38
+ end
39
+ end
40
+
41
+ def find_by_ip(ip)
42
+ nil
43
+ end
44
+
45
+ def empty!
46
+ list.each { |h| delete(h) }
47
+ nil
48
+ end
49
+
50
+ def delete(host)
51
+ `#{DeleteCmd % host.to_s}`
52
+ flush!
53
+ end
54
+
55
+ # Flushes the DNS Cache
56
+ def flush!
57
+ `dscacheutil -flushcache`
58
+ @hosts = {}
59
+ true
60
+ end
61
+
62
+ protected
63
+ def parse_host(output)
64
+ parse_value(output, 'RecordName')
65
+ end
66
+
67
+ def parse_ip(output)
68
+ parse_value(output, 'IPAddress')
69
+ end
70
+
71
+ def parse_value(output, key)
72
+ match = output.match(Regexp.new("^#{key}: (.*)$"))
73
+ match[1] unless match.nil?
74
+ end
75
+ end
76
+
77
+ def initialize(host, ip=nil)
78
+ @host = host
79
+ @ip = ip
80
+ end
81
+
82
+ def hostname
83
+ @host
84
+ end
85
+ alias :to_s :hostname
86
+ alias :host :hostname
87
+ alias :name :hostname
88
+
89
+ def ip
90
+ @ip ||= self.class.send(:parse_ip, dump)
91
+ end
92
+
93
+ private
94
+ def dump
95
+ @dump ||= `#{ReadCmd % hostname}`
96
+ end
97
+ end
data/lib/ghost.rb ADDED
@@ -0,0 +1,2 @@
1
+ $: << File.dirname(__FILE__)
2
+ require 'ghost/host'
@@ -0,0 +1,120 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ # Warning: these tests will delete all hostnames in the system. Please back them up first
4
+
5
+ Host.empty!
6
+
7
+ describe Host, ".list" do
8
+ after(:each) { Host.empty! }
9
+
10
+ it "should return an array" do
11
+ Host.list.should be_instance_of(Array)
12
+ end
13
+
14
+ it "should contain instances of Host" do
15
+ Host.add('ghost-test-hostname.local')
16
+ Host.list.first.should be_instance_of(Host)
17
+ end
18
+ end
19
+
20
+ describe Host do
21
+ after(:each) { Host.empty! }
22
+
23
+ it "should have an IP" do
24
+ hostname = 'ghost-test-hostname.local'
25
+
26
+ Host.add(hostname)
27
+ host = Host.list.first
28
+ host.ip.should eql('127.0.0.1')
29
+
30
+ Host.empty!
31
+
32
+ ip = '169.254.23.121'
33
+ host = Host.add(hostname, ip)
34
+ host.ip.should eql(ip)
35
+ end
36
+
37
+ it "should have a hostname" do
38
+ hostname = 'ghost-test-hostname.local'
39
+
40
+ Host.add(hostname)
41
+ host = Host.list.first
42
+ host.hostname.should eql(hostname)
43
+
44
+ Host.empty!
45
+
46
+ ip = '169.254.23.121'
47
+ Host.add(hostname, ip)
48
+ host.hostname.should eql(hostname)
49
+ end
50
+
51
+ it ".to_s should return hostname" do
52
+ hostname = 'ghost-test-hostname.local'
53
+
54
+ Host.add(hostname)
55
+ host = Host.list.first
56
+ host.to_s.should eql(hostname)
57
+ end
58
+ end
59
+
60
+ describe Host, "finder methods" do
61
+ after(:all) { Host.empty! }
62
+ before(:all) do
63
+ Host.add('abc.local')
64
+ Host.add('def.local')
65
+ Host.add('efg.local', '10.2.2.4')
66
+ end
67
+
68
+ it "should return valid Host when searching for host name" do
69
+ Host.find_by_host('abc.local').should be_instance_of(Host)
70
+ end
71
+
72
+ end
73
+
74
+ describe Host, ".add" do
75
+ after(:each) { Host.empty! }
76
+
77
+ it "should return Host object when passed hostname" do
78
+ Host.add('ghost-test-hostname.local').should be_instance_of(Host)
79
+ end
80
+
81
+ it "should return Host object when passed hostname" do
82
+ Host.add('ghost-test-hostname.local', '10.0.0.2').should be_instance_of(Host)
83
+ end
84
+
85
+ it "should raise error if hostname already exists and not add a duplicate" do
86
+ Host.empty!
87
+ Host.add('ghost-test-hostname.local')
88
+ lambda { Host.add('ghost-test-hostname.local') }.should raise_error
89
+ Host.list.should have(1).thing
90
+ end
91
+
92
+ it "should overwrite existing hostname if forced" do
93
+ hostname = 'ghost-test-hostname.local'
94
+
95
+ Host.empty!
96
+ Host.add(hostname)
97
+
98
+ Host.list.first.hostname.should eql(hostname)
99
+ Host.list.first.ip.should eql('127.0.0.1')
100
+
101
+ Host.add(hostname, '10.0.0.1', true)
102
+ Host.list.first.hostname.should eql(hostname)
103
+ Host.list.first.ip.should eql('10.0.0.1')
104
+
105
+ Host.list.should have(1).thing
106
+ end
107
+ end
108
+
109
+ describe Host, ".empty!" do
110
+ it "should empty the hostnames" do
111
+ Host.add('ghost-test-hostname.local') # add a hostname to be sure
112
+ Host.empty!
113
+ Host.list.should have(0).things
114
+ end
115
+ end
116
+
117
+ describe Host, ".backup and", Host, ".restore" do
118
+ it "should return a yaml file of all hosts and IPs when backing up"
119
+ it "should empty the hosts and restore only the ones in given yaml"
120
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,3 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'ghost'
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ghost
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: universal-darwin-9
6
+ authors:
7
+ - Bodaniel Jeanes
8
+ autorequire: ghost
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-09-19 00:00:00 +10:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Allows you to create, list, and modify .local hostnames in 10.5 with ease
17
+ email: me@bjeanes.com
18
+ executables:
19
+ - ghost
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - LICENSE
25
+ - TODO
26
+ files:
27
+ - LICENSE
28
+ - README
29
+ - Rakefile
30
+ - TODO
31
+ - bin/ghost
32
+ - lib/ghost
33
+ - lib/ghost/host.rb
34
+ - lib/ghost.rb
35
+ - spec/ghost_spec.rb
36
+ - spec/spec.opts
37
+ - spec/spec_helper.rb
38
+ has_rdoc: true
39
+ homepage: http://github.com/bjeanes/ghost
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --title Ghost
43
+ - --main README
44
+ - --line-numbers
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements:
60
+ - Mac OS X Leopard (10.5)
61
+ rubyforge_project: ghost
62
+ rubygems_version: 1.2.0
63
+ signing_key:
64
+ specification_version: 2
65
+ summary: Allows you to create, list, and modify .local hostnames in 10.5 with ease
66
+ test_files: []
67
+