diff options
author | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-16 18:49:26 +0900 |
---|---|---|
committer | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-30 00:39:06 +0900 |
commit | fcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch) | |
tree | 64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/rubygems/dependency.rb |
Fresh start
Diffstat (limited to 'jni/ruby/lib/rubygems/dependency.rb')
-rw-r--r-- | jni/ruby/lib/rubygems/dependency.rb | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/jni/ruby/lib/rubygems/dependency.rb b/jni/ruby/lib/rubygems/dependency.rb new file mode 100644 index 0000000..5924d2f --- /dev/null +++ b/jni/ruby/lib/rubygems/dependency.rb @@ -0,0 +1,334 @@ +## +# The Dependency class holds a Gem name and a Gem::Requirement. + +require "rubygems/requirement" + +class Gem::Dependency + + ## + # Valid dependency types. + #-- + # When this list is updated, be sure to change + # Gem::Specification::CURRENT_SPECIFICATION_VERSION as well. + # + # REFACTOR: This type of constant, TYPES, indicates we might want + # two classes, used via inheritance or duck typing. + + TYPES = [ + :development, + :runtime, + ] + + ## + # Dependency name or regular expression. + + attr_accessor :name + + ## + # Allows you to force this dependency to be a prerelease. + + attr_writer :prerelease + + ## + # Constructs a dependency with +name+ and +requirements+. The last + # argument can optionally be the dependency type, which defaults to + # <tt>:runtime</tt>. + + def initialize name, *requirements + case name + when String then # ok + when Regexp then + msg = ["NOTE: Dependency.new w/ a regexp is deprecated.", + "Dependency.new called from #{Gem.location_of_caller.join(":")}"] + warn msg.join("\n") unless Gem::Deprecate.skip + else + raise ArgumentError, + "dependency name must be a String, was #{name.inspect}" + end + + type = Symbol === requirements.last ? requirements.pop : :runtime + requirements = requirements.first if 1 == requirements.length # unpack + + unless TYPES.include? type + raise ArgumentError, "Valid types are #{TYPES.inspect}, " + + "not #{type.inspect}" + end + + @name = name + @requirement = Gem::Requirement.create requirements + @type = type + @prerelease = false + + # This is for Marshal backwards compatibility. See the comments in + # +requirement+ for the dirty details. + + @version_requirements = @requirement + end + + ## + # A dependency's hash is the XOR of the hashes of +name+, +type+, + # and +requirement+. + + def hash # :nodoc: + name.hash ^ type.hash ^ requirement.hash + end + + def inspect # :nodoc: + if prerelease? then + "<%s type=%p name=%p requirements=%p prerelease=ok>" % + [self.class, self.type, self.name, requirement.to_s] + else + "<%s type=%p name=%p requirements=%p>" % + [self.class, self.type, self.name, requirement.to_s] + end + end + + ## + # Does this dependency require a prerelease? + + def prerelease? + @prerelease || requirement.prerelease? + end + + ## + # Is this dependency simply asking for the latest version + # of a gem? + + def latest_version? + @requirement.none? + end + + def pretty_print q # :nodoc: + q.group 1, 'Gem::Dependency.new(', ')' do + q.pp name + q.text ',' + q.breakable + + q.pp requirement + + q.text ',' + q.breakable + + q.pp type + end + end + + ## + # What does this dependency require? + + def requirement + return @requirement if defined?(@requirement) and @requirement + + # @version_requirements and @version_requirement are legacy ivar + # names, and supported here because older gems need to keep + # working and Dependency doesn't implement marshal_dump and + # marshal_load. In a happier world, this would be an + # attr_accessor. The horrifying instance_variable_get you see + # below is also the legacy of some old restructurings. + # + # Note also that because of backwards compatibility (loading new + # gems in an old RubyGems installation), we can't add explicit + # marshaling to this class until we want to make a big + # break. Maybe 2.0. + # + # Children, define explicit marshal and unmarshal behavior for + # public classes. Marshal formats are part of your public API. + + # REFACTOR: See above + + if defined?(@version_requirement) && @version_requirement + version = @version_requirement.instance_variable_get :@version + @version_requirement = nil + @version_requirements = Gem::Requirement.new version + end + + @requirement = @version_requirements if defined?(@version_requirements) + end + + def requirements_list + requirement.as_list + end + + def to_s # :nodoc: + if type != :runtime then + "#{name} (#{requirement}, #{type})" + else + "#{name} (#{requirement})" + end + end + + ## + # Dependency type. + + def type + @type ||= :runtime + end + + def == other # :nodoc: + Gem::Dependency === other && + self.name == other.name && + self.type == other.type && + self.requirement == other.requirement + end + + ## + # Dependencies are ordered by name. + + def <=> other + self.name <=> other.name + end + + ## + # Uses this dependency as a pattern to compare to +other+. This + # dependency will match if the name matches the other's name, and + # other has only an equal version requirement that satisfies this + # dependency. + + def =~ other + unless Gem::Dependency === other + return unless other.respond_to?(:name) && other.respond_to?(:version) + other = Gem::Dependency.new other.name, other.version + end + + return false unless name === other.name + + reqs = other.requirement.requirements + + return false unless reqs.length == 1 + return false unless reqs.first.first == '=' + + version = reqs.first.last + + requirement.satisfied_by? version + end + + alias === =~ + + ## + # :call-seq: + # dep.match? name => true or false + # dep.match? name, version => true or false + # dep.match? spec => true or false + # + # Does this dependency match the specification described by +name+ and + # +version+ or match +spec+? + # + # NOTE: Unlike #matches_spec? this method does not return true when the + # version is a prerelease version unless this is a prerelease dependency. + + def match? obj, version=nil, allow_prerelease=false + if !version + name = obj.name + version = obj.version + else + name = obj + end + + return false unless self.name === name + + version = Gem::Version.new version + + return true if requirement.none? and not version.prerelease? + return false if version.prerelease? and + not allow_prerelease and + not prerelease? + + requirement.satisfied_by? version + end + + ## + # Does this dependency match +spec+? + # + # NOTE: This is not a convenience method. Unlike #match? this method + # returns true when +spec+ is a prerelease version even if this dependency + # is not a prerelease dependency. + + def matches_spec? spec + return false unless name === spec.name + return true if requirement.none? + + requirement.satisfied_by?(spec.version) + end + + ## + # Merges the requirements of +other+ into this dependency + + def merge other + unless name == other.name then + raise ArgumentError, + "#{self} and #{other} have different names" + end + + default = Gem::Requirement.default + self_req = self.requirement + other_req = other.requirement + + return self.class.new name, self_req if other_req == default + return self.class.new name, other_req if self_req == default + + self.class.new name, self_req.as_list.concat(other_req.as_list) + end + + def matching_specs platform_only = false + matches = Gem::Specification.stubs.find_all { |spec| + self.name === spec.name and # TODO: == instead of === + requirement.satisfied_by? spec.version + }.map(&:to_spec) + + if platform_only + matches.reject! { |spec| + not Gem::Platform.match spec.platform + } + end + + matches.sort_by { |s| s.sort_obj } # HACK: shouldn't be needed + end + + ## + # True if the dependency will not always match the latest version. + + def specific? + @requirement.specific? + end + + def to_specs + matches = matching_specs true + + # TODO: check Gem.activated_spec[self.name] in case matches falls outside + + if matches.empty? then + specs = Gem::Specification.find_all { |s| + s.name == name + }.map { |x| x.full_name } + + if specs.empty? + total = Gem::Specification.to_a.size + msg = "Could not find '#{name}' (#{requirement}) among #{total} total gem(s)\n" + else + msg = "Could not find '#{name}' (#{requirement}) - did find: [#{specs.join ','}]\n" + end + msg << "Checked in 'GEM_PATH=#{Gem.path.join(File::PATH_SEPARATOR)}', execute `gem env` for more information" + + error = Gem::LoadError.new(msg) + error.name = self.name + error.requirement = self.requirement + raise error + end + + # TODO: any other resolver validations should go here + + matches + end + + def to_spec + matches = self.to_specs + + active = matches.find { |spec| spec.activated? } + + return active if active + + matches.delete_if { |spec| spec.version.prerelease? } unless prerelease? + + matches.last + end +end |