jewelerとgithubで始める初めてのRubyGem作成

WebJourney用に新しく開発したCouchDB用のデータマッパー"CouchResource"ですが、ちゃんと公開しないとガラパゴスになりそうなので、RubyGem として利用できるようにしよう、と決心をして作業を始めました。

  • github のアカウントは作成済みである
  • githubリポジトリを作る方法は知っている
  • RubyGem の作り方は知らない

上記スペックの人間が、jewelerというgemの存在を知り、試用してみたら、[これはべんり]だったので記録しておきます。

  • まず jeweler をインストール

「jeweler って何?」っていう話ですが http://github.com/technicalpickles/jeweler/tree/master にあります。githubでRubyGemを成果物にするプロジェクトで役立つヘルパー。ということでインストール。

$ gem sources -a http://gems.github.com
$ sudo gem install technicalpickles-jeweler
  • github の設定を ~/.gitconfig に書き込む

git config --global コマンドで user.name, user.email などは設定済みですが、github.user, github.token を設定します。github.user は github のアカウント名、github.token は API Tokenです。自分のアカウントページを見ると、

Your User Information

Username: yssk22 
API Token: XXXXXXXXXXXXXXXXXXXXXXX

な具合で記載されているので、この値をgit config --global コマンドで設定します。

$ git config --global github.user yssk22
$ git config --global github.token XXXXXXXXXXXXXXXXXXXXXXX

# 一応確認するとこんな感じになってます。
$ cat ~/.gitconfig
[user]
	name = Yohei Sasaki
	email = yssk22 at gmail.com
[github]
	user = yssk22
	token = XXXXXXXXXXXXXXXXXXXXXXX

jeweler をインストール済みの環境であれば jeweler コマンドが使えます。これを使ってローカルリポジトリを作るのです。今回は couch_resource というプロジェクトを作ります。また、Ruby標準のTest::Unitでテストケースを作成しているので --testunit を引数に渡します。--rspec などもできるようです。--create-repo を使うとリポジトリの作成(+gemの有効化)もしてくれるようなのですが、既にリポジトリは手作業で作ってしまったので、よく分かりません。

$ jeweler couch_resource --testunit
	create	.gitignore
	create	Rakefile
	create	LICENSE
	create	README.rdoc
	create	lib
	create	lib/couch_resource.rb
	create	test
	create	test/test_helper.rb
	create	test/couch_resource_test.rb
Jeweler has prepared your gem in couch_resource

はい、couch_resourceディレクトリができました。このディレクトリでRakeを叩くとこんな感じでいろいろタスクがあります。

$ rake -T
(in /Users/yssk22/project/couch_resource)
rake VERSION.yml         # Setup initial version of 0.0.0
rake build               # Build gem
rake clobber_rdoc        # Remove rdoc products
rake gemspec             # Generate and validates gemspec
rake gemspec:generate    # Generates the gemspec, using version from VERSIO...
rake gemspec:validate    # Validates the gemspec
rake install             # Install gem using sudo
rake rdoc                # Build the rdoc HTML Files
rake release             # Release the current version.
rake rerdoc              # Force a rebuild of the RDOC files
rake test                # Run tests
rake version             # Displays the current version
rake version:bump:major  # Bump the gemspec by a major version.
rake version:bump:minor  # Bump the gemspec by a minor version.
rake version:bump:patch  # Bump the gemspec by a patch version.
rake version:setup       # Setup initial version of 0.0.0
rake version:write       # Writes out an explicit version.

これは便利!!!Rakefile の中身を抜粋するとこんな感じになってます。

begin
  require 'jeweler'
  Jeweler::Tasks.new do |gem|
    gem.name = "couch_resource"
    gem.summary = %Q{TODO}
    gem.email = "yssk22 AT gmail.com"
    gem.homepage = "http://github.com/yssk22/couch_resource"
    gem.authors = ["Yohei Sasaki"]
    # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
  end
rescue LoadError
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
end

で、ワークフローは、githubのjewelerのREADMEには、

Hack, commit, hack, commit, etc, etc

とありますから、ここでもう自分のソースを追加ですね。自分の場合は既にソースはあるので、そこからlib, test などにソースを追加しました。

で、とりあえずは rake test かと思って、

$ rake test
(in /Users/yssk22/project/couch_resource)
Loaded suite /opt/ruby-enterprise-1.8.6-20081215/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
Started
F
Finished in 0.000915 seconds.

  1) Failure:
test_something_for_real(CouchResourceTest) [./test/couch_resource_test.rb:5]:
hey buddy, you should probably rename this file and start testing for real.

1 tests, 1 assertions, 1 failures, 0 errors
rake aborted!
Command failed with status (1): [/opt/ruby-enterprise-1.8.6-20081215/bin/ru...]

(See full trace by running task with --trace)

あう。2つ問題。

  • couch_resource_test.rb は自分の作ったファイルではなく、jeweler が生成した初期テスト。必ず失敗するようになっている。
  • そもそも 1 tests, 1 assertions というのがあり得ない。test タスク修正必要。

Rakefile 内の Rake::TestTask の定義を見ると、

