diff options
Diffstat (limited to 'lib/rubygems/specification.rb')
-rw-r--r-- | lib/rubygems/specification.rb | 566 |
1 files changed, 301 insertions, 265 deletions
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index 56db0945eb..29139cf725 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + # #-- # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. @@ -6,11 +7,13 @@ # See LICENSE.txt for permissions. #++ -require_relative 'deprecate' -require_relative 'basic_specification' -require_relative 'stub_specification' -require_relative 'platform' -require_relative 'util/list' +require_relative "deprecate" +require_relative "basic_specification" +require_relative "stub_specification" +require_relative "platform" +require_relative "util/list" + +require "rbconfig" ## # The Specification class contains the information for a gem. Typically @@ -74,29 +77,29 @@ class Gem::Specification < Gem::BasicSpecification # key should be equal to the CURRENT_SPECIFICATION_VERSION. SPECIFICATION_VERSION_HISTORY = { # :nodoc: - -1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'], - 1 => [ + -1 => ["(RubyGems versions up to and including 0.7 did not have versioned specifications)"], + 1 => [ 'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"', '"test_file=x" is a shortcut for "test_files=[x]"', ], 2 => [ 'Added "required_rubygems_version"', - 'Now forward-compatible with future versions', + "Now forward-compatible with future versions", ], 3 => [ - 'Added Fixnum validation to the specification_version', + "Added Fixnum validation to the specification_version", ], 4 => [ - 'Added sandboxed freeform metadata to the specification version.', + "Added sandboxed freeform metadata to the specification version.", ], }.freeze MARSHAL_FIELDS = { # :nodoc: -1 => 16, - 1 => 16, - 2 => 16, - 3 => 17, - 4 => 18, + 1 => 16, + 2 => 16, + 3 => 17, + 4 => 18, }.freeze today = Time.now.utc @@ -105,7 +108,7 @@ class Gem::Specification < Gem::BasicSpecification @load_cache = {} # :nodoc: @load_cache_mutex = Thread::Mutex.new - VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/.freeze # :nodoc: + VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/ # :nodoc: # :startdoc: @@ -124,35 +127,35 @@ class Gem::Specification < Gem::BasicSpecification # Map of attribute names to default values. @@default_value = { - :authors => [], - :autorequire => nil, - :bindir => 'bin', - :cert_chain => [], - :date => nil, - :dependencies => [], - :description => nil, - :email => nil, - :executables => [], - :extensions => [], - :extra_rdoc_files => [], - :files => [], - :homepage => nil, - :licenses => [], - :metadata => {}, - :name => nil, - :platform => Gem::Platform::RUBY, - :post_install_message => nil, - :rdoc_options => [], - :require_paths => ['lib'], - :required_ruby_version => Gem::Requirement.default, - :required_rubygems_version => Gem::Requirement.default, - :requirements => [], - :rubygems_version => Gem::VERSION, - :signing_key => nil, - :specification_version => CURRENT_SPECIFICATION_VERSION, - :summary => nil, - :test_files => [], - :version => nil, + authors: [], + autorequire: nil, + bindir: "bin", + cert_chain: [], + date: nil, + dependencies: [], + description: nil, + email: nil, + executables: [], + extensions: [], + extra_rdoc_files: [], + files: [], + homepage: nil, + licenses: [], + metadata: {}, + name: nil, + platform: Gem::Platform::RUBY, + post_install_message: nil, + rdoc_options: [], + require_paths: ["lib"], + required_ruby_version: Gem::Requirement.default, + required_rubygems_version: Gem::Requirement.default, + requirements: [], + rubygems_version: Gem::VERSION, + signing_key: nil, + specification_version: CURRENT_SPECIFICATION_VERSION, + summary: nil, + test_files: [], + version: nil, }.freeze # rubocop:disable Style/MutableConstant @@ -161,19 +164,17 @@ class Gem::Specification < Gem::BasicSpecification @@default_value.each do |k,v| INITIALIZE_CODE_FOR_DEFAULTS[k] = case v - when [], {}, true, false, nil, Numeric, Symbol - v.inspect - when String - v.dump - when Numeric - "default_value(:#{k})" - else - "default_value(:#{k}).dup" + when [], {}, true, false, nil, Numeric, Symbol + v.inspect + when String + v.dump + else + "default_value(:#{k}).dup" end end - @@attributes = @@default_value.keys.sort_by {|s| s.to_s } - @@array_attributes = @@default_value.reject {|k,v| v != [] }.keys + @@attributes = @@default_value.keys.sort_by(&:to_s) + @@array_attributes = @@default_value.reject {|_k,v| v != [] }.keys @@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition do |k| @@default_value[k].nil? end @@ -262,8 +263,7 @@ class Gem::Specification < Gem::BasicSpecification @test_files, add_bindir(@executables), @extra_rdoc_files, - @extensions, - ].flatten.compact.uniq.sort + @extensions].flatten.compact.uniq.sort end ## @@ -301,7 +301,7 @@ class Gem::Specification < Gem::BasicSpecification # # Usage: # - # spec.description = <<-EOF + # spec.description = <<~EOF # Rake is a Make-like program implemented in Ruby. Tasks and # dependencies are specified in standard Ruby syntax. # EOF @@ -338,10 +338,10 @@ class Gem::Specification < Gem::BasicSpecification # The simplest way is to specify the standard SPDX ID # https://spdx.org/licenses/ for the license. # Ideally, you should pick one that is OSI (Open Source Initiative) - # http://opensource.org/licenses/alphabetical approved. + # https://opensource.org/licenses/ approved. # # The most commonly used OSI-approved licenses are MIT and Apache-2.0. - # GitHub also provides a license picker at http://choosealicense.com/. + # GitHub also provides a license picker at https://choosealicense.com/. # # You can also use a custom license file along with your gemspec and specify # a LicenseRef-<idstring>, where idstring is the name of the file containing @@ -426,11 +426,11 @@ class Gem::Specification < Gem::BasicSpecification end ## - # The path in the gem for executable scripts. Usually 'bin' + # The path in the gem for executable scripts. Usually 'exe' # # Usage: # - # spec.bindir = 'bin' + # spec.bindir = 'exe' attr_accessor :bindir @@ -473,7 +473,7 @@ class Gem::Specification < Gem::BasicSpecification # spec.platform = Gem::Platform.local def platform=(platform) - if @original_platform.nil? or + if @original_platform.nil? || @original_platform == Gem::Platform::RUBY @original_platform = platform end @@ -489,12 +489,12 @@ class Gem::Specification < Gem::BasicSpecification # legacy constants when nil, Gem::Platform::RUBY then @new_platform = Gem::Platform::RUBY - when 'mswin32' then # was Gem::Platform::WIN32 - @new_platform = Gem::Platform.new 'x86-mswin32' - when 'i586-linux' then # was Gem::Platform::LINUX_586 - @new_platform = Gem::Platform.new 'x86-linux' - when 'powerpc-darwin' then # was Gem::Platform::DARWIN - @new_platform = Gem::Platform.new 'ppc-darwin' + when "mswin32" then # was Gem::Platform::WIN32 + @new_platform = Gem::Platform.new "x86-mswin32" + when "i586-linux" then # was Gem::Platform::LINUX_586 + @new_platform = Gem::Platform.new "x86-linux" + when "powerpc-darwin" then # was Gem::Platform::DARWIN + @new_platform = Gem::Platform.new "ppc-darwin" else @new_platform = Gem::Platform.new platform end @@ -502,8 +502,6 @@ class Gem::Specification < Gem::BasicSpecification @platform = @new_platform.to_s invalidate_memoized_attributes - - @new_platform end ## @@ -533,13 +531,6 @@ class Gem::Specification < Gem::BasicSpecification attr_reader :required_rubygems_version ## - # The version of RubyGems used to create this gem. - # - # Do not set this, it is set automatically when the gem is packaged. - - attr_accessor :rubygems_version - - ## # The key used to sign this gem. See Gem::Security for details. attr_accessor :signing_key @@ -577,7 +568,7 @@ class Gem::Specification < Gem::BasicSpecification ## # Executables included in the gem. # - # For example, the rake gem has rake as an executable. You don’t specify the + # 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 # use bash or other interpreters will not work. @@ -598,7 +589,7 @@ class Gem::Specification < Gem::BasicSpecification # extconf.rb-style files used to compile extensions. # # These files will be run when the gem is installed, causing the C (or - # whatever) code to be compiled on the user’s machine. + # whatever) code to be compiled on the user's machine. # # Usage: # @@ -727,6 +718,21 @@ class Gem::Specification < Gem::BasicSpecification end ###################################################################### + # :section: Read-only attributes + + ## + # The version of RubyGems used to create this gem. + + attr_accessor :rubygems_version + + ## + # The path where this gem installs its extensions. + + def extensions_dir + @extensions_dir ||= super + end + + ###################################################################### # :section: Specification internals ## @@ -734,7 +740,7 @@ class Gem::Specification < Gem::BasicSpecification attr_accessor :activated - alias :activated? :activated + alias_method :activated?, :activated ## # Autorequire was used by old RubyGems to automatically require a file. @@ -777,7 +783,7 @@ class Gem::Specification < Gem::BasicSpecification def self.each_gemspec(dirs) # :nodoc: dirs.each do |dir| Gem::Util.glob_files_in_dir("*.gemspec", dir).each do |path| - yield path.tap(&Gem::UNTAINT) + yield path end end end @@ -856,7 +862,7 @@ class Gem::Specification < Gem::BasicSpecification installed_stubs = installed_stubs(Gem::Specification.dirs, pattern) installed_stubs.select! {|s| Gem::Platform.match_spec? s } if match_platform stubs = installed_stubs + default_stubs(pattern) - stubs = stubs.uniq {|stub| stub.full_name } + stubs = stubs.uniq(&:full_name) _resort!(stubs) stubs end @@ -883,6 +889,30 @@ class Gem::Specification < Gem::BasicSpecification end ## + # Adds +spec+ to the known specifications, keeping the collection + # properly sorted. + + def self.add_spec(spec) + return if _all.include? spec + + _all << spec + stubs << spec + (@@stubs_by_name[spec.name] ||= []) << spec + + _resort!(@@stubs_by_name[spec.name]) + _resort!(stubs) + end + + ## + # Removes +spec+ from the known specs. + + def self.remove_spec(spec) + _all.delete spec.to_spec + stubs.delete spec + (@@stubs_by_name[spec.name] || []).delete spec + end + + ## # Returns all specifications. This method is discouraged from use. # You probably want to use one of the Enumerable methods instead. @@ -913,7 +943,7 @@ class Gem::Specification < Gem::BasicSpecification # Return full names of all specs in sorted order. def self.all_names - self._all.map(&:full_name) + _all.map(&:full_name) end ## @@ -939,7 +969,7 @@ class Gem::Specification < Gem::BasicSpecification def self.dirs @@dirs ||= Gem.path.collect do |dir| - File.join dir.dup.tap(&Gem::UNTAINT), "specifications" + File.join dir, "specifications" end end @@ -948,7 +978,7 @@ class Gem::Specification < Gem::BasicSpecification # this resets the list of known specs. def self.dirs=(dirs) - self.reset + reset @@dirs = Array(dirs).map {|dir| File.join dir, "specifications" } end @@ -962,7 +992,7 @@ class Gem::Specification < Gem::BasicSpecification def self.each return enum_for(:each) unless block_given? - self._all.each do |x| + _all.each do |x| yield x end end @@ -973,8 +1003,6 @@ class Gem::Specification < Gem::BasicSpecification def self.find_all_by_name(name, *requirements) requirements = Gem::Requirement.default if requirements.empty? - # TODO: maybe try: find_all { |s| spec === dep } - Gem::Dependency.new(name, *requirements).matching_specs end @@ -992,19 +1020,24 @@ class Gem::Specification < Gem::BasicSpecification def self.find_by_name(name, *requirements) requirements = Gem::Requirement.default if requirements.empty? - # TODO: maybe try: find { |s| spec === dep } - Gem::Dependency.new(name, *requirements).to_spec end ## + # Find the best specification matching a +full_name+. + def self.find_by_full_name(full_name) + stubs.find {|s| s.full_name == full_name }&.to_spec + end + + ## # Return the best specification that contains the file matching +path+. def self.find_by_path(path) path = path.dup.freeze - spec = @@spec_with_requirable_file[path] ||= (stubs.find do |s| + spec = @@spec_with_requirable_file[path] ||= stubs.find do |s| s.contains_requirable_file? path - end || NOT_FOUND) + end || NOT_FOUND + spec.to_spec end @@ -1017,13 +1050,14 @@ class Gem::Specification < Gem::BasicSpecification next if s.activated? s.contains_requirable_file? path end - stub && stub.to_spec + stub&.to_spec end def self.find_active_stub_by_path(path) - stub = @@active_stub_with_requirable_file[path] ||= (stubs.find do |s| - s.activated? and s.contains_requirable_file? path - end || NOT_FOUND) + stub = @@active_stub_with_requirable_file[path] ||= stubs.find do |s| + s.activated? && s.contains_requirable_file?(path) + end || NOT_FOUND + stub.this end @@ -1040,7 +1074,7 @@ class Gem::Specification < Gem::BasicSpecification def self.find_in_unresolved_tree(path) unresolved_specs.each do |spec| - spec.traverse do |from_spec, dep, to_spec, trail| + spec.traverse do |_from_spec, _dep, to_spec, trail| if to_spec.has_conflicts? || to_spec.conficts_when_loaded_with?(trail) :next else @@ -1053,7 +1087,7 @@ class Gem::Specification < Gem::BasicSpecification end def self.unresolved_specs - unresolved_deps.values.map {|dep| dep.to_specs }.flatten + unresolved_deps.values.map(&:to_specs).flatten end private_class_method :unresolved_specs @@ -1105,12 +1139,14 @@ class Gem::Specification < Gem::BasicSpecification result = {} specs.reverse_each do |spec| - next if spec.version.prerelease? unless prerelease + unless prerelease + next if spec.version.prerelease? + end result[spec.name] = spec end - result.map(&:last).flatten.sort_by {|tup| tup.name } + result.map(&:last).flatten.sort_by(&:name) end ## @@ -1119,36 +1155,33 @@ class Gem::Specification < Gem::BasicSpecification def self.load(file) return unless file - _spec = @load_cache_mutex.synchronize { @load_cache[file] } - return _spec if _spec + spec = @load_cache_mutex.synchronize { @load_cache[file] } + return spec if spec - file = file.dup.tap(&Gem::UNTAINT) return unless File.file?(file) - code = Gem.open_file(file, 'r:UTF-8:-', &:read) - - code.tap(&Gem::UNTAINT) + code = Gem.open_file(file, "r:UTF-8:-", &:read) begin - _spec = eval code, binding, file + spec = eval code, binding, file - if Gem::Specification === _spec - _spec.loaded_from = File.expand_path file.to_s + if Gem::Specification === spec + spec.loaded_from = File.expand_path file.to_s @load_cache_mutex.synchronize do prev = @load_cache[file] if prev - _spec = prev + spec = prev else - @load_cache[file] = _spec + @load_cache[file] = spec end end - return _spec + return spec end - warn "[#{file}] isn't a Gem::Specification (#{_spec.class} instead)." + warn "[#{file}] isn't a Gem::Specification (#{spec.class} instead)." rescue SignalException, SystemExit raise - rescue SyntaxError, Exception => e + rescue SyntaxError, StandardError => e warn "Invalid gemspec in [#{file}]: #{e}" end @@ -1210,7 +1243,7 @@ class Gem::Specification < Gem::BasicSpecification latest_remote = remotes.sort.last yield [local_spec, latest_remote] if - latest_remote and local_spec.version < latest_remote + latest_remote && local_spec.version < latest_remote end nil @@ -1236,7 +1269,7 @@ class Gem::Specification < Gem::BasicSpecification def self.reset @@dirs = nil - Gem.pre_reset_hooks.each {|hook| hook.call } + Gem.pre_reset_hooks.each(&:call) clear_specs clear_load_cache unresolved = unresolved_deps @@ -1255,7 +1288,7 @@ class Gem::Specification < Gem::BasicSpecification warn "Please report a bug if this causes problems." unresolved.clear end - Gem.post_reset_hooks.each {|hook| hook.call } + Gem.post_reset_hooks.each(&:call) end # DOC: This method needs documented or nodoc'd @@ -1268,10 +1301,23 @@ class Gem::Specification < Gem::BasicSpecification def self._load(str) Gem.load_yaml + Gem.load_safe_marshal + + yaml_set = false + retry_count = 0 array = begin - Marshal.load str + Gem::SafeMarshal.safe_load str rescue ArgumentError => e + # Avoid an infinite retry loop when the argument error has nothing to do + # with the classes not being defined. + # 1 retry each allowed in case all 3 of + # - YAML + # - YAML::Syck::DefaultKey + # - YAML::PrivateType + # need to be defined + raise if retry_count >= 3 + # # Some very old marshaled specs included references to `YAML::PrivateType` # and `YAML::Syck::DefaultKey` constants due to bugs in the old emitter @@ -1281,17 +1327,23 @@ class Gem::Specification < Gem::BasicSpecification message = e.message raise unless message.include?("YAML::") - Object.const_set "YAML", Psych unless Object.const_defined?(:YAML) + unless Object.const_defined?(:YAML) + Object.const_set "YAML", Psych + yaml_set = true + end if message.include?("YAML::Syck::") YAML.const_set "Syck", YAML unless YAML.const_defined?(:Syck) - YAML::Syck.const_set "DefaultKey", Class.new if message.include?("YAML::Syck::DefaultKey") - elsif message.include?("YAML::PrivateType") + YAML::Syck.const_set "DefaultKey", Class.new if message.include?("YAML::Syck::DefaultKey") && !YAML::Syck.const_defined?(:DefaultKey) + elsif message.include?("YAML::PrivateType") && !YAML.const_defined?(:PrivateType) YAML.const_set "PrivateType", Class.new end + retry_count += 1 retry + ensure + Object.__send__(:remove_const, "YAML") if yaml_set end spec = Gem::Specification.new @@ -1366,7 +1418,7 @@ class Gem::Specification < Gem::BasicSpecification @required_rubygems_version, @original_platform, @dependencies, - '', # rubyforge_project + "", # rubyforge_project @email, @authors, @description, @@ -1385,7 +1437,7 @@ class Gem::Specification < Gem::BasicSpecification # there are conflicts upon activation. def activate - other = Gem.loaded_specs[self.name] + other = Gem.loaded_specs[name] if other check_version_conflict other return false @@ -1396,11 +1448,11 @@ class Gem::Specification < Gem::BasicSpecification activate_dependencies add_self_to_load_path - Gem.loaded_specs[self.name] = self + Gem.loaded_specs[name] = self @activated = true @loaded = true - return true + true end ## @@ -1411,7 +1463,7 @@ class Gem::Specification < Gem::BasicSpecification def activate_dependencies unresolved = Gem::Specification.unresolved_deps - self.runtime_dependencies.each do |spec_dep| + runtime_dependencies.each do |spec_dep| if loaded = Gem.loaded_specs[spec_dep.name] next if spec_dep.matches_spec? loaded @@ -1425,7 +1477,7 @@ class Gem::Specification < Gem::BasicSpecification begin specs = spec_dep.to_specs rescue Gem::MissingSpecError => e - raise Gem::MissingSpecError.new(e.name, e.requirement, "at: #{self.spec_file}") + raise Gem::MissingSpecError.new(e.name, e.requirement, "at: #{spec_file}") end if specs.size == 1 @@ -1471,7 +1523,7 @@ class Gem::Specification < Gem::BasicSpecification def sanitize_string(string) return string unless string - # HACK the #to_s is in here because RSpec has an Array of Arrays of + # HACK: the #to_s is in here because RSpec has an Array of Arrays of # Strings for authors. Need a way to disallow bad values on gemspec # generation. (Probably won't happen.) string.to_s @@ -1489,8 +1541,8 @@ class Gem::Specification < Gem::BasicSpecification else executables end - rescue - return nil + rescue StandardError + nil end ## @@ -1515,7 +1567,7 @@ class Gem::Specification < Gem::BasicSpecification private :add_dependency_with_type - alias add_dependency add_runtime_dependency + alias_method :add_dependency, :add_runtime_dependency ## # Adds this spec's require paths to LOAD_PATH, in the proper location. @@ -1532,7 +1584,7 @@ class Gem::Specification < Gem::BasicSpecification # Singular reader for #authors. Returns the first author in the list def author - val = authors and val.first + (val = authors) && val.first end ## @@ -1567,7 +1619,7 @@ class Gem::Specification < Gem::BasicSpecification def build_args if File.exist? build_info_file build_info = File.readlines build_info_file - build_info = build_info.map {|x| x.strip } + build_info = build_info.map(&:strip) build_info.delete "" build_info else @@ -1582,9 +1634,11 @@ class Gem::Specification < Gem::BasicSpecification def build_extensions # :nodoc: return if extensions.empty? return if default_gem? + # we need to fresh build when same name and version of default gems + return if self.class.find_by_full_name(full_name)&.default_gem? return if File.exist? gem_build_complete_path - return if !File.writable?(base_dir) - return if !File.exist?(File.join(base_dir, 'extensions')) + return unless File.writable?(base_dir) + return unless File.exist?(File.join(base_dir, "extensions")) begin # We need to require things in $LOAD_PATH without looking for the @@ -1592,9 +1646,9 @@ class Gem::Specification < Gem::BasicSpecification unresolved_deps = Gem::Specification.unresolved_deps.dup Gem::Specification.unresolved_deps.clear - require_relative 'config_file' - require_relative 'ext' - require_relative 'user_interaction' + require_relative "config_file" + require_relative "ext" + require_relative "user_interaction" ui = Gem::SilentUI.new Gem::DefaultUserInteraction.use_ui ui do @@ -1602,7 +1656,7 @@ class Gem::Specification < Gem::BasicSpecification builder.build_extensions end ensure - ui.close if ui + ui&.close Gem::Specification.unresolved_deps.replace unresolved_deps end end @@ -1642,9 +1696,9 @@ class Gem::Specification < Gem::BasicSpecification def conflicts conflicts = {} - self.runtime_dependencies.each do |dep| + runtime_dependencies.each do |dep| spec = Gem.loaded_specs[dep.name] - if spec and not spec.satisfies_requirement? dep + if spec && !spec.satisfies_requirement?(dep) (conflicts[spec] ||= []) << dep end end @@ -1658,7 +1712,7 @@ class Gem::Specification < Gem::BasicSpecification def conficts_when_loaded_with?(list_of_specs) # :nodoc: result = list_of_specs.any? do |spec| - spec.dependencies.any? {|dep| dep.runtime? && (dep.name == name) && !satisfies_requirement?(dep) } + spec.runtime_dependencies.any? {|dep| (dep.name == name) && !satisfies_requirement?(dep) } end result end @@ -1668,14 +1722,12 @@ class Gem::Specification < Gem::BasicSpecification def has_conflicts? return true unless Gem.env_requirement(name).satisfied_by?(version) - self.dependencies.any? do |dep| - if dep.runtime? - spec = Gem.loaded_specs[dep.name] - spec and not spec.satisfies_requirement? dep - else - false - end + runtime_dependencies.any? do |dep| + spec = Gem.loaded_specs[dep.name] + spec && !spec.satisfies_requirement?(dep) end + rescue ArgumentError => e + raise e, "#{name} #{version}: #{e.message}" end # The date this gem was created. @@ -1692,14 +1744,14 @@ class Gem::Specification < Gem::BasicSpecification DateLike = Object.new # :nodoc: def DateLike.===(obj) # :nodoc: - defined?(::Date) and Date === obj + defined?(::Date) && Date === obj end DateTimeFormat = # :nodoc: /\A (\d{4})-(\d{2})-(\d{2}) (\s+ \d{2}:\d{2}:\d{2}\.\d+ \s* (Z | [-+]\d\d:\d\d) )? - \Z/x.freeze + \Z/x ## # The date this gem was created @@ -1711,17 +1763,17 @@ class Gem::Specification < Gem::BasicSpecification # This is the cleanest, most-readable, faster-than-using-Date # way to do it. @date = case date - when String then - if DateTimeFormat =~ date - Time.utc($1.to_i, $2.to_i, $3.to_i) - else - raise(Gem::InvalidSpecificationException, - "invalid date format in specification: #{date.inspect}") - end - when Time, DateLike then - Time.utc(date.year, date.month, date.day) - else - TODAY + when String then + if DateTimeFormat =~ date + Time.utc($1.to_i, $2.to_i, $3.to_i) + else + raise(Gem::InvalidSpecificationException, + "invalid date format in specification: #{date.inspect}") + end + when Time, DateLike then + Time.utc(date.year, date.month, date.day) + else + TODAY end end @@ -1732,9 +1784,9 @@ class Gem::Specification < Gem::BasicSpecification # executable now. See Gem.bin_path. def default_executable # :nodoc: - if defined?(@default_executable) and @default_executable + if defined?(@default_executable) && @default_executable result = @default_executable - elsif @executables and @executables.size == 1 + elsif @executables && @executables.size == 1 result = Array(@executables).first else result = nil @@ -1771,13 +1823,12 @@ class Gem::Specification < Gem::BasicSpecification Gem::Specification.each do |spec| deps = check_dev ? spec.dependencies : spec.runtime_dependencies deps.each do |dep| - if self.satisfies_requirement?(dep) - sats = [] - find_all_satisfiers(dep) do |sat| - sats << sat - end - out << [spec, dep, sats] + next unless satisfies_requirement?(dep) + sats = [] + find_all_satisfiers(dep) do |sat| + sats << sat end + out << [spec, dep, sats] end end out @@ -1787,7 +1838,7 @@ class Gem::Specification < Gem::BasicSpecification # Returns all specs that matches this spec's runtime dependencies. def dependent_specs - runtime_dependencies.map {|dep| dep.to_specs }.flatten + runtime_dependencies.map(&:to_specs).flatten end ## @@ -1813,7 +1864,7 @@ class Gem::Specification < Gem::BasicSpecification # spec.doc_dir 'ri' # => "/path/to/gem_repo/doc/a-1/ri" def doc_dir(type = nil) - @doc_dir ||= File.join base_dir, 'doc', full_name + @doc_dir ||= File.join base_dir, "doc", full_name if type File.join @doc_dir, type @@ -1825,21 +1876,22 @@ class Gem::Specification < Gem::BasicSpecification def encode_with(coder) # :nodoc: mark_version - coder.add 'name', @name - coder.add 'version', @version + coder.add "name", @name + coder.add "version", @version platform = case @original_platform - when nil, '' then - 'ruby' - when String then - @original_platform - else - @original_platform.to_s + when nil, "" then + "ruby" + when String then + @original_platform + else + @original_platform.to_s end - coder.add 'platform', platform + coder.add "platform", platform attributes = @@attributes.map(&:to_s) - %w[name version platform] attributes.each do |name| - coder.add name, instance_variable_get("@#{name}") + value = instance_variable_get("@#{name}") + coder.add name, value unless value.nil? end end @@ -1851,7 +1903,7 @@ class Gem::Specification < Gem::BasicSpecification # Singular accessor for #executables def executable - val = executables and val.first + (val = executables) && val.first end ## @@ -1956,18 +2008,18 @@ class Gem::Specification < Gem::BasicSpecification end rubygems_deprecate :has_rdoc= - alias :has_rdoc? :has_rdoc # :nodoc: + alias_method :has_rdoc?, :has_rdoc # :nodoc: rubygems_deprecate :has_rdoc? ## # True if this gem has files in test_files def has_unit_tests? # :nodoc: - not test_files.empty? + !test_files.empty? end # :stopdoc: - alias has_test_suite? has_unit_tests? + alias_method :has_test_suite?, :has_unit_tests? # :startdoc: def hash # :nodoc: @@ -2016,7 +2068,7 @@ class Gem::Specification < Gem::BasicSpecification self.name = name if name self.version = version if version - if platform = Gem.platforms.last and platform != Gem::Platform::RUBY and platform != Gem::Platform.local + if (platform = Gem.platforms.last) && platform != Gem::Platform::RUBY && platform != Gem::Platform.local self.platform = platform end @@ -2024,7 +2076,8 @@ class Gem::Specification < Gem::BasicSpecification end ## - # Duplicates array_attributes from +other_spec+ so state isn't shared. + # Duplicates Array and Gem::Requirement attributes from +other_spec+ so state isn't shared. + # def initialize_copy(other_spec) self.class.array_attributes.each do |name| @@ -2046,6 +2099,9 @@ class Gem::Specification < Gem::BasicSpecification raise e end end + + @required_ruby_version = other_spec.required_ruby_version.dup + @required_rubygems_version = other_spec.required_rubygems_version.dup end def base_dir @@ -2131,8 +2187,8 @@ class Gem::Specification < Gem::BasicSpecification return end - if @specification_version > CURRENT_SPECIFICATION_VERSION and - sym.to_s.end_with?("=") + if @specification_version > CURRENT_SPECIFICATION_VERSION && + sym.to_s.end_with?("=") warn "ignoring #{sym} loading #{full_name}" if $DEBUG else super @@ -2158,7 +2214,7 @@ class Gem::Specification < Gem::BasicSpecification # file list. def normalize - if defined?(@extra_rdoc_files) and @extra_rdoc_files + if defined?(@extra_rdoc_files) && @extra_rdoc_files @extra_rdoc_files.uniq! @files ||= [] @files.concat(@extra_rdoc_files) @@ -2183,7 +2239,7 @@ class Gem::Specification < Gem::BasicSpecification # platform. For use with legacy gems. def original_name # :nodoc: - if platform == Gem::Platform::RUBY or platform.nil? + if platform == Gem::Platform::RUBY || platform.nil? "#{@name}-#{@version}" else "#{@name}-#{@version}-#{@original_platform}" @@ -2201,11 +2257,11 @@ class Gem::Specification < Gem::BasicSpecification # The platform this gem runs on. See Gem::Platform for details. def platform - @new_platform ||= Gem::Platform::RUBY + @new_platform ||= Gem::Platform::RUBY # rubocop:disable Naming/MemoizedInstanceVariableName end def pretty_print(q) # :nodoc: - q.group 2, 'Gem::Specification.new do |s|', 'end' do + q.group 2, "Gem::Specification.new do |s|", "end" do q.breakable attributes = @@attributes - [:name, :version] @@ -2214,23 +2270,22 @@ class Gem::Specification < Gem::BasicSpecification attributes.unshift :name attributes.each do |attr_name| - current_value = self.send attr_name - current_value = current_value.sort if %i[files test_files].include? attr_name - if current_value != default_value(attr_name) or - self.class.required_attribute? attr_name - - q.text "s.#{attr_name} = " + current_value = send attr_name + current_value = current_value.sort if [:files, :test_files].include? attr_name + next unless current_value != default_value(attr_name) || + self.class.required_attribute?(attr_name) - if attr_name == :date - current_value = current_value.utc + q.text "s.#{attr_name} = " - q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})" - else - q.pp current_value - end + if attr_name == :date + current_value = current_value.utc - q.breakable + q.text "Time.utc(#{current_value.year}, #{current_value.month}, #{current_value.day})" + else + q.pp current_value end + + q.breakable end end end @@ -2240,7 +2295,7 @@ class Gem::Specification < Gem::BasicSpecification # that is already loaded (+other+) def check_version_conflict(other) # :nodoc: - return if self.version == other.version + return if version == other.version # This gem is already loaded. If the currently loaded gem is not in the # list of candidate gems, then we have a version conflict. @@ -2248,7 +2303,7 @@ class Gem::Specification < Gem::BasicSpecification msg = "can't activate #{full_name}, already activated #{other.full_name}" e = Gem::LoadError.new msg - e.name = self.name + e.name = name raise e end @@ -2275,7 +2330,7 @@ class Gem::Specification < Gem::BasicSpecification # Singular accessor for #require_paths def require_path - val = require_paths and val.first + (val = require_paths) && val.first end ## @@ -2300,7 +2355,7 @@ class Gem::Specification < Gem::BasicSpecification # Returns the full path to this spec's ri directory. def ri_dir - @ri_dir ||= File.join base_dir, 'ri', full_name + @ri_dir ||= File.join base_dir, "ri", full_name end ## @@ -2310,16 +2365,16 @@ class Gem::Specification < Gem::BasicSpecification def ruby_code(obj) case obj when String then obj.dump + ".freeze" - when Array then '[' + obj.map {|x| ruby_code x }.join(", ") + ']' + when Array then "[" + obj.map {|x| ruby_code x }.join(", ") + "]" when Hash then seg = obj.keys.sort.map {|k| "#{k.to_s.dump} => #{obj[k].to_s.dump}" } - "{ #{seg.join(', ')} }" - when Gem::Version then obj.to_s.dump - when DateLike then obj.strftime('%Y-%m-%d').dump - when Time then obj.strftime('%Y-%m-%d').dump + "{ #{seg.join(", ")} }" + when Gem::Version then ruby_code(obj.to_s) + when DateLike then obj.strftime("%Y-%m-%d").dump + when Time then obj.strftime("%Y-%m-%d").dump when Numeric then obj.inspect when true, false, nil then obj.inspect - when Gem::Platform then "Gem::Platform.new(#{obj.to_a.inspect})" + when Gem::Platform then "Gem::Platform.new(#{ruby_code obj.to_a})" when Gem::Requirement then list = obj.as_list "Gem::Requirement.new(#{ruby_code(list.size == 1 ? obj.to_s : list)})" @@ -2340,7 +2395,7 @@ class Gem::Specification < Gem::BasicSpecification # True if this gem has the same attributes as +other+. def same_attributes?(spec) - @@attributes.all? {|name, default| self.send(name) == spec.send(name) } + @@attributes.all? {|name, _default| send(name) == spec.send(name) } end private :same_attributes? @@ -2349,7 +2404,7 @@ class Gem::Specification < Gem::BasicSpecification # Checks if this specification meets the requirement of +dependency+. def satisfies_requirement?(dependency) - return @name == dependency.name && + @name == dependency.name && dependency.requirement.satisfied_by?(@version) end @@ -2404,7 +2459,7 @@ class Gem::Specification < Gem::BasicSpecification # Singular accessor for #test_files def test_file # :nodoc: - val = test_files and val.first + (val = test_files) && val.first end ## @@ -2426,7 +2481,7 @@ class Gem::Specification < Gem::BasicSpecification @test_files = [@test_suite_file].flatten @test_suite_file = nil end - if defined?(@test_files) and @test_files + if defined?(@test_files) && @test_files @test_files else @test_files = [] @@ -2450,13 +2505,13 @@ class Gem::Specification < Gem::BasicSpecification result << " s.name = #{ruby_code name}" result << " s.version = #{ruby_code version}" - unless platform.nil? or platform == Gem::Platform::RUBY + unless platform.nil? || platform == Gem::Platform::RUBY result << " s.platform = #{ruby_code original_platform}" end result << "" result << " s.required_rubygems_version = #{ruby_code required_rubygems_version} if s.respond_to? :required_rubygems_version=" - if metadata and !metadata.empty? + if metadata && !metadata.empty? result << " s.metadata = #{ruby_code metadata} if s.respond_to? :metadata=" end result << " s.require_paths = #{ruby_code raw_require_paths}" @@ -2477,42 +2532,30 @@ class Gem::Specification < Gem::BasicSpecification @@attributes.each do |attr_name| next if handled.include? attr_name - current_value = self.send(attr_name) + current_value = send(attr_name) if current_value != default_value(attr_name) || self.class.required_attribute?(attr_name) result << " s.#{attr_name} = #{ruby_code current_value}" end end if String === signing_key - result << " s.signing_key = #{signing_key.dump}.freeze" + result << " s.signing_key = #{ruby_code signing_key}" end if @installed_by_version result << nil - result << " s.installed_by_version = \"#{Gem::VERSION}\" if s.respond_to? :installed_by_version" + result << " s.installed_by_version = #{ruby_code Gem::VERSION} if s.respond_to? :installed_by_version" end unless dependencies.empty? result << nil - result << " if s.respond_to? :specification_version then" - result << " s.specification_version = #{specification_version}" - result << " end" + result << " s.specification_version = #{specification_version}" result << nil - result << " if s.respond_to? :add_runtime_dependency then" - dependencies.each do |dep| - req = dep.requirements_list.inspect dep.instance_variable_set :@type, :runtime if dep.type.nil? # HACK - result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>.freeze, #{req})" - end - - result << " else" - dependencies.each do |dep| - version_reqs_param = dep.requirements_list.inspect - result << " s.add_dependency(%q<#{dep.name}>.freeze, #{version_reqs_param})" + result << " s.add_#{dep.type}_dependency(%q<#{dep.name}>.freeze, #{ruby_code dep.requirements_list})" end - result << " end" end result << "end" @@ -2549,14 +2592,14 @@ class Gem::Specification < Gem::BasicSpecification # back, we have to check again here to make sure that our # psych code was properly loaded, and load it if not. unless Gem.const_defined?(:NoAliasYAMLTree) - require_relative 'psych_tree' + require_relative "psych_tree" end builder = Gem::NoAliasYAMLTree.create builder << self ast = builder.tree - require 'stringio' + require "stringio" io = StringIO.new io.set_encoding Encoding::UTF_8 @@ -2572,10 +2615,9 @@ class Gem::Specification < Gem::BasicSpecification def traverse(trail = [], visited = {}, &block) trail.push(self) begin - dependencies.each do |dep| - next unless dep.runtime? + runtime_dependencies.each do |dep| dep.matching_specs(true).each do |dep_spec| - next if visited.has_key?(dep_spec) + next if visited.key?(dep_spec) visited[dep_spec] = true trail.push(dep_spec) begin @@ -2583,11 +2625,10 @@ class Gem::Specification < Gem::BasicSpecification ensure trail.pop end - unless result == :next - spec_name = dep_spec.name - dep_spec.traverse(trail, visited, &block) unless - trail.any? {|s| s.name == spec_name } - end + next if result == :next + spec_name = dep_spec.name + dep_spec.traverse(trail, visited, &block) unless + trail.any? {|s| s.name == spec_name } end end ensure @@ -2634,20 +2675,13 @@ class Gem::Specification < Gem::BasicSpecification rubygems_deprecate :validate_permissions ## - # Set the version to +version+, potentially also setting - # required_rubygems_version if +version+ indicates it is a - # prerelease. + # Set the version to +version+. def version=(version) @version = Gem::Version.create(version) - # skip to set required_ruby_version when pre-released rubygems. - # It caused to raise CircularDependencyError - if @version.prerelease? && (@name.nil? || @name.strip != "rubygems") - self.required_rubygems_version = '> 1.3.1' - end - invalidate_memoized_attributes + return if @version.nil? - return @version + invalidate_memoized_attributes end def stubbed? @@ -2659,9 +2693,9 @@ class Gem::Specification < Gem::BasicSpecification case ivar when "date" # Force Date to go through the extra coerce logic in date= - self.date = val.tap(&Gem::UNTAINT) + self.date = val else - instance_variable_set "@#{ivar}", val.tap(&Gem::UNTAINT) + instance_variable_set "@#{ivar}", val end end @@ -2678,17 +2712,19 @@ class Gem::Specification < Gem::BasicSpecification end nil_attributes.each do |attribute| - default = self.default_value attribute + default = default_value attribute value = case default - when Time, Numeric, Symbol, true, false, nil then default - else default.dup + when Time, Numeric, Symbol, true, false, nil then default + else default.dup end instance_variable_set "@#{attribute}", value end @installed_by_version ||= nil + + nil end def flatten_require_paths # :nodoc: |