allgems 0.0.1

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.
File without changes
@@ -0,0 +1,16 @@
1
+ = AllGems
2
+
3
+ == Description
4
+
5
+ AllGems is a simple project with a hefty goal: Provide easy access to documentation for all RubyGem files.
6
+
7
+ == How?
8
+
9
+ ==== Backend
10
+
11
+ AllGems runs a small daemon that monitors the index of any sources registered with the locally installed RubyGems. This daemon automatically retrieves, unpacks, and generates documentation on libraries as they are released.
12
+
13
+ ==== Frontend
14
+
15
+ AllGems uses a heavily modified version of Gembox to allow access to available documentation.
16
+
@@ -0,0 +1,24 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'allgems'
3
+ s.author = %q(spox)
4
+ s.email = %q(spox@modspox.com)
5
+ s.version = '0.0.1'
6
+ s.summary = %q(Tools to document the world)
7
+ s.platform = Gem::Platform::RUBY
8
+ s.files = Dir['**/*']
9
+ s.rdoc_options = %w(--title AllGems --main README.rdoc --line-numbers)
10
+ s.extra_rdoc_files = %w(README.rdoc CHANGELOG)
11
+ s.require_paths = %w(lib)
12
+ s.executables = %w(allgems)
13
+ s.required_ruby_version = '>= 1.8.6'
14
+ s.homepage = %q(http://github.com/spox/allgems)
15
+ s.description = 'AllGems is a tool to provide comprehensive gem documentation for an entire index'
16
+ s.add_dependency 'sequel'
17
+ s.add_dependency 'ActionPool'
18
+ s.add_dependency 'ActionTimer'
19
+ s.add_dependency 'haml'
20
+ s.add_dependency 'rdoc'
21
+ s.add_dependency 'sdoc'
22
+ s.add_dependency 'hanna'
23
+ s.add_dependency 'nokogiri'
24
+ end
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ SUCCESS = 0
5
+ CONFIGURATION_ERROR = 1
6
+
7
+ def display_help
8
+ $stdout.puts "\t\tAllGems - Document Everything
9
+ usage: allgems [opts]
10
+ --config -c:\t\t\t\tOutput apache configuration
11
+ --notall -n:\t\t\t\tDon't fetch all versions
12
+ --daemonize -D:\t\t\t\tRun as a daemon
13
+ --datadir -d /path:\t\t\tData directory for documents/gems (defaults to /tmp/allgems)
14
+ --dbfile -b /path/file.db\t\tPath to sqlite file (defaults datadir/allgems.db)
15
+ --formatter -f rdoc[,hanna,sdoc]:\t\tRDoc formatter (defaults to rdoc (which is darkfish))
16
+ --interval -i \\d+:\t\t\tSeconds to wait before updating documents (defaults to 1 hour)
17
+ --runners -r \\d+:\t\t\tNumber of threads to use (defaults to 10)
18
+ --log -L [/path/file]:\t\t\tTurn logging on (no argument defaults to STDOUT)
19
+ --verbosity -V [(debug|info|warn|fatal)]:\tLogging level (no argument defaults to info)
20
+ --help -h:\t\t\t\tDisplay this help text"
21
+ end
22
+
23
+ # load in our libraries
24
+ ['logger', 'fileutils', 'getoptlong', 'allgems', 'sequel'].each{|f|require f}
25
+
26
+ # figure out what system we are on
27
+ require 'rbconfig'
28
+ ON_WINDOWS = Config::CONFIG['host_os'] =~ /mswin|mingw/
29
+
30
+ # set default values
31
+ config_output = false
32
+ daemonize = false
33
+ datadir = '/tmp/allgems'
34
+ formatter = nil
35
+ interval = 3600 # default to hourly checks
36
+ runners = 10 # default to 10 threads
37
+ dbfile = nil
38
+ logto = nil
39
+ level = Logger::INFO
40
+ all = true
41
+
42
+ # figure out what we are doing
43
+ opts = GetoptLong.new(
44
+ ['--daemonize', '-D', GetoptLong::NO_ARGUMENT],
45
+ ['--datadir', '-d', GetoptLong::REQUIRED_ARGUMENT],
46
+ ['--dbfile', '-b', GetoptLong::REQUIRED_ARGUMENT],
47
+ ['--formatter', '-f', GetoptLong::REQUIRED_ARGUMENT],
48
+ ['--interval', '-i', GetoptLong::REQUIRED_ARGUMENT],
49
+ ['--runners', '-r', GetoptLong::REQUIRED_ARGUMENT],
50
+ ['--version', '-v', GetoptLong::NO_ARGUMENT],
51
+ ['--log', '-L', GetoptLong::OPTIONAL_ARGUMENT],
52
+ ['--verbosity', '-V', GetoptLong::OPTIONAL_ARGUMENT],
53
+ ['--help', '-h', GetoptLong::NO_ARGUMENT],
54
+ ['--config', '-c', GetoptLong::NO_ARGUMENT],
55
+ ['--notall', '-n', GetoptLong::NO_ARGUMENT]
56
+ )
57
+
58
+ opts.each do |opt, arg|
59
+ case opt
60
+ when '--daemonize'
61
+ daemonize = true
62
+ when '--datadir'
63
+ datadir = arg
64
+ when '--formatter'
65
+ formatter = arg
66
+ when '--interval'
67
+ interval = arg.to_i
68
+ when '--runners'
69
+ runners = arg.to_i
70
+ when '--dbfile'
71
+ dbfile = arg
72
+ when '--version'
73
+ puts "VERSION INFO"
74
+ when '--log'
75
+ logto = arg.empty? ? $stdout: arg
76
+ when '--verbosity'
77
+ case arg
78
+ when 'debug'
79
+ level = Logger::DEBUG
80
+ when 'info'
81
+ level = Logger::INFO
82
+ when 'warn'
83
+ level = Logger::WARN
84
+ when 'error'
85
+ level = Logger::ERROR
86
+ when 'fatal'
87
+ level = Logger::FATAL
88
+ else
89
+ level = Logger::INFO
90
+ end
91
+ when '--notall'
92
+ all = false
93
+ when '--config'
94
+ config_output = true
95
+ when '--help'
96
+ display_help
97
+ exit SUCCESS
98
+ end
99
+ end
100
+
101
+ dbfile = "#{datadir}/allgems.db" if dbfile.nil?
102
+ interval = nil unless daemonize
103
+
104
+ # yell at user for bad values
105
+ if(interval && interval <= 0)
106
+ $stderr.puts "ERROR: interval value must be greater than 1"
107
+ exit CONFIGURATION_ERROR
108
+ end
109
+ if(runners <= 0)
110
+ $stderr.puts "ERROR: runners value must be greater than 1"
111
+ exit CONFIGURATION_ERROR
112
+ end
113
+ unless(File.writable?(datadir))
114
+ created = false
115
+ #check if we can/should create it
116
+ unless(File.exists?(datadir))
117
+ begin
118
+ FileUtils.mkdir_p datadir
119
+ created = true
120
+ rescue SystemCallError
121
+ created = false
122
+ end
123
+ end
124
+ datadir = File.expand_path(datadir)
125
+ unless(created)
126
+ $stderr.puts "ERROR: data directory is not writable. (#{datadir})"
127
+ exit CONFIGURATION_ERROR
128
+ end
129
+ end
130
+ unless(File.exists?(dbfile))
131
+ begin
132
+ FileUtils.touch dbfile
133
+ rescue SystemCallError
134
+ end
135
+ end
136
+ unless(File.writable?(dbfile))
137
+ $stderr.puts "ERROR: database file is not writable. (#{dbfile})"
138
+ exit CONFIGURATION_ERROR
139
+ end
140
+
141
+ # set everything
142
+ AllGems.defaulterize
143
+ AllGems.data_directory = datadir
144
+ AllGems.doc_format = formatter unless formatter.nil?
145
+ AllGems.logger = Logger.new(logto, level)
146
+ AllGems.allgems = all
147
+
148
+ # are we just giving out configuration?
149
+ if(config_output)
150
+ puts <<-EOC
151
+ Apache virtual host configuration:
152
+
153
+ <VirtualHost *:80>
154
+ # Uncomment the next line and add your servers name if wanted
155
+ # ServerName YourServerNameHere.com
156
+
157
+ SetEnv DATA_DIR #{AllGems.data_directory}
158
+ SetEnv DATA_DB #{dbfile}
159
+
160
+ Alias /docs #{AllGems.data_directory}
161
+ Alias /docs/ #{AllGems.data_directory}/
162
+ Alias #{AllGems.data_directory}/ #{AllGems.data_directory}/
163
+ <Location "/docs">
164
+ PassengerEnabled off
165
+ RewriteEngine on
166
+ RewriteRule allgems.db / [R]
167
+ RewriteRule ^.+?rdoc.*?/tmp/allgems/(.*)$ /docs/$1 [R]
168
+ </Location>
169
+ <Location "#{AllGems.data_directory}">
170
+ PassengerEnabled off
171
+ RewriteEngine on
172
+ RewriteRule .*?rdocs/.*?/tmp/allgems/(.+)$ /docs/$1 [R]
173
+ RewriteRule (.*) /docs/$1 [R]
174
+ </Location>
175
+ <Directory #{AllGems.data_directory}>
176
+ Options -Indexes
177
+ </Directory>
178
+ ErrorDocument 403 /gems
179
+ DocumentRoot #{AllGems.public_directory}
180
+ </VirtualHost>
181
+
182
+ Required apache modules:
183
+ mod_rewrite
184
+ mod_env
185
+ mod_alias
186
+ EOC
187
+ exit SUCCESS
188
+ end
189
+
190
+ # are we supposed to be a daemon?
191
+ if(daemonize)
192
+ if(RUBY_VERSION > '1.9.0')
193
+ Process.daemon
194
+ else
195
+ if(pid = fork)
196
+ Signal.trap('HUP', 'IGNORE')
197
+ Process.detach(pid)
198
+ exit
199
+ end
200
+ end
201
+ end
202
+
203
+ # Make sure we play nice before we get started
204
+
205
+ Process.setpriority(Process::PRIO_PROCESS, 0, 19) unless ON_WINDOWS
206
+
207
+ # finally, lets get going
208
+
209
+ require 'allgems/Runner'
210
+ runner = AllGems::Runner.new(:db_path => dbfile, :runners => runners, :interval => interval)
211
+ trap('SIGINT'){runner.stop(true)}
212
+ trap('SIGHUP'){runner.stop; runner.do_sync} unless ON_WINDOWS
213
+ runner.do_sync
214
+ runner.stop unless daemonize
215
+ exit SUCCESS
@@ -0,0 +1,11 @@
1
+ %w(allgems allgems/App haml sass sequel sequel/extensions/pagination).each{|f|require f}
2
+
3
+ AllGems.defaulterize
4
+ AllGems.data_directory = ENV['DATA_DIR']
5
+ AllGems.initialize_db(Sequel.connect("sqlite://#{ENV['DATA_DB']}"))
6
+
7
+ disable :run
8
+ AllGems::App.set({
9
+ :environment => :development
10
+ })
11
+ run AllGems::App
@@ -0,0 +1,69 @@
1
+ require 'logger'
2
+ module AllGems
3
+ class << self
4
+ attr_accessor :data_directory, :logger, :db, :allgems
5
+ def defaulterize
6
+ @data_directory = nil
7
+ @doc_format = ['rdoc']
8
+ @logger = Logger.new(nil)
9
+ @db = nil
10
+ @tg = nil
11
+ @ti = nil
12
+ @allgems = true
13
+ @refresh = Time.now.to_i + 3600
14
+ end
15
+ # db: Sequel::Database
16
+ # Run any migrations needed
17
+ # NOTE: Call before using the database
18
+ def initialize_db(db)
19
+ self.db = db
20
+ require 'sequel/extensions/migration'
21
+ Sequel::Migrator.apply(db, "#{File.expand_path(__FILE__).gsub(/\/[^\/]+$/, '')}/allgems/migrations")
22
+ end
23
+ # Format for documentation
24
+ def doc_format
25
+ @doc_format
26
+ end
27
+ # f:: format
28
+ # Sets format for documentation. Should be one of: hanna, rdoc, sdoc
29
+ def doc_format=(f)
30
+ @doc_format = []
31
+ f.split(',').each do |type|
32
+ type = type.to_sym
33
+ raise ArgumentError.new("Valid types: hanna, sdoc, rdoc") unless [:hanna,:sdoc,:rdoc].include?(type)
34
+ @doc_format << type
35
+ end
36
+ end
37
+ # Location of public directory
38
+ def public_directory
39
+ File.expand_path("#{__FILE__}/../../public")
40
+ end
41
+ # Total number of unique gem names installed
42
+ def total_gems
43
+ return 0 if self.db.nil?
44
+ if(@tg.nil? || Time.now.to_i > @refresh)
45
+ @tg = self.db[:gems].count
46
+ @refresh = Time.now.to_i + 3600
47
+ end
48
+ @tg
49
+ end
50
+ # Total number of actual gems installed
51
+ def total_installs
52
+ return 0 if self.db.nil?
53
+ if(@ti.nil? || Time.now.to_i > @refresh)
54
+ @ti = self.db[:versions].count
55
+ @refresh = Time.now.to_i + 3600
56
+ end
57
+ @ti
58
+ end
59
+ # Newest gem based on release date
60
+ def newest_gem
61
+ g = AllGems.db[:versions].join(:gems, :id => :gem_id).order(:release.desc).limit(1).select(:name, :release).first
62
+ {:name => g[:name], :release => g[:release]}
63
+ end
64
+ # Path to hanna hack
65
+ def hanna_hack
66
+ File.expand_path("#{__FILE__}/../allgems/hanna_hack.rb")
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,92 @@
1
+ require 'sinatra'
2
+ require 'allgems/ViewHelpers'
3
+ require 'allgems/Specer'
4
+ require 'rubygems/specification'
5
+
6
+ module AllGems
7
+
8
+ class App < ::Sinatra::Default
9
+
10
+ include AllGems::ViewHelpers
11
+
12
+ @@root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
13
+ set :root, @@root
14
+ set :app_file, __FILE__
15
+
16
+ before do
17
+ @environment = options.environment
18
+ end
19
+
20
+ get '/stylesheets/:stylesheet.css' do
21
+ sass params[:stylesheet].to_sym
22
+ end
23
+
24
+ # root is nothing, so redirect people on their way
25
+ get '/' do
26
+ redirect '/gems'
27
+ end
28
+
29
+ # generate the gem listing
30
+ get '/gems/?' do
31
+ show_layout = params[:layout] != 'false'
32
+ @show_as = params[:as] && params[:as] == 'table' ? 'table' : 'columns'
33
+ set = AllGems.db[:gems].order(:name.asc)
34
+ @page = params[:page] ? params[:page].to_i : 1
35
+ if(@search = params[:search])
36
+ set = do_search(params[:search])
37
+ if(set.count == 1)
38
+ redirect "/gems/#{set.first[:name]}" # send user on their way if we only get one result
39
+ end
40
+ end
41
+ @gems = set.paginate(@page, 30)
42
+ haml "gems_#{@show_as}".to_sym, :layout => show_layout
43
+ end
44
+
45
+ # send the the correct place
46
+ get %r{/gems/([\w\-\_]+)/?([\d\.]+)?/?} do
47
+ gem = params[:captures][0]
48
+ version = params[:captures].size > 1 ? params[:captures][1] : nil
49
+ @gem = load_gem_spec(gem, version)
50
+ @versions = AllGems.db[:versions].join(:gems, :id => :gem_id).filter(:name => gem).order(:version.desc).select(:version, :release)
51
+ haml :gem
52
+ end
53
+
54
+ get %r{/doc/(.+)} do
55
+ parts = params[:captures][0].split('/')
56
+ @gem_name = parts[0]
57
+ @gem_version = parts[1]
58
+ @path = "/docs/#{params[:captures][0]}"
59
+ haml :doc, :layout => false
60
+ end
61
+
62
+ get %r{/load/([^/]+)/(.+)/?} do
63
+ @gem_name = params[:captures][0]
64
+ @gem_version = params[:captures][1]
65
+ haml :load, :layout => false
66
+ end
67
+
68
+ private
69
+
70
+ def load_gem_spec(gem, version=nil)
71
+ version ||= get_latest(gem)
72
+ raise 'failed gem' unless version
73
+ Specer.get_spec(gem, version)
74
+ end
75
+
76
+ # gem:: name of the gem
77
+ # Returns the latest version of the given gem or nil
78
+ def get_latest(gem)
79
+ AllGems.db[:versions].join(:gems, :id => :gem_id).filter(:name => gem).order(:version.desc).limit(1).select(:version).map(:version)[0]
80
+ end
81
+
82
+ # terms:: terms to search on
83
+ #
84
+ def do_search(terms)
85
+ terms = terms.split.map{|t|"%#{t}%"}
86
+ names = AllGems.db[:gems].filter("#{[].fill('name LIKE ?', 0, terms.size).join(' OR ')}", *terms).order(:name.asc)
87
+ desc = AllGems.db[:gems].filter("#{[].fill('description LIKE ?', 0, terms.size).join(' OR ')}", *terms).order(:name.asc)
88
+ summ = AllGems.db[:gems].filter("#{[].fill('summary LIKE ?', 0, terms.size).join(' OR ')}", *terms).order(:name.asc)
89
+ names.union(desc).union(summ)
90
+ end
91
+ end
92
+ end
File without changes
@@ -0,0 +1,229 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'rubygems/installer'
4
+ require 'allgems/Indexer'
5
+
6
+ module AllGems
7
+ class GemWorker
8
+ class << self
9
+ attr_reader :pool
10
+ # Get the worker ready to go
11
+ def setup
12
+ @glock = Mutex.new
13
+ @slock = Mutex.new
14
+ @pool = ActionPool::Pool.new(:max_threads => 10, :a_to => 60*5)
15
+ end
16
+ # :name:: name of the gem
17
+ # :version:: version of the gem
18
+ # :database:: database connection
19
+ def process(args)
20
+ AllGems.logger.info "Processing gem: #{args[:name]}-#{args[:version]}"
21
+ spec,uri = self.get_spec(args[:name], args[:version])
22
+ raise NameError.new("Name not found: #{args[:name]} - #{args[:version]}") if spec.nil?
23
+ basedir = "#{AllGems.data_directory}/#{spec.name}/#{spec.version.version}"
24
+ FileUtils.mkdir_p "#{basedir}/unpack"
25
+ AllGems.logger.info "Created new directory: #{basedir}/unpack"
26
+ gempath = self.fetch(spec, uri, "#{basedir}/#{spec.full_name}.gem")
27
+ self.unpack(gempath, basedir)
28
+ self.generate_documentation(spec, basedir)
29
+ self.save_data(spec, args[:database])
30
+ end
31
+
32
+ # name:: name of gem
33
+ # version:: version of gem
34
+ # Fetches the Gem::Specification for the given gem
35
+ def get_spec(name, version)
36
+ AllGems.logger.info "Fetching gemspec for #{name}-#{version}"
37
+ dep = Gem::Dependency.new(name, version)
38
+ spec = nil
39
+ @glock.synchronize{spec = Gem::SpecFetcher.fetcher.fetch dep, true}
40
+ spec[0]
41
+ end
42
+
43
+ # spec:: Gem::Specification
44
+ # uri:: URI of gem files home
45
+ # save_path:: path to save gem
46
+ # Fetch the gem file from the server. Returns the path to the gem
47
+ # on the local machine
48
+ def fetch(spec, uri, save_path)
49
+ AllGems.logger.info "Fetching gem from: #{uri}/gems/#{spec.full_name}.gem"
50
+ FileUtils.touch(save_path)
51
+ begin
52
+ remote_path = "#{uri}/gems/#{spec.full_name}.gem"
53
+ remote_uri = URI.parse(remote_path)
54
+ file = File.open(save_path, 'wb')
55
+ file.write(self.fetch_remote(remote_uri))
56
+ file.close
57
+ save_path
58
+ rescue StandardError => boom
59
+ raise FetchError.new(spec.name, spec.version, remote_path, boom)
60
+ end
61
+ end
62
+
63
+ # uri:: URI of gem
64
+ # depth:: number of times called
65
+ # Fetch gem from given URI
66
+ def fetch_remote(uri, depth=0)
67
+ raise IOError.new("Depth too deep") if depth > 9
68
+ response = Net::HTTP.get_response(uri)
69
+ if(response.is_a?(Net::HTTPSuccess))
70
+ response.body
71
+ elsif(response.is_a?(Net::HTTPRedirection))
72
+ self.fetch_remote(URI.parse(response['location']), depth + 1)
73
+ else
74
+ raise IOError.new("Unknown response type: #{response}")
75
+ end
76
+ end
77
+
78
+ # gempath:: path to gem file
79
+ # newname:: path to move gem file to
80
+ # Moves the gem to the given location
81
+ def save(gempath, newpath)
82
+ AllGems.logger.info "Moving #{gempath} to #{newpath}"
83
+ FileUtils.mv gempath, newpath
84
+ newpath
85
+ end
86
+
87
+ # path:: path to the gem file
88
+ # basedir:: directory to unpack in
89
+ # depth:: number of times called
90
+ # Unpacks the gem into the basedir under the 'unpack' directory
91
+ def unpack(path, basedir, depth=0)
92
+ AllGems.logger.info "Unpacking gem: #{path}"
93
+ begin
94
+ Gem::Installer.new(path, :unpack => true).unpack "#{basedir}/unpack"
95
+ FileUtils.chmod_R(0755, "#{basedir}/unpack") # fix any bad permissions
96
+ FileUtils.rm(path)
97
+ rescue
98
+ if(File.size(path) < 1 || depth > 10)
99
+ raise IOError.new("Failed to unpack gem: #{path}") unless self.direct_unpack(path, basedir)
100
+ else
101
+ self.unpack(path, basedir, depth+1)
102
+ end
103
+ end
104
+ true
105
+ end
106
+
107
+ # path:: path to the gem file
108
+ # basedir:: directory to unpack in
109
+ # Last ditch effort to unpack the gem
110
+ def direct_unpack(path, basedir)
111
+ AllGems.logger.warn "Attempting forcible unpack on: #{path}"
112
+ unpackdir = path.slice(0, path.rindex('.'))
113
+ self.run_command("cd #{basedir} && gem unpack #{path} && mv #{unpackdir}/* #{basedir}/unpack/ && rm -rf #{unpackdir}")
114
+ end
115
+
116
+ # dir:: base directory location of gem contents
117
+ # Generates the documentation of the given directory of ruby code. Appends
118
+ # 'unpack' to the given directory for code discovery. Documentation will
119
+ # be output in "#{dir}/doc"
120
+ def generate_documentation(spec, dir)
121
+ AllGems.logger.info "Generating documentation for #{spec.full_name}"
122
+ AllGems.doc_format.each do |f|
123
+ command = nil
124
+ args = []
125
+ case f.to_sym
126
+ when :rdoc
127
+ command = 'rdoc'
128
+ args << '--format=darkfish' << '-aFNqH' << "--op=#{dir}/doc/rdoc" << "#{dir}/unpack"
129
+ when :sdoc
130
+ command = 'sdoc'
131
+ args << '-T direct' << "-o #{dir}/doc/sdoc" << "#{dir}/unpack"
132
+ when :hanna
133
+ command = "ruby #{AllGems.hanna_hack}"
134
+ args << "-o #{dir}/doc/hanna" << "#{dir}/unpack"
135
+ else
136
+ next # if we don't know what to do with it, skip it
137
+ end
138
+ action = lambda do |spec, dir, command, args, f|
139
+ FileUtils.rm_r("#{dir}/doc/#{f}", :force => true) # make sure we are clean before we get dirty
140
+ result = self.run_command("#{command} #{args}")
141
+ raise DocError.new(spec.name, spec.version) unless result
142
+ AllGems.logger.info "Completed documentation for #{spec.full_name}"
143
+ FileUtils.chmod_R(0755, "#{dir}/doc/#{f}") # fix any bad permissions
144
+ Indexer.index_gem(spec, "#{dir}/doc/#{f}", f)
145
+ end
146
+ @pool << [action, [spec, dir.dup, command.dup, args.join(' '), f]]
147
+ end
148
+ end
149
+
150
+ # command:: command to run
151
+ # return_output:: return output
152
+ # Runs a command. Returns true if status returns 0. If return_output is true,
153
+ # return value is: [status, output]
154
+ def run_command(command, return_output=false)
155
+ AllGems.logger.debug "Command to be executed: #{command}"
156
+ pro = nil
157
+ output = []
158
+ status = nil
159
+ begin
160
+ pro = IO.popen(command)
161
+ Process.setpriority(Process::PRIO_PROCESS, pro.pid, 19) unless ON_WINDOWS
162
+ until(pro.closed? || pro.eof?)
163
+ output << pro.gets
164
+ end
165
+ ensure
166
+ unless(pro.nil?)
167
+ pid, status = Process.waitpid2(pro.pid)
168
+ else
169
+ status = 1
170
+ end
171
+ end
172
+ return return_output ? [status == 0, output.join] : status == 0
173
+ end
174
+
175
+ # spec:: Gem::Specification
176
+ # Save data to the database about this gem
177
+ def save_data(spec, db)
178
+ AllGems.logger.info "Saving meta data for #{spec.full_name}"
179
+ @slock.synchronize do
180
+ gid = db[:gems].filter(:name => spec.name).first
181
+ gid = gid.nil? ? db[:gems].insert(:name => spec.name) : gid[:id]
182
+ pid = db[:platforms].filter(:platform => spec.platform).first
183
+ pid = pid.nil? ? db[:platforms].insert(:platform => spec.platform) : pid[:id]
184
+ vid = db[:versions] << {:version => spec.version.version, :gem_id => gid, :platform_id => pid, :release => spec.date}
185
+ db[:specs] << {:version_id => vid, :spec => [Marshal.dump(spec)].pack('m')}
186
+ db[:gems].filter(:id => gid).update(:summary => spec.summary)
187
+ db[:gems].filter(:id => gid).update(:description => spec.description)
188
+ end
189
+ AllGems.logger.info "Meta data saving complete for #{spec.full_name}"
190
+ true
191
+ end
192
+
193
+ # Make a parent error class that we can specialize
194
+ class Error < StandardError
195
+ attr_reader :original
196
+ def initialize(e=nil)
197
+ @original = e.nil? ? self : e
198
+ end
199
+ end
200
+
201
+ # Exception class for failed documentation creation
202
+ class DocError < Error
203
+ attr_reader :gem_name, :gem_version
204
+ def initialize(gn, gv, e=nil)
205
+ super(e)
206
+ @gem_name = gn
207
+ @gem_version = gv
208
+ end
209
+ def to_s
210
+ "Failed to create documentation for: #{@gem_name}-#{@gem_version}."
211
+ end
212
+ end
213
+
214
+ # Exception class for failed gem fetching
215
+ class FetchError < Error
216
+ attr_reader :gem_name, :gem_version, :uri
217
+ def initialize(gn, gv, u, e=nil)
218
+ super(e)
219
+ @gem_name = gn
220
+ @gem_version = gv
221
+ @uri = u
222
+ end
223
+ def to_s
224
+ "Failed to fetch #{@gem_name}-#{@gem_version}.gem from #{uri}"
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end