nullobject-gemjour 1.1.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.rdoc ADDED
@@ -0,0 +1,50 @@
1
+ = Gemjour
2
+
3
+ Advertise, list, and install gems over Bonjour.
4
+
5
+ == Installation
6
+
7
+ sudo gem install dnssd
8
+ sudo gem install evanphx-gemjour --source=http://gems.github.com
9
+
10
+ == Usage
11
+
12
+ alice$ gemjour serve # advertises gems over Bonjour
13
+ bob$ gemjour list # finds alice
14
+ bob$ gemjour list alice # shows alice's gems
15
+ bob$ gemjour diff alice # shows differences between bob and alice gems
16
+ bob$ gemjour install_diff alice # installs any gem bob doesn't have installed from alice
17
+ bob$ gemjour search foo # searches for a gem "foo"
18
+
19
+ alice$ gem install foo.gem; gitjour serve
20
+ bob$ gemjour install alice foo
21
+ bob$ gemjour install alice foo -v 1.1.0
22
+
23
+ Prefix the commands with "sudo" as necessary.
24
+
25
+ == Awesome!
26
+
27
+ You know it.
28
+
29
+ == License
30
+
31
+ Copyright (c) 2008 Evan Phoenix
32
+
33
+ Permission is hereby granted, free of charge, to any person obtaining
34
+ a copy of this software and associated documentation files (the
35
+ "Software"), to deal in the Software without restriction, including
36
+ without limitation the rights to use, copy, modify, merge, publish,
37
+ distribute, sublicense, and/or sell copies of the Software, and to
38
+ permit persons to whom the Software is furnished to do so, subject to
39
+ the following conditions:
40
+
41
+ The above copyright notice and this permission notice shall be
42
+ included in all copies or substantial portions of the Software.
43
+
44
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
45
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
46
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
47
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
48
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
49
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
50
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require "date"
2
+ require "fileutils"
3
+ require "rubygems"
4
+ require "rake/gempackagetask"
5
+
6
+ require "./lib/gemjour/version.rb"
7
+
8
+ gemjour_gemspec = Gem::Specification.new do |s|
9
+ s.name = "gemjour"
10
+ s.version = Gemjour::VERSION
11
+ s.platform = Gem::Platform::RUBY
12
+ s.has_rdoc = true
13
+ s.extra_rdoc_files = ["README.rdoc"]
14
+ s.summary = "Advertise, list, and install gems over Bonjour."
15
+ s.description = s.summary
16
+ s.authors = ["Evan Phoenix", "Josh Bassett"]
17
+ s.email = "evan@fallingsnow.net"
18
+ s.homepage = "http://github.com/evanphx/gemjour"
19
+ s.require_path = "lib"
20
+ s.autorequire = "gemjour"
21
+ s.files = %w(README.rdoc Rakefile) + Dir.glob("{bin,lib}/**/*")
22
+ s.executables = %w(gemjour)
23
+
24
+ s.add_dependency "net-mdns", ">= 0.4.0"
25
+ end
26
+
27
+ Rake::GemPackageTask.new(gemjour_gemspec) do |pkg|
28
+ pkg.gem_spec = gemjour_gemspec
29
+ end
30
+
31
+ namespace :gem do
32
+ namespace :spec do
33
+ desc "Update gemjour.gemspec"
34
+ task :generate do
35
+ File.open("gemjour.gemspec", "w") do |f|
36
+ f.puts(gemjour_gemspec.to_ruby)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ task :install => :package do
43
+ sh %{sudo gem install --local pkg/gemjour-#{Gemjour::VERSION}}
44
+ end
45
+
46
+ desc "Remove all generated artifacts"
47
+ task :clean => :clobber_package
data/bin/gemjour ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require File.dirname(__FILE__) + '/../lib/gemjour'
5
+
6
+ begin
7
+ cmd = ARGV.shift
8
+
9
+ case cmd
10
+ when "serve"
11
+ Gemjour.serve(*ARGV)
12
+ when "list"
13
+ Gemjour.list(*ARGV)
14
+ when "show"
15
+ Gemjour.show(*ARGV)
16
+ when "search"
17
+ Gemjour.search(*ARGV)
18
+ when "diff"
19
+ Gemjour.diff(*ARGV)
20
+ when "install_diff"
21
+ Gemjour.install_diff(*ARGV)
22
+ when "install"
23
+ Gemjour.install(*ARGV)
24
+ else
25
+ Gemjour.usage
26
+ end
27
+ rescue => e
28
+ puts "ERROR: running '#{cmd}': #{e.message} (#{e.class})"
29
+ puts
30
+ Gemjour.usage
31
+ end
@@ -0,0 +1,3 @@
1
+ module Gemjour
2
+ VERSION = "1.1.0".freeze
3
+ end
data/lib/gemjour.rb ADDED
@@ -0,0 +1,217 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'net/dns/mdns-sd'
5
+ require 'set'
6
+ require 'gemjour/version'
7
+
8
+ DNSSD = Net::DNS::MDNSSD
9
+
10
+ Thread.abort_on_exception = true
11
+
12
+ module Gemjour
13
+ Server = Struct.new(:name, :host, :port)
14
+ PORT = 8808
15
+ SERVICE = "_rubygems._tcp"
16
+
17
+ GEM_PREAMBLE_RE = /$\s*\*\*\* REMOTE GEMS \*\*\*\s*/
18
+
19
+ def self.usage
20
+ puts <<-HELP
21
+ Usage:
22
+
23
+ serve [<name>] [<port>]
24
+ Start up a gem server as <name> on <port>. <name> is your username
25
+ by default, and <port> is 8088. If you want to use the default <name>,
26
+ pass it as "".
27
+
28
+ install <server> <gem> [<version>]
29
+ Installs <gem> from <server>. <server> is the name the server announces
30
+ itself as.
31
+
32
+ list
33
+ List all available gem servers.
34
+
35
+ show <server>
36
+ List all gems available from <server>.
37
+
38
+ search <string>
39
+ List all gems which contain <string>.
40
+
41
+ HELP
42
+ end
43
+
44
+ # Finds the host with the specified name.
45
+ def self.find(name)
46
+ host = nil
47
+
48
+ waiting = Thread.current
49
+
50
+ service = DNSSD.browse(SERVICE) do |reply|
51
+ if name === reply.name
52
+ DNSSD.resolve(reply.name, reply.type, reply.domain) do |rr|
53
+ host = Server.new(reply.name, rr.target, rr.port)
54
+ waiting.run
55
+ end
56
+ end
57
+ end
58
+
59
+ sleep 5
60
+ service.stop
61
+
62
+ host
63
+ end
64
+
65
+ # Finds all hosts.
66
+ def self.find_all
67
+ hosts = []
68
+
69
+ puts "Gathering for up to 5 seconds..."
70
+
71
+ waiting = Thread.current
72
+
73
+ service = DNSSD.browse(SERVICE) do |reply|
74
+ DNSSD.resolve(reply.name, reply.type, reply.domain) do |rr|
75
+ host = Server.new(reply.name, rr.target, rr.port)
76
+ unless hosts.include? host
77
+ hosts << host
78
+ end
79
+ end
80
+ end
81
+
82
+ sleep 5
83
+ service.stop
84
+
85
+ hosts
86
+ end
87
+
88
+ # Lists the hosts. Aliased to the show command if a name is specified.
89
+ def self.list(name = nil)
90
+ if name
91
+ show(name)
92
+ else
93
+ hosts = find_all
94
+ hosts.each { |host| print_host(host) }
95
+ end
96
+ end
97
+
98
+ def self.show(name)
99
+ results = {}
100
+ host = find(name)
101
+
102
+ unless host
103
+ puts "ERROR: Unable to find server '#{name}'"
104
+ return
105
+ end
106
+
107
+ # List gems on host.
108
+ output =`gem list -a -r --source=http://#{host.host}:#{host.port}`
109
+
110
+ gems = parse_gems(output)
111
+ results[host] = gems unless gems.empty?
112
+
113
+ print_gems(results)
114
+ end
115
+
116
+ def self.search(q)
117
+ results = {}
118
+ hosts = find_all
119
+
120
+ unless hosts.empty?
121
+ puts "Searching #{hosts.length} hosts..."
122
+
123
+ hosts.each do |host|
124
+ # Search for gems on host.
125
+ output = `gem search #{q} -a -r --source=http://#{host.host}:#{host.port}`
126
+
127
+ gems = parse_gems(output)
128
+ results[host] = gems unless gems.empty?
129
+ end
130
+ end
131
+
132
+ if results.empty?
133
+ puts "Search returned no results"
134
+ else
135
+ print_gems(results)
136
+ end
137
+ end
138
+
139
+ # Parses the gems from the raw gem output.
140
+ def self.parse_gems(s)
141
+ # Strip the preamble off the gem output.
142
+ s.gsub!(GEM_PREAMBLE_RE, '')
143
+
144
+ s.split(/\n/)
145
+ end
146
+
147
+ # Prints the gems for each host.
148
+ def self.print_gems(results)
149
+ results.each do |host, gems|
150
+ print_host(host)
151
+ gems.each do |gem|
152
+ puts "\t#{gem}"
153
+ end
154
+ end
155
+ end
156
+
157
+ def self.print_host(host)
158
+ puts "=== #{host.name} (#{host.host}:#{host.port})"
159
+ end
160
+
161
+ def self._diff(name)
162
+ host = find(name)
163
+
164
+ unless host
165
+ puts "ERROR: Unable to find server named '#{name}'"
166
+ return
167
+ end
168
+
169
+ require "tempfile"
170
+ local_gems, remote_gems = Tempfile.new("local_gems"), Tempfile.new("remote_gems")
171
+ local_gems.print(`gem list --no-versions --no-verbose`)
172
+ local_gems.close
173
+ remote_gems.print(`gem list --no-versions --no-verbose -r --source=http://#{host.host}:#{host.port}`)
174
+ remote_gems.close
175
+ `diff -u #{local_gems.path} #{remote_gems.path}`
176
+ end
177
+
178
+ def self.diff(name)
179
+ return unless diff = _diff(name)
180
+ puts diff
181
+ end
182
+
183
+ def self.install_diff(name)
184
+ return unless gem_diff = _diff(name)
185
+ gem_diff.scan(/^\+([\w_\-]+)/) do |match|
186
+ install(name, match)
187
+ end
188
+ end
189
+
190
+ def self.install(name, gem, version = nil)
191
+ host = find(name)
192
+
193
+ unless host
194
+ puts "ERROR: Unable to find server named '#{name}'"
195
+ return
196
+ end
197
+
198
+ puts "Installing gem '#{gem}' from #{host.host}:#{host.port}"
199
+
200
+ command = "gem install #{gem} -s http://#{host.host}:#{host.port}"
201
+ command << " #{version}" if version
202
+ system command
203
+ end
204
+
205
+ def self.serve(name = "", port = PORT)
206
+ name = ENV['USER'] if name.empty?
207
+
208
+ tr = DNSSD::TextRecord.new
209
+ tr['description'] = "#{name}'s gem server"
210
+
211
+ DNSSD.register(name, SERVICE, "local", port, tr.encode) do |reply|
212
+ puts "Serving gems under name '#{name}'..."
213
+ end
214
+
215
+ system "gem server --port=#{port} -q > /dev/null 2>&1"
216
+ end
217
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nullobject-gemjour
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Evan Phoenix
8
+ - Josh Bassett
9
+ autorequire: gemjour
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-03-26 00:00:00 -07:00
14
+ default_executable: gemjour
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: net-mdns
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.4.0
25
+ version:
26
+ description: Advertise, list, and install gems over Bonjour.
27
+ email: evan@fallingsnow.net
28
+ executables:
29
+ - gemjour
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - README.rdoc
34
+ files:
35
+ - README.rdoc
36
+ - Rakefile
37
+ - bin/gemjour
38
+ - lib/gemjour
39
+ - lib/gemjour/version.rb
40
+ - lib/gemjour.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/evanphx/gemjour
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.2.0
64
+ signing_key:
65
+ specification_version: 2
66
+ summary: Advertise, list, and install gems over Bonjour.
67
+ test_files: []
68
+