ghost 0.1.0-universal-darwin-9

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/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
+