aboutsummaryrefslogtreecommitdiffstats
path: root/lib/rubygems/specification.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/specification.rb')
-rw-r--r--lib/rubygems/specification.rb256
1 files changed, 123 insertions, 133 deletions
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 08614cd9eb..596f6ff69f 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -5,10 +5,13 @@
# See LICENSE.txt for permissions.
#++
+
require 'rubygems/version'
require 'rubygems/requirement'
require 'rubygems/platform'
require 'rubygems/deprecate'
+require 'rubygems/basic_specification'
+require 'rubygems/stub_specification'
# :stopdoc:
# date.rb can't be loaded for `make install` due to miniruby
@@ -45,7 +48,7 @@ class Date; end
#
# s.metadata = { "bugtracker" => "http://somewhere.com/blah" }
-class Gem::Specification
+class Gem::Specification < Gem::BasicSpecification
# REFACTOR: Consider breaking out this version stuff into a separate
# module. There's enough special stuff around it that it may justify
@@ -107,6 +110,10 @@ class Gem::Specification
today = Time.now.utc
TODAY = Time.utc(today.year, today.month, today.day)
+ LOAD_CACHE = {}
+
+ private_constant :LOAD_CACHE if defined? private_constant
+
# :startdoc:
##
@@ -156,6 +163,17 @@ class Gem::Specification
:version => nil,
}
+ Dupable = { }
+
+ @@default_value.each do |k,v|
+ case v
+ when Time, Numeric, Symbol, true, false, nil
+ Dupable[k] = false
+ else
+ Dupable[k] = true
+ end
+ end
+
@@attributes = @@default_value.keys.sort_by { |s| s.to_s }
@@array_attributes = @@default_value.reject { |k,v| v != [] }.keys
@@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition { |k|
@@ -584,11 +602,6 @@ class Gem::Specification
attr_writer :default_executable
##
- # Path this gemspec was loaded from. This attribute is not persisted.
-
- attr_reader :loaded_from
-
- ##
# Allows deinstallation of gems with legacy platforms.
attr_writer :original_platform # :nodoc:
@@ -615,58 +628,68 @@ class Gem::Specification
attr_accessor :specification_version
- class << self
- def default_specifications_dir
- File.join(Gem.default_dir, "specifications", "default")
+ def self._all # :nodoc:
+ unless defined?(@@all) && @@all then
+ @@all = stubs.map(&:to_spec)
+
+ # After a reset, make sure already loaded specs
+ # are still marked as activated.
+ specs = {}
+ Gem.loaded_specs.each_value{|s| specs[s] = true}
+ @@all.each{|s| s.activated = true if specs[s]}
+
+ _resort!(@@all)
end
+ @@all
+ end
- def each_spec(search_dirs) # :nodoc:
- search_dirs.each { |dir|
- Dir[File.join(dir, "*.gemspec")].each { |path|
- spec = Gem::Specification.load path.untaint
- # #load returns nil if the spec is bad, so we just ignore
- # it at this stage
- yield(spec) if spec
- }
- }
+ def self._clear_load_cache # :nodoc:
+ LOAD_CACHE.clear
+ end
+
+ # :nodoc:
+ def self.each_gemspec(dirs)
+ dirs.each do |dir|
+ Dir[File.join(dir, "*.gemspec")].each do |path|
+ yield path.untaint
+ end
end
+ end
- def each_default(&block) # :nodoc:
- each_spec([default_specifications_dir],
- &block)
+ # :nodoc:
+ def self.each_stub(dirs)
+ each_gemspec(dirs) do |path|
+ stub = Gem::StubSpecification.new(path)
+ yield stub if stub.valid?
end
+ end
- def each_normal(&block) # :nodoc:
- each_spec(dirs, &block)
+ # :nodoc:
+ def self.each_spec(dirs)
+ each_gemspec(dirs) do |path|
+ spec = self.load path
+ yield spec if spec
end
end
- def self._all # :nodoc:
- unless defined?(@@all) && @@all then
+ ##
+ # Returns a Gem::StubSpecification for every installed gem
- specs = {}
- each_default do |spec|
- specs[spec.full_name] ||= spec
+ def self.stubs
+ @@stubs ||= begin
+ stubs = {}
+ each_stub([default_specifications_dir] + dirs) do |stub|
+ stubs[stub.full_name] ||= stub
end
- each_normal do |spec|
- specs[spec.full_name] ||= spec
- end
-
- @@all = specs.values
-
- # After a reset, make sure already loaded specs
- # are still marked as activated.
- specs = {}
- Gem.loaded_specs.each_value{|s| specs[s] = true}
- @@all.each{|s| s.activated = true if specs[s]}
- _resort!
+ stubs = stubs.values
+ _resort!(stubs)
+ stubs
end
- @@all
end
- def self._resort! # :nodoc:
- @@all.sort! { |a, b|
+ def self._resort!(specs) # :nodoc:
+ specs.sort! { |a, b|
names = a.name <=> b.name
next names if names.nonzero?
b.version <=> a.version
@@ -677,7 +700,9 @@ class Gem::Specification
# Loads the default specifications. It should be called only once.
def self.load_defaults
- each_default do |spec|
+ each_spec([default_specifications_dir]) do |spec|
+ # #load returns nil if the spec is bad, so we just ignore
+ # it at this stage
Gem.register_default_spec(spec)
end
end
@@ -700,7 +725,9 @@ class Gem::Specification
return if _all.include? spec
_all << spec
- _resort!
+ stubs << spec
+ _resort!(_all)
+ _resort!(stubs)
end
##
@@ -843,9 +870,10 @@ class Gem::Specification
# amongst the specs that are not activated.
def self.find_inactive_by_path path
- self.find { |spec|
- spec.contains_requirable_file? path unless spec.activated?
+ stub = stubs.find { |s|
+ s.contains_requirable_file? path unless s.activated?
}
+ stub && stub.to_spec
end
##
@@ -937,6 +965,9 @@ class Gem::Specification
file = file.dup.untaint
return unless File.file?(file)
+ spec = LOAD_CACHE[file]
+ return spec if spec
+
code = if defined? Encoding
File.read file, :mode => 'r:UTF-8:-'
else
@@ -950,6 +981,7 @@ class Gem::Specification
if Gem::Specification === spec
spec.loaded_from = file.to_s
+ LOAD_CACHE[file] = spec
return spec
end
@@ -1013,6 +1045,7 @@ class Gem::Specification
raise "wtf: #{spec.full_name} not in #{all_names.inspect}" unless
_all.include? spec
_all.delete spec
+ stubs.delete_if { |s| s.full_name == spec.full_name }
end
##
@@ -1037,6 +1070,8 @@ class Gem::Specification
@@dirs = nil
Gem.pre_reset_hooks.each { |hook| hook.call }
@@all = nil
+ @@stubs = nil
+ _clear_load_cache
unresolved = unresolved_deps
unless unresolved.empty? then
w = "W" + "ARN"
@@ -1281,20 +1316,6 @@ class Gem::Specification
end
##
- # Returns the full path to the base gem directory.
- #
- # eg: /usr/local/lib/ruby/gems/1.8
-
- def base_dir
- return Gem.dir unless loaded_from
- @base_dir ||= if default_gem? then
- File.dirname File.dirname File.dirname loaded_from
- else
- File.dirname File.dirname loaded_from
- end
- end
-
- ##
# Returns the full path to installed gem's bin directory.
#
# NOTE: do not confuse this with +bindir+, which is just 'bin', not
@@ -1368,19 +1389,6 @@ class Gem::Specification
end
##
- # Return true if this spec can require +file+.
-
- def contains_requirable_file? file
- root = full_gem_path
- suffixes = Gem.suffixes
-
- require_paths.any? do |lib|
- base = "#{root}/#{lib}/#{file}"
- suffixes.any? { |suf| File.file? "#{base}#{suf}" }
- end
- end
-
- ##
# The date this gem was created. Lazily defaults to TODAY.
def date
@@ -1623,35 +1631,14 @@ class Gem::Specification
spec
end
- ##
- # The full path to the gem (install path + full name).
-
- def full_gem_path
- # TODO: This is a heavily used method by gems, so we'll need
- # to aleast just alias it to #gem_dir rather than remove it.
-
- # TODO: also, shouldn't it default to full_name if it hasn't been written?
- return @full_gem_path if defined?(@full_gem_path) && @full_gem_path
-
- @full_gem_path = File.expand_path File.join(gems_dir, full_name)
- @full_gem_path.untaint
-
- return @full_gem_path if File.directory? @full_gem_path
-
- @full_gem_path = File.expand_path File.join(gems_dir, original_name)
+ # :nodoc:
+ def find_full_gem_path
+ super || File.expand_path(File.join(gems_dir, original_name))
end
-
- ##
- # Returns the full name (name-version) of this Gem. Platform information
- # is included (name-version-platform) if it is specified and not the
- # default Ruby platform.
+ private :find_full_gem_path
def full_name
- @full_name ||= if platform == Gem::Platform::RUBY or platform.nil? then
- "#{@name}-#{@version}".untaint
- else
- "#{@name}-#{@version}-#{platform}".untaint
- end
+ @full_name ||= super
end
##
@@ -1663,15 +1650,6 @@ class Gem::Specification
end
##
- # Returns the full path to the gems directory containing this spec's
- # gem directory. eg: /usr/local/lib/ruby/1.8/gems
-
- def gems_dir
- # TODO: this logic seems terribly broken, but tests fail if just base_dir
- @gems_dir ||= File.join(loaded_from && base_dir || Gem.dir, "gems")
- end
-
- ##
# Deprecated and ignored, defaults to true.
#
# Formerly used to indicate this gem was RDoc-capable.
@@ -1703,9 +1681,7 @@ class Gem::Specification
# :startdoc:
def hash # :nodoc:
- @@attributes.inject(0) { |hash_code, (name, _)|
- hash_code ^ self.send(name).hash
- }
+ name.hash ^ version.hash
end
def init_with coder # :nodoc:
@@ -1720,7 +1696,7 @@ class Gem::Specification
def initialize name = nil, version = nil
@loaded = false
@activated = false
- @loaded_from = nil
+ self.loaded_from = nil
@original_platform = nil
@@nil_attributes.each do |key|
@@ -1729,11 +1705,7 @@ class Gem::Specification
@@non_nil_attributes.each do |key|
default = default_value(key)
- value = case default
- when Time, Numeric, Symbol, true, false, nil then default
- else default.dup
- end
-
+ value = Dupable[key] ? default.dup : default
instance_variable_set "@#{key}", value
end
@@ -1828,28 +1800,31 @@ class Gem::Specification
@licenses ||= []
end
- ##
- # Set the location a Specification was loaded from. +obj+ is converted
- # to a String.
+ def filename= path
+ super
- def loaded_from= path
- @loaded_from = path.to_s
-
- # reset everything @loaded_from depends upon
- @base_dir = nil
@bin_dir = nil
@cache_dir = nil
@cache_file = nil
@doc_dir = nil
- @full_gem_path = nil
@gem_dir = nil
- @gems_dir = nil
@ri_dir = nil
@spec_dir = nil
@spec_file = nil
end
##
+ # Path this gemspec was loaded from. This attribute is not persisted.
+
+ alias loaded_from filename
+
+ ##
+ # Set the location a Specification was loaded from. +obj+ is converted
+ # to a String.
+
+ alias loaded_from= filename=
+
+ ##
# Sets the rubygems_version to the current RubyGems version.
def mark_version
@@ -1878,6 +1853,11 @@ class Gem::Specification
end
end
+ # Prevent ruby hitting spec.method_missing when [[spec]].flatten is called
+ def to_ary # :nodoc:
+ nil
+ end
+
##
# Normalize the list of files so that:
# * All file lists have redundancies removed.
@@ -2094,6 +2074,13 @@ class Gem::Specification
end
##
+ # Used by Gem::DependencyResolver to order Gem::Specification objects
+
+ def source # :nodoc:
+ self
+ end
+
+ ##
# Returns the full path to the directory containing this spec's
# gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications
@@ -2172,6 +2159,7 @@ class Gem::Specification
mark_version
result = []
result << "# -*- encoding: utf-8 -*-"
+ result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{require_paths.join("\0")}"
result << nil
result << "Gem::Specification.new do |s|"
@@ -2259,6 +2247,13 @@ class Gem::Specification
"#<Gem::Specification name=#{@name} version=#{@version}>"
end
+ ##
+ # Returns self
+
+ def to_spec
+ self
+ end
+
def to_yaml(opts = {}) # :nodoc:
if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? then
# Because the user can switch the YAML engine behind our
@@ -2559,11 +2554,6 @@ class Gem::Specification
end
end
- def default_gem?
- loaded_from &&
- File.dirname(loaded_from) == self.class.default_specifications_dir
- end
-
extend Gem::Deprecate
# TODO: