我正在用Ruby写一些需要比较版本以便确定是否需要更新的东西。
但是当我运行current_version <=> desired_version
并冻结了至少一个版本时,我得到:
4: from .../ruby/2.6.0/rubygems/version.rb:344:in `<=>'
3: from .../ruby/2.6.0/rubygems/version.rb:371:in `canonical_segments'
2: from .../ruby/2.6.0/rubygems/version.rb:393:in `_split_segments'
1: from .../ruby/2.6.0/rubygems/version.rb:387:in `_segments'
FrozenError (can't modify frozen Gem::Version)
根据the docs,源代码是这样的:
def <=>(other)
return unless Gem::Version === other
return 0 if @version == other._version || canonical_segments == other.canonical_segments
lhsegments = _segments
rhsegments = other._segments
lhsize = lhsegments.size
rhsize = rhsegments.size
limit = (lhsize > rhsize ? lhsize : rhsize) - 1
i = 0
while i <= limit
lhs, rhs = lhsegments[i] || 0, rhsegments[i] || 0
i += 1
next if lhs == rhs
return -1 if String === lhs && Numeric === rhs
return 1 if Numeric === lhs && String === rhs
return lhs <=> rhs
end
return 0
end
我不明白为什么这段代码会改变宝石的状态。有什么我想念的吗?
答案 0 :(得分:1)
该错误告诉您在哪里:<=>
方法调用canonical_segments
,后者调用_split_segments
,后者调用_segments
。因此,那是必须发生突变的地方;不直接使用您在帖子中复制的方法。
更具体地说,here's the offending source code:
def canonical_segments
@canonical_segments ||=
_split_segments.map! do |segments|
segments.reverse_each.drop_while {|s| s == 0 }.reverse
end.reduce(&:concat)
end
protected
def _version
@version
end
def _segments
# segments is lazy so it can pick up version values that come from
# old marshaled versions, which don't go through marshal_load.
# since this version object is cached in @@all, its @segments should be frozen
@segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
/^\d+$/ =~ s ? s.to_i : s
end.freeze
end
def _split_segments
string_start = _segments.index {|s| s.is_a?(String) }
string_segments = segments
numeric_segments = string_segments.slice!(0, string_start || string_segments.size)
return numeric_segments, string_segments
end