#!/usr/local/bin/ruby # # Gem server that uses Mongrel versus WebBrick # Adapted the code to use Mongrel from the original gem_server # # # author Dave Bryson require 'rubygems' Gem.manage_gems require 'mongrel' require 'yaml' require 'optparse' require 'rdoc/template' # Handle request for the yaml file class YamlHandler < Mongrel::HttpHandler def initialize(spec_dir) @spec_dir = spec_dir end def process(request, response) response.start do |head,out| head["Content-Type"] = "text/plain" head["data"] = File.stat(spec_dir).mtime out << Gem::SourceIndex.from_gems_in(@spec_dir).to_yaml end end end # Handle the embedded CSS class CssHandler < Mongrel::HttpHandler def initialize(spec_dir, css) @spec_dir = spec_dir @css = css end def process(request, response) response.start do |head,out| head["Content-Type"] = "text/css" head["data"] = File.stat(@spec_dir).mtime out << @css end end end # Handle the Gems class GemHandler < Mongrel::HttpHandler def initialize(spec_dir, template) @spec_dir = spec_dir @template = template end def process(request, response) response.start do |head,out| specs = [] total_file_count = 0 Gem::SourceIndex.from_gems_in(@spec_dir).each do |path, spec| total_file_count += spec.files.size deps = spec.dependencies.collect { |dep| { "name" => dep.name, "version" => dep.version_requirements.to_s, } } deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] } deps.last["is_last"] = true unless deps.empty? # executables executables = spec.executables.sort.collect { |exec| {"executable" => exec} } executables = nil if executables.empty? executables.last["is_last"] = true if executables specs << { "authors" => spec.authors.sort.join(", "), "date" => spec.date.to_s, "dependencies" => deps, "doc_path" => ('/doc_root/' + spec.full_name + '/rdoc/index.html'), "executables" => executables, "only_one_executable" => (executables && executables.size==1), "full_name" => spec.full_name, "has_deps" => !deps.empty?, "homepage" => spec.homepage, "name" => spec.name, "rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?, "summary" => spec.summary, "version" => spec.version.to_s, } end specs << { "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others", "dependencies" => [], "doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html", "executables" => [{"executable" => 'gem', "is_last" => true}], "only_one_executable" => true, "full_name" => "rubygems-#{Gem::RubyGemsVersion}", "has_deps" => false, "homepage" => "http://rubygems.org/", "name" => 'rubygems', "rdoc_installed" => true, "summary" => "RubyGems itself", "version" => Gem::RubyGemsVersion, } specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] } specs.last["is_last"] = true # tag all specs with first_name_entry last_spec = nil specs.each do |spec| is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase) spec["first_name_entry"] = is_first last_spec = spec end # create page from template template = TemplatePage.new(@template) head['content-type'] = 'text/html' template.write_html_on out, "gem_count" => specs.size.to_s, "specs" => specs, "total_file_count" => total_file_count.to_s end end end class MongrelGemServer DOC_TEMPLATE = <<-WEBPAGE RubyGems Documentation Index

RubyGems Documentation Index

Summary

There are %gem_count% gems installed:

START:specs IFNOT:is_last %name%, ENDIF:is_last IF:is_last %name%. ENDIF:is_last END:specs

Gems

START:specs
IF:first_name_entry ENDIF:first_name_entry %name% %version% IF:rdoc_installed [rdoc] ENDIF:rdoc_installed IFNOT:rdoc_installed [rdoc] ENDIF:rdoc_installed IF:homepage [www] ENDIF:homepage IFNOT:homepage [www] ENDIF:homepage IF:has_deps - depends on START:dependencies IFNOT:is_last %name%, ENDIF:is_last IF:is_last %name%. ENDIF:is_last END:dependencies ENDIF:has_deps
%summary% IF:executables
IF:only_one_executable Executable is ENDIF:only_one_executable IFNOT:only_one_executable Executables are ENDIF:only_one_executable START:executables IFNOT:is_last %executable%, ENDIF:is_last IF:is_last %executable%. ENDIF:is_last END:executables ENDIF:executables

END:specs

[Validate]

Powered by Mongrel

