Bundler 是由 Carl Lerche、Yehuda Katz、André Arko 和众多优秀贡献者创建的工具,用于管理 Ruby 库中的 Rubygems 依赖项。Bundler 1.0 与 Rails 3 同时发布,Bundler 最著名的用法可能是在 Rails 项目中。但请记住,Bundler 不仅仅用于 Rails!
您知道 Bundler 不仅可以用于 gem 依赖项管理,还可以用于编写我们自己的 gem 吗?这样做非常容易,Bundler 提供了一些工具来帮助您完成此过程。
相关阅读
我们为什么要创建 gem?我们不能只是将一些代码放入我们的其他库中并使用它吗?当然,我们可以这样做。但如果我们想在其他地方使用这段代码,或者想分享它怎么办?这就是 gem 的完美之处。我们可以将我们的库和 gem 分别编码,让库只需要 gem。如果我们想在另一个库中使用 gem,那么只需要进行很小的修改,而不是大量复制。
此外:分享是一种关怀。
本指南使用 bundler 的 1.9.0 版本创建。我们可以使用其他版本,但输出可能不完全相同。要检查我们当前使用的 bundler 版本,请运行以下命令
$ bundle -v
我们应该看到类似于Bundler version 1.9.0
的内容。如果需要,我们可以通过运行gem update bundler
来更新到最新版本的 Bundler。
要开始使用 Bundler 创建 gem,请使用以下命令
$ bundle gem foodie
我们将 gem 命名为foodie
,因为这个 gem 将围绕食物做一些事情,例如将它们描述为“美味!”或“恶心!”。敬请关注。
有关 gem 命名规范的信息,您可以阅读 RubyGems 网站上的“命名您的 gem”指南。
此命令为我们的新 gem 创建了一个脚手架目录,如果我们安装了 Git,则会在此目录中初始化一个 Git 仓库,以便我们可以立即开始提交。如果这是您第一次运行bundle gem
命令,系统会询问您是否要将CODE_OF_CONDUCT.md
和LICENSE.txt
文件包含在您的项目中。生成的的文件是
Gemfile:用于管理库开发的 gem 依赖项。此文件包含一个gemspec
行,这意味着 Bundler 也会包含foodie.gemspec中指定的依赖项。最佳实践是在gemspec中指定库依赖的所有 gem。
Rakefile: 需要 Bundler 并通过调用 `Bundler::GemHelper.install_tasks` 添加 `build`、`install` 和 `release` Rake 任务。`build` 任务将构建当前版本的 gem 并将其存储在 *pkg* 文件夹下,`install` 任务将构建 *并* 将 gem 安装到我们的系统中(就像我们使用 `gem install` 一样),而 `release` 将 gem 推送到 Rubygems 供公众使用。
CODE_OF_CONDUCT.md: 提供您期望所有 gem 贡献者遵循的行为准则。只有在您选择将其包含在内时才会包含它。
LICENSE.txt: 包含 MIT 许可证。只有在您选择将其包含在内时才会包含它。
.gitignore: (仅当我们有 Git 时)。这将忽略 *pkg* 目录中的任何内容(通常是 `rake build` 放置在那里的文件)、任何具有 *gem* 扩展名的内容以及 *bundle* 目录。
foodie.gemspec: Gem 规范文件。在这里,我们提供用于 Rubygems 使用的信息,例如 gem 的名称、描述和主页。我们还在这里指定 gem 运行所需的依赖项。
lib/foodie.rb: 定义 gem 代码的主要文件。当我们的 gem 加载时,Bundler(或任何类似的智能系统)将需要此文件。此文件定义了一个 `module`,我们可以将其用作 gem 所有代码的命名空间。最佳实践是将我们的代码放在…
lib/foodie: 这里。此文件夹应包含 gem 的所有代码(类等)。*lib/foodie.rb* 文件用于设置 gem 的环境,而其所有部分都位于此文件夹中。如果我们的 gem 有多种用途,将它们分开以便人们可以一次只要求一个类/文件会非常有用。
lib/foodie/version.rb: 定义一个 `Foodie` 模块,并在其中定义一个 `VERSION` 常量。此文件由 *foodie.gemspec* 加载以指定 gem 规范的版本。当我们发布 gem 的新版本时,我们将增加此版本号的一部分,以指示 Rubygems 我们正在发布新版本。
这是我们的基础和布局,现在开始开发吧!
在本指南中,我们将使用 RSpec 来测试我们的 gem。我们编写测试以确保一切按计划进行,并防止未来的我们建造时光机回到过去,然后踢我们的屁股。
要开始编写测试,我们将在 gem 的根目录中创建一个名为 spec 的目录,使用命令 mkdir spec
。接下来,我们将在 foodie.gemspec 文件中指定 rspec
是一个开发依赖项,在 Gem::Specification
块中添加以下行:
spec.add_development_dependency "rspec", "~> 3.2"
因为我们在 Gemfile 中有 gemspec
方法调用,Bundler 会自动将此 gem 添加到名为“development”的组中,然后我们可以在任何时候使用以下行引用这些 gem:
Bundler.require(:default, :development)
将此依赖项规范放在 foodie.gemspec 而不是 Gemfile 中的好处是,任何运行 gem install foodie --dev
的人都将获得这些开发依赖项的安装。此命令用于当人们希望测试 gem 时,而无需从 GitHub 上分叉或克隆它。
当我们运行 bundle install
时,rspec 将被安装到此库以及我们使用 Bundler 的任何其他库中,但不会安装到系统中。这是一个重要的区别:任何由 Bundler 安装的 gem 不会与由 gem install
安装的 gem 发生冲突。它实际上是一个沙箱环境。最佳实践是使用 Bundler 来管理我们的 gem,这样我们就不会出现 gem 版本冲突。
通过运行 bundle install
,Bundler 将生成一个 **极其重要** 的 Gemfile.lock 文件。此文件负责确保此库开发的每个系统都具有 **完全相同的** gem,因此它应该始终被检入版本控制。有关此文件的更多信息,请 阅读 bundle install
手册页的“THE GEMFILE.LOCK”部分。
此外,在 bundle install
输出中,我们将看到以下行:
Using foodie (0.1.0) from source at /path/to/foodie
Bundler 检测到我们的 gem,加载 gemspec 并像其他 gem 一样捆绑我们的 gem。
现在框架已经就位,我们可以编写第一个测试。为了进行测试,首先创建一个名为spec的文件夹来存放我们的测试(mkdir spec
)。然后,在spec目录的根目录下,为每个要测试的类创建一个新的RSpec文件。如果我们的 gem 有多个方面,我们会将它们分组在一个目录下,例如spec/facet;但这是一个简单的 gem,所以我们不会这样做。让我们将这个新文件命名为spec/foodie_spec.rb
,并用以下内容填充它
describe Foodie::Food do
it "broccoli is gross" do
expect(Foodie::Food.portray("Broccoli")).to eql("Gross!")
end
it "anything else is delicious" do
expect(Foodie::Food.portray("Not Broccoli")).to eql("Delicious!")
end
end
当我们再次运行bundle exec rspec spec
时,会提示我们Foodie::Food
常量不存在。这是真的,我们应该在lib/foodie/food.rb
中定义它,如下所示
module Foodie
class Food
def self.portray(food)
if food.downcase == "broccoli"
"Gross!"
else
"Delicious!"
end
end
end
end
为了加载这个文件,我们需要在lib/foodie.rb
中添加一个 require 行来引用它
require 'foodie/food'
我们还需要在spec/foodie_spec.rb
的顶部引用lib/foodie.rb
require 'foodie'
当我们使用bundle exec rspec spec
运行我们的规范时,这个测试将通过
2 example, 0 failures
取得了巨大的成功!如果我们使用 Git(或任何其他源代码控制系统),这是一个很好的代码提交检查点。请记住经常提交代码!
我们可以编写自己的代码,这很好,但如果我们想依赖另一个 gem 呢?这也很容易。
我们现在将使用 Active Support 的pluralize
方法,通过调用 gem 中的一个方法来调用它。
要使用另一个 gem,我们必须首先在我们的foodie.gemspec中将其指定为依赖项。我们可以在foodie.gemspec中添加以下行,在Gem::Specification
对象中指定对activesupport
gem 的依赖
spec.add_dependency "activesupport"
如果我们想指定一个特定的版本,我们可以使用以下行
spec.add_dependency "activesupport", "4.2.0"
或者指定一个版本约束
spec.add_dependency "activesupport", ">= 4.2.0"
但是,依赖于一个仅仅大于当时最新版本的版本,无疑会在以后导致问题。尽量始终使用~>
来指定依赖项
spec.add_dependency "activesupport", "~> 4.2.0"
当我们再次运行bundle install
时,activesupport
gem 将被安装,供我们使用。当然,作为勤奋的 TDD/BDD 狂热者,我们将在编写代码之前测试我们的pluralize
方法。让我们现在将这个测试添加到spec/food_spec.rb中,在我们的describe Foodie::Food
块内
it "pluralizes a word" do
expect(Foodie::Food.pluralize("Tomato")).to eql("Tomatoes")
end
当然,当我们用 bundle exec rspec spec
运行这个规范时,它会失败。
expect(Failure/Error: Foodie::Food.pluralize("Tomato")).to eql("Tomatoes")
undefined method `pluralize' for Foodie::Food:Class
现在,我们可以通过首先引入包含 pluralize
方法的 Active Support 部分,在 lib/foodie/food.rb 中定义这个 pluralize
方法。这行代码应该放在文件的最上面,就像所有好的 require
一样。
require 'active_support/inflector'
接下来,我们可以像这样定义 pluralize
方法
def self.pluralize(word)
word.pluralize
end
当我们运行 bundle exec rspec spec
时,我们的规范将通过。
3 examples, 0 failures
这带来了另一个检查点,在这里,将我们到目前为止的努力提交是一个好主意。
现在能够调用我们 gem 的方法(总共两个!)并让它们返回字符串真是太好了,但大家都知道,最好的 gem 都带有命令行界面(以下简称“CLI”)。你现在就可以看出这个 gem 有多不酷,因为它没有 CLI,对吧?它需要一个。它渴望一个。
它应该有一个。
在我们一头扎进给我们的 gem 提供最好的 CLI(一个只有两个方法的 gem,这两个方法都返回无用的字符串)之前,让我们考虑一下我们首先要如何测试它。我们是狂热分子,记住?如果只有一个工具我们可以使用。当然,它必须有一个酷炫的名字。
比如“Aruba”。BAM
David Chelimsky 和 Aslak Hellesøy 合作创建了 Aruba,一个 CLI 测试工具,他们都将它用于 RSpec 和 Cucumber,现在我们也可以用它来测试我们的 gem。哦,说到 Cucumber,这也是我们将用来定义 Aruba 测试的内容。人类可读的代码客户端测试是未来的趋势,伙计。
我们现在将在 foodie.gemspec 中为 Cucumber 东西定义新的开发依赖项
spec.add_development_dependency "cucumber"
spec.add_development_dependency "aruba"
酷。让我们运行 bundle install
来设置这些很棒的工具。
我们的 CLI 将有两个方法,对应于我们在 Foodie::Food
中定义的两个方法。我们现在将创建一个 features 目录,我们将在其中使用 Aruba 为我们的 CLI 编写测试。在这个目录中,我们将创建一个名为 features/food.feature 的新文件,并用以下代码填充它
Feature: Food
In order to portray or pluralize food
As a CLI
I want to be as objective as possible
Scenario: Broccoli is gross
When I run `foodie portray broccoli`
Then the output should contain "Gross!"
Scenario: Tomato, or Tomato?
When I run `foodie pluralize --word Tomato`
Then the output should contain "Tomatoes"
这些场景测试了我们的 gem 将提供的 CLI。在 When I run
步骤中,引号内的第一个词是我们的可执行文件的名字,第二个是任务的名字,任何进一步的文本都是参数或选项。是的,它正在测试看起来与我们的规范相同的东西。你真是太敏锐了。金星!但它通过 CLI 进行测试,这使得它非常棒。人为的例子是今年的趋势。
第一个场景确保我们可以调用一个特定的任务,并传递给它一个参数,该参数将成为输出文本的一部分。第二个场景有效地确保了相同的事情,但我们将该值作为选项而不是参数传递。
要运行此功能,我们使用 cucumber
命令,但当然,因为它在我们 bundle 的上下文中可用,所以我们使用 bundle exec cucumber
,如下所示
$ bundle exec cucumber features/
看到那些黄色的东西了吗?它们是未定义的步骤。
When /^I run "([^"]*)"$/ do |arg1|
pending # express the regexp above with the code you wish you had
end
Then /^the output should contain "([^"]*)"$/ do |arg1|
pending # express the regexp above with the code you wish you had
end
我们可以通过要求 Aruba 来定义它们。在 Cucumber 中,features/support 目录下的所有 .rb 文件都会被自动加载。为了验证这一点,我们可以添加一个 features/support/setup.rb 文件(先创建 support 目录),并在其中添加一行代码
require 'aruba/cucumber'
这会加载 Aruba 提供的 Cucumber 步骤,这些步骤是我们 Cucumber 功能需要变得很棒的相同步骤。
我们必须重新运行 bundle exec cucumber features
,只是为了看看接下来会发生什么。我们看到红色。红色就像从墙上不断渗出的血一样。它包含这条神秘的信息
sh: foodie: command not found
好吧,它并没有那么神秘。它只是意味着它找不到我们 gem 的可执行文件。不用担心,我们可以在 gem 的根目录下创建一个 exe 目录,并在其中放入一个名为 foodie 的文件。这个文件没有扩展名,因为它是一个可执行文件而不是脚本。我们不想到处调用 foodie.rb
,对吧?不,我们不想。我们将用以下内容填充此文件
#!/usr/bin/env ruby
print "nothing."
如果这个文件完全为空,我们会遇到一个不友好的 Errno::ENOEXEC
错误。嘿,说到运行,我们应该从终端用 chmod
将此文件设置为可执行文件
$ chmod +x exe/foodie
好了,我们有了可执行文件,现在怎么办?如果我们重新运行我们的功能,我们将得到没有任何输出。什么都没有!真的!
got: "nothing."
我们的 exe/foodie 文件是空的,这会导致这种什么都没有的悲剧。删除 print "nothing."
行,并用运行我们的 CLI 所需的所有代码替换它,这些代码包括两行
require 'foodie/cli'
Foodie::CLI.start
砰!当我们再次运行 bundle exec cucumber features
时,它会抱怨没有 foodie/cli 文件可以加载。在我们深入了解这个文件的作用之前,我们应该解释一下 exe/foodie 文件中另一行的代码。start
方法启动我们的 CLI
类,并将查找与我们要求的任务匹配的任务。
好的,因此很明显,下一步是创建这个文件,但它有什么作用呢?
这个新的 lib/foodie/cli.rb 文件将使用另一个名为 Thor
的 gem 来定义命令行界面。Thor 是由 Yehuda Katz(以及合作者)创建的,作为 Rake 构建工具的替代方案。Thor 为我们提供了一个方便的 API 来定义我们的 CLI,包括使用横幅和帮助输出。语法与 Rake 非常相似。此外,Rails 和 Bundler 都使用 Thor 来构建它们的 CLI 界面以及它们的生成器基础。是的,Thor 甚至可以生成生成器!
现在,我们只看看如何使用 Thor 制作一个 CLI,然后,如果你表现良好,我们也会看看如何使用它编写一个生成器。
为了使这个 CLI 工作,我们需要创建一个 Foodie::CLI
类,并在其中定义一个 start
方法。或者你知道,可能有一个 gem 可以供我们使用。比如 Thor。这个 gem 以北欧神话中强大的雷神 Thor 命名,它绝对正在快速成为一个同样强大的 gem。这个 gem 是我们将用来构建我们的 CLI 界面,然后是生成器(如果你表现良好,记住?)。
现在让我们像这样定义lib/foodie/cli.rb
文件
require 'thor'
module Foodie
class CLI < Thor
end
end
Thor
类有一系列方法——比如我们在exe/foodie
中引用的start
方法——我们可以用它们来创建这个CLI。顺便说一下,我们的类不必叫CLI
,只是这样做是最佳实践。我们不会神奇地获得这个Thor
类;我们需要告诉我们的gemspec我们依赖于这个gem,方法是在我们之前的add_dependency
下面添加这一行
spec.add_dependency "thor"
要安装这个新的依赖项,我们使用bundle install
。当我们再次运行bundle exec cucumber features
时,我们会发现它现在抱怨找不到我们调用的任务
Could not find task "portray"
...
Could not find task "pluralize"
Thor任务被定义为普通的旧方法,但有一点小变化。为了在我们的Foodie::CLI
类中定义portray
任务,我们将在Foodie::CLI
类中写入以下内容
desc "portray ITEM", "Determines if a piece of food is gross or delicious"
def portray(name)
puts Foodie::Food.portray(name)
end
desc
方法是这里的“小变化”。它之后定义的方法将成为具有给定描述的任务。desc
的第一个参数是任务的使用说明,第二个参数是对该任务完成内容的简短描述。portray
方法定义了一个参数,它将是命令行上传递给该任务的第一个参数。在portray
方法中,我们调用Foodie::Food.portray
并将此参数传递给它。
在Foodie::CLI
类中,我们引用了Foodie::Food
类,而没有要求定义它的文件。在文件顶部的require 'thor'
下面,添加以下行来要求定义Foodie::Food
的文件
require 'foodie'
当我们使用bundle exec cucumber features
重新运行我们的功能时,我们的第一个场景将通过
2 scenarios (1 failed, 1 passed)
4 steps (1 failed, 3 passed)
第二个场景仍然失败,因为我们还没有定义pluralize
任务。这次,我们不是定义一个接受参数的任务,而是定义一个从传递给任务的选项中读取值的任务。为了定义pluralize
任务,我们在Foodie::CLI
中使用以下代码
desc "pluralize", "Pluralizes a word"
method_option :word, aliases: "-w"
def pluralize
puts Foodie::Food.pluralize(options[:word])
end
这里有我们使用的新的method_option
方法,它定义了,嗯,一个方法选项。它接受一个哈希,该哈希指示选项的详细信息,以及它们应该如何返回给我们的任务。查看Thor README以获取所有有效类型的完整列表。我们还可以使用传递给method_option
的:aliases
选项为该方法定义别名。在任务中,我们通过options
哈希引用选项的值,并使用Foodie::Food.pluralize
来复数化一个词。
当我们再次使用bundle exec cucumber features
运行我们的场景时,两个场景都将通过
2 scenarios (2 passed)
4 steps (4 passed)
我们可以尝试通过运行bundle exec exe/foodie portray broccoli
来执行 CLI 应用程序。
如果我们想稍后添加更多选项,我们可以使用method_options
助手来定义它们,如下所示
method_options word: :string, uppercase: :boolean
def pluralize
# accessed as options[:word], options[:uppercase]
end
在这个例子中,options[:word]
将返回一个String
对象,而options[:uppercase]
将返回true
或false
,具体取决于它接收的值。
本介绍应该激发了您对学习更多关于 Thor 的兴趣,建议您现在就开始学习。查看Bundler::CLI
,这是一个使用 Thor 作为 CLI 工具的绝佳示例。
现在我们的功能和规格都通过了,我们已经可以提交代码了。
前面提到过,我们可以将 Thor 用于 CLI 以外的用途,例如创建生成器。这是真的。我们甚至可以创建多个生成器,但现在让我们不要太过分,只专注于创建一个。
你看到这个双关语了吗?是的,很明显。
我们将稍微改变一下,为我们的 gem 添加一个新功能:一个用于recipes目录的生成器。我们的想法是,我们可以像这样运行我们的生成器
foodie recipe dinner steak
这将在当前位置生成一个recipes目录,在该目录中生成一个dinner目录,然后在该目录中生成一个steak.txt文件。这个steak.txt文件将包含食谱的模板,例如配料和说明。
幸运的是,Aruba 有方法可以测试生成器是否生成了文件和目录。让我们创建一个名为features/generator.feature
的新文件,并用以下内容填充它
Feature: Generating things
In order to generate many a thing
As a CLI newbie
I want foodie to hold my hand, tightly
Scenario: Recipes
When I run `foodie recipe dinner steak`
Then the following files should exist:
| dinner/steak.txt |
Then the file "dinner/steak.txt" should contain:
"""
##### Ingredients #####
Ingredients for delicious steak go here.
##### Instructions #####
Tips on how to make delicious steak go here.
"""
需要注意的是,两次“delicious”后面的词都是“steak”,这非常美味。它也是我们传递给运行命令的最后一个参数,因此应该是我们模板中的动态变量。我们很快就会看到如何做到这一点。
当我们运行这个功能时,我们会发现它找不到我们要求生成器生成的dinner/steak.txt文件。为什么呢?
嗯,因为目前我们在Foodie::CLI
中没有定义一个执行此操作的recipe
任务。我们可以像定义 CLI 类一样定义一个生成器类。
desc "recipe", "Generates a recipe scaffold"
def recipe(group, name)
Foodie::Generators::Recipe.start([group, name])
end
此方法的第一个参数是传递给生成器的参数。我们也需要为这个新类引入文件,我们可以通过在lib/foodie/cli.rb的顶部添加这行代码来实现。
require 'foodie/generators/recipe'
要定义这个类,我们从Thor::Group
而不是Thor
继承。我们还需要包含Thor::Actions
模块来定义生成器的辅助方法,其中包括创建文件和目录等方法。由于这是一个生成器类,我们将把它放在一个名为“generators”的新命名空间中,使该文件的位置为lib/foodie/generators/recipe.rb。
require 'thor/group'
module Foodie
module Generators
class Recipe < Thor::Group
include Thor::Actions
argument :group, type: :string
argument :name, type: :string
end
end
end
通过从Thor::Group
继承,我们定义了一个生成器而不是一个 CLI。当我们调用argument
时,我们正在为我们的生成器定义参数。这些参数与从Foodie::CLI
中的recipe
任务传递过来的参数相同,顺序也相同。
为了让这个生成器,你知道,生成东西,我们只需在类中定义方法。在Thor::Group
后代中定义的所有方法将在对其调用start
时运行。让我们在这个类中定义一个create_group
方法,它将使用我们传入的名称创建一个目录。
def create_group
empty_directory(group)
end
为了将文件放在这个目录中,并为我们的 foodie 朋友节省一些打字,我们将使用template
方法。这将从预定义的源位置复制一个文件,并将其评估为 ERB 模板。我们现在将定义一个copy_recipe
方法来执行此操作。
def copy_recipe
template("recipe.txt", "#{group}/#{name}.txt")
end
如果我们在该文件中进行任何 ERB 调用,它们将被评估,结果将输出到新的模板文件中。
我们已经很久没有运行任何东西了。嘿,这里有一个想法!让我们运行我们的生成器!我们可以通过运行bundle exec exe/foodie recipe dinner steak
来实现这一点,但仅此一次。通常我们只通过 Cucumber 来测试它。当我们运行此命令时,我们将被告知所有这些内容。
create dinner
Could not find "recipe.txt" in any of your source paths. Please invoke Foodie::Generators::Recipe.source_root(PATH) with the PATH containing your templates. Currently you have no source paths.
第一行告诉我们dinner目录已经创建。那里没什么特别的。
第二行更令人兴奋!它要求我们为生成器定义 `source_root` 方法。这很简单!我们可以像这样在 `Foodie::Generators::Recipe` 中将其定义为类方法
def self.source_root
File.dirname(__FILE__) + "/recipe"
end
这告诉我们的生成器在哪里找到模板。现在我们只需要创建模板,我们可以将其放在 `lib/foodie/generators/recipe/recipe.txt` 中
##### Ingredients #####
Ingredients for delicious <%= name %> go here.
##### Instructions #####
Tips on how to make delicious <%= name %> go here.
当我们使用 `template` 方法时,模板文件被视为 ERB 模板,它在当前 `binding` 中进行评估,这意味着它可以访问与调用它的方法相同的方法和变量。
就是这样!当我们运行 `bundle exec cucumber features` 时,我们所有的功能都将通过!
3 scenarios (3 passed)
7 steps (7 passed)
太棒了,对吧?
如果我们还没有,我们应该提交存储库的所有文件
$ git add .
$ git commit -m "The beginnings of the foodie gem"
这是因为 `foodie.gemspec` 文件使用 `git ls-files` 来检测在发布 gem 时应添加哪些文件。
在发布 gem 之前,最后一步是在 `foodie.gemspec` 文件中为其提供摘要和描述。
现在我们将确保我们的 gem 准备好发布。为此,我们可以运行 `rake build`,它将构建 gem 的本地副本,然后运行 `gem install pkg/foodie-0.1.0.gem` 来安装它。然后我们可以通过运行它提供的命令在本地尝试它。一旦我们知道一切正常,我们就可以发布第一个版本。
要发布 gem 的第一个版本,我们可以使用 `rake release` 命令,前提是我们已经提交了所有内容。此命令执行几件事。首先,它将 gem 构建到 `pkg` 目录中,准备推送到 Rubygems.org。
其次,它为当前提交创建一个标签,反映当前版本,并将其推送到 git 远程仓库。建议我们在 GitHub 上托管代码,以便其他人可以轻松找到它。
如果此推送成功,那么最后一步将是推送到 Rubygems.org,这将允许其他人下载和安装 gem。
如果我们想发布 gem 的第二个版本,我们应该进行更改,然后将它们提交到 GitHub。之后,我们将 `lib/foodie/version.rb` 中的版本号更改为我们认为合适的任何版本,对 GitHub 进行另一个提交,并附上一个有用的消息,例如“bumped to 0.0.2”,然后再次运行 `rake release`。
如果我们想让这个过程更容易,我们可以使用以下命令安装“gem-release” gem:
$ gem install gem-release
此 gem 提供了一些方法来帮助进行 gem 开发,但最有用的是 `gem bump` 命令,它将 gem 版本提升到下一个补丁级别。此方法还接受选项来执行以下操作
$ gem bump --version minor # bumps to the next minor version
$ gem bump --version major # bumps to the next major version
$ gem bump --version 1.1.1 # bumps to the specified version
有关更多信息,请查看 “gem-release” GitHub 仓库主页。
虽然这不是一个关于 gem 开发的详尽指南,但它涵盖了 gem 开发所需的必要基础知识。强烈建议您查看 Bundler、Rails 和 RSpec 的源代码,以获取优秀的 gem 开发示例。
如果您正在寻找此示例的完整源代码,可以在 此处找到。