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, launch = nil, addresses = nil) click to toggle source

Only the first directory in gem_dirs is used for serving gems

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

Public Instance Methods

Marshal(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 460
460:   def Marshal(req, res)
461:     Gem::Specification.reset
462: 
463:     add_date res
464: 
465:     index = Gem::Deprecate.skip_during { Marshal.dump Gem.source_index }
466: 
467:     if req.request_method == 'HEAD' then
468:       res['content-length'] = index.length
469:       return
470:     end
471: 
472:     if req.path =~ /Z$/ then
473:       res['content-type'] = 'application/x-deflate'
474:       index = Gem.deflate index
475:     else
476:       res['content-type'] = 'application/octet-stream'
477:     end
478: 
479:     res.body << index
480:   end
add_date(res) click to toggle source
     # File lib/rubygems/server.rb, line 482
482:   def add_date res
483:     res['date'] = @spec_dirs.map do |spec_dir|
484:       File.stat(spec_dir).mtime
485:     end.max
486:   end
latest_specs(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 488
488:   def latest_specs(req, res)
489:     Gem::Specification.reset
490: 
491:     res['content-type'] = 'application/x-gzip'
492: 
493:     add_date res
494: 
495:     latest_specs = Gem::Specification.latest_specs
496: 
497:     specs = latest_specs.sort.map do |spec|
498:       platform = spec.original_platform || Gem::Platform::RUBY
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
launch() click to toggle source
     # File lib/rubygems/server.rb, line 822
822:   def launch
823:     listeners = @server.listeners.map{|l| l.addr[2] }
824: 
825:     # TODO: 0.0.0.0 == any, not localhost.
826:     host = listeners.any?{|l| l == '0.0.0.0'} ? 'localhost' : listeners.first
827: 
828:     say "Launching browser to http://#{host}:#{@port}"
829: 
830:     system("#{@launch} http://#{host}:#{@port}")
831:   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:     Gem::Specification.reset
551: 
552:     res['content-type'] = 'text/plain'
553:     add_date res
554: 
555:     case req.request_uri.path
556:     when %^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+)(-.*?)?\.gemspec\.rz$| then
557:       marshal_format, name, version, platform = $1, $2, $3, $4
558:       specs = Gem::Specification.find_all_by_name name, version
559: 
560:       selector = [name, version, platform].map(&:inspect).join ' '
561: 
562:       platform = if platform then
563:                    Gem::Platform.new platform.sub(/^-/, '')
564:                  else
565:                    Gem::Platform::RUBY
566:                  end
567: 
568:       specs = specs.select { |s| s.platform == platform }
569: 
570:       if specs.empty? then
571:         res.status = 404
572:         res.body = "No gems found matching #{selector}"
573:       elsif specs.length > 1 then
574:         res.status = 500
575:         res.body = "Multiple gems found matching #{selector}"
576:       elsif marshal_format then
577:         res['content-type'] = 'application/x-deflate'
578:         res.body << Gem.deflate(Marshal.dump(specs.first))
579:       end
580:     else
581:       raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found."
582:     end
583:   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 704
704:   def rdoc(req, res)
705:     query = req.query['q']
706:     show_rdoc_for_pattern("#{query}*", res) && return
707:     show_rdoc_for_pattern("*#{query}*", res) && return
708: 
709:     template = ERB.new RDOC_NO_DOCUMENTATION
710: 
711:     res['content-type'] = 'text/html'
712:     res.body = template.result binding
713:   end
root(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 585
585:   def root(req, res)
586:     Gem::Specification.reset
587:     add_date res
588: 
589:     raise WEBrick::HTTPStatus::NotFound, "`#{req.path}' not found." unless
590:       req.path == '/'
591: 
592:     specs = []
593:     total_file_count = 0
594: 
595:     Gem::Specification.each do |spec|
596:       total_file_count += spec.files.size
597:       deps = spec.dependencies.map { |dep|
598:         {
599:           "name"    => dep.name,
600:           "type"    => dep.type,
601:           "version" => dep.requirement.to_s,
602:         }
603:       }
604: 
605:       deps = deps.sort_by { |dep| [dep["name"].downcase, dep["version"]] }
606:       deps.last["is_last"] = true unless deps.empty?
607: 
608:       # executables
609:       executables = spec.executables.sort.collect { |exec| {"executable" => exec} }
610:       executables = nil if executables.empty?
611:       executables.last["is_last"] = true if executables
612: 
613:       specs << {
614:         "authors"             => spec.authors.sort.join(", "),
615:         "date"                => spec.date.to_s,
616:         "dependencies"        => deps,
617:         "doc_path"            => "/doc_root/#{spec.full_name}/rdoc/index.html",
618:         "executables"         => executables,
619:         "only_one_executable" => (executables && executables.size == 1),
620:         "full_name"           => spec.full_name,
621:         "has_deps"            => !deps.empty?,
622:         "homepage"            => spec.homepage,
623:         "name"                => spec.name,
624:         "rdoc_installed"      => Gem::DocManager.new(spec).rdoc_installed?,
625:         "summary"             => spec.summary,
626:         "version"             => spec.version.to_s,
627:       }
628:     end
629: 
630:     specs << {
631:       "authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
632:       "dependencies" => [],
633:       "doc_path" => "/doc_root/rubygems-#{Gem::VERSION}/rdoc/index.html",
634:       "executables" => [{"executable" => 'gem', "is_last" => true}],
635:       "only_one_executable" => true,
636:       "full_name" => "rubygems-#{Gem::VERSION}",
637:       "has_deps" => false,
638:       "homepage" => "http://docs.rubygems.org/",
639:       "name" => 'rubygems',
640:       "rdoc_installed" => true,
641:       "summary" => "RubyGems itself",
642:       "version" => Gem::VERSION,
643:     }
644: 
645:     specs = specs.sort_by { |spec| [spec["name"].downcase, spec["version"]] }
646:     specs.last["is_last"] = true
647: 
648:     # tag all specs with first_name_entry
649:     last_spec = nil
650:     specs.each do |spec|
651:       is_first = last_spec.nil? || (last_spec["name"].downcase != spec["name"].downcase)
652:       spec["first_name_entry"] = is_first
653:       last_spec = spec
654:     end
655: 
656:     # create page from template
657:     template = ERB.new(DOC_TEMPLATE)
658:     res['content-type'] = 'text/html'
659: 
660:     values = { "gem_count" => specs.size.to_s, "specs" => specs,
661:                "total_file_count" => total_file_count.to_s }
662: 
663:     # suppress 1.9.3dev warning about unused variable
664:     values = values
665: 
666:     result = template.result binding
667:     res.body = result
668:   end
run() click to toggle source
     # File lib/rubygems/server.rb, line 754
754:   def run
755:     listen
756: 
757:     WEBrick::Daemon.start if @daemon
758: 
759:     @server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal)
760:     @server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal)
761: 
762:     @server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs)
763:     @server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs)
764: 
765:     @server.mount_proc "/latest_specs.#{Gem.marshal_version}",
766:                        method(:latest_specs)
767:     @server.mount_proc "/latest_specs.#{Gem.marshal_version}.gz",
768:                        method(:latest_specs)
769: 
770:     @server.mount_proc "/quick/", method(:quick)
771: 
772:     @server.mount_proc("/gem-server-rdoc-style.css") do |req, res|
773:       res['content-type'] = 'text/css'
774:       add_date res
775:       res.body << RDOC_CSS
776:     end
777: 
778:     @server.mount_proc "/", method(:root)
779: 
780:     @server.mount_proc "/rdoc", method(:rdoc)
781: 
782:     paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
783:     paths.each do |mount_point, mount_dir|
784:       @server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
785:                     File.join(@gem_dirs.first, mount_dir), true)
786:     end
787: 
788:     trap("INT") { @server.shutdown; exit! }
789:     trap("TERM") { @server.shutdown; exit! }
790: 
791:     launch if @launch
792: 
793:     @server.start
794:   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 723
723:   def show_rdoc_for_pattern(pattern, res)
724:     found_gems = Dir.glob("{#{@gem_dirs.join ','}}/doc/#{pattern}").select {|path|
725:       File.exist? File.join(path, 'rdoc/index.html')
726:     }
727:     case found_gems.length
728:     when 0
729:       return false
730:     when 1
731:       new_path = File.basename(found_gems[0])
732:       res.status = 302
733:       res['Location'] = "/doc_root/#{new_path}/rdoc/index.html"
734:       return true
735:     else
736:       doc_items = []
737:       found_gems.each do |file_name|
738:         base_name = File.basename(file_name)
739:         doc_items << {
740:           :name => base_name,
741:           :url => "/doc_root/#{base_name}/rdoc/index.html",
742:           :summary => ''
743:         }
744:       end
745: 
746:       template = ERB.new(RDOC_SEARCH_TEMPLATE)
747:       res['content-type'] = 'text/html'
748:       result = template.result binding
749:       res.body = result
750:       return true
751:     end
752:   end
specs(req, res) click to toggle source
     # File lib/rubygems/server.rb, line 796
796:   def specs(req, res)
797:     Gem::Specification.reset
798: 
799:     add_date res
800: 
801:     specs = Gem::Specification.sort_by(&:sort_obj).map do |spec|
802:       platform = spec.original_platform || Gem::Platform::RUBY
803:       [spec.name, spec.version, platform]
804:     end
805: 
806:     specs = Marshal.dump specs
807: 
808:     if req.path =~ /\.gz$/ then
809:       specs = Gem.gzip specs
810:       res['content-type'] = 'application/x-gzip'
811:     else
812:       res['content-type'] = 'application/octet-stream'
813:     end
814: 
815:     if req.request_method == 'HEAD' then
816:       res['content-length'] = specs.length
817:     else
818:       res.body << specs
819:     end
820:   end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.