📦

10stepで行うGemの作成からリリースまで

2013/02/19に公開

最近gemを作る機会がやたら多いので、これを機にGemの作り方をまとめがてら ちょっとしたGemを作ってRubyGemsで公開するための一連の流れをメモしていきます。

1. Gemのひな形を作る

まず最初にGemファイルのひな形を作ります。
※ hogeまたはHogeのところは適当に読み替えてください
※ コミットログ(-mの後の英語っぽいもの)も適宜書き換えてください

$ bundle gem hoge
      create  hoge/Gemfile
      create  hoge/Rakefile
      create  hoge/LICENSE.txt
      create  hoge/README.md
      create  hoge/.gitignore
      create  hoge/hoge.gemspec
      create  hoge/lib/hoge.rb
      create  hoge/lib/hoge/version.rb
Initializating git repo in /tmp/hoge

2. ひな形を記憶する

もともとのひな形を記憶します

$ cd hoge
$ git add .
$ git commit -a -m 'first commit.'
[master (root-commit) 8e2ae2c] first commit.
 8 files changed, 100 insertions(+), 0 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 Gemfile
 create mode 100644 LICENSE.txt
 create mode 100644 README.md
 create mode 100644 Rakefile
 create mode 100644 hoge.gemspec
 create mode 100644 lib/hoge.rb
 create mode 100644 lib/hoge/version.rb

3. .gitignoreを追記する

不要なファイルをコミットしないように.gitignoreを編集します

まず下記を追記します。

.gitignore
+ vendor/bundle
+ *.swp

次に下記を削除します

.gitignore
- Gemfile.lock

ここまできたらまたコミットします

$ git commit -a -m 'Modified .gitignore'
[master 8e30bfb] Modified .gitignore
 1 files changed, 2 insertions(+), 1 deletions(-)

4. 依存ライブラリをインストール

Gemfileに対してテストに必要なライブラリ等を追記します

$ vi Gemfile
Gemfile
〜〜〜〜〜(前略)〜〜〜〜〜

+ # 下記を追記
+ gem 'rake'
+ gem 'yard', require: false
+ gem 'redcarpet', require: false
+ 
+ group :test do
+   gem 'rspec', '~>2.12.0'
+   gem 'cucumber'
+   gem 'aruba'
+ end

rvmを使っている場合はgemsetを作っておくことをお勧めします。

$ rvm use 1.9.3@hoge --rvmrc --create --install

bundlerを使って各種gemをインストールします

$ bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/.........
Fetching gem metadata from https://rubygems.org/..
Installing rake (10.0.3)
Installing ffi (1.4.0) with native extensions
Installing childprocess (0.3.8)
Installing builder (3.1.4)
Installing diff-lcs (1.1.3)
Installing json (1.7.7) with native extensions
Installing gherkin (2.11.6) with native extensions
Installing cucumber (1.2.1)
Installing rspec-expectations (2.12.1)
Installing aruba (0.5.1)
Using hoge (0.0.1) from source at /tmp/hoge
Installing redcarpet (2.2.2) with native extensions
Installing rspec-core (2.12.2)
Installing rspec-mocks (2.12.2)
Installing rspec (2.12.0)
Installing yard (0.8.4.1)
Using bundler (1.2.3)
Your bundle is complete! It was installed into ./vendor/bundle

ここまでできたらまたコミットします

$ git add .
$ git commit -a -m 'Config dependency.'

5. 実際にビルドするテストを書く

実際にビルドするためのテストを書いてみます。 まずテストのためのディレクトリを作成します。

$ mkdir -p features/{step_definitions,support}

次にcucumber用の環境設定ファイルを作成します

$ vi features/support/env.rb
features/support/env.rb
# encoding: utf-8
 
 
require 'rake'
require 'aruba/cucumber'
 
 
Before do
  @features_root = File.expand_path('../../../', __FILE__)
  @dirs          = [@features_root]
end

そしてfeatureとstepを作成します

$ vi features/build.feature
features/build.feature
# language: ja
 
機能: Gemのビルド
 
  シナリオ: gemコマンドを使ったビルド
    前提 プロジェクトルートにいる
    かつ ディレクトリ"pkg"が削除されている
    もし rakeタスク"build"を実行する
    ならば 標準出力が下記の正規表現にマッチする:
      """
      hoge \d.\d.\d(.\w+)? built to pkg/hoge-\d.\d.\d(.\w+)?.gem
      """
    かつ 終了コードが"0"である
    かつ ディレクトリ"pkg"が存在している
    かつ 下記のファイルが存在している:
      |pkg/hoge-0.0.1.gem|

$ vi features/step_definitions/gem_steps.rb
features/step_definitions/gem_steps.rb
# encoding: utf-8
 
 
前提 /^プロジェクトルートにいる$/ do
  `pwd`.strip.should == @features_root
end
 
前提 /^ディレクトリ"(.*?)"が削除されている$/ do |path|
  FileUtils.rm_rf path
  check_directory_presence([path], false)
