如何开发一个自己的 RubyGem?

李哲

什么是 RubyGem

RubyGem是Ruby语言的标准源码打包格式。

大家一直都在用gem这个命令,但是很少有人知道这个东西是怎么来的,这里我从网上扒下一些资料汇总一下,分享给大家。最后面会有这些链接,想进一步了解的,可以点进去看看。Ruby语言深受其他几种脚本语言的影响,其中就有Perl,而Perl有个CPAN(Comprehensive Perl Archive Network),这个东西也就像是现在的RubyGems.org,但是当时Ruby是没有这样一个东西的。像CPAN和RubyGem,它们仅仅是定义好的一种源码的打包和安装方式,另外还有一些组织,会提供这种免费的公共的源码包的存储,这也就现在大家每天都要使用的安装方式:

gem install rails

在RubyGem的发展历史当中,有几位重要的人物,这里也作为八卦知识给大家晒一晒,就当做大家茶余饭后的谈点吧。Ruby社区的人应该都知道Jim Weirich这个人,他已经在2014年2月份去世了,是一个可爱的白胡子大叔,他和另外的四位Rich Kilmer, Chad Fowler, David Black, Paul Brannan在2003年制定了RubyGem的基本规范和实现,但是其实RubyGem最早是Ryan Leavengood在2001年开发的,可惜没有流行起来,最后到了2003年,前面的5个人经过Ryan Leavengood的同意,使用了RubyGem这个名称,开发了新版的RubyGem,其中并没有使用Ryan Leavengood的代码。这里附上rubygems的执行文件链接,看看注释,里面有上面几个人的名字 rubygems/blob/master/bin/gem

rubygems有默认的源,也可以更改,国内的基本就是https://rubygems.taobao.org了,有些公司有自己的需求,也会搭建自己的私源。当前的官方源为https://rubygems.org,这个源也是几经辗转,早期的Ruby用户都知道http://gems.rubyforge.orghttp://gemcutter.org,甚至github都曾经作为源使用过,也就是http://gems.github.com,这三个现在都已经弃用了。

看看,一个简单的gem install历史还不少啊。

RubyGem的基本使用方法

gem install rails  //安装rails
gem install rails -v 4.2.0   //安装指定版本的rails
gem search rails  //查找所有名称中含有rails的gem
gem search ^rails  //查找所有名称中以rails开头的gem
gem search ^rails -d  //查找所有名称中以rails开头的gem,并显示描述
gem build package.gemspec  //构建一个gem,就是把你自己写的gem源码,打包成一个.gem文件
gem push pack-1.0.gem  //发布到源上,默认是rubygems.org

这里只是简单列出了最常用的使用方法,大家看看就好,命令很有限,也很简单,执行gem --help,基本上所有的东西你都能10分钟内学会。

如何制作一个自己的RubyGem

前几年还是有这样那样的工具,现在用bundler就够了。

$ bundler gem mygem
      create  mygem/Gemfile
      create  mygem/Rakefile
      create  mygem/LICENSE.txt
      create  mygem/README.md
      create  mygem/.gitignore
      create  mygem/mygem.gemspec
      create  mygem/lib/mygem.rb
      create  mygem/lib/mygem/version.rb
Initializing git repo in /home/lizhe/Workspace/mygem

一个bundler命令就搞定了。来看看mygem这个文件夹下的东西:

total 24
-rw-rw-r-- 1 lizhe lizhe   90  7月  2 15:52 Gemfile
drwxrwxr-x 3 lizhe lizhe 4096  7月  2 15:52 lib
-rw-rw-r-- 1 lizhe lizhe 1062  7月  2 15:52 LICENSE.txt
-rw-rw-r-- 1 lizhe lizhe  850  7月  2 15:52 mygem.gemspec
-rw-rw-r-- 1 lizhe lizhe   29  7月  2 15:52 Rakefile
-rw-rw-r-- 1 lizhe lizhe  556  7月  2 15:52 README.md

