如何製作自己的gem

 Rails支持多種方式的擴展,包括plugin,gem, 或者放到lib文件下等多種方式。 但是隨着bundler的出現, Rails3時代我們還是寫gem比較好, 方便管理。

所以下面只介紹如何製作屬於自己的gem。開始之前先確保你機器了安裝了git。

在我們的項目裏面, 我看到在config/initializers/下有一個hash.rb文件, 是使用了一個monkey patch的方式來對Hash進行了一個擴展:

  class Hash
    def # my_reverse
        hash_new = Hash.new
        self.each {|key,value|
           if not hash_new.has_key?(key) then hash_new[value] = key end
         }
         return hash_new
      end
   end

像這種擴展方式不是很好, 我想把它修改爲一個gem, 如果以後有其他的擴展,比如對Array,Hash等其他方法擴展,我們可以升級gem,這樣就方便管理了,不至於那麼混亂。

我們開始吧:
1. 安裝bundler, 因爲我要用bundler來生成一個基礎gem結構。

          gem install bundler
     


2. 使用bundler來生成一個基礎gem結構:

           bundle gem ruby_extendsions
      


命令執行以後會看到生成下面這些文件:

      create  ruby_extendsions/Gemfile
      create  ruby_extendsions/Rakefile
      create  ruby_extendsions/.gitignore
      create  ruby_extendsions/ruby_extendsions.gemspec
      create  ruby_extendsions/lib/ruby_extendsions.rb
      create  ruby_extendsions/lib/ruby_extendsions/version.rb
Initializating git repo in /Users/alex/work/mygems/test/ruby_extendsions
      


說明一下:
1). ruby_extendsions.gemspec , 類似於這樣的.gemspec文件,就相當於gem的說明書, 將來你打gem包的時候, 就靠這個文件了。
2). lib下放我們的代碼實現
3). 整個項目至於git版本控制下。方便我們push到rubygem.org.

3. 使用Rspec進行TDD方式開發我們的代碼:
確保我們能用rspec, 在ruby_extendsions.gemspec裏添加:

s.add_development_dependency "rspec"


然後,執行:

bundle install


這下我們就可以使用rspec了。
在根目錄下增加一個spec文件夾, 考慮到我們要實現的功能, 是對ruby的一些擴展, 所以我暫時就命名爲了ruby_extendsions, 而此次只是針對Hash類的擴展, 所以我們需要在spec目錄下建立一個測試文件hash_spec.rb和spec_helper.rb來測試我們對Hash擴展功能是否正確:

#spec/hash_spec.rb

 require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

describe "RubyExtend::HashExtendsions" do
  before(:each) do
    @hash1 = {:a => 1, :b => 2}
  end

  it "A hash data should not be reversed if havn't use ruby_extend " do
    #當沒有使用我們的擴展的時候, Hash類是不應該包含hash_reverse這個方法的, 這個測試也保證了我們在擴展一個Ruby內部類的時候不會和其內部方法名衝突。
    @hash1.respond_to?("hash_reverse").should eql false  
  end

  it "A hash data should be reversed" do
    require 'ruby_extendsions'
    @hash1.hash_reverse.should eql({1=>:a, 2=>:b} )
  end
end
     


上面的代碼:spec_helper.rb文件爲將來準備,如果擴展其他方法, 會用到一些公用的配置。
測試由一 個正例和一個反例組成。

4. 運行測試:

bundle exec rspec spec

, bundle exec確保rspec是使用我們在.gemspec文件裏聲明的版本。
當然,測試結果是會失敗,因爲我們還沒有擴展hash_reverse方法呢。

5. 實現代碼:
打開lib/ruby_extendsions.rb:

         require 'ruby_extendsions/hash_extendsions'   #我們今天要實現的Hash擴展
         require 'ruby_extendsions/array_extendsions'  #以後有可能用到對Array方法的擴展,我們就放這個文件裏。
    
然後在lib下建立hash_extendsions.rb文件:
module RubyExtendsions      
  module HashExtendsions      #注意模塊的命名要和文件名和路徑保持一致。
    def self.included(base)     #hook, 當這個module在被include的時候觸發
      base.send :include, InstanceMethods  #引入實例方法
      base.send :extend, ClassMethods          # 引入類方法
    end

    module InstanceMethods
      def hash_reverse   #我們的hash_reverse是實例方法
        hash_new = {}
        self.each {|key,value|
          if not hash_new.has_key?(key) then hash_new[value] = key end
        }
        return hash_new
      end
    end#InstanceMethods

    module ClassMethods
      #TODO
    end#ClassMethods

  end #HashExtendsions
end #RubyExtendsions

#真正的擴展
class Hash
  include RubyExtendsions::HashExtendsions
end

上面的代碼都帶註釋了。
然後我們運行測試, oK, 都通過了。大功告成了。

6. 發佈我們的gem:
我們修改一下.gemspec文件, 把項目相關信息都寫裏面去:

# -*- encoding: utf-8 -*-

Gem::Specification.new do |s|
  s.name        = "ruby_extendsions" 
  s.version     = "0.1" 
  s.platform    = Gem::Platform::RUBY
  s.authors     = ["blackanger"]
  s.email       = ["[email protected]"]
  s.homepage    = "http://rubygems.org/gems/ruby_extendsions" 
  s.summary     = "Ruby Extendsions For Hash" 
  s.description = "Ruby Extendsions" 

  s.required_rubygems_version = ">= 1.3.6" 
  s.rubyforge_project         = "ruby_extendsions" 

  s.add_development_dependency "bundler", ">= 1.0.0" 
  s.add_development_dependency "rspec" 

  s.files        = `git ls-files`.split("\n")
  s.executables  = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
  s.require_path = 'lib'
end

然後, 我們在根目錄下執行命令:
rake install

這個命令會生成pkg/ruby_extendsions-0.1.gem. 到此爲止,我們的gem包就打好了。
你可以把你的gem項目上傳到github。 但是github從去年開始就停止了自動打包服務。所以你如果想使用gem install ruby_extendsions命令來安裝你的gem, 需要把它上傳到rubygems.org裏。 
首先,你需要在rubygems.org裏註冊一個帳號。 
其次, gem install gemcutter
然後你就push吧。   gem push ruby_extendsions-0.1.gem
如果碰到權限的錯誤,那說明在rubygems裏面已經有了和你一樣名字的gem包,換個名字就oK了。

P.S 我的這個gem已經上傳到了rubygems.org上面, 也上傳到了github. (完整代碼:http://github.com/ZhangHanDong/ruby_extend).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章