目录
1.命名一致性
2.版本语义化
3.声明依赖
4.加载代码
5.preRelease gem
一、命名一致性
There are only two hard things in Computer Science: cache invalidation and naming things. -Phil Karlton
1.文件名
保持lib和bin目录下文件命名的一致性。
hola gem的命名如下:
% tree
.
├── Rakefile
├── bin
│ └── hola
├── hola.gemspec
├── lib
│ ├── hola
│ │ └── translator.rb
│ └── hola.rb
└── test
└── test_hola.rb
可执行文件和lib下的主文件命令相同。开发者可以轻易的使用require 'hola'来加载。
2.命名你的gem
命名自己的gem非常重要。在你命名你的gem之前,到on RubyGems.org and GitHub 搜索下,看看别人是否已经使用了这个名字。每一个发布的gem都必须使用一个唯一的名字。当你想好自己gem的名字时,请阅读 naming recommendations 。
二、版本语义化
版本控制策略只是一个版本如何被分配的简单规则。它非常简单(例如从1自增),或者非常特别(Knuth’s TeX 项目的 version numbers: 3, 3.1, 3.14, 3.141, 3.1415; each successive version added another digit to PI)
RubyGems团队使用语义化版本号来给gem规定版本。RubyGems并未严格强调命名策略,但是使用不合理的命名策略是在给其他使用你gem的人帮倒忙。
假设你有一个stack的gem,Stack class里面包含push和pop两个方法。如果你使用语义化版本控制,那么你的CHANGELOG需要像这样:
Version 0.0.1: The initial Stack class is released.
Version 0.0.2: Switched to a linked list implementation because it is cooler.
Version 0.1.0: Added a depth method.
Version 1.0.0: Added top and made pop return nil (pop used to return the old top item).
Version 1.1.0: push now returns the value pushed (it used it return nil).
Version 1.1.1: Fixed a bug in the linked list implementation.
Version 1.1.2: Fixed a bug introduced in the last fix.
语义化版本控制归结如下:
PATCH 0.0.x level changes for implementation level detail changes, such as small bug fixes
MINOR 0.x.0 level changes for any backwards compatible API changes, such as new functionality/features
MAJOR x.0.0 level changes for backwards incompatible API changes, such as changes that will break existing users code if they update
三、声明依赖
gem可以喝其他gem一起协同工作。下面是一些tips以确保他们之前正常工作。
1.运行时 vs 开发
rubygems提供两种类型的依赖:运行时和开发。运行时依赖是你的gem工作时所需要的(例如such as rails needing activesupport)
开发时依赖对于别人想修改你的gem时非常有用。当你指定开发时依赖,其他开发者可以使用install --dev your_gem命令,rubygems会同时抓取两种依赖。典型的开发时依赖就是测试框架和构建系统。
在gemspec中设置依赖非常简单。只需要使用add_runtime_dependency
and add_development_dependency
Gem::Specification.new do |s|
s.name = "hola"
s.version = "2.0.0"
s.add_runtime_dependency "daemons",
["= 1.1.0"]
s.add_development_dependency "bourne",
[">= 0"]
2.不要在你得gem中使用gem
你也许有像下面一样的这些代码,确保你指定了gem的版本。
gem "extlib", ">= 1.0.8"
require "extlib"
对于应用来说,这样做有理由说是在consume gems(虽然他们也可能使用其他工具,例如 Bundler).gems本身不会这样做。他们会在gemspec中使用依赖,这样rubygems可以加载它,而不是用户自己去加载。
It’s reasonable for applications that consume gems to use this (though they could also use a tool like Bundler). Gems themselves should not do this. They should instead use dependencies in the gemspec so RubyGems can handle loading the dependency instead of the user.
3.封闭式的版本约束
如果你的gem版本计划遵循语义化版本2.0.0(http://semver.org/lang/zh-CN/),那么其他ruby开发者将会大受裨益,当他们在自己的应用中使用版本约束锁定gem的时候。
看看下面这样的gem版本releases
- Version 2.1.0 — Baseline
- Version 2.2.0 — Introduced some new (backward compatible) features.
- Version 2.2.1 — Removed some bugs
- Version 2.2.2 — Streamlined your code
- Version 2.3.0 — More new features (but still backwards compatible).
- Version 3.0.0 — Reworked the interface. Code written to version 2.x might not work.
某个其他使用你gem的人发现2.2.0在他的应用中工作良好,但是2.1.0不工作。那么他的gem配置,或者Gemfile(使用bundler)配置如下
# gemspec
spec.add_runtime_dependency 'library',
'>= 2.2.0'
# bundler
gem 'library', '>= 2.2.0'
这是一个好的版本约束。他表示所有2.X版本的在我的应用中工作良好,但是3.X的不确定。
The alternative here is to be “pessimistic”. This explicitly(明白地)excludes(排除) the version that might break your code.
# gemspec
spec.add_runtime_dependency 'library',
['>= 2.2.0', '< 3.0']
# bundler
gem 'library', '>= 2.2.0', '< 3.0'
RubyGems 提供了一种简单的方式,叫做
twiddle-wakka:
# gemspec
spec.add_runtime_dependency 'library',
'~> 2.2'
# bundler
gem 'library', '~> 2.2'
注意:他会抛弃补丁级别的版本号。
这里的~>2.2.0是指在[">=2.2.0", "<2.3.0"]这个区间。
If you want to allow use of newer backwards-compatible(向下兼容) 版本 but need a specific bug fix you can use a compound requirement:
# gemspec
spec.add_runtime_dependency 'library', '~> 2.2', '>= 2.2.1'
# bundler
gem 'library', '~> 2.2', '>= 2.2.1'
The important note to take home here is to be aware others
will be using your gems, so guard yourself from potential bugs/failures in future releases by using
~>
instead of
>=
if at all possible.
小提示:如果你不得不在你的应用中处理大量的gem依赖,那么我建议你看看bundler和isolate这样的工具,他会帮助你管理复杂的gem依赖问题。
If you want to allow prereleases and regular releases use a compound requirement:
# gemspec
spec.add_runtime_dependency 'library', '>= 2.0.0.a', '< 3'
在prerelease版本中使用~>限制你只能使用的prerelease版本。
4.require rubygems
摘要:不要。
这一行。。。。。。
require 'rubygems'
没必要出现在你的gem代码中,当一个gem required的时候,RubyGems 已经被加载了。
require 'rubygems'意味着这个gem使用简单,没必要运行RubyGems 客户端。
关于这个主题,如果你想了解更多,请查看 Ryan Tomayko
四、加载代码
从其核心,RubyGems的存在时帮助你管理ruby的$LOAD_PATH,他关系到require语句怎样加载你的代码。有几个方法可以使你确定你正确的加载了代码。
1.respect the global load path
当打包你的gem的时候,你需要特别注意你的lib目录里面是些什么。你安装的每一个gem都会把lib目录添加到你的$LOAD_PATH中去。这意味着lib目录中的顶层文件将会被加载。
例如,我们的foo这个gem有如下的目录结构:
.
└── lib
├── foo
│ └── cgi.rb
├── erb.rb
├── foo.rb
└── set.rb
这看上去你把自定义的erb和set文件放在这里是无害的。但这确实是有害的,每一个加载这个gem的用户就不能从ruby的标准库中加载erb和set了。避免这个的最好办法是在lib目录下使用其他目录。通常的做法是把他们放在和你的gem name同名的目录下。例如lib/foo/cgi.rb.
2.requiring files relative each other
Gems不应该使用__FILE__来加载其他文件到你的gem中。如下代码出人意料的普遍:
require File.join(
File.dirname(__FILE__),
"foo", "bar")
# or
require File.expand_path(File.join(
File.dirname(__FILE__),
"foo", "bar"))
The fix is simple,, just require the file relative to the load path:
require 'foo/bar'
#or 使用require_relative
require_relative 'foo/bar'
The make your own gem guide has a great example of this behavior in practice, including a working test suite. The code for that gem is on GitHub as well.
3.mangling the load path
gems不应该改变$_LOAD_PATH常量。RubyGems 帮你管理这些。如下代码是必须的:
lp = File.expand_path(File.dirname(__FILE__))
unless $LOAD_PATH.include?(lp)
$LOAD_PATH.unshift(lp)
end
# or
__DIR__ = File.dirname(__FILE__)
$LOAD_PATH.unshift __DIR__ unless
$LOAD_PATH.include?(__DIR__) ||
$LOAD_PATH.include?(File.expand_path(__DIR__))
当RubyGems激活一个gem,退添加lib目录到你的 $LOAD_PATH
以准备好被其他lib或者应用加载。 It is safe to assume you can then require
any file in your lib
folder.
五、preRelease gem
当一个大的gem更新前,许多gem开发者会放出testing或者edge版本。RubyGems 支持prerelease的观点,prerelease可以使betas, alphas或者其他。
他的优点很明显。在你的gem版本中只需要多一个或者几个字母。例如,下面是一个prerelease gemspec的字段:
Gem::Specification.new do |s|
s.name = "hola"
s.version = "1.0.0.pre"
其他prerelease版本可能包含 2.0.0.rc1
, 或者 1.5.0.beta.3
. 只需要包含几个字母,你就做大了。这样的gem可以使用--pre,就想这样:
% gem list factory_girl -r --pre
*** REMOTE GEMS ***
factory_girl (2.0.0.beta2, 2.0.0.beta1)
factory_girl_rails (1.1.beta1)
% gem install factory_girl --pre
Successfully installed factory_girl-2.0.0.beta2
1 gem installed
六.扩展阅读
Several sources were used for content for this guide: