如何開發一個自己的 RubyGem?

OneAPM官方技術部落格發表於2015-07-06

什麼是 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是中國基礎軟體領域的新興領軍企業,能幫助企業使用者和開發者輕鬆實現:緩慢的程式程式碼和SQL語句的實時抓取。想閱讀更多技術文章,請訪問OneAPM官方技術部落格

相關文章