现在来看看gemspec这个文件,它描述了这个Gem的各种信息

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'mygem/version'

Gem::Specification.new do |spec|
  spec.name          = "mygem"
  spec.version       = Mygem::VERSION
  spec.authors       = ["lizhe"]
  spec.email         = ["lizhe@oneapm.com"]
  spec.summary       = %q{TODO: Write a short summary. Required.}
  spec.description   = %q{TODO: Write a longer description. Optional.}
  spec.homepage      = ""
  spec.license       = "MIT"

  spec.files         = `git ls-files -z`.split("\x0")
  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.7"
  spec.add_development_dependency "rake", "~> 10.0"
end

我发现有人看到这个文件的内容时,倒是关心那个'git ls-files -z'.split("\x0")是什么意思?以及那个\x0是什么?附上一个链接,解释一下,参考链接。这个文件最上面先把lib文件夹添加到load path中,Gem::Specification的第一部分主要是描述这个gem的信息,包括名称,版本等等,第二部分是这个gem都包括哪些文件,执行文件,测试文件以及哪些路径下的文件可以添加到load path中。第三部分是开发mygem需要依赖的其他gem。这些信息都可以自定义,先按照默认走。让我们build第一个gem吧。

$ rake build

rake aborted!
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
ERROR:  While executing gem ... (Gem::InvalidSpecificationException)
    "FIXME" or "TODO" is not a description
/home/lizhe/.rvm/gems/ruby-2.1.5@global/gems/bundler-1.7.12/lib/bundler/gem_helper.rb:149:in `sh'
/home/lizhe/.rvm/gems/ruby-2.1.5@global/gems/bundler-1.7.12/lib/bundler/gem_helper.rb:57:in `build_gem'
/home/lizhe/.rvm/gems/ruby-2.1.5@global/gems/bundler-1.7.12/lib/bundler/gem_helper.rb:39:in `block in install'
/home/lizhe/.rvm/gems/ruby-2.1.5@global/bin/ruby_executable_hooks:15:in `eval'
/home/lizhe/.rvm/gems/ruby-2.1.5@global/bin/ruby_executable_hooks:15:in `<main>'
Tasks: TOP => build
(See full trace by running task with --trace)

这个警告是提醒我们需要替换gemspec中的FIXMETODO,这个警告如果不解决是无法build一个gem的,直接删除掉summary和description中的TODO

  spec.summary       = %q{Write a short summary. Required.}
  spec.description   = %q{Write a longer description. Optional.}

再来执行:

$ rake build

mygem 0.0.1 built to pkg/mygem-0.0.1.gem.

好了,第一个gem诞生了。它就在当前目录的pkg下:mygem-0.0.1.gem。如何使用呢?不考虑bundler的情况下,如果你开起了一个irb或者pry的session时,一般都会这样写:require "mygem",如果你现在这样做,那肯定不行,因为它还没有被安装到ruby的load path中,那就把它安装上。

$ rake install

mygem 0.0.1 built to pkg/mygem-0.0.1.gem.
mygem (0.0.1) installed.

安装好了,那就来使用一下,打开irb:

require "mygem"
=> true
Mygem
=> Mygem

看,已经可以使用这个module了,不过这个gem啥也干不了,那么我们就给它添加一个方法吧,打开lib/mygem.rb,添加一个方法:

require "mygem/version"

module Mygem
  def self.hello
    p "hello from my gem"
  end
end

保存,然后执行rake install,这个命令会先build然后install,再重新打开irb:

require "mygem"
=> true
Mygem.hello
=> "hello from my gem"

能够正常运行了,那就来发布第一个gem吧:

rake release
// 输入你在rubygems.org上的账号和密码

如果你的一个rails应用正好需要输出一个hello from my gem,那么现在你可以在Gemfile中添加这个gem了:

gem 'mygem'

添加完执行bundle install,你就可以在你的rails应用中使用它了。

参考链接:

本文由OneAPM工程师原创文章 ,想阅读更多技术文章,请访问OneAPM官方技术博客