Parent

Included Modules

Class Index [+]

Quicksearch

Gem::Server

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:

Usage

  gem_server = Gem::Server.new Gem.dir, 8089, false
  gem_server.run

Constants

SEARCH
DOC_TEMPLATE
RDOC_CSS

CSS is copy & paste from rdoc-style.css, RDoc V1.0.1 - 20041108

RDOC_NO_DOCUMENTATION
RDOC_SEARCH_TEMPLATE

Attributes

spec_dirs[R]

Public Class Methods

new(gem_dirs, port, daemon, addresses = nil) click to toggle source

Only the first directory in gem_dirs is used for serving gems

     # File lib/rubygems/server.rb, line 438
438:   def initialize(gem_dirs, port, daemon, addresses = nil)
439:     Socket.do_not_reverse_lookup = true
440: 
441:     @gem_dirs = Array gem_dirs
442:     @port = port
443:     @daemon = daemon
444:     @addresses = addresses
445:     logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
446:     @server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger
447: 
448:     @spec_dirs = @gem_dirs.map do |gem_dir|
449:       spec_dir = File.join gem_dir, 'specifications'
450: 
451:       unless File.directory? spec_dir then
452:         raise ArgumentError, "#{gem_dir} does not appear to be a gem repository"
453:       end
454: 
455:       spec_dir
456:     end
457: 
458:     @source_index = Gem::SourceIndex.from_gems_in(*@spec_dirs)
459:   end
run(options) click to toggle source
     # File lib/rubygems/server.rb, line 430
430:   def self.run(options)
431:     new(options[:gemdir], options[:port], options[:daemon],
432:         options[:addresses]).run
433:   end

Public Instance Methods