WEBPAGE # CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108 RDOC_CSS = <<-RDOCCSS body { font-family: Verdana,Arial,Helvetica,sans-serif; font-size: 90%; margin: 0; margin-left: 40px; padding: 0; background: white; } h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; } h1 { font-size: 150%; } h2,h3,h4 { margin-top: 1em; } a { background: #eef; color: #039; text-decoration: none; } a:hover { background: #039; color: #eef; } /* Override the base stylesheets Anchor inside a table cell */ td > a { background: transparent; color: #039; text-decoration: none; } /* and inside a section title */ .section-title > a { background: transparent; color: #eee; text-decoration: none; } /* === Structural elements =================================== */ div#index { margin: 0; margin-left: -40px; padding: 0; font-size: 90%; } div#index a { margin-left: 0.7em; } div#index .section-bar { margin-left: 0px; padding-left: 0.7em; background: #ccc; font-size: small; } div#classHeader, div#fileHeader { width: auto; color: white; padding: 0.5em 1.5em 0.5em 1.5em; margin: 0; margin-left: -40px; border-bottom: 3px solid #006; } div#classHeader a, div#fileHeader a { background: inherit; color: white; } div#classHeader td, div#fileHeader td { background: inherit; color: white; } div#fileHeader { background: #057; } div#classHeader { background: #048; } .class-name-in-header { font-size: 180%; font-weight: bold; } div#bodyContent { padding: 0 1.5em 0 1.5em; } div#description { padding: 0.5em 1.5em; background: #efefef; border: 1px dotted #999; } div#description h1,h2,h3,h4,h5,h6 { color: #125;; background: transparent; } div#validator-badges { text-align: center; } div#validator-badges img { border: 0; } div#copyright { color: #333; background: #efefef; font: 0.75em sans-serif; margin-top: 5em; margin-bottom: 0; padding: 0.5em 2em; } /* === Classes =================================== */ table.header-table { color: white; font-size: small; } .type-note { font-size: small; color: #DEDEDE; } .xxsection-bar { background: #eee; color: #333; padding: 3px; } .section-bar { color: #333; border-bottom: 1px solid #999; margin-left: -20px; } .section-title { background: #79a; color: #eee; padding: 3px; margin-top: 2em; margin-left: -30px; border: 1px solid #999; } .top-aligned-row { vertical-align: top } .bottom-aligned-row { vertical-align: bottom } /* --- Context section classes ----------------------- */ .context-row { } .context-item-name { font-family: monospace; font-weight: bold; color: black; } .context-item-value { font-size: small; color: #448; } .context-item-desc { color: #333; padding-left: 2em; } /* --- Method classes -------------------------- */ .method-detail { background: #efefef; padding: 0; margin-top: 0.5em; margin-bottom: 1em; border: 1px dotted #ccc; } .method-heading { color: black; background: #ccc; border-bottom: 1px solid #666; padding: 0.2em 0.5em 0 0.5em; } .method-signature { color: black; background: inherit; } .method-name { font-weight: bold; } .method-args { font-style: italic; } .method-description { padding: 0 0.5em 0 0.5em; } /* --- Source code sections -------------------- */ a.source-toggle { font-size: 90%; } div.method-source-code { background: #262626; color: #ffdead; margin: 1em; padding: 0.5em; border: 1px dashed #999; overflow: hidden; } div.method-source-code pre { color: #ffdead; overflow: hidden; } /* --- Ruby keyword styles --------------------- */ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; } .ruby-constant { color: #7fffd4; background: transparent; } .ruby-keyword { color: #00ffff; background: transparent; } .ruby-ivar { color: #eedd82; background: transparent; } .ruby-operator { color: #00ffee; background: transparent; } .ruby-identifier { color: #ffdead; background: transparent; } .ruby-node { color: #ffa07a; background: transparent; } .ruby-comment { color: #b22222; font-weight: bold; background: transparent; } .ruby-regexp { color: #ffa07a; background: transparent; } .ruby-value { color: #7fffd4; background: transparent; } RDOCCSS def self.handle_args(args) options = {} options[:port] = 8809 options[:gemdir] = Gem.dir options[:daemon] = false opts = OptionParser.new do |opts| opts.on_tail("--help", "show this message") do puts opts exit end opts.on('-p', '--port=PORT', "Specify the port to listen on") do |port| options[:port] = port end opts.on('-d', '--dir=GEMDIR', "Specify the directory from which to serve Gems") do |gemdir| options[:gemdir] = gemdir end opts.on('--daemon', "Run as a daemon") do |daemon| options[:daemon] = true end end opts.parse! args options end def self.run(args = ARGV) options = handle_args(args) p options new(options[:gemdir], options[:port], options[:daemon]).run end def initialize(gemdir, port, daemon) @gemdir = gemdir @port = port @daemon = daemon end def run spec_dir = File.join @gemdir, "specifications" cache_dir = File.join @gemdir, "cache" doc_dir = File.join @gemdir, "doc" do_daemonize = @daemon config = Mongrel::Configurator.new :host => "0.0.0.0", :port => @port do daemonize :cwd => Dir.pwd, :log_file => "mongrel_gemserver.log", :pid_file => "mongrel_gemserver.pid" if do_daemonize listener do uri "/yaml", :handler => YamlHandler.new(spec_dir) uri "/rdoc-style.css", :handler => CssHandler.new(spec_dir,RDOC_CSS) uri "/", :handler => GemHandler.new(spec_dir,DOC_TEMPLATE) uri "/gems", :handler => Mongrel::DirHandler.new(cache_dir) uri "/doc_root", :handler => Mongrel::DirHandler.new(doc_dir) end trap("INT") { stop } trap("TERM") { stop; exit! } run end puts "Mongrel Gem Server running on port #{@port}" config.join end end MongrelGemServer.run