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/ruby_extendsions目录下建立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 = ["naitnix"]
s.email = ["naitnix@126.com"]
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了。
所以下面只介绍如何制作属于自己的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/ruby_extendsions目录下建立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 = ["naitnix"]
s.email = ["naitnix@126.com"]
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了。