模块 Kernel
私有实例方法
gem(gem_name, *requirements) 点击切换源代码
使用 Kernel#gem
来激活 gem_name
的特定版本。
requirements
是指定的 gem 必须匹配的版本要求列表,最常见的是 “= example.version.number”。有关如何指定版本要求,请参阅 Gem::Requirement
。
如果要激活 gem 的最新版本,则无需调用 Kernel#gem
,Kernel#require
会为您做正确的事情。
如果 gem 已激活,Kernel#gem
返回 true,否则返回 false。如果找不到 gem,不符合版本要求,或者已激活了不同的版本,则会引发异常。
应该在任何 require 语句之前调用 Kernel#gem
(否则 RubyGems 可能会加载冲突的库版本)。
Kernel#gem
仅在给出预发布版 requirements
时才加载预发布版本
gem 'rake', '>= 1.1.a', '< 2'
在较旧的 RubyGems 版本中,可以使用环境变量 GEM_SKIP 来跳过指定 gem 的激活,例如,测试尚未安装的更改。现在,RubyGems 会推迟到 -I 和 RUBYLIB 环境变量,以跳过 gem 的激活。
示例
GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
# File rubygems/core_ext/kernel_gem.rb, line 35 def gem(gem_name, *requirements) # :doc: skip_list = (ENV["GEM_SKIP"] || "").split(/:/) raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name if gem_name.is_a? Gem::Dependency unless Gem::Deprecate.skip warn "#{Gem.location_of_caller.join ":"}:Warning: Kernel.gem no longer "\ "accepts a Gem::Dependency object, please pass the name "\ "and requirements directly" end requirements = gem_name.requirement gem_name = gem_name.name end dep = Gem::Dependency.new(gem_name, *requirements) loaded = Gem.loaded_specs[gem_name] return false if loaded && dep.matches_spec?(loaded) spec = dep.to_spec if spec if Gem::LOADED_SPECS_MUTEX.owned? spec.activate else Gem::LOADED_SPECS_MUTEX.synchronize { spec.activate } end end end
require(path) 点击切换源代码
当 require RubyGems 时,Kernel#require
会被我们自己的版本替换,该版本能够按需加载 gem。
当您调用 require 'x'
时,会发生以下情况
-
如果可以从现有的 Ruby 加载路径加载该文件,则会加载。
-
否则,将在已安装的 gem 中搜索匹配的文件。如果它在 gem 'y' 中找到,则会激活该 gem(添加到加载路径)。
如果该文件已被加载,则会保留返回 false 的正常 require
功能。
# File rubygems/core_ext/kernel_require.rb, line 36 def require(path) # :doc: return gem_original_require(path) unless Gem.discover_gems_on_require RUBYGEMS_ACTIVATION_MONITOR.synchronize do path = File.path(path) # If +path+ belongs to a default gem, we activate it and then go straight # to normal require if spec = Gem.find_default_spec(path) name = spec.name next if Gem.loaded_specs[name] # Ensure -I beats a default gem resolved_path = begin rp = nil load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths Gem.suffixes.find do |s| $LOAD_PATH[0...load_path_check_index].find do |lp| if File.symlink? lp # for backward compatibility next end full_path = File.expand_path(File.join(lp, "#{path}#{s}")) rp = full_path if File.file?(full_path) end end rp end Kernel.send(:gem, name, Gem::Requirement.default_prerelease) unless resolved_path next end # If there are no unresolved deps, then we can use just try # normal require handle loading a gem from the rescue below. if Gem::Specification.unresolved_deps.empty? next end # If +path+ is for a gem that has already been loaded, don't # bother trying to find it in an unresolved gem, just go straight # to normal require. #-- # TODO request access to the C implementation of this to speed up RubyGems if Gem::Specification.find_active_stub_by_path(path) next end # Attempt to find +path+ in any unresolved gems... found_specs = Gem::Specification.find_in_unresolved path # If there are no directly unresolved gems, then try and find +path+ # in any gems that are available via the currently unresolved gems. # For example, given: # # a => b => c => d # # If a and b are currently active with c being unresolved and d.rb is # requested, then find_in_unresolved_tree will find d.rb in d because # it's a dependency of c. # if found_specs.empty? found_specs = Gem::Specification.find_in_unresolved_tree path found_specs.each(&:activate) # We found +path+ directly in an unresolved gem. Now we figure out, of # the possible found specs, which one we should activate. else # Check that all the found specs are just different # versions of the same gem names = found_specs.map(&:name).uniq if names.size > 1 raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ", "}" end # Ok, now find a gem that has no conflicts, starting # at the highest version. valid = found_specs.find {|s| !s.has_conflicts? } unless valid le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate" le.name = names.first raise le end valid.activate end end begin gem_original_require(path) rescue LoadError => load_error if load_error.path == path && RUBYGEMS_ACTIVATION_MONITOR.synchronize { Gem.try_activate(path) } return gem_original_require(path) end raise load_error end end