end
 
もし /^rakeタスク"(.*?)"を実行する$/ do |task_name|
  run_simple "rake #{task_name}"
end
 
 
ならば /^終了コードが"(.*?)"である$/ do |exit_status|
  assert_exit_status(exit_status.to_i)
end
 
ならば /^標準出力が下記の正規表現にマッチする:$/ do |expected|
  assert_matching_output(expected, all_stdout)
end
 
ならば /^ディレクトリ"(.*?)"が存在して(いる|いない)$/ do |directory,presence|
  check_directory_presence([directory], (presence == "いる"))
end
 
ならば /^下記のファイルが存在している:$/ do |files|
  check_file_presence(files.raw.map{|file_row| file_row[0]}, true)
end

この時点でテストが失敗することを確認します。

$ bundle exec cucumber features
# language: ja
機能: Gemのビルド

  シナリオ: gemコマンドを使ったビルド     # features/build.feature:5
    前提プロジェクトルートにいる         # features/step_definitions/gem_steps.rb:4
    かつディレクトリ"pkg"が削除されている  # features/step_definitions/gem_steps.rb:8
    もしrakeタスク"build"を実行する  # features/step_definitions/gem_steps.rb:13
      Exit status was 1 but expected it to be 0. Output:
      
      rake aborted!
      ERROR:  While executing gem ... (Gem::InvalidSpecificationException)
          "FIXME" or "TODO" is not a description
      ~/.rvm/gems/ruby-1.9.3-p385@hoge/gems/bundler-1.2.3/lib/bundler/gem_helper.rb:146:in `sh'
      ~/.rvm/gems/ruby-1.9.3-p385@hoge/gems/bundler-1.2.3/lib/bundler/gem_helper.rb:56:in `build_gem'
      ~/.rvm/gems/ruby-1.9.3-p385@hoge/gems/bundler-1.2.3/lib/bundler/gem_helper.rb:38:in `block in install'
      Tasks: TOP => build
      (See full trace by running task with --trace)
      
       (RSpec::Expectations::ExpectationNotMetError)
      ./features/step_definitions/gem_steps.rb:14:in `/^rakeタスク"(.*?)"を実行する$/'
      features/build.feature:8:in `もしrakeタスク"build"を実行する'
    ならば標準出力が下記の正規表現にマッチする: # features/step_definitions/gem_steps.rb:22
      """
      hoge \d.\d.\d(.\w+)? built to pkg/hoge-\d.\d.\d(.\w+)?.gem
      """
    かつ終了コードが"0"である         # features/step_definitions/gem_steps.rb:18
    かつディレクトリ"pkg"が存在している   # features/step_definitions/gem_steps.rb:26
    かつ下記のファイルが存在している:      # features/step_definitions/gem_steps.rb:30
      | pkg/hoge-0.0.1.gem |

Failing Scenarios:
cucumber features/build.feature:5 # Scenario: gemコマンドを使ったビルド

1 scenario (1 failed)
7 steps (1 failed, 4 skipped, 2 passed)
0m0.982s

エラーメッセージに出ている通りにhoge.gemspecにあるgem.descriptionとgem.summaryをGemの説明に書き換えます

$ vi hoge.gemspec
hoge.gemspec
  gem.description   = %q{Hoge hoge}
  gem.summary       = %q{Fuga fuga}

もう一度テストを実行し、正常に終了することを確認します。

$ bundle exec cucumber features
# language: ja
機能: Gemのビルド

  シナリオ: gemコマンドを使ったビルド     # features/build.feature:5
    前提プロジェクトルートにいる         # features/step_definitions/gem_steps.rb:4
    かつディレクトリ"pkg"が削除されている  # features/step_definitions/gem_steps.rb:8
    もしrakeタスク"build"を実行する  # features/step_definitions/gem_steps.rb:13
    ならば標準出力が下記の正規表現にマッチする: # features/step_definitions/gem_steps.rb:22
      """
      hoge \d.\d.\d(.\w+)? built to pkg/hoge-\d.\d.\d(.\w+)?.gem
      """
    かつ終了コードが"0"である         # features/step_definitions/gem_steps.rb:18
    かつディレクトリ"pkg"が存在している   # features/step_definitions/gem_steps.rb:26
    かつ下記のファイルが存在している:      # features/step_definitions/gem_steps.rb:30
      | pkg/hoge-0.0.1.gem |

1 scenario (1 passed)
7 steps (7 passed)
0m0.967s

ここまで来たらまたコミットします

$ git add .
$ git commit -a -m 'Add build features'
[master dd40ddf] Add build features
 4 files changed, 72 insertions(+), 2 deletions(-)
 create mode 100644 features/build.feature
 create mode 100644 features/step_definitions/gem_steps.rb
 create mode 100644 features/support/env.rb

6. rakeタスクを作成する

rakeでテストとドキュメンテーションができるようにタスクを書いて行きます。

まずタスクファイルを入れるためのディレクトリを作成します

$ mkdir lib/tasks

次にcucumber用のタスクファイルを作成します

$ vi lib/tasks/cucumber.rake
lib/tasks/cucumber.rake
require 'cucumber'
require 'cucumber/rake/task'
 
 
Cucumber::Rake::Task.new(:features) do |t|
  t.cucumber_opts = "features --format pretty"
end

さらにyard用のタスクファイルを作成します

$ vi lib/tasks/yard.rake
lib/tasks/yard.rake
require 'yard'
require 'yard/rake/yardoc_task'
 
YARD::Rake::YardocTask.new do |t|
  t.files   = ['app/controllers/**/*.rb','app/helpers/**/*.rb', 'app/mailers/**/*.rb', 'app/models/**/*.rb', 'lib/**/*.rb']
  t.options = ['--no-private']
  t.options << '--debug' << '--verbose' if $trace
end

最後にRakefileに下記を追記します

Rakefile
Dir.glob(File.expand_path('../lib/tasks/*.rake', __FILE__)).each { |f| load f }

task :default => [ :features, :yard ]

実際に追加したものが動くか確認します。 下記のようになれば成功です。

$ bundle exec rake
~/.rvm/rubies/ruby-1.9.3-p385/bin/ruby -S bundle exec cucumber features --format pretty
# language: ja
機能: Gemのビルド

  シナリオ: gemコマンドを使ったビルド     # features/build.feature:5
    前提プロジェクトルートにいる         # features/step_definitions/gem_steps.rb:4
    かつディレクトリ"pkg"が削除されている  # features/step_definitions/gem_steps.rb:8
    もしrakeタスク"build"を実行する  # features/step_definitions/gem_steps.rb:13
    ならば標準出力が下記の正規表現にマッチする: # features/step_definitions/gem_steps.rb:22
      """
      hoge \d.\d.\d(.\w+)? built to pkg/hoge-\d.\d.\d(.\w+)?.gem
      """
    かつ終了コードが"0"である         # features/step_definitions/gem_steps.rb:18
    かつディレクトリ"pkg"が存在している   # features/step_definitions/gem_steps.rb:26
    かつ下記のファイルが存在している:      # features/step_definitions/gem_steps.rb:30
      | pkg/hoge-0.0.1.gem |

1 scenario (1 passed)
7 steps (7 passed)
0m1.105s
Files:           2
Modules:         1 (    1 undocumented)
Classes:         0 (    0 undocumented)
Constants:       1 (    1 undocumented)
Methods:         0 (    0 undocumented)
 0.00% documented

ここまでの作業を記録します

$ git add .
$ git commit -a -m 'Add rake tasks.'
[master 47906c7] Add rake tasks.
 3 files changed, 24 insertions(+), 0 deletions(-)
 create mode 100644 lib/tasks/cucumber.rake
 create mode 100644 lib/tasks/yard.rake

7. ドキュメントを記述する

READMEやモジュール(Hoge)のYARDを必要に応じて記述します。

変更した後はその作業をコミットします。

$ git commit -a -m 'Add Documents.'
[master 563a5b1] Add Documents.
 4 files changed, 33 insertions(+), 33 deletions(-)
 rewrite README.md (90%)

8. Rubygemsにサインアップします

Rubygemsのサイト( https://rubygems.org )に行き、右上にある”sign up”からユーザー登録をします

サインアップが完了したら、下記のコマンドを使ってAPIキーを取得します。

※REGISTED_HANDLEの部分は、登録したHandleを指定してください
※curlがインストールされていない場合はyum install curlでインストールしてください

$ curl -u REGISTED_HANDLE https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials
Enter host password for user 'REGISTED_HANDLE': 登録したパスワードを入力
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0    56    0    56    0     0     95      0 --:--:-- --:--:-- --:--:--     0

catした時に、rubygems_api_keyに32桁の半角英数が入っていれば成功です。

$ cat ~/.gem/credentials
---
:rubygems_api_key: 1234567890abcdefghijklmnopqrstuv

9. RubyGemsで公開します

下記のコマンドを実行することで、rubygemsで作成したGemを公開することができます。

$ bundle exec rake release
hoge 0.0.1.pre built to pkg/hoge-0.0.1.gem
Tagged v0.0.1
Pushed git commits and tags
Pushed hoge 0.0.1 to rubygems.org

10. 公開されたGemを確認する

公開されたGemを確認するには、RubyGemsのサイト右上にある自分の名前をクリックするか、 同じく右上にあるdashbordから確認することができます。

また、下記のコマンドでインストール可能になっています。

$ gem install hoge

Gemはホントに小さいプログラムでも作成/登録できるので、ちょっとしたツールを作って試してみると良いかもしれません。 途中で出てきたbundlerやgit,yard,cucumber,arubaについてそのうちまとめて記事にします。

Discussion