Rake::TestTask.new(:test) do |test|
  test.libs << 'lib' << 'test'
  test.pattern = 'test/**/*_test.rb'
  test.verbose = false
end

となっているので修正。*_test.rb というファイル名は好かんのです。test_*.rbがいいです(その方がtab補完が便利に使えるケースが多いので)。

Rake::TestTask.new(:test) do |t|
  t.libs << 'lib' << 'test'
  test.pattern = 'test/**/test_*.rb'
  t.verbose = true
end

さて、これでテストも通ったので、いよいよリリースです。gemとかよく分からないけれどとりあえずspecは必要だろうと、rake gemspec を発行。

$ rake gemspec
(in /Users/yssk22/project/couch_resource)
Created VERSION.yml: 0.0.0
Generated: couch_resource.gemspec
couch_resource.gemspec is valid.

VERSION.yml: 0.0.0 ってww

$ cat VERSION.yml 
--- 
:major: 0
:minor: 0
:patch: 0

せめて、0.1.0 ぐらいにはしよう、と思って、

$ rake version:bump:minor
(in /Users/yssk22/project/couch_resource)
Current version: 0.0.0
Updated version: 0.1.0
$ cat VERSION.yml
--- 
:major: 0
:minor: 1
:patch: 0

OK。これで再び rake gemspec を実行し、生成されたgemspecを見ます。

$ cat couch_resource.gemspec 
# -*- encoding: utf-8 -*-

Gem::Specification.new do |s|
  s.name = %q{couch_resource}
  s.version = "0.1.0"

  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
  s.authors = ["Yohei Sasaki"]
  s.date = %q{2009-03-10}
  s.email = %q{yssk22@gmail.com}
  s.extra_rdoc_files = ["README.rdoc", "LICENSE"]
  s.files = ["README.rdoc", "VERSION.yml", "lib/couch_resource", "lib/couch_resource/base.rb", "lib/couch_resource/callbacks.rb", "lib/couch_resource/connection.rb", "lib/couch_resource/error.rb", "lib/couch_resource/struct.rb", "lib/couch_resource/validations.rb", "lib/couch_resource/view.rb", "lib/couch_resource.rb", "test/test_base.rb", "test/test_callbacks.rb", "test/test_connection.rb", "test/test_struct.rb", "test/test_validations.rb", "LICENSE"]
  s.has_rdoc = true
  s.homepage = %q{http://github.com/yssk22/couch_resource}
  s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
  s.require_paths = ["lib"]
  s.rubygems_version = %q{1.3.1}
  s.summary = %q{TODO}

  if s.respond_to? :specification_version then
    current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
    s.specification_version = 2

    if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
    else
    end
  else
  end
end

エクセレント!!!じゃ、gem作ろう。build タスクでgemがpkgディレクトリにできます。

$ rake build
(in /Users/yssk22/project/couch_resource)
WARNING:  no rubyforge_project specified
  Successfully built RubyGem
  Name: couch_resource
  Version: 0.1.0
  File: couch_resource-0.1.0.gem
mac:couch_resource yssk22$ ls pkg
couch_resource-0.1.0.gem
<<

すげー。install タスクを使うと、生成されたgemをローカルにインストールできるようです。

>||
$ rake install
(in /Users/yssk22/project/couch_resource)
Executing "sudo gem install ./pkg/couch_resource-0.1.0.gem":
Password:
Successfully installed couch_resource-0.1.0
1 gem installed
Installing ri documentation for couch_resource-0.1.0...
Installing RDoc documentation for couch_resource-0.1.0...

ついでに、動くか試してみます。

$ irb
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'couch_resource'
=> true
irb(main):003:0> class TestCouch < CouchResource::Base
irb(main):004:1> database = "http://localhost:5984/test_couch"
irb(main):005:1> string :title
irb(main):006:1> end
=> []

簡単gem化。さて、大丈夫そうなので、いい加減ソースのコミットをしましょうか。.gitignore を確認するといい具合に設定されてます。.DS_Store がある当たりMacも考慮。

$ cat .gitignore 
*1236626942*.sw?
.DS_Store
coverage
rdoc
pkg

ということで、気兼ねなく git add して、git commit しておきます。で push。jewelerでディレクトリを作った場合、remote add はいらないです*1

で、gemspec なども追加されているので、最後にgithub側のリポジトリの設定で、リポジトリのWebコンソールにアクセスし(https://github.com/{user_name}/{repo_name}、edit を選択して、RubyGem のチェックボックスにチェックをします。

これで、github側でgemの生成をしてくれるようになり、ユーザー側のgemのソースに http://gems.github.com を追加している場合は、gem install {username}-{gemname} でインストールできるようになります。

gemの生成には少し時間がかかるようで、http://gems.github.com/list.html にリストされればreadyです。

さて、次にreleaseタスクの話になるわけですが(gem/push/tag付けの自動化)、とりあえずはいいや、ということでこのエントリはここまで。

それにしても、すばらしかった!

併せて読みたい:
http://d.hatena.ne.jp/secondlife/20080520/1211286537

*1:ディレクトリ名.gitで設定済み