Bundler作为Ruby应用的依赖管理工具,它的核心功能是安装,隔离你项目中的Gem,并且通过依赖图解决不同Gem中的依赖关系。但初次之外Bundler还做了其他重要的事。在Gemfile中指定的gem的代码是如何被添加进Rails的应用的呢?
通过查看 bin/rails
#!/usr/bin/env ruby
begin
load File.expand_path("../spring", __FILE__)
rescue LoadError
end
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'
你会看到,它通过 加载 ../config/boot来加载 Rails,那让我们再看看 config/boot文件。
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
Bundler在这个文件中被使用,(同时在这里你可以通过设置不同的环境变量来使用不同的Gemfile文件)。
bundler/setup做了这几件事
- 它从$LOAD_PATH中移除了所有gem的路径
- 然后,它将所有Gemfile.lock中的gem的路径添加回$LOAD_PATH
这样你就只能require,那些Gemfile中指定的gem了。
那么,所有你需要的Gem,都已经添加到你的加载路径当中了,但是当你只使用 RubyGems的话,你仍然需要require你需要的文件,当你使用Rails和bundler时为什么不需要require gem包呢。
来快速的看一下 config/application.rb这个文件,当rails启动时:
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
又是Bundler!,Bundler.require require 所有你在Gemfile中的分组中的Gem。
Rails.groups会返回哪些分组呢?
# Returns all rails groups for loading based on:
#
# * The Rails environment;
# * The environment variable RAILS_GROUPS;
# * The optional envs given as argument and the hash with group dependencies;
#
# groups assets: [:development, :test]
#
# # Returns
# # => [:default, :development, :assets] for Rails.env == "development"
# # => [:default, :production] for Rails.env == "production"
def groups(*groups)
hash = groups.extract_options!
env = Rails.env
groups.unshift(:default, env)
groups.concat ENV["RAILS_GROUPS"].to_s.split(",")
groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) }
groups.compact!
groups.uniq!
groups
end
从上面可以看出,当你的Rails应用运行在开发模式下Rails.groups就会是 [:default, :development],同理生成模式下是[:default, :production]。
所以,Bundler会在Gemfile中找到这些指定的分支,然后使用require加载每一个Gem,如果Gemfile中有faker,它就会require "faker",这就是为什么你不是需要手动加载Gem,它们就可以在Rails中使用的原因。