潜心研究Gem包装的内部情况
包管理器对大多数现代语言来说都是不可或缺的。包管理器使开发者能够创建和分发可用于引导复杂网络应用的构建块。在这篇文章中,我们将深入了解Rubygems的内部情况,Ruby编程语言的包管理器。
任何包管理器都有一个简单的任务:将代码分离成一个模块,并将其转换为一种格式,以便于在许多不同的环境中分发和安装。
宝石的剖析
一个 gem 至少由一个gemspec 文件和一个与软件包名称相同的 Ruby 文件组成。惯例是将这个Ruby文件放在一个lib 文件夹中:
-- hola
|
-- hola.gemspec
|
-- lib
|
-- hola.rb
gemspec 文件用于列出 gem 的规格。gemspec必须指定 gem 的作者、文件、名称和摘要以及版本。另外,你还可以指定运行时和开发依赖。这些规格将在构建和安装 gem 时使用:
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
require "rspec/version"
Gem::Specification.new do |s|
s.name = "rspec"
s.version = RSpec::Version::STRING
s.platform = Gem::Platform::RUBY
s.license = "MIT"
s.authors = ["Steven Baker", "David Chelimsky", "Myron Marston"]
s.email = "rspec@googlegroups.com"
s.homepage = "http://github.com/rspec"
s.summary = "rspec-#{RSpec::Version::STRING}"
s.description = "BDD for Ruby"
s.metadata = {
'bug_tracker_uri' => 'https://github.com/rspec/rspec/issues',
'documentation_uri' => 'https://rspec.info/documentation/',
'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/rspec',
'source_code_uri' => 'https://github.com/rspec/rspec',
}
s.files = `git ls-files -- lib/*`.split("\n")
s.files += ["LICENSE.md"]
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.extra_rdoc_files = [ "README.md" ]
s.rdoc_options = ["--charset=UTF-8"]
s.require_path = "lib"
end
gem构建
为了将 gem 代码库转换为可分发的软件包,Rubygems 提供了一个build 命令。该构建命令需要一个 gemspec 文件作为参数,并使用所提供的规范来创建.gem 文件,然后可以分发。
这个.gem 文件只是一个tarball文件,它又由三个.gz 文件组成:一个data.tar.gz 文件、一个checksum.yaml.gz 文件和一个metatdata.gz 文件。一个经过加密签名的宝石也会包含相应的包含签名的.sig 文件:
-- hola.gem
|
-- checksum.yaml.gz
|
-- metadata.gz
|
-- data.tar.gz
Rubygems使用一个Gem::Package::TarWriter 类来完成这项工作,该类根据gemspec中提供的信息和数据创建tarballs。metadata 文件是一个压缩的 YAML 文件,其中列出了 gemspec 中提供的信息:
--- !ruby/object:Gem::Specification
name: rspec
version: !ruby/object:Gem::Version
version: 3.9.0
platform: ruby
authors:
- Steven Baker
- David Chelimsky
- Myron Marston
~~~~~~~~~~~~~~~ removed for brevity ~~~~~~~~~~~~~~~~~~~~~~~~
dependencies:
- !ruby/object:Gem::Dependency
name: rspec-core
requirement: !ruby/object:Gem::Requirement
requirements:
- - "~>"
- !ruby/object:Gem::Version
version: 3.9.0
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - "~>"
- !ruby/object:Gem::Version
version: 3.9.0
~~~~~~~~~~~~~~~ removed for brevity ~~~~~~~~~~~~~~~~~~~~~~~~
executables: []
extensions: []
extra_rdoc_files:
- README.md
files:
- LICENSE.md
- README.md
- lib/rspec.rb
- lib/rspec/version.rb
homepage: http://github.com/rspec
licenses:
- MIT
~~~~~~~~~~~~~~~ removed for brevity ~~~~~~~~~~~~~~~~~~~~~~~~
required_ruby_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - ">="
- !ruby/object:Gem::Version
version: '0'
requirements: []
rubygems_version: 3.0.6
signing_key:
specification_version: 4
summary: rspec-3.9.0
test_files: []
data.tar.gz 文件是一个由实际的 gem 代码组成的压缩包,这些代码列在 gemspec 的文件属性中。
checksum.yaml.gz 文件是一个压缩的 YAML 文件,包含数据和元数据文件的校验和。
---
SHA256:
metadata.gz: 717820f4463baa76607e57e500d14c680608fe6aac01405c7cfe6fd2dcd990db
data.tar.gz: 919fc9aedde011882f1814d4d16cf92fdfedc728a979f0f814c819211787627f
SHA512:
metadata.gz: c39a368fbab5da77ca12870485b0d7663fce1deb90b9528f57f695a8543525d61494ac55ffb4ffc7fc6a6c80c2ca2e5492499965bb26485f5c76a49916b699b7
data.tar.gz: 90ee39bf3cb841049201bdec98d2d45dcdfd0d7c927566621ca7be5529a6c89c8b6b85cab37166e8005aeb44279d3fcf20001aa80cdf4f1d62cd92be391bea82
gem install
当运行 gem install 时,Rubygems 会从配置的 gem 仓库(默认为rubygems.org)中获取该.gem 文件,并使用Gem::Package::TarReader 类将文件解压缩并复制到其适当的位置。这个位置取决于操作系统和使用的Ruby版本管理工具(Rbenv、rvm)。
一旦安装了gem,该gem可以通过require 语句加载到任何Ruby文件中。require 语句改变了$LOAD_PATH 全局变量,该变量包含一个应该加载代码的路径列表。