Ruby Gems 与 Bundler

Gem 是 Ruby 的(library),Bundler 是项目级依赖管理工具。Ruby 生态之所以繁荣,正是因为 rubygems.org 上有十几万个 gem——Rails、Sinatra、Sidekiq、Devise 全是 gem。本章讲清楚:怎么装、怎么管、怎么锁定版本。

1. gem 命令:系统级包管理

gem 是 Ruby 自带的命令,用来装/卸/查 gem:

# gem 是 Ruby 的包管理命令,gem 包 = 一个 Ruby 库
# Ruby 默认从 rubygems.org 下载

# 安装最新版
gem install rails

# 安装指定版本
gem install rails -v 7.0.0
gem install rails -v '~> 7.0'    # >= 7.0 且 < 7.1(乐观约束)

# 查看已装 gem
gem list                  # 列出全部
gem list rails            # 只看含 rails 的

# 卸载
gem uninstall rails

# 查看远程可下载的版本
gem list rails --remote --all

# 查看 gem 安装位置
gem which rails

# 换源(国内常用)
gem sources --remove https://rubygems.org/
gem sources -a https://gems.ruby-china.com/

国内访问 rubygems.org 慢,可以换 ruby-china 镜像。注意 gem install 装的是全局 gem——多个项目共用同一版本,容易冲突。这就是 Bundler 要解决的问题。

2. Gemfile:项目依赖清单

每个 Ruby 项目根目录放一个 Gemfile,声明依赖:

# Gemfile:项目依赖清单,放在项目根目录
# 声明"这个项目要哪些 gem、要什么版本"

source 'https://rubygems.org'    # 或国内镜像

ruby '3.2.2'                     # 指定 Ruby 版本(可选)

# 主依赖
gem 'rails', '~> 7.0'            # pessimistic 约束:>= 7.0 且 < 8.0
gem 'pg', '>= 1.0'               # 最低版本
gem 'puma', '5.6.4'              # 精确版本

# 分组:不同环境用不同 gem
group :development, :test do
  gem 'rspec'
  gem 'pry'                      # irb 增强版
  gem 'rubocop'                  # 风格检查
end

group :production do
  gem 'newrelic_rpm'             # 监控
end

# 从 git 安装(分支/tag/ref)
gem 'sidekiq', github: 'sidekiq/sidekiq', branch: 'main'

# 从本地路径安装(开发自己的 gem 时)
gem 'my_gem', path: '../my_gem'

group 把开发依赖和生产依赖分开——生产环境不会装 rspec/rubocop,部署更快、攻击面更小。这是 Rails 项目的标配。

3. 版本约束符号

Gemfile 里版本约束的符号很多,新手容易晕。重点记住悲观约束 ~>:

# 版本约束符号速查

gem 'rails'              # 不写 = 任意版本(不推荐)
gem 'rails', '7.0.0'     # 精确版本
gem 'rails', '= 7.0.0'   # 同上(显式)
gem 'rails', '> 6.0'     # 大于 6.0
gem 'rails', '>= 6.0'    # 大于等于
gem 'rails', '< 7.0'     # 小于
gem 'rails', '<= 7.0'    # 小于等于
gem 'rails', '!= 5.0'    # 不等于

gem 'rails', '~> 7.0.0'  # 乐观约束(pessimistic)
# 含义:>= 7.0.0 且 < 7.1.0(允许最后一位涨,前面固定)
gem 'rails', '~> 7.0'    # 含义:>= 7.0 且 < 8.0(允许末两位涨)

# 经验:用 ~> 比较安全
#   gem 'rails', '~> 7.0'  会接受 7.0~7.x(补丁更新),不接受 8.0
#   gem 'rails', '~> 7.0.0'  只接受 7.0.x(更保守)

口诀:~> X.Y 允许 Y 涨,不允许 X 涨;~> X.Y.Z 只允许 Z 涨。它比 >= 保守(避免主版本升级带来破坏),比 = 灵活(能拿到补丁修复)。

4. Bundler:锁定版本,环境一致

# bundler 是项目级依赖管理工具
# 它根据 Gemfile 装依赖,锁定版本

bundle install       # 按 Gemfile 装依赖(生成 Gemfile.lock)
bundle update        # 升级所有 gem(在约束范围内)
bundle update rails  # 只升级指定 gem
bundle exec rspec    # 在当前 bundle 环境下运行命令
bundle outdated      # 列出有过时 gem
bundle check         # 检查依赖是否都装好
bundle info rails    # 查看 gem 信息
bundle clean         # 删除未使用的 gem

# bundle exec 是关键
# 它确保用 Gemfile.lock 里锁定的版本运行命令
# 而不是系统全局版本(避免"我这能跑你那不能跑"的问题)

bundle exec 是新手必知的关键命令——它确保用 Gemfile.lock 里锁定的版本运行程序,而不是系统里碰巧存在的版本。"我这能跑你那不能跑"的 99% 都是因为没用 bundle exec。

5. Gemfile.lock:版本锁

bundle install 会生成 Gemfile.lock,记录每个 gem 装的精确版本依赖树:

# Gemfile.lock:bundler 自动生成,锁定精确版本
# 不要手动编辑,但应该 commit 进 git

GEM
  remote: https://rubygems.org/
  specs:
    actioncable (7.0.4)
      actionpack (= 7.0.4)
      ...
    rails (7.0.4)
      actioncable (= 7.0.4)
      ...

PLATFORMS
  ruby

DEPENDENCIES
  rails (~> 7.0)

BUNDLED WITH
  2.3.7

# 意义:任何人 clone 项目 + bundle install
# 都会装完全一样的版本,环境一致

原则:Gemfile.lock 必须 commit 进 git(Gemfile 也要)。这样别人 clone 后 bundle install 会装完全一样的版本,彻底解决环境不一致问题。这和 Node 的 package-lock.json、Python 的 poetry.lock 是同一思路。

6. 自己发布一个 gem

写完一个有用的库,可以打包成 gem 发布到 rubygems.org,让全世界用:

# .gemspec:发布自己的 gem 时用的元信息文件
# 文件名通常 <gem_name>.gemspec

Gem::Specification.new do |spec|
  spec.name        = "my_gem"
  spec.version     = "0.1.0"
  spec.summary     = "一个示例 gem"
  spec.description = "更长的描述..."
  spec.authors     = ["Your Name"]
  spec.email       = "you@example.com"
  spec.homepage    = "https://github.com/you/my_gem"
  spec.license     = "MIT"

  spec.files       = Dir["lib/**/*.rb"]   # 打包哪些文件
  spec.require_paths = ["lib"]

  # 运行时依赖
  spec.add_dependency "json", "~> 2.0"

  # 开发时依赖(不会被使用者装)
  spec.add_development_dependency "rspec", "~> 3.0"
end

# 构建 + 发布
gem build my_gem.gemspec    # 生成 my_gem-0.1.0.gem
gem push my_gem-0.1.0.gem   # 发布到 rubygems.org

发布前用 bundle gem my_gem 命令生成项目骨架(自带 gemspec、rspec、CI 配置),省去手写。发布后再 gem push 就上线了——rubygems.org 是公开的,任何人都能 gem install

7. 常见问题

小结

本章覆盖 Ruby 包管理的全流程:gem 命令管全局,Gemfile 声明项目依赖,bundle exec 保证版本一致,Gemfile.lock 锁住环境。下一篇看文件与 IO——读写文件、目录操作、CSV 处理、异常处理。

← 上一篇 Ruby 模块

下一篇 Ruby 文件与 IO

✈️💬