仕事でformによるファイルアップローダを作っていた。これはヘルパーライブラリとして切り出すと他のところでも使い回せるなーとか思っていたら、「社内ライブラリを OSS 化すべきだ」という記事のことを思い出した。

一方、QA@ITで「今時rubygems作るんならBundlerだよねー」というご指摘をたくさんいただいたことも思い出した。

ということで、仕事のために書いたけれどもビジネスロジックは含んでいないライブラリを「Tupper」として切り出し、Bundlerを使ってrubygemsとして公開するところまでの作業記録を晒してみる。

Tupper

Tupper

スケルトンを作る

% bundle gem tupper
      create  tupper/Gemfile
      create  tupper/Rakefile
      create  tupper/LICENSE
      create  tupper/README.md
      create  tupper/.gitignore
      create  tupper/tupper.gemspec
      create  tupper/lib/tupper.rb
      create  tupper/lib/tupper/version.rb
Initializating git repo in /path/to/current_dir/tupper

Gemfile.lockはバージョン管理すべき?

スケルトンを作った時点ですでにGemfile.lockは.gitignoreに書かれているので、「バージョン管理しちゃダメだよ」というメッセージが込められている。今回まるっと参考にさせていただいている「Bundlerを使ったGem作成メモ (自分用) – ただのにっき(2012-02-18)」では、

ところで.gitignoreに「Gemfile.lock」が含まれている(つまりリポジトリには含めない)んだけど、これをリポジトリに含める場合と含めない場合の判断基準がよくわからん。

と迷っておられた。同じくQA@ITから「Gemfile.lockをバージョン管理するかどうかの指針はありますか – QA@IT」→「Clarifying the Roles of the .gemspec and Gemfile « Katz Got Your Tongue?」→「LangTurn: gemspecとGemfileの役割をはっきりさせておく」…とたどり、

Gemを開発するときは、Gemfileにはgemspecを使って重複を回避しましょう。普通、GemfileにはRubygemsのソースとgemspecの一行だけが書かれるべきです。Gemfile.lockはバージョン管理にチェックインしてはいけません。

…とあったので納得。

各種ファイルを編集する

すっきりしたところで続き。bundlerが作った各種ファイルを編集していく。

tupper.gemspec

tupper.gemspecには…

  • gemのメタ情報
  • 開発時に依存するgem

…を記述する。順に見ていこう。

gemのメタ情報を書く

gem.authorsとgem.emailは(たぶん)gitのグローバル設定から取得して入れてくれてる。最低限書くのは「TODO」の表示があるgem.descriptionとgem.summary。gem.homepageにはとりあえずGitHubのリポジトリのURLを書いておいた。

開発時に依存するgemを書く

テストを書かない奴は屑だ!テストを書く奴はよく訓練された屑だ!」ということなので、テストを添付することにする。今時はもうRSpecで書きたいよね、ということで、依存するgemも.gemspecに書いておく。

Gem::Specification.new do |gem|

# snip

  gem.add_development_dependency "rspec"
  gem.add_development_dependency "fakefs"
end

Tupperの場合はファイルを取り扱うので、ファイルシステムのモックライブラリである「fakefs」も追加してある。これでbundle installするとrspec / fakefsがインストールされるので、テストを実行できるようになる。

あ、gem本体の動作が依存するgemはちゃんとGemfileに書くこと。Tupperは特に依存gemが存在しないので何も追記していない。

Rakefile

デフォルトの状態では…

% rake -T
rake build    # Build tupper-0.0.1.gem into the pkg directory
rake install  # Build and install tupper-0.0.1.gem into system gems
rake release  # Create tag v0.0.1 and build and push tupper-0.0.1.gem to Rubygems

build, install, releaseの3タスクが用意されている。

specのタスクを書く

ビルドしてリリースするにはデフォルトでも十分だが、せっかくRSpecでテストを実行できるよう.gemspecを書いたのだし、テストの実行手段も準備しておこう。

% bundle exec rspec spec/

としてもテストを実行することはできるが、どうせRakefileがあるならテストを実行できるようにしておきたい。bundlerが作ってくれるRakefileには最初の2行しか書かれていないので、RSpecを走らせるタスクを追加しておく。

4行目以降が追加した部分。

#!/usr/bin/env rake
require "bundler/gem_tasks"

Bundler.setup
require 'rspec/core/rake_task'

desc "run spec"
RSpec::Core::RakeTask.new(:spec) do |t|
  t.rspec_opts = ["-c", "-fs"]
end

これでrake specとすると、spce/以下のテストケースがすべて実行される。

lib/tupper.rb

本体のコードを書く。スケルトンでは「module Tupper」と定義されるが、「class Tupper」に修正して実装コードを追記した。

spec/tupper.rb

テストコードを書く。特に変わったことはしていない。fakefsはまだ最低限の使い方しかしていないが、なかなか便利に使える。

lib/tupper/version.rb

バージョン情報を書くだけのファイルが用意されているので、適切なバージョンを設定する。デフォルトだと0.0.1なので、ちょっとプロダクトコードに使うのはためらわれる雰囲気。Tupperはこのままプロダクトに突っ込む予定なので、1.0.0でリリースしてしまう。

class Tupper
  VERSION = "1.0.0"
end

本体同様moduleをclassに変更してある。

README.md

GitHubで公開したときのトップページになるので、descriptionとusageぐらいは書いておこう。

git / GitHub

スケルトンを作った段階で、すでにgit initはされた状態になっている。だいたい整ったらコミットしておこう。

書いたコードは当然GitHubで公開する前提なので、GitHubにリモートリポジトリを作り、pushしておく。Webから新しいリポジトリを作っておき…

% git remote add origin git@github.com:kwappa/tupper.git
% git push -u origin master

gemのビルド / インストール / リリース

ビルド
% rake build
tupper 1.0.0 built to pkg/tupper-1.0.0.gem
% ls pkg
tupper-1.0.0.gem
インストール
% rake install
tupper 1.0.0 built to pkg/tupper-1.0.0.gem
tupper (1.0.0) installed
リリース
% rake release
tupper 1.0.0 built to pkg/tupper-1.0.0.gem
Tagged v1.0.0
Pushed git commits and tags
Pushed tupper 1.0.0 to rubygems.org

コマンド一発は簡単すぎないか?ということで、GitHubを確認してみると…おおタグが打たれている。RubyGems.orgを確認してみると…おお公開されている。

というわけで拍子抜けするほど簡単だった。rubygems.orgへのアップロードはたぶん~/.gem/credentialsに書いてあるAPI keyで認証されているはずなので、うまく行かないときはRubyGems.orgのプロフィールページを参考に、API keyをローカルに取り込んでおこう。

Tupperの使い方は あとで書く サンプル書いた。公開するつもりで書くと結合度は疎になるし、よいコード / わかりやすいコードを書こうという心理が働くし、テストもきちんと書くし、社内ライブラリをOSS化すべき理由は3つどころかもっとたくさんあるような気がする。

ということで今後も可能な限り晒していこうと思う。pebblesばっかりなのも切ないしね。