зеркало из https://github.com/github/ruby.git
This commit is contained in:
Родитель
cd2410f9d8
Коммит
1056489ea3
|
@ -736,30 +736,13 @@ module Bundler
|
|||
|
||||
def metadata_dependencies
|
||||
@metadata_dependencies ||= begin
|
||||
ruby_versions = ruby_version_requirements(@ruby_version)
|
||||
[
|
||||
Dependency.new("Ruby\0", ruby_versions),
|
||||
Dependency.new("Ruby\0", RubyVersion.system.gem_version),
|
||||
Dependency.new("RubyGems\0", Gem::VERSION),
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def ruby_version_requirements(ruby_version)
|
||||
return [] unless ruby_version
|
||||
if ruby_version.patchlevel
|
||||
[ruby_version.to_gem_version_with_patchlevel]
|
||||
else
|
||||
ruby_version.versions.map do |version|
|
||||
requirement = Gem::Requirement.new(version)
|
||||
if requirement.exact?
|
||||
"~> #{version}.0"
|
||||
else
|
||||
requirement
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expand_dependencies(dependencies, remote = false)
|
||||
deps = []
|
||||
dependencies.each do |dep|
|
||||
|
|
|
@ -312,29 +312,66 @@ module Bundler
|
|||
|
||||
e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty?
|
||||
|
||||
solver_name = "Bundler"
|
||||
possibility_type = "gem"
|
||||
e.message_with_trees(
|
||||
:solver_name => solver_name,
|
||||
:possibility_type => possibility_type,
|
||||
:reduce_trees => lambda do |trees|
|
||||
:full_message_for_conflict => lambda do |name, conflict|
|
||||
o = if name.end_with?("\0")
|
||||
String.new("Bundler found conflicting requirements for the #{name} version:")
|
||||
else
|
||||
String.new("Bundler could not find compatible versions for gem \"#{name}\":")
|
||||
end
|
||||
o << %(\n)
|
||||
if conflict.locked_requirement
|
||||
o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
|
||||
o << %( #{SharedHelpers.pretty_dependency(conflict.locked_requirement)}\n)
|
||||
o << %(\n)
|
||||
end
|
||||
o << %( In #{name_for_explicit_dependency_source}:\n)
|
||||
trees = conflict.requirement_trees
|
||||
|
||||
# called first, because we want to reduce the amount of work required to find maximal empty sets
|
||||
trees = trees.uniq {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } }
|
||||
|
||||
# bail out if tree size is too big for Array#combination to make any sense
|
||||
return trees if trees.size > 15
|
||||
maximal = 1.upto(trees.size).map do |size|
|
||||
trees.map(&:last).flatten(1).combination(size).to_a
|
||||
end.flatten(1).select do |deps|
|
||||
Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
|
||||
end.min_by(&:size)
|
||||
if trees.size <= 15
|
||||
maximal = 1.upto(trees.size).map do |size|
|
||||
trees.map(&:last).flatten(1).combination(size).to_a
|
||||
end.flatten(1).select do |deps|
|
||||
Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement)))
|
||||
end.min_by(&:size)
|
||||
|
||||
trees.reject! {|t| !maximal.include?(t.last) } if maximal
|
||||
trees.reject! {|t| !maximal.include?(t.last) } if maximal
|
||||
|
||||
trees.sort_by! {|t| t.reverse.map(&:name) }
|
||||
end
|
||||
|
||||
metadata_requirements = {}
|
||||
|
||||
o << trees.map do |tree|
|
||||
t = "".dup
|
||||
depth = 2
|
||||
|
||||
base_tree = tree.first
|
||||
base_tree_name = base_tree.name
|
||||
|
||||
if base_tree_name.end_with?("\0")
|
||||
metadata_requirements[base_tree_name] = base_tree
|
||||
t = nil
|
||||
else
|
||||
tree.each do |req|
|
||||
t << " " * depth << SharedHelpers.pretty_dependency(req)
|
||||
unless tree.last == req
|
||||
if spec = conflict.activated_by_name[req.name]
|
||||
t << %( was resolved to #{spec.version}, which)
|
||||
end
|
||||
t << %( depends on)
|
||||
end
|
||||
t << %(\n)
|
||||
depth += 1
|
||||
end
|
||||
end
|
||||
t
|
||||
end.compact.join("\n")
|
||||
|
||||
trees.sort_by {|t| t.reverse.map(&:name) }
|
||||
end,
|
||||
:printable_requirement => lambda {|req| SharedHelpers.pretty_dependency(req) },
|
||||
:additional_message_for_conflict => lambda do |o, name, conflict|
|
||||
if name == "bundler"
|
||||
o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION}))
|
||||
|
||||
|
@ -355,11 +392,13 @@ module Bundler
|
|||
o << "Your bundle requires a different version of Bundler than the one you're running, and that version could not be found.\n"
|
||||
end
|
||||
end
|
||||
elsif name.end_with?("\0")
|
||||
o << %(\n Current #{name} version:\n #{SharedHelpers.pretty_dependency(metadata_requirements[name])}\n\n)
|
||||
elsif conflict.locked_requirement
|
||||
o << "\n"
|
||||
o << %(Running `bundle update` will rebuild your snapshot from scratch, using only\n)
|
||||
o << %(the gems in your Gemfile, which may resolve the conflict.\n)
|
||||
elsif !conflict.existing && !name.end_with?("\0")
|
||||
elsif !conflict.existing
|
||||
o << "\n"
|
||||
|
||||
relevant_source = conflict.requirement.source || source_for(name)
|
||||
|
@ -372,14 +411,8 @@ module Bundler
|
|||
|
||||
o << gem_not_found_message(name, conflict.requirement, relevant_source, extra_message)
|
||||
end
|
||||
end,
|
||||
:version_for_spec => lambda {|spec| spec.version },
|
||||
:incompatible_version_message_for_conflict => lambda do |name, _conflict|
|
||||
if name.end_with?("\0")
|
||||
%(#{solver_name} found conflicting requirements for the #{name} version:)
|
||||
else
|
||||
%(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
|
||||
end
|
||||
|
||||
o
|
||||
end
|
||||
)
|
||||
end
|
||||
|
|
|
@ -97,10 +97,10 @@ module Bundler
|
|||
spec = @specs[platform].first
|
||||
return [] if spec.is_a?(LazySpecification)
|
||||
dependencies = []
|
||||
if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none?
|
||||
unless spec.required_ruby_version.none?
|
||||
dependencies << DepProxy.get_proxy(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform)
|
||||
end
|
||||
if !spec.required_rubygems_version.nil? && !spec.required_rubygems_version.none?
|
||||
unless spec.required_rubygems_version.none?
|
||||
dependencies << DepProxy.get_proxy(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform)
|
||||
end
|
||||
dependencies
|
||||
|
|
|
@ -110,19 +110,6 @@ module Bundler
|
|||
@ruby_version ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version)
|
||||
end
|
||||
|
||||
def to_gem_version_with_patchlevel
|
||||
@gem_version_with_patch ||= begin
|
||||
Gem::Version.create("#{@gem_version}.#{@patchlevel}")
|
||||
rescue ArgumentError
|
||||
@gem_version
|
||||
end
|
||||
end
|
||||
|
||||
def exact?
|
||||
return @exact if defined?(@exact)
|
||||
@exact = versions.all? {|v| Gem::Requirement.create(v).exact? }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def matches?(requirements, version)
|
||||
|
|
|
@ -67,6 +67,23 @@ module Gem
|
|||
full_gem_path
|
||||
end
|
||||
|
||||
unless const_defined?(:LATEST_RUBY_WITHOUT_PATCH_VERSIONS)
|
||||
LATEST_RUBY_WITHOUT_PATCH_VERSIONS = Gem::Version.new("2.1")
|
||||
|
||||
alias_method :rg_required_ruby_version=, :required_ruby_version=
|
||||
def required_ruby_version=(req)
|
||||
self.rg_required_ruby_version = req
|
||||
|
||||
@required_ruby_version.requirements.map! do |op, v|
|
||||
if v >= LATEST_RUBY_WITHOUT_PATCH_VERSIONS && v.release.segments.size == 4
|
||||
[op == "~>" ? "=" : op, Gem::Version.new(v.segments.tap {|s| s.delete_at(3) }.join("."))]
|
||||
else
|
||||
[op, v]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def groups
|
||||
@groups ||= []
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ module Bundler
|
|||
class Metadata < Source
|
||||
def specs
|
||||
@specs ||= Index.build do |idx|
|
||||
idx << Gem::Specification.new("Ruby\0", RubyVersion.system.to_gem_version_with_patchlevel)
|
||||
idx << Gem::Specification.new("Ruby\0", RubyVersion.system.gem_version)
|
||||
idx << Gem::Specification.new("RubyGems\0", Gem::VERSION) do |s|
|
||||
s.required_rubygems_version = Gem::Requirement.default
|
||||
end
|
||||
|
|
|
@ -32,7 +32,7 @@ module Bundler::Molinillo
|
|||
# all belong to the same graph.
|
||||
# @return [Array<Vertex>] The sorted vertices.
|
||||
def self.tsort(vertices)
|
||||
TSort.tsort(
|
||||
Bundler::TSort.tsort(
|
||||
lambda { |b| vertices.each(&b) },
|
||||
lambda { |v, &b| (v.successors & vertices).each(&b) }
|
||||
)
|
||||
|
|
|
@ -107,36 +107,42 @@ module Bundler::Molinillo
|
|||
end
|
||||
end
|
||||
|
||||
conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
|
||||
o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
|
||||
if conflict.locked_requirement
|
||||
o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
|
||||
o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
|
||||
o << %(\n)
|
||||
end
|
||||
o << %( In #{name_for_explicit_dependency_source}:\n)
|
||||
trees = reduce_trees.call(conflict.requirement_trees)
|
||||
|
||||
o << trees.map do |tree|
|
||||
t = ''.dup
|
||||
depth = 2
|
||||
tree.each do |req|
|
||||
t << ' ' * depth << printable_requirement.call(req)
|
||||
unless tree.last == req
|
||||
if spec = conflict.activated_by_name[name_for(req)]
|
||||
t << %( was resolved to #{version_for_spec.call(spec)}, which)
|
||||
end
|
||||
t << %( depends on)
|
||||
end
|
||||
t << %(\n)
|
||||
depth += 1
|
||||
full_message_for_conflict = opts.delete(:full_message_for_conflict) do
|
||||
proc do |name, conflict|
|
||||
o = "\n".dup << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
|
||||
if conflict.locked_requirement
|
||||
o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
|
||||
o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
|
||||
o << %(\n)
|
||||
end
|
||||
t
|
||||
end.join("\n")
|
||||
o << %( In #{name_for_explicit_dependency_source}:\n)
|
||||
trees = reduce_trees.call(conflict.requirement_trees)
|
||||
|
||||
additional_message_for_conflict.call(o, name, conflict)
|
||||
o << trees.map do |tree|
|
||||
t = ''.dup
|
||||
depth = 2
|
||||
tree.each do |req|
|
||||
t << ' ' * depth << printable_requirement.call(req)
|
||||
unless tree.last == req
|
||||
if spec = conflict.activated_by_name[name_for(req)]
|
||||
t << %( was resolved to #{version_for_spec.call(spec)}, which)
|
||||
end
|
||||
t << %( depends on)
|
||||
end
|
||||
t << %(\n)
|
||||
depth += 1
|
||||
end
|
||||
t
|
||||
end.join("\n")
|
||||
|
||||
o
|
||||
additional_message_for_conflict.call(o, name, conflict)
|
||||
|
||||
o
|
||||
end
|
||||
end
|
||||
|
||||
conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
|
||||
o << full_message_for_conflict.call(name, conflict)
|
||||
end.strip
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
module Bundler::Molinillo
|
||||
# The version of Bundler::Molinillo.
|
||||
VERSION = '0.7.0'.freeze
|
||||
VERSION = '0.8.0'.freeze
|
||||
end
|
||||
|
|
|
@ -6,24 +6,24 @@
|
|||
#
|
||||
|
||||
#
|
||||
# TSort implements topological sorting using Tarjan's algorithm for
|
||||
# Bundler::TSort implements topological sorting using Tarjan's algorithm for
|
||||
# strongly connected components.
|
||||
#
|
||||
# TSort is designed to be able to be used with any object which can be
|
||||
# Bundler::TSort is designed to be able to be used with any object which can be
|
||||
# interpreted as a directed graph.
|
||||
#
|
||||
# TSort requires two methods to interpret an object as a graph,
|
||||
# Bundler::TSort requires two methods to interpret an object as a graph,
|
||||
# tsort_each_node and tsort_each_child.
|
||||
#
|
||||
# * tsort_each_node is used to iterate for all nodes over a graph.
|
||||
# * tsort_each_child is used to iterate for child nodes of a given node.
|
||||
#
|
||||
# The equality of nodes are defined by eql? and hash since
|
||||
# TSort uses Hash internally.
|
||||
# Bundler::TSort uses Hash internally.
|
||||
#
|
||||
# == A Simple Example
|
||||
#
|
||||
# The following example demonstrates how to mix the TSort module into an
|
||||
# The following example demonstrates how to mix the Bundler::TSort module into an
|
||||
# existing class (in this case, Hash). Here, we're treating each key in
|
||||
# the hash as a node in the graph, and so we simply alias the required
|
||||
# #tsort_each_node method to Hash's #each_key method. For each key in the
|
||||
|
@ -32,10 +32,10 @@
|
|||
# method, which fetches the array of child nodes and then iterates over that
|
||||
# array using the user-supplied block.
|
||||
#
|
||||
# require 'tsort'
|
||||
# require 'bundler/vendor/tsort/lib/tsort'
|
||||
#
|
||||
# class Hash
|
||||
# include TSort
|
||||
# include Bundler::TSort
|
||||
# alias tsort_each_node each_key
|
||||
# def tsort_each_child(node, &block)
|
||||
# fetch(node).each(&block)
|
||||
|
@ -52,7 +52,7 @@
|
|||
#
|
||||
# A very simple `make' like tool can be implemented as follows:
|
||||
#
|
||||
# require 'tsort'
|
||||
# require 'bundler/vendor/tsort/lib/tsort'
|
||||
#
|
||||
# class Make
|
||||
# def initialize
|
||||
|
@ -70,7 +70,7 @@
|
|||
# each_strongly_connected_component_from(target) {|ns|
|
||||
# if ns.length != 1
|
||||
# fs = ns.delete_if {|n| Array === n}
|
||||
# raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
|
||||
# raise Bundler::TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
|
||||
# end
|
||||
# n = ns.first
|
||||
# if Array === n
|
||||
|
@ -93,7 +93,7 @@
|
|||
# def tsort_each_child(node, &block)
|
||||
# @dep[node].each(&block)
|
||||
# end
|
||||
# include TSort
|
||||
# include Bundler::TSort
|
||||
# end
|
||||
#
|
||||
# def command(arg)
|
||||
|
@ -120,334 +120,333 @@
|
|||
# R. E. Tarjan, "Depth First Search and Linear Graph Algorithms",
|
||||
# <em>SIAM Journal on Computing</em>, Vol. 1, No. 2, pp. 146-160, June 1972.
|
||||
#
|
||||
module Bundler
|
||||
module TSort
|
||||
class Cyclic < StandardError
|
||||
end
|
||||
|
||||
# Returns a topologically sorted array of nodes.
|
||||
# The array is sorted from children to parents, i.e.
|
||||
# the first element has no child and the last node has no parent.
|
||||
#
|
||||
# If there is a cycle, TSort::Cyclic is raised.
|
||||
#
|
||||
# class G
|
||||
# include TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# p graph.tsort #=> [4, 2, 3, 1]
|
||||
#
|
||||
# graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
|
||||
# p graph.tsort # raises TSort::Cyclic
|
||||
#
|
||||
def tsort
|
||||
each_node = method(:tsort_each_node)
|
||||
each_child = method(:tsort_each_child)
|
||||
TSort.tsort(each_node, each_child)
|
||||
end
|
||||
module Bundler::TSort
|
||||
class Cyclic < StandardError
|
||||
end
|
||||
|
||||
# Returns a topologically sorted array of nodes.
|
||||
# The array is sorted from children to parents, i.e.
|
||||
# the first element has no child and the last node has no parent.
|
||||
#
|
||||
# The graph is represented by _each_node_ and _each_child_.
|
||||
# _each_node_ should have +call+ method which yields for each node in the graph.
|
||||
# _each_child_ should have +call+ method which takes a node argument and yields for each child node.
|
||||
#
|
||||
# If there is a cycle, TSort::Cyclic is raised.
|
||||
#
|
||||
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# p TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
|
||||
#
|
||||
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# p TSort.tsort(each_node, each_child) # raises TSort::Cyclic
|
||||
#
|
||||
def TSort.tsort(each_node, each_child)
|
||||
TSort.tsort_each(each_node, each_child).to_a
|
||||
end
|
||||
# Returns a topologically sorted array of nodes.
|
||||
# The array is sorted from children to parents, i.e.
|
||||
# the first element has no child and the last node has no parent.
|
||||
#
|
||||
# If there is a cycle, Bundler::TSort::Cyclic is raised.
|
||||
#
|
||||
# class G
|
||||
# include Bundler::TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# p graph.tsort #=> [4, 2, 3, 1]
|
||||
#
|
||||
# graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
|
||||
# p graph.tsort # raises Bundler::TSort::Cyclic
|
||||
#
|
||||
def tsort
|
||||
each_node = method(:tsort_each_node)
|
||||
each_child = method(:tsort_each_child)
|
||||
Bundler::TSort.tsort(each_node, each_child)
|
||||
end
|
||||
|
||||
# The iterator version of the #tsort method.
|
||||
# <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
|
||||
# modification of _obj_ during the iteration may lead to unexpected results.
|
||||
#
|
||||
# #tsort_each returns +nil+.
|
||||
# If there is a cycle, TSort::Cyclic is raised.
|
||||
#
|
||||
# class G
|
||||
# include TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# graph.tsort_each {|n| p n }
|
||||
# #=> 4
|
||||
# # 2
|
||||
# # 3
|
||||
# # 1
|
||||
#
|
||||
def tsort_each(&block) # :yields: node
|
||||
each_node = method(:tsort_each_node)
|
||||
each_child = method(:tsort_each_child)
|
||||
TSort.tsort_each(each_node, each_child, &block)
|
||||
end
|
||||
# Returns a topologically sorted array of nodes.
|
||||
# The array is sorted from children to parents, i.e.
|
||||
# the first element has no child and the last node has no parent.
|
||||
#
|
||||
# The graph is represented by _each_node_ and _each_child_.
|
||||
# _each_node_ should have +call+ method which yields for each node in the graph.
|
||||
# _each_child_ should have +call+ method which takes a node argument and yields for each child node.
|
||||
#
|
||||
# If there is a cycle, Bundler::TSort::Cyclic is raised.
|
||||
#
|
||||
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# p Bundler::TSort.tsort(each_node, each_child) #=> [4, 2, 3, 1]
|
||||
#
|
||||
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# p Bundler::TSort.tsort(each_node, each_child) # raises Bundler::TSort::Cyclic
|
||||
#
|
||||
def self.tsort(each_node, each_child)
|
||||
tsort_each(each_node, each_child).to_a
|
||||
end
|
||||
|
||||
# The iterator version of the TSort.tsort method.
|
||||
#
|
||||
# The graph is represented by _each_node_ and _each_child_.
|
||||
# _each_node_ should have +call+ method which yields for each node in the graph.
|
||||
# _each_child_ should have +call+ method which takes a node argument and yields for each child node.
|
||||
#
|
||||
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# TSort.tsort_each(each_node, each_child) {|n| p n }
|
||||
# #=> 4
|
||||
# # 2
|
||||
# # 3
|
||||
# # 1
|
||||
#
|
||||
def TSort.tsort_each(each_node, each_child) # :yields: node
|
||||
return to_enum(__method__, each_node, each_child) unless block_given?
|
||||
# The iterator version of the #tsort method.
|
||||
# <tt><em>obj</em>.tsort_each</tt> is similar to <tt><em>obj</em>.tsort.each</tt>, but
|
||||
# modification of _obj_ during the iteration may lead to unexpected results.
|
||||
#
|
||||
# #tsort_each returns +nil+.
|
||||
# If there is a cycle, Bundler::TSort::Cyclic is raised.
|
||||
#
|
||||
# class G
|
||||
# include Bundler::TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# graph.tsort_each {|n| p n }
|
||||
# #=> 4
|
||||
# # 2
|
||||
# # 3
|
||||
# # 1
|
||||
#
|
||||
def tsort_each(&block) # :yields: node
|
||||
each_node = method(:tsort_each_node)
|
||||
each_child = method(:tsort_each_child)
|
||||
Bundler::TSort.tsort_each(each_node, each_child, &block)
|
||||
end
|
||||
|
||||
TSort.each_strongly_connected_component(each_node, each_child) {|component|
|
||||
if component.size == 1
|
||||
yield component.first
|
||||
else
|
||||
raise Cyclic.new("topological sort failed: #{component.inspect}")
|
||||
end
|
||||
}
|
||||
end
|
||||
# The iterator version of the Bundler::TSort.tsort method.
|
||||
#
|
||||
# The graph is represented by _each_node_ and _each_child_.
|
||||
# _each_node_ should have +call+ method which yields for each node in the graph.
|
||||
# _each_child_ should have +call+ method which takes a node argument and yields for each child node.
|
||||
#
|
||||
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# Bundler::TSort.tsort_each(each_node, each_child) {|n| p n }
|
||||
# #=> 4
|
||||
# # 2
|
||||
# # 3
|
||||
# # 1
|
||||
#
|
||||
def self.tsort_each(each_node, each_child) # :yields: node
|
||||
return to_enum(__method__, each_node, each_child) unless block_given?
|
||||
|
||||
# Returns strongly connected components as an array of arrays of nodes.
|
||||
# The array is sorted from children to parents.
|
||||
# Each elements of the array represents a strongly connected component.
|
||||
#
|
||||
# class G
|
||||
# include TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
|
||||
#
|
||||
# graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
|
||||
# p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
|
||||
#
|
||||
def strongly_connected_components
|
||||
each_node = method(:tsort_each_node)
|
||||
each_child = method(:tsort_each_child)
|
||||
TSort.strongly_connected_components(each_node, each_child)
|
||||
end
|
||||
each_strongly_connected_component(each_node, each_child) {|component|
|
||||
if component.size == 1
|
||||
yield component.first
|
||||
else
|
||||
raise Cyclic.new("topological sort failed: #{component.inspect}")
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Returns strongly connected components as an array of arrays of nodes.
|
||||
# The array is sorted from children to parents.
|
||||
# Each elements of the array represents a strongly connected component.
|
||||
#
|
||||
# The graph is represented by _each_node_ and _each_child_.
|
||||
# _each_node_ should have +call+ method which yields for each node in the graph.
|
||||
# _each_child_ should have +call+ method which takes a node argument and yields for each child node.
|
||||
#
|
||||
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# p TSort.strongly_connected_components(each_node, each_child)
|
||||
# #=> [[4], [2], [3], [1]]
|
||||
#
|
||||
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# p TSort.strongly_connected_components(each_node, each_child)
|
||||
# #=> [[4], [2, 3], [1]]
|
||||
#
|
||||
def TSort.strongly_connected_components(each_node, each_child)
|
||||
TSort.each_strongly_connected_component(each_node, each_child).to_a
|
||||
end
|
||||
# Returns strongly connected components as an array of arrays of nodes.
|
||||
# The array is sorted from children to parents.
|
||||
# Each elements of the array represents a strongly connected component.
|
||||
#
|
||||
# class G
|
||||
# include Bundler::TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# p graph.strongly_connected_components #=> [[4], [2], [3], [1]]
|
||||
#
|
||||
# graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
|
||||
# p graph.strongly_connected_components #=> [[4], [2, 3], [1]]
|
||||
#
|
||||
def strongly_connected_components
|
||||
each_node = method(:tsort_each_node)
|
||||
each_child = method(:tsort_each_child)
|
||||
Bundler::TSort.strongly_connected_components(each_node, each_child)
|
||||
end
|
||||
|
||||
# The iterator version of the #strongly_connected_components method.
|
||||
# <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
|
||||
# <tt><em>obj</em>.strongly_connected_components.each</tt>, but
|
||||
# modification of _obj_ during the iteration may lead to unexpected results.
|
||||
#
|
||||
# #each_strongly_connected_component returns +nil+.
|
||||
#
|
||||
# class G
|
||||
# include TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# graph.each_strongly_connected_component {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2]
|
||||
# # [3]
|
||||
# # [1]
|
||||
#
|
||||
# graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
|
||||
# graph.each_strongly_connected_component {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2, 3]
|
||||
# # [1]
|
||||
#
|
||||
def each_strongly_connected_component(&block) # :yields: nodes
|
||||
each_node = method(:tsort_each_node)
|
||||
each_child = method(:tsort_each_child)
|
||||
TSort.each_strongly_connected_component(each_node, each_child, &block)
|
||||
end
|
||||
# Returns strongly connected components as an array of arrays of nodes.
|
||||
# The array is sorted from children to parents.
|
||||
# Each elements of the array represents a strongly connected component.
|
||||
#
|
||||
# The graph is represented by _each_node_ and _each_child_.
|
||||
# _each_node_ should have +call+ method which yields for each node in the graph.
|
||||
# _each_child_ should have +call+ method which takes a node argument and yields for each child node.
|
||||
#
|
||||
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# p Bundler::TSort.strongly_connected_components(each_node, each_child)
|
||||
# #=> [[4], [2], [3], [1]]
|
||||
#
|
||||
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# p Bundler::TSort.strongly_connected_components(each_node, each_child)
|
||||
# #=> [[4], [2, 3], [1]]
|
||||
#
|
||||
def self.strongly_connected_components(each_node, each_child)
|
||||
each_strongly_connected_component(each_node, each_child).to_a
|
||||
end
|
||||
|
||||
# The iterator version of the TSort.strongly_connected_components method.
|
||||
#
|
||||
# The graph is represented by _each_node_ and _each_child_.
|
||||
# _each_node_ should have +call+ method which yields for each node in the graph.
|
||||
# _each_child_ should have +call+ method which takes a node argument and yields for each child node.
|
||||
#
|
||||
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2]
|
||||
# # [3]
|
||||
# # [1]
|
||||
#
|
||||
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2, 3]
|
||||
# # [1]
|
||||
#
|
||||
def TSort.each_strongly_connected_component(each_node, each_child) # :yields: nodes
|
||||
return to_enum(__method__, each_node, each_child) unless block_given?
|
||||
# The iterator version of the #strongly_connected_components method.
|
||||
# <tt><em>obj</em>.each_strongly_connected_component</tt> is similar to
|
||||
# <tt><em>obj</em>.strongly_connected_components.each</tt>, but
|
||||
# modification of _obj_ during the iteration may lead to unexpected results.
|
||||
#
|
||||
# #each_strongly_connected_component returns +nil+.
|
||||
#
|
||||
# class G
|
||||
# include Bundler::TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# graph.each_strongly_connected_component {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2]
|
||||
# # [3]
|
||||
# # [1]
|
||||
#
|
||||
# graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
|
||||
# graph.each_strongly_connected_component {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2, 3]
|
||||
# # [1]
|
||||
#
|
||||
def each_strongly_connected_component(&block) # :yields: nodes
|
||||
each_node = method(:tsort_each_node)
|
||||
each_child = method(:tsort_each_child)
|
||||
Bundler::TSort.each_strongly_connected_component(each_node, each_child, &block)
|
||||
end
|
||||
|
||||
id_map = {}
|
||||
stack = []
|
||||
each_node.call {|node|
|
||||
unless id_map.include? node
|
||||
TSort.each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
|
||||
# The iterator version of the Bundler::TSort.strongly_connected_components method.
|
||||
#
|
||||
# The graph is represented by _each_node_ and _each_child_.
|
||||
# _each_node_ should have +call+ method which yields for each node in the graph.
|
||||
# _each_child_ should have +call+ method which takes a node argument and yields for each child node.
|
||||
#
|
||||
# g = {1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# Bundler::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2]
|
||||
# # [3]
|
||||
# # [1]
|
||||
#
|
||||
# g = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
|
||||
# each_node = lambda {|&b| g.each_key(&b) }
|
||||
# each_child = lambda {|n, &b| g[n].each(&b) }
|
||||
# Bundler::TSort.each_strongly_connected_component(each_node, each_child) {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2, 3]
|
||||
# # [1]
|
||||
#
|
||||
def self.each_strongly_connected_component(each_node, each_child) # :yields: nodes
|
||||
return to_enum(__method__, each_node, each_child) unless block_given?
|
||||
|
||||
id_map = {}
|
||||
stack = []
|
||||
each_node.call {|node|
|
||||
unless id_map.include? node
|
||||
each_strongly_connected_component_from(node, each_child, id_map, stack) {|c|
|
||||
yield c
|
||||
}
|
||||
end
|
||||
}
|
||||
nil
|
||||
end
|
||||
|
||||
# Iterates over strongly connected component in the subgraph reachable from
|
||||
# _node_.
|
||||
#
|
||||
# Return value is unspecified.
|
||||
#
|
||||
# #each_strongly_connected_component_from doesn't call #tsort_each_node.
|
||||
#
|
||||
# class G
|
||||
# include Bundler::TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# graph.each_strongly_connected_component_from(2) {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2]
|
||||
#
|
||||
# graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
|
||||
# graph.each_strongly_connected_component_from(2) {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2, 3]
|
||||
#
|
||||
def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
|
||||
Bundler::TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
|
||||
end
|
||||
|
||||
# Iterates over strongly connected components in a graph.
|
||||
# The graph is represented by _node_ and _each_child_.
|
||||
#
|
||||
# _node_ is the first node.
|
||||
# _each_child_ should have +call+ method which takes a node argument
|
||||
# and yields for each child node.
|
||||
#
|
||||
# Return value is unspecified.
|
||||
#
|
||||
# #Bundler::TSort.each_strongly_connected_component_from is a class method and
|
||||
# it doesn't need a class to represent a graph which includes Bundler::TSort.
|
||||
#
|
||||
# graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
|
||||
# each_child = lambda {|n, &b| graph[n].each(&b) }
|
||||
# Bundler::TSort.each_strongly_connected_component_from(1, each_child) {|scc|
|
||||
# p scc
|
||||
# }
|
||||
# #=> [4]
|
||||
# # [2, 3]
|
||||
# # [1]
|
||||
#
|
||||
def self.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
|
||||
return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
|
||||
|
||||
minimum_id = node_id = id_map[node] = id_map.size
|
||||
stack_length = stack.length
|
||||
stack << node
|
||||
|
||||
each_child.call(node) {|child|
|
||||
if id_map.include? child
|
||||
child_id = id_map[child]
|
||||
minimum_id = child_id if child_id && child_id < minimum_id
|
||||
else
|
||||
sub_minimum_id =
|
||||
each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
|
||||
yield c
|
||||
}
|
||||
end
|
||||
}
|
||||
nil
|
||||
end
|
||||
|
||||
# Iterates over strongly connected component in the subgraph reachable from
|
||||
# _node_.
|
||||
#
|
||||
# Return value is unspecified.
|
||||
#
|
||||
# #each_strongly_connected_component_from doesn't call #tsort_each_node.
|
||||
#
|
||||
# class G
|
||||
# include TSort
|
||||
# def initialize(g)
|
||||
# @g = g
|
||||
# end
|
||||
# def tsort_each_child(n, &b) @g[n].each(&b) end
|
||||
# def tsort_each_node(&b) @g.each_key(&b) end
|
||||
# end
|
||||
#
|
||||
# graph = G.new({1=>[2, 3], 2=>[4], 3=>[2, 4], 4=>[]})
|
||||
# graph.each_strongly_connected_component_from(2) {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2]
|
||||
#
|
||||
# graph = G.new({1=>[2], 2=>[3, 4], 3=>[2], 4=>[]})
|
||||
# graph.each_strongly_connected_component_from(2) {|scc| p scc }
|
||||
# #=> [4]
|
||||
# # [2, 3]
|
||||
#
|
||||
def each_strongly_connected_component_from(node, id_map={}, stack=[], &block) # :yields: nodes
|
||||
TSort.each_strongly_connected_component_from(node, method(:tsort_each_child), id_map, stack, &block)
|
||||
end
|
||||
|
||||
# Iterates over strongly connected components in a graph.
|
||||
# The graph is represented by _node_ and _each_child_.
|
||||
#
|
||||
# _node_ is the first node.
|
||||
# _each_child_ should have +call+ method which takes a node argument
|
||||
# and yields for each child node.
|
||||
#
|
||||
# Return value is unspecified.
|
||||
#
|
||||
# #TSort.each_strongly_connected_component_from is a class method and
|
||||
# it doesn't need a class to represent a graph which includes TSort.
|
||||
#
|
||||
# graph = {1=>[2], 2=>[3, 4], 3=>[2], 4=>[]}
|
||||
# each_child = lambda {|n, &b| graph[n].each(&b) }
|
||||
# TSort.each_strongly_connected_component_from(1, each_child) {|scc|
|
||||
# p scc
|
||||
# }
|
||||
# #=> [4]
|
||||
# # [2, 3]
|
||||
# # [1]
|
||||
#
|
||||
def TSort.each_strongly_connected_component_from(node, each_child, id_map={}, stack=[]) # :yields: nodes
|
||||
return to_enum(__method__, node, each_child, id_map, stack) unless block_given?
|
||||
|
||||
minimum_id = node_id = id_map[node] = id_map.size
|
||||
stack_length = stack.length
|
||||
stack << node
|
||||
|
||||
each_child.call(node) {|child|
|
||||
if id_map.include? child
|
||||
child_id = id_map[child]
|
||||
minimum_id = child_id if child_id && child_id < minimum_id
|
||||
else
|
||||
sub_minimum_id =
|
||||
TSort.each_strongly_connected_component_from(child, each_child, id_map, stack) {|c|
|
||||
yield c
|
||||
}
|
||||
minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
|
||||
end
|
||||
}
|
||||
|
||||
if node_id == minimum_id
|
||||
component = stack.slice!(stack_length .. -1)
|
||||
component.each {|n| id_map[n] = nil}
|
||||
yield component
|
||||
minimum_id = sub_minimum_id if sub_minimum_id < minimum_id
|
||||
end
|
||||
}
|
||||
|
||||
minimum_id
|
||||
if node_id == minimum_id
|
||||
component = stack.slice!(stack_length .. -1)
|
||||
component.each {|n| id_map[n] = nil}
|
||||
yield component
|
||||
end
|
||||
|
||||
# Should be implemented by a extended class.
|
||||
#
|
||||
# #tsort_each_node is used to iterate for all nodes over a graph.
|
||||
#
|
||||
def tsort_each_node # :yields: node
|
||||
raise NotImplementedError.new
|
||||
end
|
||||
minimum_id
|
||||
end
|
||||
|
||||
# Should be implemented by a extended class.
|
||||
#
|
||||
# #tsort_each_child is used to iterate for child nodes of _node_.
|
||||
#
|
||||
def tsort_each_child(node) # :yields: child
|
||||
raise NotImplementedError.new
|
||||
end
|
||||
# Should be implemented by a extended class.
|
||||
#
|
||||
# #tsort_each_node is used to iterate for all nodes over a graph.
|
||||
#
|
||||
def tsort_each_node # :yields: node
|
||||
raise NotImplementedError.new
|
||||
end
|
||||
|
||||
# Should be implemented by a extended class.
|
||||
#
|
||||
# #tsort_each_child is used to iterate for child nodes of _node_.
|
||||
#
|
||||
def tsort_each_child(node) # :yields: child
|
||||
raise NotImplementedError.new
|
||||
end
|
||||
end
|
||||
|
|
|
@ -864,9 +864,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
|
|||
return @ruby_version if defined? @ruby_version
|
||||
version = RUBY_VERSION.dup
|
||||
|
||||
if defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1
|
||||
version << ".#{RUBY_PATCHLEVEL}"
|
||||
elsif defined?(RUBY_DESCRIPTION)
|
||||
unless defined?(RUBY_PATCHLEVEL) && RUBY_PATCHLEVEL != -1
|
||||
if RUBY_ENGINE == "ruby"
|
||||
desc = RUBY_DESCRIPTION[/\Aruby #{Regexp.quote(RUBY_VERSION)}([^ ]+) /, 1]
|
||||
else
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
#
|
||||
# === New to \Gem::OptionParser?
|
||||
#
|
||||
# See the {Tutorial}[./doc/optparse/tutorial_rdoc.html].
|
||||
# See the {Tutorial}[optparse/tutorial.rdoc].
|
||||
#
|
||||
# === Introduction
|
||||
#
|
||||
|
@ -420,7 +420,7 @@
|
|||
# === Further documentation
|
||||
#
|
||||
# The above examples, along with the accompanying
|
||||
# {Tutorial}[./doc/optparse/tutorial_rdoc.html],
|
||||
# {Tutorial}[optparse/tutorial.rdoc],
|
||||
# should be enough to learn how to use this class.
|
||||
# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
|
||||
#
|
||||
|
@ -674,6 +674,29 @@ class Gem::OptionParser
|
|||
end
|
||||
end
|
||||
|
||||
def pretty_print_contents(q) # :nodoc:
|
||||
if @block
|
||||
q.text ":" + @block.source_location.join(":") + ":"
|
||||
first = false
|
||||
else
|
||||
first = true
|
||||
end
|
||||
[@short, @long].each do |list|
|
||||
list.each do |opt|
|
||||
if first
|
||||
q.text ":"
|
||||
first = false
|
||||
end
|
||||
q.breakable
|
||||
q.text opt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pretty_print(q) # :nodoc:
|
||||
q.object_group(self) {pretty_print_contents(q)}
|
||||
end
|
||||
|
||||
#
|
||||
# Switch that takes no arguments.
|
||||
#
|
||||
|
@ -693,6 +716,10 @@ class Gem::OptionParser
|
|||
def self.pattern
|
||||
Object
|
||||
end
|
||||
|
||||
def pretty_head # :nodoc:
|
||||
"NoArgument"
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -710,6 +737,10 @@ class Gem::OptionParser
|
|||
end
|
||||
conv_arg(*parse_arg(arg, &method(:raise)))
|
||||
end
|
||||
|
||||
def pretty_head # :nodoc:
|
||||
"Required"
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -727,6 +758,10 @@ class Gem::OptionParser
|
|||
conv_arg(arg)
|
||||
end
|
||||
end
|
||||
|
||||
def pretty_head # :nodoc:
|
||||
"Optional"
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -750,6 +785,10 @@ class Gem::OptionParser
|
|||
end
|
||||
val
|
||||
end
|
||||
|
||||
def pretty_head # :nodoc:
|
||||
"Placed"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -781,6 +820,17 @@ class Gem::OptionParser
|
|||
@list = []
|
||||
end
|
||||
|
||||
def pretty_print(q) # :nodoc:
|
||||
q.group(1, "(", ")") do
|
||||
@list.each do |sw|
|
||||
next unless Switch === sw
|
||||
q.group(1, "(" + sw.pretty_head, ")") do
|
||||
sw.pretty_print_contents(q)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# See Gem::OptionParser.accept.
|
||||
#
|
||||
|
@ -1293,6 +1343,29 @@ XXX
|
|||
def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end
|
||||
alias to_s help
|
||||
|
||||
def pretty_print(q) # :nodoc:
|
||||
q.object_group(self) do
|
||||
first = true
|
||||
if @stack.size > 2
|
||||
@stack.each_with_index do |s, i|
|
||||
next if i < 2
|
||||
next if s.list.empty?
|
||||
if first
|
||||
first = false
|
||||
q.text ":"
|
||||
end
|
||||
q.breakable
|
||||
s.pretty_print(q)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def inspect # :nodoc:
|
||||
require 'pp'
|
||||
pretty_print_inspect
|
||||
end
|
||||
|
||||
#
|
||||
# Returns option summary list.
|
||||
#
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: false
|
||||
require 'rubygems/optparse/lib/optparse'
|
||||
require_relative '../optparse'
|
||||
|
||||
class Gem::OptionParser::AC < Gem::OptionParser
|
||||
private
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: false
|
||||
require 'rubygems/optparse/lib/optparse'
|
||||
require_relative '../optparse'
|
||||
require 'date'
|
||||
|
||||
Gem::OptionParser.accept(DateTime) do |s,|
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
require 'rubygems/optparse/lib/optparse'
|
||||
require_relative '../optparse'
|
||||
|
||||
class Gem::OptionParser
|
||||
# :call-seq:
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
# -*- ruby -*-
|
||||
|
||||
require 'shellwords'
|
||||
require 'rubygems/optparse/lib/optparse'
|
||||
require_relative '../optparse'
|
||||
|
||||
Gem::OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: false
|
||||
require 'rubygems/optparse/lib/optparse'
|
||||
require_relative '../optparse'
|
||||
require 'time'
|
||||
|
||||
Gem::OptionParser.accept(Time) do |s,|
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: false
|
||||
# -*- ruby -*-
|
||||
|
||||
require 'rubygems/optparse/lib/optparse'
|
||||
require_relative '../optparse'
|
||||
require 'uri'
|
||||
|
||||
Gem::OptionParser.accept(URI) {|s,| URI.parse(s) if s}
|
||||
|
|
|
@ -657,6 +657,8 @@ class Gem::Specification < Gem::BasicSpecification
|
|||
@rdoc_options ||= []
|
||||
end
|
||||
|
||||
LATEST_RUBY_WITHOUT_PATCH_VERSIONS = Gem::Version.new("2.1")
|
||||
|
||||
##
|
||||
# The version of Ruby required by this gem. The ruby version can be
|
||||
# specified to the patch-level:
|
||||
|
@ -683,6 +685,14 @@ class Gem::Specification < Gem::BasicSpecification
|
|||
|
||||
def required_ruby_version=(req)
|
||||
@required_ruby_version = Gem::Requirement.create req
|
||||
|
||||
@required_ruby_version.requirements.map! do |op, v|
|
||||
if v >= LATEST_RUBY_WITHOUT_PATCH_VERSIONS && v.release.segments.size == 4
|
||||
[op == "~>" ? "=" : op, Gem::Version.new(v.segments.tap {|s| s.delete_at(3) }.join("."))]
|
||||
else
|
||||
[op, v]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -498,31 +498,5 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_gem_version_with_patchlevel" do
|
||||
shared_examples_for "the patchlevel is omitted" do
|
||||
it "does not include a patch level" do
|
||||
expect(subject.to_gem_version_with_patchlevel.to_s).to eq(version)
|
||||
end
|
||||
end
|
||||
|
||||
context "with nil patch number" do
|
||||
let(:patchlevel) { nil }
|
||||
|
||||
it_behaves_like "the patchlevel is omitted"
|
||||
end
|
||||
|
||||
context "with negative patch number" do
|
||||
let(:patchlevel) { -1 }
|
||||
|
||||
it_behaves_like "the patchlevel is omitted"
|
||||
end
|
||||
|
||||
context "with a valid patch number" do
|
||||
it "uses the specified patchlevel as patchlevel" do
|
||||
expect(subject.to_gem_version_with_patchlevel.to_s).to eq("#{version}.#{patchlevel}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -542,6 +542,40 @@ RSpec.describe "bundle lock" do
|
|||
bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
||||
end
|
||||
|
||||
it "respects lower bound ruby requirements" do
|
||||
skip "this spec does not work with prereleases because their version is actually lower than their reported `RUBY_VERSION`" if RUBY_PATCHLEVEL == -1
|
||||
|
||||
build_repo4 do
|
||||
build_gem "our_private_gem", "0.1.0" do |s|
|
||||
s.required_ruby_version = ">= #{RUBY_VERSION}"
|
||||
end
|
||||
end
|
||||
|
||||
gemfile <<-G
|
||||
source "https://localgemserver.test"
|
||||
|
||||
gem "our_private_gem"
|
||||
G
|
||||
|
||||
lockfile <<-L
|
||||
GEM
|
||||
remote: https://localgemserver.test/
|
||||
specs:
|
||||
our_private_gem (0.1.0)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
our_private_gem
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
bundle "install", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
||||
end
|
||||
|
||||
context "when an update is available" do
|
||||
let(:repo) { gem_repo2 }
|
||||
|
||||
|
|
|
@ -1182,6 +1182,8 @@ RSpec.describe "bundle update --bundler" do
|
|||
end
|
||||
|
||||
it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo, :realworld do
|
||||
skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1
|
||||
|
||||
pristine_system_gems "bundler-2.3.9"
|
||||
|
||||
build_repo4 do
|
||||
|
@ -1226,6 +1228,8 @@ RSpec.describe "bundle update --bundler" do
|
|||
end
|
||||
|
||||
it "errors if the explicit target version does not exist", :realworld do
|
||||
skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1
|
||||
|
||||
pristine_system_gems "bundler-2.3.9"
|
||||
|
||||
build_repo4 do
|
||||
|
|
|
@ -301,7 +301,28 @@ RSpec.describe "bundle install with install-time dependencies" do
|
|||
end
|
||||
|
||||
let(:ruby_requirement) { %("#{RUBY_VERSION}") }
|
||||
let(:error_message_requirement) { "~> #{RUBY_VERSION}.0" }
|
||||
let(:error_message_requirement) { "= #{RUBY_VERSION}" }
|
||||
|
||||
it "raises a proper error that mentions the current Ruby version during resolution" do
|
||||
install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false
|
||||
source "http://localgemserver.test/"
|
||||
gem 'require_ruby'
|
||||
G
|
||||
|
||||
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
|
||||
|
||||
nice_error = strip_whitespace(<<-E).strip
|
||||
Bundler found conflicting requirements for the Ruby\0 version:
|
||||
In Gemfile:
|
||||
require_ruby was resolved to 1.0, which depends on
|
||||
Ruby\0 (> 9000)
|
||||
|
||||
Current Ruby\0 version:
|
||||
Ruby\0 (#{error_message_requirement})
|
||||
|
||||
E
|
||||
expect(err).to end_with(nice_error)
|
||||
end
|
||||
|
||||
shared_examples_for "ruby version conflicts" do
|
||||
it "raises an error during resolution" do
|
||||
|
@ -316,10 +337,12 @@ RSpec.describe "bundle install with install-time dependencies" do
|
|||
nice_error = strip_whitespace(<<-E).strip
|
||||
Bundler found conflicting requirements for the Ruby\0 version:
|
||||
In Gemfile:
|
||||
Ruby\0 (#{error_message_requirement})
|
||||
|
||||
require_ruby was resolved to 1.0, which depends on
|
||||
Ruby\0 (> 9000)
|
||||
|
||||
Current Ruby\0 version:
|
||||
Ruby\0 (#{error_message_requirement})
|
||||
|
||||
E
|
||||
expect(err).to end_with(nice_error)
|
||||
end
|
||||
|
@ -329,7 +352,6 @@ RSpec.describe "bundle install with install-time dependencies" do
|
|||
|
||||
describe "with a < requirement" do
|
||||
let(:ruby_requirement) { %("< 5000") }
|
||||
let(:error_message_requirement) { "< 5000" }
|
||||
|
||||
it_behaves_like "ruby version conflicts"
|
||||
end
|
||||
|
@ -337,7 +359,6 @@ RSpec.describe "bundle install with install-time dependencies" do
|
|||
describe "with a compound requirement" do
|
||||
let(:reqs) { ["> 0.1", "< 5000"] }
|
||||
let(:ruby_requirement) { reqs.map(&:dump).join(", ") }
|
||||
let(:error_message_requirement) { Gem::Requirement.new(reqs).to_s }
|
||||
|
||||
it_behaves_like "ruby version conflicts"
|
||||
end
|
||||
|
@ -361,10 +382,12 @@ RSpec.describe "bundle install with install-time dependencies" do
|
|||
nice_error = strip_whitespace(<<-E).strip
|
||||
Bundler found conflicting requirements for the RubyGems\0 version:
|
||||
In Gemfile:
|
||||
RubyGems\0 (= #{Gem::VERSION})
|
||||
|
||||
require_rubygems was resolved to 1.0, which depends on
|
||||
RubyGems\0 (> 9000)
|
||||
|
||||
Current RubyGems\0 version:
|
||||
RubyGems\0 (= #{Gem::VERSION})
|
||||
|
||||
E
|
||||
expect(err).to end_with(nice_error)
|
||||
end
|
||||
|
|
|
@ -120,7 +120,7 @@ RSpec.shared_examples "bundle install --standalone" do
|
|||
|
||||
realworld_system_gems "tsort --version 0.1.0"
|
||||
|
||||
necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.0"]
|
||||
necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.1"]
|
||||
necessary_system_gems += ["shellwords --version 0.1.0", "base64 --version 0.1.0", "resolv --version 0.2.1"] if Gem.rubygems_version < Gem::Version.new("3.3.a")
|
||||
necessary_system_gems += ["yaml --version 0.1.1"] if Gem.rubygems_version < Gem::Version.new("3.4.a")
|
||||
realworld_system_gems(*necessary_system_gems, :path => scoped_gem_path(bundled_app("bundle")))
|
||||
|
|
|
@ -1098,7 +1098,7 @@ Also, a list:
|
|||
Zlib::Deflate.deflate data
|
||||
end
|
||||
|
||||
def util_set_RUBY_VERSION(version, patchlevel = nil, revision = nil, description = nil, engine = "ruby", engine_version = nil)
|
||||
def util_set_RUBY_VERSION(version, patchlevel, revision, description, engine = "ruby", engine_version = nil)
|
||||
if Gem.instance_variables.include? :@ruby_version
|
||||
Gem.send :remove_instance_variable, :@ruby_version
|
||||
end
|
||||
|
@ -1106,16 +1106,16 @@ Also, a list:
|
|||
@RUBY_VERSION = RUBY_VERSION
|
||||
@RUBY_PATCHLEVEL = RUBY_PATCHLEVEL if defined?(RUBY_PATCHLEVEL)
|
||||
@RUBY_REVISION = RUBY_REVISION if defined?(RUBY_REVISION)
|
||||
@RUBY_DESCRIPTION = RUBY_DESCRIPTION if defined?(RUBY_DESCRIPTION)
|
||||
@RUBY_DESCRIPTION = RUBY_DESCRIPTION
|
||||
@RUBY_ENGINE = RUBY_ENGINE
|
||||
@RUBY_ENGINE_VERSION = RUBY_ENGINE_VERSION if defined?(RUBY_ENGINE_VERSION)
|
||||
|
||||
util_clear_RUBY_VERSION
|
||||
|
||||
Object.const_set :RUBY_VERSION, version
|
||||
Object.const_set :RUBY_PATCHLEVEL, patchlevel if patchlevel
|
||||
Object.const_set :RUBY_REVISION, revision if revision
|
||||
Object.const_set :RUBY_DESCRIPTION, description if description
|
||||
Object.const_set :RUBY_PATCHLEVEL, patchlevel
|
||||
Object.const_set :RUBY_REVISION, revision
|
||||
Object.const_set :RUBY_DESCRIPTION, description
|
||||
Object.const_set :RUBY_ENGINE, engine
|
||||
Object.const_set :RUBY_ENGINE_VERSION, engine_version if engine_version
|
||||
end
|
||||
|
@ -1128,8 +1128,7 @@ Also, a list:
|
|||
defined?(@RUBY_PATCHLEVEL)
|
||||
Object.const_set :RUBY_REVISION, @RUBY_REVISION if
|
||||
defined?(@RUBY_REVISION)
|
||||
Object.const_set :RUBY_DESCRIPTION, @RUBY_DESCRIPTION if
|
||||
defined?(@RUBY_DESCRIPTION)
|
||||
Object.const_set :RUBY_DESCRIPTION, @RUBY_DESCRIPTION
|
||||
Object.const_set :RUBY_ENGINE, @RUBY_ENGINE
|
||||
Object.const_set :RUBY_ENGINE_VERSION, @RUBY_ENGINE_VERSION if
|
||||
defined?(@RUBY_ENGINE_VERSION)
|
||||
|
|
|
@ -1106,22 +1106,6 @@ class TestGem < Gem::TestCase
|
|||
assert_equal Gem::Requirement.default, Gem.env_requirement('qux')
|
||||
end
|
||||
|
||||
def test_self_ruby_version_with_patchlevel_less_ancient_rubies
|
||||
util_set_RUBY_VERSION '1.8.5'
|
||||
|
||||
assert_equal Gem::Version.new('1.8.5'), Gem.ruby_version
|
||||
ensure
|
||||
util_restore_RUBY_VERSION
|
||||
end
|
||||
|
||||
def test_self_ruby_version_with_release
|
||||
util_set_RUBY_VERSION '1.8.6', 287
|
||||
|
||||
assert_equal Gem::Version.new('1.8.6.287'), Gem.ruby_version
|
||||
ensure
|
||||
util_restore_RUBY_VERSION
|
||||
end
|
||||
|
||||
def test_self_ruby_version_with_non_mri_implementations
|
||||
util_set_RUBY_VERSION '2.5.0', 0, 60928, 'jruby 9.2.0.0 (2.5.0) 2018-05-24 81156a8 OpenJDK 64-Bit Server VM 25.171-b11 on 1.8.0_171-8u171-b11-0ubuntu0.16.04.1-b11 [linux-x86_64]'
|
||||
|
||||
|
|
|
@ -261,8 +261,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rb-sys"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/ianks/rb-sys?tag=v0.6.0#1aa5b589e86a14e01aba806511818c19f85d71f6"
|
||||
version = "0.7.3"
|
||||
source = "git+https://github.com/ianks/rb-sys?tag=v0.7.3#4a5dd9782075fc6e197976eb2188231a388c3c95"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"libc",
|
||||
|
@ -271,9 +271,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
version = "1.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
|
|
@ -7,4 +7,4 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
# Needed until bindgen has the `allowlist_file` feature
|
||||
rb-sys = { git = "https://github.com/ianks/rb-sys", tag = "v0.6.0" }
|
||||
rb-sys = { git = "https://github.com/ianks/rb-sys", tag = "v0.7.3" }
|
||||
|
|
|
@ -254,8 +254,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rb-sys"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/ianks/rb-sys?tag=v0.6.0#1aa5b589e86a14e01aba806511818c19f85d71f6"
|
||||
version = "0.7.3"
|
||||
source = "git+https://github.com/ianks/rb-sys?tag=v0.7.3#4a5dd9782075fc6e197976eb2188231a388c3c95"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"libc",
|
||||
|
@ -264,9 +264,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
version = "1.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
|
|
@ -7,4 +7,4 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
# Needed until bindgen has the `allowlist_file` feature
|
||||
rb-sys = { git = "https://github.com/ianks/rb-sys", tag = "v0.6.0" }
|
||||
rb-sys = { git = "https://github.com/ianks/rb-sys", tag = "v0.7.3" }
|
||||
|
|
|
@ -69,7 +69,7 @@ class TestGemResolverInstallerSet < Gem::TestCase
|
|||
fetcher.gem 'a', 1
|
||||
end
|
||||
|
||||
# GitHub has an issue in which it will generate a misleading prerelease output in its RubyGems server API and
|
||||
# Github has an issue in which it will generate a misleading prerelease output in its RubyGems server API and
|
||||
# returns a 0 version for the gem while it doesn't exist.
|
||||
@fetcher.data["#{@gem_repo}prerelease_specs.#{Gem.marshal_version}.gz"] = util_gzip(Marshal.dump([
|
||||
Gem::NameTuple.new('a', Gem::Version.new(0), 'ruby'),
|
||||
|
|
Загрузка…
Ссылка в новой задаче