Gem.latest_version_for(name) 方法的奇怪结果
Strange results for the Gem.latest_version_for(name) method
我正在开发一个 gem 相关的实用程序,我在使用 Gem.latest_version_for 方法时观察到奇怪的结果。以下是 irb 下的一些观察:
irb(main):001:0> Gem.latest_version_for('rails').to_s
=> "5.2.2"
irb(main):002:0> Gem.latest_version_for('gosu').to_s
=> "0.7.38"
注意第一行如何获得 rails 的正确版本,在我写这篇文章时是 5.2.2,并通过 rubygems.org 检查确认了这一点。 gosu gem returns 0.7.38 的查询是错误的。正确答案应该是 0.14.4
我无法解释这里发生的事情。
我可以确认我的主机是 https://rubygems.org 和
C:\Sites\mysh
8 mysh>ruby --version
ruby 2.3.3p222 (2016-11-21 revision 56859) [i386-mingw32]
C:\Sites\mysh
9 mysh>gem --version
2.5.2
可用于 i386-mingw32
平台的最新版本是 0.7.38。您会注意到这与您报告的 ruby 版本一致。
https://rubygems.org/gems/gosu/versions
latest_version_for
调用 latest_spec_for
,后者调用 Gem::SpecFetcher.spec_for_dependency
,仅将 gem 的名称作为参数。 spec_for_dependency
采用另一个参数,matching_platform
,默认为 true。
看起来 latest_version_for
通过该链限定为您当前的平台,默认值为 matching_platform
。 gem install
命令可能会将 i386/x386 视为 same/equivalent 并允许它们。
if matching_platform is false, gems for all platforms are returned
您应该能够镜像 latest_spec_for
方法并传入 multi_platform
参数以覆盖。像
dependency = Gem::Dependency.new name
fetcher = Gem::SpecFetcher.fetcher
spec_tuples, _ = fetcher.spec_for_dependency dependency, true # true added here
在 Jay Dorsey 的大力帮助下,我想我在这里取得了一些进步。我需要说的内容太多,不适合发表评论,而且是对有关奇怪行为的问题的实际答案。好吧,至少我很确定它是。
如上所述:latest_version_for调用latest_spec_for,后者调用Gem::SpecFetcher.spec_for_dependency。
关键是该方法随后调用 Gem::SpecFetcher.search_for_dependency。这是一个长篇大论的方法。我想关注获得规格后出现的一行:
tuples = tuples.sort_by { |x| x[0] }
这对元组进行排序,这些元组是 [spec, source] 数组的数组。它按升序 version/platform 对它们进行排序(据我所知)
现在我们 return Gem class 方法 latest_spec_for(name) 尤其是行:
spec, = spec_tuples.first
这会获取第一个子数组并保留规范并丢弃源。
注意它抓取了第一个元素。版本号最低的那个。这通常不是问题,因为对于绝大多数 gem 来说,只会出现一个规范。 gosu gem 并非如此。由于 gosu 包含特定于平台的代码,因此这里有三个。它似乎抓住了两个 Gem 平台("ruby" 和 "x86-mingw32")以及 ruby 平台(i386-mingw32)的规格。
为了测试我的想法,我创建了文件 glmp.rb(获取最后一个猴子补丁)它是:
# The latest_spec_for(name) monkey patch.
module Gem
# Originally in File rubygems.rb at line 816
def self.latest_spec_for(name)
dependency = Gem::Dependency.new name
fetcher = Gem::SpecFetcher.fetcher
spec_tuples, = fetcher.spec_for_dependency dependency
spec_tuples[-1][0]
end
end
现在我知道猴子补丁是不受欢迎的,但现在这只是为了测试这个想法。这是我的结果:
36 mysh>=Gem.latest_version_for('gosu')
Gem::Version.new("0.7.38")
C:\Sites\ideas\gem_usage
37 mysh>ls
gem_latest.rb gem_usage.rb glmp.rb
C:\Sites\ideas\gem_usage
39 mysh>=require './glmp'
true
C:\Sites\ideas\gem_usage
40 mysh>=Gem.latest_version_for('gosu')
Gem::Version.new("0.14.4")
虽然我现在可以使用这个 hack 来解决我的问题,但我想我会提出一个问题 rubygems 提出这个问题。
我正在开发一个 gem 相关的实用程序,我在使用 Gem.latest_version_for 方法时观察到奇怪的结果。以下是 irb 下的一些观察:
irb(main):001:0> Gem.latest_version_for('rails').to_s
=> "5.2.2"
irb(main):002:0> Gem.latest_version_for('gosu').to_s
=> "0.7.38"
注意第一行如何获得 rails 的正确版本,在我写这篇文章时是 5.2.2,并通过 rubygems.org 检查确认了这一点。 gosu gem returns 0.7.38 的查询是错误的。正确答案应该是 0.14.4
我无法解释这里发生的事情。
我可以确认我的主机是 https://rubygems.org 和
C:\Sites\mysh
8 mysh>ruby --version
ruby 2.3.3p222 (2016-11-21 revision 56859) [i386-mingw32]
C:\Sites\mysh
9 mysh>gem --version
2.5.2
可用于 i386-mingw32
平台的最新版本是 0.7.38。您会注意到这与您报告的 ruby 版本一致。
https://rubygems.org/gems/gosu/versions
latest_version_for
调用 latest_spec_for
,后者调用 Gem::SpecFetcher.spec_for_dependency
,仅将 gem 的名称作为参数。 spec_for_dependency
采用另一个参数,matching_platform
,默认为 true。
看起来 latest_version_for
通过该链限定为您当前的平台,默认值为 matching_platform
。 gem install
命令可能会将 i386/x386 视为 same/equivalent 并允许它们。
if matching_platform is false, gems for all platforms are returned
您应该能够镜像 latest_spec_for
方法并传入 multi_platform
参数以覆盖。像
dependency = Gem::Dependency.new name
fetcher = Gem::SpecFetcher.fetcher
spec_tuples, _ = fetcher.spec_for_dependency dependency, true # true added here
在 Jay Dorsey 的大力帮助下,我想我在这里取得了一些进步。我需要说的内容太多,不适合发表评论,而且是对有关奇怪行为的问题的实际答案。好吧,至少我很确定它是。
如上所述:latest_version_for调用latest_spec_for,后者调用Gem::SpecFetcher.spec_for_dependency。
关键是该方法随后调用 Gem::SpecFetcher.search_for_dependency。这是一个长篇大论的方法。我想关注获得规格后出现的一行:
tuples = tuples.sort_by { |x| x[0] }
这对元组进行排序,这些元组是 [spec, source] 数组的数组。它按升序 version/platform 对它们进行排序(据我所知)
现在我们 return Gem class 方法 latest_spec_for(name) 尤其是行:
spec, = spec_tuples.first
这会获取第一个子数组并保留规范并丢弃源。
注意它抓取了第一个元素。版本号最低的那个。这通常不是问题,因为对于绝大多数 gem 来说,只会出现一个规范。 gosu gem 并非如此。由于 gosu 包含特定于平台的代码,因此这里有三个。它似乎抓住了两个 Gem 平台("ruby" 和 "x86-mingw32")以及 ruby 平台(i386-mingw32)的规格。
为了测试我的想法,我创建了文件 glmp.rb(获取最后一个猴子补丁)它是:
# The latest_spec_for(name) monkey patch.
module Gem
# Originally in File rubygems.rb at line 816
def self.latest_spec_for(name)
dependency = Gem::Dependency.new name
fetcher = Gem::SpecFetcher.fetcher
spec_tuples, = fetcher.spec_for_dependency dependency
spec_tuples[-1][0]
end
end
现在我知道猴子补丁是不受欢迎的,但现在这只是为了测试这个想法。这是我的结果:
36 mysh>=Gem.latest_version_for('gosu')
Gem::Version.new("0.7.38")
C:\Sites\ideas\gem_usage
37 mysh>ls
gem_latest.rb gem_usage.rb glmp.rb
C:\Sites\ideas\gem_usage
39 mysh>=require './glmp'
true
C:\Sites\ideas\gem_usage
40 mysh>=Gem.latest_version_for('gosu')
Gem::Version.new("0.14.4")
虽然我现在可以使用这个 hack 来解决我的问题,但我想我会提出一个问题 rubygems 提出这个问题。