diff options
Diffstat (limited to 'lib/rubygems/specification.rb')
-rw-r--r-- | lib/rubygems/specification.rb | 291 |
1 files changed, 148 insertions, 143 deletions
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 12943a3e24..87557cee5f 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -5,13 +5,10 @@ # 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 @@ -34,7 +31,7 @@ class Date; end # s.homepage = 'https://rubygems.org/gems/example' # end # -# Starting in RubyGems 2.0, a Specification can hold arbitrary +# Starting in RubyGems 1.9.0, a Specification can hold arbitrary # metadata. This metadata is accessed via Specification#metadata # and has the following restrictions: # @@ -48,7 +45,7 @@ class Date; end # # s.metadata = { "bugtracker" => "http://somewhere.com/blah" } -class Gem::Specification < Gem::BasicSpecification +class Gem::Specification # REFACTOR: Consider breaking out this version stuff into a separate # module. There's enough special stuff around it that it may justify @@ -110,10 +107,6 @@ class Gem::Specification < Gem::BasicSpecification today = Time.now.utc TODAY = Time.utc(today.year, today.month, today.day) - LOAD_CACHE = {} - - private_constant :LOAD_CACHE if defined? private_constant - # :startdoc: ## @@ -163,17 +156,6 @@ class Gem::Specification < Gem::BasicSpecification :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| @@ -458,7 +440,7 @@ class Gem::Specification < Gem::BasicSpecification # # For example, the rake gem has rake as an executable. You don’t specify the # full path (as in bin/rake); all application-style files are expected to be - # found in bindir. These files must be executable Ruby files. Files that + # found in bindir. These files must be executable ruby files. Files that # use bash or other interpreters will not work. # # Usage: @@ -617,12 +599,17 @@ class Gem::Specification < Gem::BasicSpecification 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: ## - # The version of Ruby required by this gem + # The version of ruby required by this gem attr_reader :required_ruby_version @@ -646,68 +633,58 @@ class Gem::Specification < Gem::BasicSpecification attr_accessor :specification_version - 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) + class << self + def default_specifications_dir + File.join(Gem.default_dir, "specifications", "default") end - @@all - end - 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 + 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 + } + } end - end - # :nodoc: - def self.each_stub(dirs) - each_gemspec(dirs) do |path| - stub = Gem::StubSpecification.new(path) - yield stub if stub.valid? + def each_default(&block) # :nodoc: + each_spec([default_specifications_dir], + &block) end - end - # :nodoc: - def self.each_spec(dirs) - each_gemspec(dirs) do |path| - spec = self.load path - yield spec if spec + def each_normal(&block) # :nodoc: + each_spec(dirs, &block) end end - ## - # Returns a Gem::StubSpecification for every installed gem + def self._all # :nodoc: + unless defined?(@@all) && @@all then - def self.stubs - @@stubs ||= begin - stubs = {} - each_stub([default_specifications_dir] + dirs) do |stub| - stubs[stub.full_name] ||= stub + specs = {} + each_default do |spec| + specs[spec.full_name] ||= spec end + each_normal do |spec| + specs[spec.full_name] ||= spec + end + + @@all = specs.values - stubs = stubs.values - _resort!(stubs) - stubs + # 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! end + @@all end - def self._resort!(specs) # :nodoc: - specs.sort! { |a, b| + def self._resort! # :nodoc: + @@all.sort! { |a, b| names = a.name <=> b.name next names if names.nonzero? b.version <=> a.version @@ -718,9 +695,7 @@ class Gem::Specification < Gem::BasicSpecification # Loads the default specifications. It should be called only once. def self.load_defaults - each_spec([default_specifications_dir]) do |spec| - # #load returns nil if the spec is bad, so we just ignore - # it at this stage + each_default do |spec| Gem.register_default_spec(spec) end end @@ -743,9 +718,7 @@ class Gem::Specification < Gem::BasicSpecification return if _all.include? spec _all << spec - stubs << spec - _resort!(_all) - _resort!(stubs) + _resort! end ## @@ -786,7 +759,7 @@ class Gem::Specification < Gem::BasicSpecification # -- wilsonb def self.all= specs - @@all = @@stubs = specs + @@all = specs end ## @@ -888,10 +861,9 @@ class Gem::Specification < Gem::BasicSpecification # amongst the specs that are not activated. def self.find_inactive_by_path path - stub = stubs.find { |s| - s.contains_requirable_file? path unless s.activated? + self.find { |spec| + spec.contains_requirable_file? path unless spec.activated? } - stub && stub.to_spec end ## @@ -972,7 +944,7 @@ class Gem::Specification < Gem::BasicSpecification result.map(&:last).map(&:values).flatten.reject { |spec| minimum = native[spec.name] minimum && spec.version < minimum - }.sort_by{ |tup| tup.name } + } end ## @@ -983,9 +955,6 @@ class Gem::Specification < Gem::BasicSpecification 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 @@ -999,7 +968,6 @@ class Gem::Specification < Gem::BasicSpecification if Gem::Specification === spec spec.loaded_from = file.to_s - LOAD_CACHE[file] = spec return spec end @@ -1034,43 +1002,25 @@ class Gem::Specification < Gem::BasicSpecification end ## - # Return a list of all outdated local gem names. This method is HEAVY + # Return a list of all outdated specifications. This method is HEAVY # as it must go fetch specifications from the server. - # - # Use outdated_and_latest_version if you wish to retrieve the latest remote - # version as well. def self.outdated - outdated_and_latest_version.map { |local, _| local.name } - end - - ## - # Enumerates the outdated local gems yielding the local specification and - # the latest remote version. - # - # This method may take some time to return as it must check each local gem - # against the server's index. - - def self.outdated_and_latest_version - return enum_for __method__ unless block_given? + outdateds = [] # TODO: maybe we should switch to rubygems' version service? fetcher = Gem::SpecFetcher.fetcher - latest_specs(true).each do |local_spec| - dependency = - Gem::Dependency.new local_spec.name, ">= #{local_spec.version}" + latest_specs(true).each do |local| + dependency = Gem::Dependency.new local.name, ">= #{local.version}" + remotes, _ = fetcher.search_for_dependency dependency + remotes = remotes.map { |n, _| n.version } + latest = remotes.sort.last - remotes, = fetcher.search_for_dependency dependency - remotes = remotes.map { |n, _| n.version } - - latest_remote = remotes.sort.last - - yield [local_spec, latest_remote] if - latest_remote and local_spec.version < latest_remote + outdateds << local.name if latest and local.version < latest end - nil + outdateds end ## @@ -1081,7 +1031,6 @@ class Gem::Specification < Gem::BasicSpecification 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 ## @@ -1106,8 +1055,6 @@ class Gem::Specification < Gem::BasicSpecification @@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" @@ -1354,6 +1301,20 @@ class Gem::Specification < Gem::BasicSpecification 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 @@ -1427,6 +1388,19 @@ class Gem::Specification < Gem::BasicSpecification 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 the current UTC date. # # There is no need to set this in your gem specification. @@ -1671,14 +1645,35 @@ class Gem::Specification < Gem::BasicSpecification spec end - # :nodoc: - def find_full_gem_path - super || File.expand_path(File.join(gems_dir, original_name)) + ## + # 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) end - private :find_full_gem_path + + ## + # 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. def full_name - @full_name ||= super + @full_name ||= if platform == Gem::Platform::RUBY or platform.nil? then + "#{@name}-#{@version}".untaint + else + "#{@name}-#{@version}-#{platform}".untaint + end end ## @@ -1690,6 +1685,15 @@ class Gem::Specification < Gem::BasicSpecification 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. @@ -1721,7 +1725,9 @@ class Gem::Specification < Gem::BasicSpecification # :startdoc: def hash # :nodoc: - name.hash ^ version.hash + @@attributes.inject(0) { |hash_code, (name, _)| + hash_code ^ self.send(name).hash + } end def init_with coder # :nodoc: @@ -1736,7 +1742,7 @@ class Gem::Specification < Gem::BasicSpecification def initialize name = nil, version = nil @loaded = false @activated = false - self.loaded_from = nil + @loaded_from = nil @original_platform = nil @@nil_attributes.each do |key| @@ -1745,7 +1751,11 @@ class Gem::Specification < Gem::BasicSpecification @@non_nil_attributes.each do |key| default = default_value(key) - value = Dupable[key] ? default.dup : default + value = case default + when Time, Numeric, Symbol, true, false, nil then default + else default.dup + end + instance_variable_set "@#{key}", value end @@ -1840,14 +1850,22 @@ class Gem::Specification < Gem::BasicSpecification @licenses ||= [] end - def loaded_from= path # :nodoc: - super + ## + # Set the location a Specification was loaded from. +obj+ is converted + # to a String. + + 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 @@ -2032,10 +2050,6 @@ class Gem::Specification < Gem::BasicSpecification @requirements = Array req end - def respond_to_missing? m, include_private = false # :nodoc: - false - end - ## # Returns the full path to this spec's ri directory. @@ -2097,17 +2111,11 @@ class Gem::Specification < Gem::BasicSpecification # Returns an object you can use to sort specifications in #sort_by. def sort_obj + # TODO: this is horrible. Deprecate it. [@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1] 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 @@ -2186,7 +2194,6 @@ class Gem::Specification < Gem::BasicSpecification 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|" @@ -2274,13 +2281,6 @@ class Gem::Specification < Gem::BasicSpecification "#<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 @@ -2581,6 +2581,11 @@ class Gem::Specification < Gem::BasicSpecification end end + def default_gem? + loaded_from && + File.dirname(loaded_from) == self.class.default_specifications_dir + end + extend Gem::Deprecate # TODO: |