如何开发一个自己的 RubyGem?

jopen 4年前

什么是 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.org和http://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中的FIXME和TODO,这个警告如果不解决是无法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官方技术博客