Class: Bundler::GemVersionPromoter
Relationships & Source Files | |
Inherits: | Object |
Defined in: | lib/bundler/gem_version_promoter.rb |
Overview
This class contains all of the logic for determining the next version of a ::Gem
to update to based on the requested level (patch, minor, major). Primarily designed to work with Resolver
which will provide it the list of available dependency versions as found in its index, before returning it to to the resolution engine to select the best version.
Constant Summary
-
DEBUG =
# File 'lib/bundler/gem_version_promoter.rb', line 10ENV["DEBUG_RESOLVER"]
Class Method Summary
-
.new(locked_specs = SpecSet.new([]), unlock_gems = []) ⇒ GemVersionPromoter
constructor
Given a list of locked_specs and a list of gems to unlock creates a
GemVersionPromoter
instance.
Instance Attribute Summary
- #level rw
- #level=(value) rw
- #locked_specs readonly
- #major? ⇒ bool readonly
- #minor? ⇒ bool readonly
- #prerelease_specified rw
-
#strict
rw
By default, strict is false, meaning every available version of a gem is returned from sort_versions.
- #unlock_gems readonly
- #unlocking_gem? ⇒ Boolean readonly private
Instance Method Summary
-
#sort_versions(dep, spec_groups) ⇒ SpecGroup
Given a
Dependency
and an Array of SpecGroups of available versions for a gem, this method will return the Array of SpecGroups sorted (and possibly truncated if strict is true) in an order to give preference to the current level (:major,:minor
or:patch
) when resolution is deciding what versions best resolve all dependencies in the bundle. - #debug_format_result(dep, spec_groups) private
- #either_version_older_than_locked private
- #filter_dep_specs(spec_groups, locked_spec) private
- #move_version_to_end(result, version) private
-
#post_sort(result)
private
Specific version moves can’t always reliably be done during sorting as not all elements are compared against each other.
- #segments_do_not_match(level) private
- #sort_dep_specs(spec_groups, locked_spec) private
Constructor Details
.new(locked_specs = SpecSet.new([]), unlock_gems = []) ⇒ GemVersionPromoter
Given a list of locked_specs and a list of gems to unlock creates a GemVersionPromoter
instance.
# File 'lib/bundler/gem_version_promoter.rb', line 38
def initialize(locked_specs = SpecSet.new([]), unlock_gems = []) @level = :major @strict = false @locked_specs = locked_specs @unlock_gems = unlock_gems @sort_versions = {} @prerelease_specified = {} end
Instance Attribute Details
#level (rw)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 12
attr_reader :level, :locked_specs, :unlock_gems
#level=(value) (rw)
# File 'lib/bundler/gem_version_promoter.rb', line 48
def level=(value) v = case value when String, Symbol value.to_sym end raise ArgumentError, "Unexpected level #{v}. Must be :major, :minor or :patch" unless [:major, :minor, :patch].include?(v) @level = v end
#locked_specs (readonly)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 12
attr_reader :level, :locked_specs, :unlock_gems
#major? ⇒ bool
(readonly)
# File 'lib/bundler/gem_version_promoter.rb', line 92
def major? level == :major end
#minor? ⇒ bool
(readonly)
# File 'lib/bundler/gem_version_promoter.rb', line 97
def minor? level == :minor end
#prerelease_specified (rw)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 27
attr_accessor :prerelease_specified
#strict (rw)
By default, strict is false, meaning every available version of a gem is returned from sort_versions. The order gives preference to the requested level (:patch, :minor
, :major
) but in complicated requirement cases some gems will by necessity by promoted past the requested level, or even reverted to older versions.
If strict is set to true, the results from sort_versions will be truncated, eliminating any version outside the current level scope. This can lead to unexpected outcomes or even VersionConflict
exceptions that report a version of a gem not existing for versions that indeed do existing in the referenced source.
# File 'lib/bundler/gem_version_promoter.rb', line 25
attr_accessor :strict
#unlock_gems (readonly)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 12
attr_reader :level, :locked_specs, :unlock_gems
#unlocking_gem? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/bundler/gem_version_promoter.rb', line 162
def unlocking_gem? unlock_gems.empty? || unlock_gems.include?(@gem_name) end
Instance Method Details
#debug_format_result(dep, spec_groups) (private)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 183
def debug_format_result(dep, spec_groups) a = [dep.to_s, spec_groups.map {|sg| [sg.version, sg.dependencies_for_activated_platforms.map {|dp| [dp.name, dp.requirement.to_s] }] }] last_map = a.last.map {|sg_data| [sg_data.first.version, sg_data.last.map {|aa| aa.join(" ") }] } [a.first, last_map, level, strict ? :strict : :not_strict] end
#either_version_older_than_locked (private)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 153
def either_version_older_than_locked @a_ver < @locked_version || @b_ver < @locked_version end
#filter_dep_specs(spec_groups, locked_spec) (private)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 103
def filter_dep_specs(spec_groups, locked_spec) res = spec_groups.select do |spec_group| if locked_spec && !major? gsv = spec_group.version lsv = locked_spec.version must_match = minor? ? [0] : [0, 1] matches = must_match.map {|idx| gsv.segments[idx] == lsv.segments[idx] } matches.uniq == [true] ? (gsv >= lsv) : false else true end end sort_dep_specs(res, locked_spec) end
#move_version_to_end(result, version) (private)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 178
def move_version_to_end(result, version) move, keep = result.partition {|s| s.version.to_s == version.to_s } keep.concat(move) end
#post_sort(result) (private)
Specific version moves can’t always reliably be done during sorting as not all elements are compared against each other.
# File 'lib/bundler/gem_version_promoter.rb', line 168
def post_sort(result) # default :major behavior in Bundler does not do this return result if major? if unlocking_gem? result else move_version_to_end(result, @locked_version) end end
#segments_do_not_match(level) (private)
[ GitHub ]#sort_dep_specs(spec_groups, locked_spec) (private)
[ GitHub ]# File 'lib/bundler/gem_version_promoter.rb', line 121
def sort_dep_specs(spec_groups, locked_spec) return spec_groups unless locked_spec @gem_name = locked_spec.name @locked_version = locked_spec.version result = spec_groups.sort do |a, b| @a_ver = a.version @b_ver = b.version unless @prerelease_specified[@gem_name] a_pre = @a_ver.prerelease? b_pre = @b_ver.prerelease? next -1 if a_pre && !b_pre next 1 if b_pre && !a_pre end if major? @a_ver <=> @b_ver elsif either_version_older_than_locked @a_ver <=> @b_ver elsif segments_do_not_match(:major) @b_ver <=> @a_ver elsif !minor? && segments_do_not_match(:minor) @b_ver <=> @a_ver else @a_ver <=> @b_ver end end post_sort(result) end
#sort_versions(dep, spec_groups) ⇒ SpecGroup
Given a Dependency
and an Array of SpecGroups of available versions for a gem, this method will return the Array of SpecGroups sorted (and possibly truncated if strict is true) in an order to give preference to the current level (:major, :minor
or :patch
) when resolution is deciding what versions best resolve all dependencies in the bundle.
# File 'lib/bundler/gem_version_promoter.rb', line 68
def sort_versions(dep, spec_groups) before_result = "before sort_versions: #{debug_format_result(dep, spec_groups).inspect}" if DEBUG @sort_versions[dep] ||= begin gem_name = dep.name # An Array per version returned, different entries for different platforms. # We only need the version here so it's ok to hard code this to the first instance. locked_spec = locked_specs[gem_name].first if strict filter_dep_specs(spec_groups, locked_spec) else sort_dep_specs(spec_groups, locked_spec) end.tap do |specs| if DEBUG warn before_result warn " after sort_versions: #{debug_format_result(dep, specs).inspect}" end end end end