Permalink
Browse files

* lib/rubygems/custom_require.rb: new file to replace

 loadpath_manager.rb and speed up the custom require functionality.

 * lib/rubygems/timer.rb: new file; simple benchmarking tool that's
 useful at the moment but needn't stay in RubyGems permanently.

 * lib/rubygems/source_index.rb: include Enumerable and implement #size
 and #length.

 * lib/rubygems/specification.rb: removed lazy initialisation of
 attributes to reduce #copy_of invocations; folded
 Specification.copy_of into Specification#copy_of.

 * lib/rubygems.rb: require 'rubygems/custom_require' instead of
 'rubygems/loadpath_manager'; removed out of date documentation; added
 "require 'rubygems/timer'" to temporarily assist with benchmarks.


git-svn-id: svn+ssh://rubyforge.org/var/svn/rubygems/trunk@704 3d4018f9-ac1a-0410-99e9-8a154d859a19
  • Loading branch information...
1 parent 3d53b44 commit fd924552b0a8cf6c9f3893776bddccc71f00ed92 @gsinclair gsinclair committed Dec 11, 2004
View
19 rubygems/ChangeLog
@@ -1,3 +1,22 @@
+2004-12-12 Gavin Sinclair <gsinclair@soyabean.com.au>
+
+ * lib/rubygems/custom_require.rb: new file to replace
+ loadpath_manager.rb and speed up the custom require functionality.
+
+ * lib/rubygems/timer.rb: new file; simple benchmarking tool that's
+ useful at the moment but needn't stay in RubyGems permanently.
+
+ * lib/rubygems/source_index.rb: include Enumerable and implement #size
+ and #length.
+
+ * lib/rubygems/specification.rb: removed lazy initialisation of
+ attributes to reduce #copy_of invocations; folded
+ Specification.copy_of into Specification#copy_of.
+
+ * lib/rubygems.rb: require 'rubygems/custom_require' instead of
+ 'rubygems/loadpath_manager'; removed out of date documentation; added
+ "require 'rubygems/timer'" to temporarily assist with benchmarks.
+
2004-12-07 Jim Weirich <jim@weirichhouse.org>
* Released 0.8.3
View
114 rubygems/lib/rubygems.rb
@@ -1,3 +1,6 @@
+require 'rubygems/timer'
+require 'rbconfig'
+
module Gem
class LoadError < ::LoadError
attr_accessor :name, :version_requirement
@@ -14,22 +17,11 @@ module Kernel
# a required Gem is not found, a Gem::LoadError is raised. More information on
# version requirements can be found in the Gem::Version documentation.
#
- # As a shortcut, the +gem+ parameter can be a _path_, for example:
- #
- # require_gem 'rake/packagetask'
- #
- # This is strictly short for
- #
- # require_gem 'rake'
- # require 'rake/packagetask'
+ # You can define the environment variable GEM_SKIP as a way to not
+ # load specified gems. you might do this to test out changes that haven't
+ # been intsalled yet. Example:
#
- # You can define the environment variable GEM_SKIP as a way to not
- # load specified gems. you might do this to test out changes that haven't
- # been intsalled yet. Example:
- #
- # GEM_SKIP=libA:libB ruby-I../libA -I../libB ./mycode.rb
- #
- # <i>This is an experimental feature added after versoin 0.7, on 2004-07-13. </i>
+ # GEM_SKIP=libA:libB ruby-I../libA -I../libB ./mycode.rb
#
# gem:: [String or Gem::Dependency] The gem name or dependency instance.
# version_requirement:: [default="> 0.0.0"] The version requirement.
@@ -200,9 +192,9 @@ def use_paths(home, paths=[])
def all_load_paths
result = []
Gem.path.each do |gemdir|
- each_load_path(all_partials(gemdir)) do |load_path|
- result << load_path
- end
+ each_load_path(all_partials(gemdir)) do |load_path|
+ result << load_path
+ end
end
result
end
@@ -212,9 +204,9 @@ def all_load_paths
def latest_load_paths
result = []
Gem.path.each do |gemdir|
- each_load_path(latest_partials(gemdir)) do |load_path|
- result << load_path
- end
+ each_load_path(latest_partials(gemdir)) do |load_path|
+ result << load_path
+ end
end
result
end
@@ -225,8 +217,8 @@ def required_location(gemname, libfile, *version_constraints)
return nil if matches.empty?
spec = matches.last
spec.require_paths.each do |path|
- result = File.join(spec.full_gem_path, path, libfile)
- return result if File.exists?(result)
+ result = File.join(spec.full_gem_path, path, libfile)
+ return result if File.exists?(result)
end
end
@@ -242,13 +234,13 @@ def all_partials(gemdir)
def latest_partials(gemdir)
latest = {}
all_partials(gemdir).each do |gp|
- base = File.basename(gp)
+ base = File.basename(gp)
matches = /(.*)-((\d+\.)*\d+)/.match(base)
- name, version = [matches[1], matches[2]]
- ver = Gem::Version.new(version)
- if latest[name].nil? || ver > latest[name][0]
- latest[name] = [ver, gp]
- end
+ name, version = [matches[1], matches[2]]
+ ver = Gem::Version.new(version)
+ if latest[name].nil? || ver > latest[name][0]
+ latest[name] = [ver, gp]
+ end
end
latest.collect { |k,v| v[1] }
end
@@ -257,17 +249,17 @@ def latest_partials(gemdir)
# specified in the Gem spec. Each expanded path is yielded.
def each_load_path(partials)
partials.each do |gp|
- base = File.basename(gp)
- specfn = File.join(dir, "specifications", base + ".gemspec")
- if File.exist?(specfn)
- spec = eval(File.read(specfn))
- spec.require_paths.each do |rp|
- yield(File.join(gp, rp))
- end
- else
- filename = File.join(gp, 'lib')
- yield(filename) if File.exist?(filename)
- end
+ base = File.basename(gp)
+ specfn = File.join(dir, "specifications", base + ".gemspec")
+ if File.exist?(specfn)
+ spec = eval(File.read(specfn))
+ spec.require_paths.each do |rp|
+ yield(File.join(gp, rp))
+ end
+ else
+ filename = File.join(gp, 'lib')
+ yield(filename) if File.exist?(filename)
+ end
end
end
@@ -299,19 +291,19 @@ def set_paths(gpaths)
#
def find_home
['HOME', 'USERPROFILE'].each do |homekey|
- return ENV[homekey] if ENV[homekey]
+ return ENV[homekey] if ENV[homekey]
end
if ENV['HOMEDRIVE'] && ENV['HOMEPATH']
- return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
+ return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
end
begin
- File.expand_path("~")
+ File.expand_path("~")
rescue Exception => ex
- if File::ALT_SEPARATOR
- "C:/"
- else
- "/"
- end
+ if File::ALT_SEPARATOR
+ "C:/"
+ else
+ "/"
+ end
end
end
@@ -320,12 +312,20 @@ def find_home
# Default home directory path to be used if an alternate value is
# not specified in the environment.
def default_dir
- #rbconfig = Dir.glob("{#{($LOAD_PATH).join(',')}}/rbconfig.rb").first
- #if rbconfig
- # module_eval File.read(rbconfig) unless const_defined?("Config")
- #else
- require 'rbconfig'
- #end
+ ## rbconfig = Dir.glob("{#{($LOAD_PATH).join(',')}}/rbconfig.rb").first
+ ## if rbconfig
+ ## module_eval File.read(rbconfig) unless const_defined?("Config")
+ ## else
+ ## require 'rbconfig'
+ ## end
+ #
+ # Note on above code: we have an issue if a Config class is already defined and we load
+ # 'rbconfig'. The above code is supposed to work around that but it's been commented
+ # out. In any case, I moved "require 'rbconfig'" to the top of this file, because there
+ # was a circular dependency between this method and our custom require. In any case,
+ # rbconfig is a fundamental RubyGems dependency, so it might as well be up the top.
+ # -- Gavin Sinclair, 2004-12-12
+ #
File.join(Config::CONFIG['libdir'], 'ruby', 'gems', Config::CONFIG['ruby_version'])
end
@@ -339,7 +339,7 @@ def ensure_gem_subdirectories(gemdir)
fn = File.join(gemdir, filename)
if ! File.exists?(fn) && File.writable?(fn)
require 'fileutils'
- FileUtils.mkdir_p(fn)
+ FileUtils.mkdir_p(fn)
end
end
end
@@ -350,4 +350,6 @@ def ensure_gem_subdirectories(gemdir)
require 'rubygems/source_index'
require 'rubygems/specification'
require 'rubygems/version'
-require 'rubygems/loadpath_manager'
+#require 'rubygems/loadpath_manager' # custom_require replaces this
+require 'rubygems/custom_require'
+
View
109 rubygems/lib/rubygems/custom_require.rb
@@ -0,0 +1,109 @@
+require 'rubygems/source_index'
+
+module Kernel
+ alias require__ require
+
+ #
+ # We replace Ruby's require with our own, which is capable of loading gems on demand.
+ #
+ # When you call <tt>require 'x'</tt>, this is what happens:
+ # * If the file can be loaded from the existing Ruby loadpath, it is.
+ # * Otherwise, installed gems are searched for a file that matches. If it's found in gem
+ # 'y', that gem is activated (added to the loadpath).
+ #
+ # The normal <tt>require</tt> functionality of returning false if that file has already been
+ # loaded is preserved.
+ #
+ def require(path)
+ require__ path
+ rescue LoadError
+ begin
+ @gempath_searcher ||= Gem::GemPathSearcher.new
+ if spec = @gempath_searcher.find(path)
+ Gem.activate(spec.name, true, "= #{spec.version}")
+ require__ path
+ else
+ raise LoadError, "No such file to load -- #{path}"
+ end
+ end
+ end
+end # module Kernel
+
+
+module Gem
+
+ #
+ # GemPathSearcher has the capability to find loadable files inside gems. It generates data
+ # up front to speed up searches later.
+ #
+ class GemPathSearcher
+
+ #
+ # Initialise the data we need to make searches later.
+ #
+ def initialize
+ # We want a record of all the installed gemspecs, in the order we wish to examine them.
+ @gemspecs = init_gemspecs
+ # Map gem spec to glob of full require_path directories. Preparing this information may
+ # speed up searches later.
+ @lib_dirs = {}
+ @gemspecs.each do |spec|
+ @lib_dirs[spec.object_id] = lib_dirs(spec)
+ end
+ end
+
+ #
+ # Look in all the installed gems until a matching _path_ is found. Return the _gemspec_
+ # of the gem where it was found. If no match is found, return nil.
+ #
+ # The gems are searched in alphabetical order, and in reverse version order.
+ #
+ # For example:
+ #
+ # find('log4r') # -> (log4r-1.1 spec)
+ # find('log4r.rb') # -> (log4r-1.1 spec)
+ # find('rake/rdoctask') # -> (rake-0.4.12 spec)
+ # find('foobarbaz') # -> nil
+ #
+ # Matching paths can have various suffixes ('.rb', '.so', and others), which may or may
+ # not already be attached to _file_. This method doesn't care about the full filename
+ # that matches; only that there is a match.
+ #
+ def find(path)
+ @gemspecs.each do |spec|
+ return spec if matching_file(spec, path)
+ end
+ nil
+ end
+
+ private
+
+ SUFFIX_PATTERN = "{,.rb,.so,.bundle,.dll,.sl}"
+
+ #
+ # Attempts to find a matching path using the require_paths of the given _spec_.
+ #
+ # Some of the intermediate results are cached in @lib_dirs for speed.
+ #
+ def matching_file(spec, path) # :doc:
+ glob = "#{@lib_dirs[spec.object_id]}/#{path}#{SUFFIX_PATTERN}"
+ return true unless Dir[glob].select { |f| File.file?(f) }.empty?
+ end
+
+ # Return a list of all installed gemspecs, sorted by alphabetical order and in reverse
+ # version order.
+ def init_gemspecs
+ Gem.source_index.map { |_, spec| spec }.specs.sort_by { |spec|
+ [spec.name, spec.version.to_ints.map { |n| -n } ]
+ }
+ end
+
+ # Returns library directories glob for a gemspec. For example,
+ # '/usr/local/lib/ruby/gems/1.8/gems/foobar-1.0/{lib,ext}'
+ def lib_dirs(spec)
+ "#{spec.full_gem_path}/#{spec.require_paths.join(',')}"
+ end
+
+ end # class Gem::GemPathLoader
+
+end # module Gem
View
12 rubygems/lib/rubygems/source_index.rb
@@ -1,4 +1,7 @@
require 'rubygems/user_interaction'
+
+require 'forwardable'
+
module Gem
# The SourceIndex object indexes all the gems available from a
@@ -12,6 +15,9 @@ module Gem
# old YAMLized source index objects to load properly.
#
class SourceIndex
+ extend Forwardable
+ include Enumerable
+
class << self
include Gem::UserInteraction
end
@@ -40,15 +46,15 @@ def initialize(specifications)
def self.from_installed_gems(*spec_dirs)
gems = {}
if spec_dirs.empty?
- spec_dirs = Gem.path.collect {|dir| File.join(dir, "specifications")}
+ spec_dirs = Gem.path.collect { |dir| File.join(dir, "specifications") }
end
Dir.glob("{#{spec_dirs.join(',')}}/*.gemspec").each do |file_name|
gemspec = load_specification(file_name)
gems[gemspec.full_name] = gemspec if gemspec
end
self.new(gems)
end
-
+
# Load a specification from a file (eval'd Ruby code)
#
# file_name:: [String] The .gemspec file
@@ -81,6 +87,8 @@ def each(&block)
@gems.each(&block)
end
+ def_delegators :@gems, :size, :length
+
# Search for a gem by name and optional version
#
# gem_name::
View
17 rubygems/lib/rubygems/specification.rb
@@ -21,7 +21,7 @@ module Platform
# Potentially raised when a specification is validated.
class InvalidSpecificationException < Gem::Exception; end
class EndOfYAMLException < Gem::Exception; end
-
+
##
# == Gem::Specification
#
@@ -124,12 +124,7 @@ def self.list
def self.attribute(name, default=nil)
@@attributes << [name, default]
@@default_value[name] = default
- attr_writer(name)
- class_eval %{
- def #{name}
- @#{name} ||= copy_of(@@default_value[:#{name}])
- end
- }
+ attr_accessor(name)
end
# Same as attribute above, but also records this attribute as mandatory.
@@ -582,18 +577,13 @@ def find_all_satisfiers(dep)
end
# Duplicate an object unless it's an immediate value.
- def self.copy_of(obj)
+ def copy_of(obj)
case obj
when Numeric, Symbol, true, false, nil then obj
else obj.dup
end
end
- # Duplicate an object unless it's an immediate value.
- def copy_of(obj)
- self.class.copy_of(obj)
- end
-
# Return a string containing a Ruby code representation of the given object.
def ruby_code(obj)
case obj
@@ -609,5 +599,6 @@ def ruby_code(obj)
end
end # class Specification
+
end # module Gem
View
19 rubygems/lib/rubygems/timer.rb
@@ -0,0 +1,19 @@
+#
+# This file defines a $log variable for logging, and a time() method for recording timing
+# information.
+#
+
+$log = Object.new
+def $log.debug(str)
+ STDERR.puts str
+end
+
+def time(msg, width=25)
+ t = Time.now
+ return_value = yield
+ elapsed = Time.now.to_f - t.to_f
+ elapsed = sprintf("%3.3f", elapsed)
+ $log.debug "#{msg.ljust(width)}: #{elapsed}s"
+ return_value
+end
+

0 comments on commit fd92455

Please sign in to comment.