Marshal(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 461
461:   def Marshal(req, res)
462:     @source_index.refresh!
463: 
464:     add_date res
465: 
466:     index = Marshal.dump @source_index
467: 
468:     if req.request_method == 'HEAD' then
469:       res['content-length'] = index.length
470:       return
471:     end
472: 
473:     if req.path =~ /Z$/ then
474:       res['content-type'] = 'application/x-deflate'
475:       index = Gem.deflate index
476:     else
477:       res['content-type'] = 'application/octet-stream'
478:     end
479: 
480:     res.body << index
481:   end
add_date(res) click to toggle source
     # File lib/rubygems/server.rb, line 483
483:   def add_date res
484:     res['date'] = @spec_dirs.map do |spec_dir|
485:       File.stat(spec_dir).mtime
486:     end.max
487:   end
latest_specs(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 489
489:   def latest_specs(req, res)
490:     @source_index.refresh!
491: 
492:     res['content-type'] = 'application/x-gzip'
493: 
494:     add_date res
495: 
496:     specs = @source_index.latest_specs.sort.map do |spec|
497:       platform = spec.original_platform
498:       platform = Gem::Platform::RUBY if platform.nil?
499:       [spec.name, spec.version, platform]
500:     end
501: 
502:     specs = Marshal.dump specs
503: 
504:     if req.path =~ /\.gz$/ then
505:       specs = Gem.gzip specs
506:       res['content-type'] = 'application/x-gzip'
507:     else
508:       res['content-type'] = 'application/octet-stream'
509:     end
510: 
511:     if req.request_method == 'HEAD' then
512:       res['content-length'] = specs.length
513:     else
514:       res.body << specs
515:     end
516:   end
listen(addresses = @addresses) click to toggle source

Creates server sockets based on the addresses option. If no addresses were given a server socket for all interfaces is created.

     # File lib/rubygems/server.rb, line 522
522:   def listen addresses = @addresses
523:     addresses = [nil] unless addresses
524: 
525:     listeners = 0
526: 
527:     addresses.each do |address|
528:       begin
529:         @server.listen address, @port
530:         @server.listeners[listeners..1].each do |listener|
531:           host, port = listener.addr.values_at 2, 1
532:           host = "[#{host}]" if host =~ /:/ # we don't reverse lookup
533:           say "Server started at http://#{host}:#{port}"
534:         end
535: 
536:         listeners = @server.listeners.length
537:       rescue SystemCallError
538:         next
539:       end
540:     end
541: 
542:     if @server.listeners.empty? then
543:       say "Unable to start a server."
544:       say "Check for running servers or your --bind and --port arguments"
545:       terminate_interaction 1
546:     end
547:   end
quick(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 549
549:   def quick(req, res)
550:     @source_index.refresh!
551: 
552:     res['content-type'] = 'text/plain'
553:     add_date res
554: 
555:     case req.request_uri.path
556:     when '/quick/index' then
557:       res.body << @source_index.map { |name,| name }.sort.join("\n")
558:     when '/quick/index.rz' then
559:       index = @source_index.map { |name,| name }.sort.join("\n")
560:       res['content-type'] = 'application/x-deflate'
561:       res.body << Gem.deflate(index)
562:     when '/quick/latest_index' then
563:       index = @source_index.latest_specs.map { |spec| spec.full_name }
564:       res.body << index.sort.join("\n")
565:     when '/quick/latest_index.rz' then
566:       index = @source_index.latest_specs.map { |spec| spec.full_name }
567:       res['content-type'] = 'application/x-deflate'
568:       res.body << Gem.deflate(index.sort.join("\n"))
569:     when %^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then
570:       dep = Gem::Dependency.new $2, $3
571:       specs = @source_index.search dep
572:       marshal_format = $1
573: 
574:       selector = [$2, $3, $4].map { |s| s.inspect }.join ' '
575: 
576:       platform = if $4 then
577:                    Gem::Platform.new $4.sub(/^-/, '')
578:                  else
579:                    Gem::Platform::RUBY
580:                  end
581: 
582:       specs = specs.select { |s| s.platform == platform }
583: 
584:       if specs.empty? then
585:         res.status = 404
586:         res.body = "No gems found matching #{selector}"
587:       elsif specs.length > 1 then
588:         res.status = 500
589:         res.body = "Multiple gems found matching #{selector}"
590:       elsif marshal_format then
591:         res['content-type'] = 'application/x-deflate'
592:         res.body << Gem.deflate(Marshal.dump(specs.first))
593:       else # deprecated YAML format
594:         res['content-type'] = 'application/x-deflate'
595:         res.body << Gem.deflate(specs.first.to_yaml)
596:       end
597:     else
598:       raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
599:     end
600:   end
rdoc(req, res) click to toggle source

Can be used for quick navigation to the rdoc documentation. You can then define a search shortcut for your browser. E.g. in Firefox connect ‘shortcut:rdoc’ to localhost:8808/rdoc?q=%s template. Then you can directly open the ActionPack documentation by typing ‘rdoc actionp’. If there are multiple hits for the search term, they are presented as a list with links.

Search algorithm aims for an intuitive search:

  1. first try to find the gems and documentation folders which name starts with the search term

  2. search for entries, that contain the search term

  3. show all the gems

If there is only one search hit, user is immediately redirected to the documentation for the particular gem, otherwise a list with results is shown.

Additional trick - install documentation for ruby core

Note: please adjust paths accordingly use for example ‘locate yaml.rb’ and ‘gem environment’ to identify directories, that are specific for your local installation

  1. install ruby sources

      cd /usr/src
      sudo apt-get source ruby
    
  2. generate documentation

      rdoc -o /usr/lib/ruby/gems/1.8/doc/core/rdoc \
        /usr/lib/ruby/1.8 ruby1.8-1.8.7.72
    

By typing ‘rdoc core’ you can now access the core documentation

     # File lib/rubygems/server.rb, line 716
716:   def rdoc(req, res)
717:     query = req.query['q']
718:     show_rdoc_for_pattern("#{query}*", res) && return
719:     show_rdoc_for_pattern("*#{query}*", res) && return
720: 
721:     template = ERB.new RDOC_NO_DOCUMENTATION
722: 
723:     res['content-type'] = 'text/html'
724:     res.body = template.result binding
725:   end
root(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 602
602:   def root(req, res)
603:     @source_index.refresh!
604:     add_date res
605: 
606:     raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
607:       req.path == '/'
608: 
609:     specs = []
610:     total_file_count = 0
611: 
612:     @source_index.each do |path, spec|
613:       total_file_count += spec.files.size
614:       deps = spec.dependencies.map do |dep|
615:         { "name"    => dep.name,
616:           "type"    => dep.type,
617:           "version" => dep.requirement.to_s, }
618:       end
619: 
620:       deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
621:       deps.last["is_last"] = true unless deps.empty?
622: 
623:       # executables
624:       executables = spec.executables.sort.collect { |exec| {"executable" => exec} }
625:       executables = nil if executables.empty?
626:       executables.last["is_last"] = true if executables
627: 
628:       specs << {
629:         "authors"             => spec.authors.sort.join(", "),
630:         "date"                => spec.date.to_s,
631:         "dependencies"        => deps,
632:         "doc_path"            => "/doc_root/#{spec.full_name}/rdoc/index.html",
633:         "executables"         => executables,
634:         "only_one_executable" => (executables && executables.size == 1),
635:         "full_name"           => spec.full_name,
636:         "has_deps"            => !deps.empty?,
637:         "homepage"            => spec.homepage,
638:         "name"                => spec.name,
639:         "rdoc_installed"      => Gem::DocManager.new(spec).rdoc_installed?,
640:         "summary"             => spec.summary,
641:         "version"             => spec.version.to_s,
642:       }
643:     end
644: 
645:     specs << {
646:       "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
647:       "dependencies" => [],
648:       "doc_path" => "/doc_root/rubygems-#{Gem::VERSION}/rdoc/index.html",
649:       "executables" => [{"executable" => 'gem', "is_last" => true}],
650:       "only_one_executable" => true,
651:       "full_name" => "rubygems-#{Gem::VERSION}",
652:       "has_deps" => false,
653:       "homepage" => "http://docs.rubygems.org/",
654:       "name" => 'rubygems',
655:       "rdoc_installed" => true,
656:       "summary" => "RubyGems itself",
657:       "version" => Gem::VERSION,
658:     }
659: 
660:     specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] }
661:     specs.last["is_last"] = true
662: 
663:     # tag all specs with first_name_entry
664:     last_spec = nil
665:     specs.each do |spec|
666:       is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
667:       spec["first_name_entry"] = is_first
668:       last_spec = spec
669:     end
670: 
671:     # create page from template
672:     template = ERB.new(DOC_TEMPLATE)
673:     res['content-type'] = 'text/html'
674: 
675:     values = { "gem_count" => specs.size.to_s, "specs" => specs,
676:                "total_file_count" => total_file_count.to_s }
677: 
678:     result = template.result binding
679:     res.body = result
680:   end
run() click to toggle source
     # File lib/rubygems/server.rb, line 766
766:   def run
767:     listen
768: 
769:     WEBrick::Daemon.start if @daemon
770: 
771:     @server.mount_proc "/yaml", method(:yaml)
772:     @server.mount_proc "/yaml.Z", method(:yaml)
773: 
774:     @server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal)
775:     @server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal)
776: 
777:     @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs)
778:     @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs)
779: 
780:     @server.mount_proc "/latest_specs.#{Gem.marshal_version}",
781:                        method(:latest_specs)
782:     @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz",
783:                        method(:latest_specs)
784: 
785:     @server.mount_proc "/quick/", method(:quick)
786: 
787:     @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
788:       res['content-type'] = 'text/css'
789:       add_date res
790:       res.body << RDOC_CSS
791:     end
792: 
793:     @server.mount_proc "/", method(:root)
794: 
795:     @server.mount_proc "/rdoc", method(:rdoc)
796: 
797:     paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
798:     paths.each do |mount_point, mount_dir|
799:       @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
800:                     File.join(@gem_dirs.first, mount_dir), true)
801:     end
802: 
803:     trap("INT") { @server.shutdown; exit! }
804:     trap("TERM") { @server.shutdown; exit! }
805: 
806:     @server.start
807:   end
show_rdoc_for_pattern(pattern, res) click to toggle source

