Class | Gem::Server |
In: |
lib/rubygems/server.rb
|
Parent: | Object |
Gem::Server and allows users to serve gems for consumption by `gem —remote-install`.
gem_server starts an HTTP server on the given port and serves the following:
gem_server = Gem::Server.new Gem.dir, 8089, false gem_server.run
DOC_TEMPLATE | = | <<-'WEBPAGE' <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>RubyGems Documentation Index</title> <link rel="stylesheet" href="gem-server-rdoc-style.css" type="text/css" media="screen" /> </head> <body> <div id="fileHeader"> <h1>RubyGems Documentation Index</h1> </div> <!-- banner header --> <div id="bodyContent"> <div id="contextContent"> <div id="description"> <h1>Summary</h1> <p>There are <%=values["gem_count"]%> gems installed:</p> <p> <%= values["specs"].map { |v| "<a href=\"##{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>. <h1>Gems</h1> <dl> <% values["specs"].each do |spec| %> <dt> <% if spec["first_name_entry"] then %> <a name="<%=spec["name"]%>"></a> <% end %> <b><%=spec["name"]%> <%=spec["version"]%></b> <% if spec["rdoc_installed"] then %> <a href="<%=spec["doc_path"]%>">[rdoc]</a> <% else %> <span title="rdoc not installed">[rdoc]</span> <% end %> <% if spec["homepage"] then %> <a href="<%=spec["homepage"]%>" title="<%=spec["homepage"]%>">[www]</a> <% else %> <span title="no homepage available">[www]</span> <% end %> <% if spec["has_deps"] then %> - depends on <%= spec["dependencies"].map { |v| "<a href=\"##{v["name"]}\">#{v["name"]}</a>" }.join ', ' %>. <% end %> </dt> <dd> <%=spec["summary"]%> <% if spec["executables"] then %> <br/> <% if spec["only_one_executable"] then %> Executable is <% else %> Executables are <%end%> <%= spec["executables"].map { |v| "<span class=\"context-item-name\">#{v["executable"]}</span>"}.join ', ' %>. <%end%> <br/> <br/> </dd> <% end %> </dl> </div> </div> </div> <div id="validator-badges"> <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p> </div> </body> </html> WEBPAGE | ||
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 | CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108 |
# File lib/rubygems/server.rb, line 336 336: def initialize(gem_dir, port, daemon) 337: Socket.do_not_reverse_lookup = true 338: 339: @gem_dir = gem_dir 340: @port = port 341: @daemon = daemon 342: logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL 343: @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger 344: 345: @spec_dir = File.join @gem_dir, 'specifications' 346: 347: unless File.directory? @spec_dir then 348: raise ArgumentError, "#{@gem_dir} does not appear to be a gem repository" 349: end 350: 351: @source_index = Gem::SourceIndex.from_gems_in @spec_dir 352: end
# File lib/rubygems/server.rb, line 332 332: def self.run(options) 333: new(options[:gemdir], options[:port], options[:daemon]).run 334: end
# File lib/rubygems/server.rb, line 354 354: def Marshal(req, res) 355: @source_index.refresh! 356: 357: res['date'] = File.stat(@spec_dir).mtime 358: 359: index = Marshal.dump @source_index 360: 361: if req.request_method == 'HEAD' then 362: res['content-length'] = index.length 363: return 364: end 365: 366: if req.path =~ /Z$/ then 367: res['content-type'] = 'application/x-deflate' 368: index = Gem.deflate index 369: else 370: res['content-type'] = 'application/octet-stream' 371: end 372: 373: res.body << index 374: end
# File lib/rubygems/server.rb, line 376 376: def latest_specs(req, res) 377: @source_index.refresh! 378: 379: res['content-type'] = 'application/x-gzip' 380: 381: res['date'] = File.stat(@spec_dir).mtime 382: 383: specs = @source_index.latest_specs.sort.map do |spec| 384: platform = spec.original_platform 385: platform = Gem::Platform::RUBY if platform.nil? 386: [spec.name, spec.version, platform] 387: end 388: 389: specs = Marshal.dump specs 390: 391: if req.path =~ /\.gz$/ then 392: specs = Gem.gzip specs 393: res['content-type'] = 'application/x-gzip' 394: else 395: res['content-type'] = 'application/octet-stream' 396: end 397: 398: if req.request_method == 'HEAD' then 399: res['content-length'] = specs.length 400: else 401: res.body << specs 402: end 403: end
# File lib/rubygems/server.rb, line 405 405: def quick(req, res) 406: @source_index.refresh! 407: 408: res['content-type'] = 'text/plain' 409: res['date'] = File.stat(@spec_dir).mtime 410: 411: case req.request_uri.path 412: when '/quick/index' then 413: res.body << @source_index.map { |name,| name }.sort.join("\n") 414: when '/quick/index.rz' then 415: index = @source_index.map { |name,| name }.sort.join("\n") 416: res['content-type'] = 'application/x-deflate' 417: res.body << Gem.deflate(index) 418: when '/quick/latest_index' then 419: index = @source_index.latest_specs.map { |spec| spec.full_name } 420: res.body << index.sort.join("\n") 421: when '/quick/latest_index.rz' then 422: index = @source_index.latest_specs.map { |spec| spec.full_name } 423: res['content-type'] = 'application/x-deflate' 424: res.body << Gem.deflate(index.sort.join("\n")) 425: when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then 426: dep = Gem::Dependency.new $2, $3 427: specs = @source_index.search dep 428: marshal_format = $1 429: 430: selector = [$2, $3, $4].map { |s| s.inspect }.join ' ' 431: 432: platform = if $4 then 433: Gem::Platform.new $4.sub(/^-/, '') 434: else 435: Gem::Platform::RUBY 436: end 437: 438: specs = specs.select { |s| s.platform == platform } 439: 440: if specs.empty? then 441: res.status = 404 442: res.body = "No gems found matching #{selector}" 443: elsif specs.length > 1 then 444: res.status = 500 445: res.body = "Multiple gems found matching #{selector}" 446: elsif marshal_format then 447: res['content-type'] = 'application/x-deflate' 448: res.body << Gem.deflate(Marshal.dump(specs.first)) 449: else # deprecated YAML format 450: res['content-type'] = 'application/x-deflate' 451: res.body << Gem.deflate(specs.first.to_yaml) 452: end 453: else 454: raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." 455: end 456: end
# File lib/rubygems/server.rb, line 458 458: def root(req, res) 459: @source_index.refresh! 460: res['date'] = File.stat(@spec_dir).mtime 461: 462: raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless 463: req.path == '/' 464: 465: specs = [] 466: total_file_count = 0 467: 468: @source_index.each do |path, spec| 469: total_file_count += spec.files.size 470: deps = spec.dependencies.map do |dep| 471: { "name" => dep.name, 472: "type" => dep.type, 473: "version" => dep.version_requirements.to_s, } 474: end 475: 476: deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] } 477: deps.last["is_last"] = true unless deps.empty? 478: 479: # executables 480: executables = spec.executables.sort.collect { |exec| {"executable" => exec} } 481: executables = nil if executables.empty? 482: executables.last["is_last"] = true if executables 483: 484: specs << { 485: "authors" => spec.authors.sort.join(", "), 486: "date" => spec.date.to_s, 487: "dependencies" => deps, 488: "doc_path" => "/doc_root/#{spec.full_name}/rdoc/index.html", 489: "executables" => executables, 490: "only_one_executable" => (executables && executables.size == 1), 491: "full_name" => spec.full_name, 492: "has_deps" => !deps.empty?, 493: "homepage" => spec.homepage, 494: "name" => spec.name, 495: "rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?, 496: "summary" => spec.summary, 497: "version" => spec.version.to_s, 498: } 499: end 500: 501: specs << { 502: "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others", 503: "dependencies" => [], 504: "doc_path" => "/doc_root/rubygems-#{Gem::RubyGemsVersion}/rdoc/index.html", 505: "executables" => [{"executable" => 'gem', "is_last" => true}], 506: "only_one_executable" => true, 507: "full_name" => "rubygems-#{Gem::RubyGemsVersion}", 508: "has_deps" => false, 509: "homepage" => "http://rubygems.org/", 510: "name" => 'rubygems', 511: "rdoc_installed" => true, 512: "summary" => "RubyGems itself", 513: "version" => Gem::RubyGemsVersion, 514: } 515: 516: specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] } 517: specs.last["is_last"] = true 518: 519: # tag all specs with first_name_entry 520: last_spec = nil 521: specs.each do |spec| 522: is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase) 523: spec["first_name_entry"] = is_first 524: last_spec = spec 525: end 526: 527: # create page from template 528: template = ERB.new(DOC_TEMPLATE) 529: res['content-type'] = 'text/html' 530: 531: values = { "gem_count" => specs.size.to_s, "specs" => specs, 532: "total_file_count" => total_file_count.to_s } 533: 534: result = template.result binding 535: res.body = result 536: end
# File lib/rubygems/server.rb, line 538 538: def run 539: @server.listen nil, @port 540: 541: say "Starting gem server on http://localhost:#{@port}/" 542: 543: WEBrick::Daemon.start if @daemon 544: 545: @server.mount_proc "/yaml", method(:yaml) 546: @server.mount_proc "/yaml.Z", method(:yaml) 547: 548: @server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal) 549: @server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal) 550: 551: @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs) 552: @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs) 553: 554: @server.mount_proc "/latest_specs.#{Gem.marshal_version}", 555: method(:latest_specs) 556: @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz", 557: method(:latest_specs) 558: 559: @server.mount_proc "/quick/", method(:quick) 560: 561: @server.mount_proc("/gem-server-rdoc-style.css") do |req, res| 562: res['content-type'] = 'text/css' 563: res['date'] = File.stat(@spec_dir).mtime 564: res.body << RDOC_CSS 565: end 566: 567: @server.mount_proc "/", method(:root) 568: 569: paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" } 570: paths.each do |mount_point, mount_dir| 571: @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler, 572: File.join(@gem_dir, mount_dir), true) 573: end 574: 575: trap("INT") { @server.shutdown; exit! } 576: trap("TERM") { @server.shutdown; exit! } 577: 578: @server.start 579: end
# File lib/rubygems/server.rb, line 581 581: def specs(req, res) 582: @source_index.refresh! 583: 584: res['date'] = File.stat(@spec_dir).mtime 585: 586: specs = @source_index.sort.map do |_, spec| 587: platform = spec.original_platform 588: platform = Gem::Platform::RUBY if platform.nil? 589: [spec.name, spec.version, platform] 590: end 591: 592: specs = Marshal.dump specs 593: 594: if req.path =~ /\.gz$/ then 595: specs = Gem.gzip specs 596: res['content-type'] = 'application/x-gzip' 597: else 598: res['content-type'] = 'application/octet-stream' 599: end 600: 601: if req.request_method == 'HEAD' then 602: res['content-length'] = specs.length 603: else 604: res.body << specs 605: end 606: end
# File lib/rubygems/server.rb, line 608 608: def yaml(req, res) 609: @source_index.refresh! 610: 611: res['date'] = File.stat(@spec_dir).mtime 612: 613: index = @source_index.to_yaml 614: 615: if req.path =~ /Z$/ then 616: res['content-type'] = 'application/x-deflate' 617: index = Gem.deflate index 618: else 619: res['content-type'] = 'text/plain' 620: end 621: 622: if req.request_method == 'HEAD' then 623: res['content-length'] = index.length 624: return 625: end 626: 627: res.body << index 628: end