semantic-range-3.0.0/0000755000175000017500000000000014101556632013126 5ustar srudsrudsemantic-range-3.0.0/semantic_range.gemspec0000644000175000017500000000252714101556632017460 0ustar srudsrud######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: semantic_range 3.0.0 ruby lib Gem::Specification.new do |s| s.name = "semantic_range".freeze s.version = "3.0.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Andrew Nesbitt".freeze] s.date = "2021-03-04" s.email = ["andrewnez@gmail.com".freeze] s.files = ["LICENSE.txt".freeze, "README.md".freeze, "lib/semantic_range.rb".freeze, "lib/semantic_range/comparator.rb".freeze, "lib/semantic_range/pre_release.rb".freeze, "lib/semantic_range/range.rb".freeze, "lib/semantic_range/version.rb".freeze] s.homepage = "https://libraries.io/github/librariesio/semantic_range".freeze s.licenses = ["MIT".freeze] s.rubygems_version = "3.2.0.rc.2".freeze s.summary = "node-semver rewritten in ruby, for comparison and inclusion of semantic versions and ranges".freeze if s.respond_to? :specification_version then s.specification_version = 4 end if s.respond_to? :add_runtime_dependency then s.add_development_dependency(%q.freeze, ["~> 3.4"]) else s.add_dependency(%q.freeze, ["~> 3.4"]) end end semantic-range-3.0.0/lib/0000755000175000017500000000000014101556632013674 5ustar srudsrudsemantic-range-3.0.0/lib/semantic_range/0000755000175000017500000000000014101556632016653 5ustar srudsrudsemantic-range-3.0.0/lib/semantic_range/comparator.rb0000644000175000017500000000540714101556632021355 0ustar srudsrudmodule SemanticRange class Comparator attr_reader :semver, :operator, :value def initialize(comp, loose) comp = comp.value if comp.is_a?(Comparator) @loose = loose parse(comp) @value = @semver == ANY ? '' : @operator + @semver.version end def to_s @value end def test(version) return true if @semver == ANY version = Version.new(version, @loose) if version.is_a?(String) SemanticRange.cmp(version, @operator, @semver, loose: @loose) end def parse(comp) m = comp.match(@loose ? COMPARATORLOOSE : COMPARATOR) raise InvalidComparator.new(comp) unless m @operator = m[1] @operator = '' if @operator == '=' @semver = !m[2] ? ANY : Version.new(m[2], loose: @loose) end def intersects?(comp, loose: false, platform: nil) comp = Comparator.new(comp, loose) if @operator == '' range_b = Range.new(comp.value, loose: loose, platform: platform) SemanticRange.satisfies?(@value, range_b, loose: loose, platform: platform) elsif comp.operator == '' range_a = Range.new(@value, loose: loose, platform: platform) SemanticRange.satisfies?(comp.semver, range_a, loose: loose, platform: platform) else same_direction_increasing = (@operator == '>=' || @operator == '>') && (comp.operator == '>=' || comp.operator == '>') same_direction_decreasing = (@operator == '<=' || @operator == '<') && (comp.operator == '<=' || comp.operator == '<') same_version = @semver.raw == comp.semver.raw different_directions_inclusive = (@operator == '>=' || @operator == '<=') && (comp.operator == '>=' || comp.operator == '<=') opposite_directions_lte = SemanticRange.cmp(@semver, '<', comp.semver, loose: loose) && ((@operator == '>=' || @operator == '>') && (comp.operator == '<=' || comp.operator == '<')) opposite_directions_gte = SemanticRange.cmp(@semver, '>', comp.semver, loose: loose) && ((@operator == '<=' || @operator == '<') && (comp.operator == '>=' || comp.operator == '>')) same_direction_increasing || same_direction_decreasing || (same_version && different_directions_inclusive) || opposite_directions_lte || opposite_directions_gte end end def satisfies_range?(range, loose: false, platform: nil) range = Range.new(range, loose: loose, platform: platform) range.set.any? do |comparators| comparators.all? do |comparator| intersects?(comparator, loose: loose, platform: platform) end end end # Support for older non-inquisitive method versions alias_method :intersects, :intersects? alias_method :satisfies_range, :satisfies_range? end end semantic-range-3.0.0/lib/semantic_range/version.rb0000644000175000017500000001015014101556632020662 0ustar srudsrudmodule SemanticRange VERSION = "3.0.0" class Version attr_reader :major, :minor, :patch, :prerelease def initialize(version, loose: false) @raw = version @loose = loose @raw = version.raw if version.is_a?(Version) match = @raw.to_s.strip.match(loose ? LOOSE : FULL) raise InvalidVersion.new(version) if match.nil? if String(version.to_s).length > MAX_LENGTH raise InvalidVersion.new("#{version} is too long") end @major = match[1] ? match[1].to_i : 0 @minor = match[2] ? match[2].to_i : 0 @patch = match[3] ? match[3].to_i : 0 @prerelease = PreRelease.new match[4] @build = match[5] ? match[5].split('.') : [] end def format v = "#{@major}.#{@minor}.#{@patch}" prerelease.length > 0 ? "#{v}-#{prerelease}" : v end def to_s @version end def version format end def raw version end def compare(other) other = Version.new(other, loose: @loose) unless other.is_a?(Version) res = truthy(compare_main(other)) || truthy(compare_pre(other)) res.is_a?(FalseClass) ? 0 : res end def compare_main(other) other = Version.new(other, loose: @loose) unless other.is_a?(Version) truthy(self.class.compare_identifiers(@major, other.major)) || truthy(self.class.compare_identifiers(@minor, other.minor)) || truthy(self.class.compare_identifiers(@patch, other.patch)) end def truthy(val) return val unless val.is_a?(Integer) val.zero? ? false : val end def compare_pre(other) prerelease <=> other.prerelease end def self.compare_identifiers(a,b) anum = /^[0-9]+$/.match(a.to_s) bnum = /^[0-9]+$/.match(b.to_s) if anum && bnum a = a.to_i b = b.to_i end return (anum && !bnum) ? -1 : (bnum && !anum) ? 1 : a < b ? -1 : a > b ? 1 : 0; end def increment!(release, identifier) case release when 'premajor' @prerelease.clear! @patch = 0 @minor = 0 @major = @major + 1 increment! 'pre', identifier when 'preminor' @prerelease.clear! @patch = 0 @minor = @minor + 1 increment! 'pre', identifier when 'prepatch' # If this is already a prerelease, it will bump to the next version # drop any prereleases that might already exist, since they are not # relevant at this point. @prerelease.clear! increment! 'patch', identifier increment! 'pre', identifier # If the input is a non-prerelease version, this acts the same as # prepatch. when 'prerelease' if @prerelease.empty? increment! 'patch', identifier end increment! 'pre', identifier when 'major' # If this is a pre-major version, bump up to the same major version. # Otherwise increment major. # 1.0.0-5 bumps to 1.0.0 # 1.1.0 bumps to 2.0.0 if @minor != 0 || @patch != 0 || @prerelease.empty? @major = @major + 1 end @minor = 0 @patch = 0 @prerelease.clear! when 'minor' # If this is a pre-minor version, bump up to the same minor version. # Otherwise increment minor. # 1.2.0-5 bumps to 1.2.0 # 1.2.1 bumps to 1.3.0 if @patch != 0 || @prerelease.empty? @minor = @minor + 1 end @patch = 0 @prerelease.clear! when 'patch' # If this is not a pre-release version, it will increment the patch. # If it is a pre-release it will bump up to the same patch version. # 1.2.0-5 patches to 1.2.0 # 1.2.0 patches to 1.2.1 if @prerelease.empty? @patch = @patch + 1 end @prerelease.clear! # This probably shouldn't be used publicly. # 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. when 'pre' @prerelease.increment!(identifier) else raise InvalidIncrement.new release end self end end end semantic-range-3.0.0/lib/semantic_range/range.rb0000644000175000017500000001601414101556632020276 0ustar srudsrudmodule SemanticRange class Range attr_reader :loose, :raw, :range, :set, :platform def initialize(range, loose: false, platform: nil) range = range.raw if range.is_a?(Range) @platform = platform @raw = range @loose = loose split = range.split(/\s*\|\|\s*/) split = ['', ''] if range == '||' split = [''] if split == [] begin @set = split.map {|range| parse_range(range.strip) } rescue InvalidComparator @set = [] end raise InvalidRange.new(range) if @set.empty? || @set == [[]] format end def format @range = @set.map do |comps| comps.join(' ').strip end.join('||').strip @range end def test(version) return false if !version version = Version.new(version, loose: @loose) if version.is_a?(String) @set.any?{|s| test_set(s, version) } end def test_set(set, version) return false if set.any? {|comp| !comp.test(version) } if version.prerelease.length > 0 set.each do |comp| next if comp.semver == ANY if comp.semver.prerelease.length > 0 allowed = comp.semver return true if allowed.major == version.major && allowed.minor == version.minor && allowed.patch == version.patch end end return false end return true end def parse_range(range) # expand hyphens range = range.gsub(@loose ? HYPHENRANGELOOSE : HYPHENRANGE){ hyphen_replace(Regexp.last_match) } # comparator trim range = range.gsub(COMPARATORTRIM, '\1\2\3') # tilde trim range = range.gsub(TILDETRIM, '\1~') # caret trim range = range.gsub(CARETTRIM, '\1^') # comma trim range = range.gsub(',', ' ') # normalise spaces range = range.split(/\s+/).join(' ') set = range.split(' ').map do |comp| parse_comparator(comp, @loose) end.join(' ').split(/\s+/) set = [''] if set == [] set = set.select{|comp| !!comp.match(COMPARATORLOOSE) } if @loose set.map{|comp| Comparator.new(comp, @loose) } end def isX(id) !id || id.downcase == 'x' || id == '*' end def parse_comparator(comp, loose) comp = replace_carets(comp, loose) comp = replace_tildes(comp, loose) comp = replace_x_ranges(comp, loose) replace_stars(comp, loose) end def replace_carets(comp, loose) comp.strip.split(/\s+/).map do |comp| replace_caret(comp, loose) end.join(' ') end def replace_caret(comp, loose) comp.gsub(loose ? CARETLOOSE : CARET) do match = Regexp.last_match mj = match[1] m = match[2] p = match[3] pr = match[4] if isX(mj) '' elsif isX(m) ">=#{mj}.0.0 <#{(mj.to_i + 1)}.0.0" elsif isX(p) if mj == '0' ">=#{mj}.#{m}.0 <#{mj}.#{(m.to_i + 1)}.0" else ">=#{mj}.#{m}.0 <#{(mj.to_i + 1)}.0.0" end elsif pr if pr[0] != '-' pr = "-#{pr}" end if mj == '0' if m == '0' ">=#{mj}.#{m}.#{p}#{pr} <#{mj}.#{m}.#{(p.to_i + 1)}" else ">=#{mj}.#{m}.#{p}#{pr} <#{mj}.#{(m.to_i + 1)}.0" end else ">=#{mj}.#{m}.#{p}#{pr} <#{(mj.to_i + 1)}.0.0" end else if mj == '0' if m == '0' ">=#{mj}.#{m}.#{p} <#{mj}.#{m}.#{(p.to_i + 1)}" else ">=#{mj}.#{m}.#{p} <#{mj}.#{(m.to_i + 1)}.0" end else ">=#{mj}.#{m}.#{p} <#{(mj.to_i + 1)}.0.0" end end end end def replace_tildes(comp, loose) comp.strip.split(/\s+/).map do |comp| replace_tilde(comp, loose) end.join(' ') end def replace_tilde(comp, loose) comp.gsub(loose ? TILDELOOSE : TILDE) do match = Regexp.last_match mj = match[1] m = match[2] p = match[3] pr = match[4] if isX(mj) '' elsif isX(m) ">=#{mj}.0.0 <#{(mj.to_i + 1)}.0.0" elsif isX(p) if ['Rubygems', 'Packagist'].include?(platform) ">=#{mj}.#{m}.0 <#{mj.to_i+1}.0.0" else ">=#{mj}.#{m}.0 <#{mj}.#{(m.to_i + 1)}.0" end elsif pr pr = '-' + pr if (pr[0] != '-') ">=#{mj}.#{m}.#{p}#{pr} <#{mj}.#{(m.to_i + 1)}.0" else ">=#{mj}.#{m}.#{p} <#{mj}.#{(m.to_i + 1)}.0" end end end def replace_x_ranges(comp, loose) comp.strip.split(/\s+/).map do |comp| replace_x_range(comp, loose) end.join(' ') end def replace_x_range(comp, loose) comp = comp.strip comp.gsub(loose ? XRANGELOOSE : XRANGE) do match = Regexp.last_match ret = match[0] gtlt = match[1] mj = match[2] m = match[3] p = match[4] pr = match[5] xM = isX(mj) xm = xM || isX(m) xp = xm || isX(p) anyX = xp gtlt = '' if gtlt == '=' && anyX if xM if gtlt == '>' || gtlt == '<' '<0.0.0' else '*' end elsif !gtlt.nil? && gtlt != '' && anyX m = 0 if xm p = 0 if xp if gtlt == '>' gtlt = '>=' if xm mj = mj.to_i + 1 m = 0 p = 0 elsif xp m = m.to_i + 1 p = 0 end elsif gtlt == '<=' gtlt = '<' if xm mj = mj.to_i + 1 else m = m.to_i + 1 end end "#{gtlt}#{mj}.#{m}.#{p}" elsif xm ">=#{mj}.0.0 <#{(mj.to_i + 1)}.0.0" elsif xp ">=#{mj}.#{m}.0 <#{mj}.#{(m.to_i + 1)}.0" else ret end end end def replace_stars(comp, loose) comp.strip.gsub(STAR, '') end def hyphen_replace(match) from = match[1] fM = match[2] fm = match[3] fp = match[4] fpr = match[5] fb = match[6] to = match[7] tM = match[8] tm = match[9] tp = match[10] tpr = match[11] tb = match[12] if isX(fM) from = '' elsif isX(fm) from = ">=#{fM}.0.0" elsif isX(fp) from = ">=#{fM}.#{fm}.0" else from = ">=#{from}" end if isX(tM) to = '' elsif isX(tm) to = "<#{(tM.to_i + 1)}.0.0" elsif isX(tp) to = "<#{tM}.#{(tm.to_i + 1)}.0" elsif tpr to = "<=#{tM}.#{tm}.#{tp}-#{tpr}" else to = "<=#{to}" end "#{from} #{to}".strip end def intersects(range, loose: false, platform: nil) range = Range.new(range, loose: loose, platform: platform) @set.any? do |comparators| comparators.all? do |comparator| comparator.satisfies_range(range, loose: loose, platform: platform) end end end end end semantic-range-3.0.0/lib/semantic_range/pre_release.rb0000644000175000017500000000321614101556632021470 0ustar srudsrudmodule SemanticRange class PreRelease attr_reader :parts def initialize(input) @parts = parse(input) end def parse(str) str.to_s.split('.').map { |id| convert(id) } end def convert(str) str.match(/^[0-9]+$/) ? str.to_i : str end def length parts.length end def empty? parts.empty? end def to_s parts.join '.' end def clear! @parts = [] end def zero! @parts = [0] end def <=>(other) return unless other.is_a?(self.class) return -1 if parts.any? && !other.parts.any? return 1 if !parts.any? && other.parts.any? return 0 if !parts.any? && !other.parts.any? i = 0 while true a = parts[i] b = other.parts[i] if a.nil? && b.nil? return 0 elsif b.nil? return 1 elsif a.nil? return -1 elsif a == b else return Version.compare_identifiers(a, b) end i += 1 end end def last_number_index parts.rindex { |e| e.is_a? Fixnum } end def increment!(identifier = nil) if empty? zero! else if last_number_index @parts[last_number_index] += 1 else @parts << 0 end end if identifier # 1.2.0-beta.1 bumps to 1.2.0-beta.2, # 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 if parts[0] == identifier unless parts[1].kind_of?(Fixnum) @parts = [identifier, 0] end else @parts = [identifier, 0] end end end end end semantic-range-3.0.0/lib/semantic_range.rb0000644000175000017500000002157314101556632017210 0ustar srudsrudrequire "semantic_range/version" require "semantic_range/pre_release" require "semantic_range/range" require "semantic_range/comparator" module SemanticRange BUILDIDENTIFIER = /[0-9A-Za-z-]+/ BUILD = /(?:\+(#{BUILDIDENTIFIER.source}(?:\.#{BUILDIDENTIFIER.source})*))/ NUMERICIDENTIFIER = /0|[1-9]\d*/ NUMERICIDENTIFIERLOOSE = /[0-9]+/ NONNUMERICIDENTIFIER = /\d*[a-zA-Z-][a-zA-Z0-9-]*/ XRANGEIDENTIFIERLOOSE = /#{NUMERICIDENTIFIERLOOSE.source}|x|X|\*/ PRERELEASEIDENTIFIERLOOSE = /(?:#{NUMERICIDENTIFIERLOOSE.source}|#{NONNUMERICIDENTIFIER.source})/ PRERELEASELOOSE = /(?:-?(#{PRERELEASEIDENTIFIERLOOSE.source}(?:\.#{PRERELEASEIDENTIFIERLOOSE.source})*))/ XRANGEPLAINLOOSE = /[v=\s]*(#{XRANGEIDENTIFIERLOOSE.source})(?:\.(#{XRANGEIDENTIFIERLOOSE.source})(?:\.(#{XRANGEIDENTIFIERLOOSE.source})(?:#{PRERELEASELOOSE.source})?#{BUILD.source}?)?)?/ HYPHENRANGELOOSE = /^\s*(#{XRANGEPLAINLOOSE.source})\s+-\s+(#{XRANGEPLAINLOOSE.source})\s*$/ PRERELEASEIDENTIFIER = /(?:#{NUMERICIDENTIFIER.source}|#{NONNUMERICIDENTIFIER.source})/ PRERELEASE = /(?:-(#{PRERELEASEIDENTIFIER.source}(?:\.#{PRERELEASEIDENTIFIER.source})*))/ XRANGEIDENTIFIER = /#{NUMERICIDENTIFIER.source}|x|X|\*/ XRANGEPLAIN = /[v=\s]*(#{XRANGEIDENTIFIER.source})(?:\.(#{XRANGEIDENTIFIER.source})(?:\.(#{XRANGEIDENTIFIER.source})(?:#{PRERELEASE.source})?#{BUILD.source}?)?)?/ HYPHENRANGE = /^\s*(#{XRANGEPLAIN.source})\s+-\s+(#{XRANGEPLAIN.source})\s*$/ MAINVERSIONLOOSE = /(#{NUMERICIDENTIFIERLOOSE.source})\.(#{NUMERICIDENTIFIERLOOSE.source})\.(#{NUMERICIDENTIFIERLOOSE.source})/ LOOSEPLAIN = /[v=\s]*#{MAINVERSIONLOOSE.source}#{PRERELEASELOOSE.source}?#{BUILD.source}?/ GTLT = /((?:<|>)?=?)/ COMPARATORTRIM = /(\s*)#{GTLT.source}\s*(#{LOOSEPLAIN.source}|#{XRANGEPLAIN.source})/ LONETILDE = /(?:~>?)/ TILDETRIM = /(\s*)#{LONETILDE.source}\s+/ LONECARET = /(?:\^)/ CARETTRIM = /(\s*)#{LONECARET.source}\s+/ STAR = /(<|>)?=?\s*\*/ CARET = /^#{LONECARET.source}#{XRANGEPLAIN.source}$/ CARETLOOSE = /^#{LONECARET.source}#{XRANGEPLAINLOOSE.source}$/ MAINVERSION = /(#{NUMERICIDENTIFIER.source})\.(#{NUMERICIDENTIFIER.source})\.(#{NUMERICIDENTIFIER.source})/ FULLPLAIN = /v?#{MAINVERSION.source}#{PRERELEASE.source}?#{BUILD.source}?/ FULL = /^#{FULLPLAIN.source}$/ LOOSE = /^#{LOOSEPLAIN.source}$/ TILDE = /^#{LONETILDE.source}#{XRANGEPLAIN.source}$/ TILDELOOSE = /^#{LONETILDE.source}#{XRANGEPLAINLOOSE.source}$/ XRANGE = /^#{GTLT.source}\s*#{XRANGEPLAIN.source}$/ XRANGELOOSE = /^#{GTLT.source}\s*#{XRANGEPLAINLOOSE.source}$/ COMPARATOR = /^#{GTLT.source}\s*(#{FULLPLAIN.source})$|^$/ COMPARATORLOOSE = /^#{GTLT.source}\s*(#{LOOSEPLAIN.source})$|^$/ ANY = {} MAX_LENGTH = 256 class InvalidIncrement < StandardError; end class InvalidVersion < StandardError; end class InvalidComparator < StandardError; end class InvalidRange < StandardError; end def self.ltr?(version, range, loose: false, platform: nil) outside?(version, range, '<', loose: loose, platform: platform) end def self.gtr?(version, range, loose: false, platform: nil) outside?(version, range, '>', loose: loose, platform: platform) end def self.cmp(a, op, b, loose: false) case op when '===' a = a.version if !a.is_a?(String) b = b.version if !b.is_a?(String) a == b when '!==' a = a.version if !a.is_a?(String) b = b.version if !b.is_a?(String) a != b when '', '=', '==' eq?(a, b, loose: loose) when '!=' neq?(a, b, loose: loose) when '>' gt?(a, b, loose: loose) when '>=' gte?(a, b, loose: loose) when '<' lt?(a, b, loose: loose) when '<=' lte?(a, b, loose: loose) else raise 'Invalid operator: ' + op end end def self.outside?(version, range, hilo, loose: false, platform: nil) version = Version.new(version, loose: loose) range = Range.new(range, loose: loose, platform: platform) return false if satisfies?(version, range, loose: loose, platform: platform) case hilo when '>' comp = '>' ecomp = '>=' when '<' comp = '<' ecomp = '<=' end range.set.each do |comparators| high = nil low = nil comparators.each do |comparator| if comparator.semver == ANY comparator = Comparator.new('>=0.0.0', loose) end high = high || comparator low = low || comparator case hilo when '>' if gt?(comparator.semver, high.semver, loose: loose) high = comparator elsif lt?(comparator.semver, low.semver, loose: loose) low = comparator end when '<' if lt?(comparator.semver, high.semver, loose: loose) high = comparator elsif gt?(comparator.semver, low.semver, loose: loose) low = comparator end end end return false if (high.operator == comp || high.operator == ecomp) case hilo when '>' if (low.operator.empty? || low.operator == comp) && lte?(version, low.semver, loose: loose) return false; elsif (low.operator == ecomp && lt?(version, low.semver, loose: loose)) return false; end when '<' if (low.operator.empty? || low.operator == comp) && gte?(version, low.semver, loose: loose) return false; elsif low.operator == ecomp && gt?(version, low.semver, loose: loose) return false; end end end true end def self.satisfies?(version, range, loose: false, platform: nil) return false if !valid_range(range, loose: loose, platform: platform) Range.new(range, loose: loose, platform: platform).test(version) end def self.filter(versions, range, loose: false, platform: nil) return [] if !valid_range(range, loose: loose, platform: platform) versions.filter { |v| SemanticRange.satisfies?(v, range, loose: loose, platform: platform) } end def self.max_satisfying(versions, range, loose: false, platform: nil) versions.select { |version| satisfies?(version, range, loose: loose, platform: platform) }.sort { |a, b| rcompare(a, b, loose: loose) }[0] || nil end def self.valid_range(range, loose: false, platform: nil) begin r = Range.new(range, loose: loose, platform: platform).range r = '*' if r.nil? || r.empty? r rescue nil end end def self.compare(a, b, loose: false) Version.new(a, loose: loose).compare(b) end def self.compare_loose(a, b) compare(a, b, loose: true) end def self.rcompare(a, b, loose: false) compare(b, a, loose: true) end def self.sort(list, loose: false) # TODO end def self.rsort(list, loose: false) # TODO end def self.lt?(a, b, loose: false) compare(a, b, loose: loose) < 0 end def self.gt?(a, b, loose: false) compare(a, b, loose: loose) > 0 end def self.eq?(a, b, loose: false) compare(a, b, loose: loose) == 0 end def self.neq?(a, b, loose: false) compare(a, b, loose: loose) != 0 end def self.gte?(a, b, loose: false) compare(a, b, loose: loose) >= 0 end def self.lte?(a, b, loose: false) compare(a, b, loose: loose) <= 0 end def self.valid(version, loose: false) v = parse(version, loose: loose) return v ? v.version : nil end def self.clean(version, loose: false) s = parse(version.strip.gsub(/^[=v]+/, ''), loose: loose) return s ? s.version : nil end def self.parse(version, loose: false) return version if version.is_a?(Version) return nil unless version.is_a?(String) stripped_version = version.strip return nil if stripped_version.length > MAX_LENGTH rxp = loose ? LOOSE : FULL return nil if !rxp.match(stripped_version) Version.new(stripped_version, loose: loose) end def self.increment!(version, release, identifier, loose: false) Version.new(version, loose: loose).increment!(release, identifier).version rescue InvalidIncrement, InvalidVersion nil end def self.diff(a, b) a = Version.new(a, loose: false) unless a.kind_of?(Version) b = Version.new(b, loose: false) unless b.kind_of?(Version) pre_diff = a.prerelease.to_s != b.prerelease.to_s pre = pre_diff ? 'pre' : '' return "#{pre}major" if a.major != b.major return "#{pre}minor" if a.minor != b.minor return "#{pre}patch" if a.patch != b.patch return "prerelease" if pre_diff end def self.to_comparators(range, loose: false, platform: nil) Range.new(range, loose: loose, platform: platform).set.map do |comp| comp.map(&:to_s) end end class << self # Support for older non-inquisitive method versions alias_method :gt, :gt? alias_method :gtr, :gtr? alias_method :gte, :gte? alias_method :lt, :lt? alias_method :ltr, :ltr? alias_method :lte, :lte? alias_method :eq, :eq? alias_method :neq, :neq? alias_method :outside, :outside? alias_method :satisfies, :satisfies? end end semantic-range-3.0.0/README.md0000644000175000017500000000463114101556632014411 0ustar srudsrud# SemanticRange [node-semver](https://github.com/npm/node-semver) written in Ruby for comparison and inclusion of semantic versions and ranges. **NOTE: current `master` and releases `>= 3` use keyword arguments instead of positional arguments to pass options. If you used `SemanticRange.compare(a, b, true)` in SemanticRange 2, in SemanticRange 3 use `SemanticRange.compare(a, b, loose: true)`** ## Installation Add this line to your application's Gemfile: ```ruby gem 'semantic_range' ``` And then execute: $ bundle Or install it yourself as: $ gem install semantic_range ## Usage ```ruby SemanticRange.valid('1.2.3') # '1.2.3' SemanticRange.valid('a.b.c') # nil SemanticRange.clean(' =v1.2.3 ') # '1.2.3' SemanticRange.filter(['0.9.0', '1.3.0', '1.5.0', '2.0.5'], '1.3.0 || <1.0.0 || >2.0.0') # ['0.9.0', '1.3.0', '2.0.5'] SemanticRange.satisfies?('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') # true SemanticRange.gt?('1.2.3', '9.8.7') # false SemanticRange.lt?('1.2.3', '9.8.7') # true ``` ### Options All functions support optional keyword arguments that modify default behavior. The options supported are: * `loose` Be more forgiving about not-quite-valid semver strings. Any resulting output will always be 100% strict compliant. `false` by default. Some functions support `platform` option: * `platform` Changes behavior for `'Rubygems'` and `'Packagist'`. `nil` by default. See https://github.com/librariesio/semantic_range/issues/59 ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/librariesio/semantic_range. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://www.contributor-covenant.org/) code of conduct. ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). semantic-range-3.0.0/LICENSE.txt0000644000175000017500000000207114101556632014751 0ustar srudsrudThe MIT License (MIT) Copyright (c) 2016 Andrew Nesbitt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.