Returns true and prepares http response, if rdoc for the requested gem name pattern was found.

The search is based on the file system content, not on the gems metadata. This allows additional documentation folders like ‘core’ for the ruby core documentation - just put it underneath the main doc folder.

     # File lib/rubygems/server.rb, line 735
735:   def show_rdoc_for_pattern(pattern, res)
736:     found_gems = Dir.glob("{#{@gem_dirs.join ','}}/doc/#{pattern}").select {|path|
737:       File.exist? File.join(path, 'rdoc/index.html')
738:     }
739:     case found_gems.length
740:     when 0
741:       return false
742:     when 1
743:       new_path = File.basename(found_gems[0])
744:       res.status = 302
745:       res['Location'] = "/doc_root/#{new_path}/rdoc/index.html"
746:       return true
747:     else
748:       doc_items = []
749:       found_gems.each do |file_name|
750:         base_name = File.basename(file_name)
751:         doc_items << {
752:           :name => base_name,
753:           :url => "/doc_root/#{base_name}/rdoc/index.html",
754:           :summary => ''
755:         }
756:       end
757: 
758:       template = ERB.new(RDOC_SEARCH_TEMPLATE)
759:       res['content-type'] = 'text/html'
760:       result = template.result binding
761:       res.body = result
762:       return true
763:     end
764:   end
specs(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 809
809:   def specs(req, res)
810:     @source_index.refresh!
811: 
812:     add_date res
813: 
814:     specs = @source_index.sort.map do |_, spec|
815:       platform = spec.original_platform
816:       platform = Gem::Platform::RUBY if platform.nil?
817:       [spec.name, spec.version, platform]
818:     end
819: 
820:     specs = Marshal.dump specs
821: 
822:     if req.path =~ /\.gz$/ then
823:       specs = Gem.gzip specs
824:       res['content-type'] = 'application/x-gzip'
825:     else
826:       res['content-type'] = 'application/octet-stream'
827:     end
828: 
829:     if req.request_method == 'HEAD' then
830:       res['content-length'] = specs.length
831:     else
832:       res.body << specs
833:     end
834:   end
yaml(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 836
836:   def yaml(req, res)
837:     @source_index.refresh!
838: 
839:     add_date res
840: 
841:     index = @source_index.to_yaml
842: 
843:     if req.path =~ /Z$/ then
844:       res['content-type'] = 'application/x-deflate'
845:       index = Gem.deflate index
846:     else
847:       res['content-type'] = 'text/plain'
848:     end
849: 
850:     if req.request_method == 'HEAD' then
851:       res['content-length'] = index.length
852:       return
853:     end
854: 
855:     res.body << index
856:   end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.