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. 常见问题
- 依赖冲突:两个 gem 要求同一个 gem 不同版本。
bundle install会自动求解,解不出就报错,改 Gemfile 调整约束。 - Bundler 版本:lock 文件底部
BUNDLED WITH记录生成它的 bundler 版本,bundler 会自动切到该版本运行。 - bundle exec 太长:可以装
direnv或配 Rakefile 简化,或用binstubs(bundle binstubs rspec生成bin/rspec)。
小结
本章覆盖 Ruby 包管理的全流程:gem 命令管全局,Gemfile 声明项目依赖,bundle exec 保证版本一致,Gemfile.lock 锁住环境。下一篇看文件与 IO——读写文件、目录操作、CSV 处理、异常处理。
← 上一篇 Ruby 模块
下一篇 Ruby 文件与 IO →