先日、ラクガキサービスLeenoのAPIをラップしたgemを公開しました。
http://rubygems.org/gems/leeno
ソースはこちらから。
https://github.com/soplana/leeno
ということで、今回gemを作成する手順をサンプルgemを作成しながら備忘録として残しておきます。
今回サンプルで作成するgemはto_gunmaというgemで、最近流行りの「◯◯県は群馬県になりました。」というメッセージで有名な「ぐんまのやぼう」というアプリに肖って、[].to_gunmaみたいに呼び出すと、"Arrayは群馬県になりました。"ってメッセージが帰ってくるだけのショボイgemを作ろうと思います。
グンマーがRubyのオブジェクトを制圧するgemという事です。
グンマー凄い。
gemを作成する方法としてはいくつかあるようですが、今回はbundlerを使用して作成していきます。
また今回作成したgemは gem install to_gunma で実際install可能となっております。
gemプロジェクトを作成
まずはbundleでgemプロジェクトのひな形を作成します。
soplana ~/work $bundle gem to_gunma
create to_gunma/Gemfile
create to_gunma/Rakefile
create to_gunma/LICENSE
create to_gunma/README.md
create to_gunma/.gitignore
create to_gunma/to_gunma.gemspec
create to_gunma/lib/to_gunma.rb
create to_gunma/lib/to_gunma/version.rb
Initializating git repo in /Users/soplana/work/to_gunma
githubに登録
githubアカウントを取得してリポジトリを作成し、最初のcommitをしておきます。
soplana ~/work $cd to_gunma/
soplana ~/work/to_gunma $git init
Reinitialized existing Git repository in /Users/soplana/work/to_gunma/.git/
soplana ~/work/to_gunma $git add .
soplana ~/work/to_gunma $git commit -m "init"
[master (root-commit) c1dd106] init
8 files changed, 99 insertions(+), 0 deletions(-)
create mode 100644 .gitignore
create mode 100644 Gemfile
create mode 100644 LICENSE
create mode 100644 README.md
create mode 100644 Rakefile
create mode 100644 lib/to_gunma.rb
create mode 100644 lib/to_gunma/version.rb
create mode 100644 to_gunma.gemspec
soplana ~/work/to_gunma $git remote add origin git@github.com:soplana/to_gunma.git
soplana ~/work/to_gunma $git push -u origin master
Counting objects: 12, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (12/12), 2.16 KiB, done.
Total 12 (delta 0), reused 0 (delta 0)
To git@github.com:soplana/to_gunma.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
.gemspecの編集
to_gunma.gemspecを開き、gem.descriptionとgem.summaryを編集します。
ついでにgem.homepageに先程作成したgithubページでも追加しておきましょう。
ここにTODOが残っているとエラーになりビルド出来ないので、取り敢えずなんでもいいので入れておきましょう。
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/to_gunma/version', __FILE__)
Gem::Specification.new do |gem|
gem.authors = ["soplana"]
gem.email = ["sonosheet.jp@gmail.com"]
gem.description = %q{to_gunma}
gem.summary = %q{to_gunma}
gem.homepage = "https://github.com/soplana/to_gunma"
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "to_gunma"
gem.require_paths = ["lib"]
gem.version = ToGunma::VERSION
end
ちなみに関連gemを定義しておきたい場合はこのファイルにgem.add_dependency "gem-name"
と記述しておけばいいようです。
leenoのgemはfaradayに依存しているので.gemspecは以下の様になっています。
# -*- encoding: utf-8 -*-
require File.expand_path('../lib/leeno/version', __FILE__)
Gem::Specification.new do |gem|
gem.authors = ["soplana"]
gem.email = ["sonosheet.jp@gmail.com"]
gem.description = %q{LeenoAPI client for Ruby}
gem.summary = %q{LeenoAPI client for Ruby}
gem.homepage = "https://github.com/soplana/leeno"
gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "leeno"
gem.require_paths = ["lib"]
gem.version = Leeno::VERSION
gem.add_dependency "faraday", "~>0.8.0"
gem.add_dependency "faraday_middleware", "~>0.8.7"
end
テスト(rspec)の準備
gemの開発はテスト駆動開発を強いられます。
というのも、普通のスクリプトやRailsと違って、書いて試して書いて試してのフローが出来無いからです。(出来るの?)
実行するためには、ビルドしてローカルにインストールしてrequireして実行、といった手順になるため、例えば小さなif文一つ変更して動作確認したい、って場合なんかはかなり面倒です。
でも、テストを書いておけば小さな変更であろうと大きな変更であろうと、テストが通ればいいわけですから安心です。書きましょう。
今回、テストにはrspecを使います。
まずはspec_hepler.rbを作ります。
soplana ~/work/to_gunma $mkdir spec
soplana ~/work/to_gunma $vim spec/spec_helper.rb
中身
# encoding: utf-8
require 'rubygems'
require 'to_gunma'
具体的な実装
まずはversionの話から。
bundleでgemのひな形を作成した段階で、lib/to_gunma/version.rbというファイルが作成されます。
中身はとてもシンプルです。
module ToGunma
VERSION = "0.0.1"
end
この状態でビルドすると、to_gunma(0.0.1)が作成される訳ですね。
今後変更を加えてバージョンアップしていく時は、このファイルを変更してリビルドして公開、という流れになります。
さて、gemはrequire時にlib/to_gunma.rbを読み込むようになっているそうです。
今回作りたいto_gunmaはRubyのObject Classを拡張して、全てのオブジェクトをグンマーにしてしまう凶悪なgemなので、lib/to_gunma.rbからObject Classをオープンしてインスタンスメソッドを追加するための.rbファイルをrequireするようにしておきます。
まずは、Object Class拡張用のファイルを作成します。
適当にextentionというディレクトリを作り、その下にobject.rbを作成。
soplana ~/work/to_gunma $mkdir lib/to_gunma/extention
soplana ~/work/to_gunma $vim lib/to_gunma/extention/object.rb
# -*- coding: utf-8 -*-
class Object
def to_gunma
"#{self.class}は群馬県になりました。"
end
end
はい、取り敢えずこれだけ。
そしたら、作成したobject.rbをlib/to_gunma.rbへrequireします。
require "to_gunma/version"
require "to_gunma/extention/object"
module ToGunma
# Your code goes here...
end
はい、これでRuby上の全てのオブジェクトをグンマーが制圧しました。
テストを書く
書きます。テスト。
まずspec_hepler.rbにto_gunmaした結果の文字列を返すだけのメソッドを定義しておきます。
# encoding: utf-8
require 'rubygems'
require 'to_gunma'
def gunma
"は群馬県になりました。"
end
次に、object_spec.rbファイル作り、ゴリゴリテストを書きます。
soplana ~/work/to_gunma $mkdir spec/extention
soplana ~/work/to_gunma $vim spec/extention/object_spec.rb
# -*- encoding: UTF-8 -*-
require File.expand_path(File.join('../', 'spec_helper'), File.dirname(__FILE__))
describe Object do
it "Array should be gunma" do [].to_gunma.should == [].class.to_s+gunma; end
it "nil should be gunma" do nil.to_gunma.should == nil.class.to_s+gunma; end
it "Hash should be gunma" do {}.to_gunma.should == {}.class.to_s+gunma; end
it "String should be gunma" do "".to_gunma.should == "".class.to_s+gunma; end
it "Fixnum should be gunma" do 1.to_gunma.should == 1.class.to_s+gunma; end
it "Float should be gunma" do 1.1.to_gunma.should == 1.1.class.to_s+gunma; end
it "Regexp should be gunma" do //.to_gunma.should == //.class.to_s+gunma; end
it "Time should be gunma" do Time.now.to_gunma.should == Time.now.class.to_s+gunma; end
it "Proc should be gunma" do Proc.new{}.to_gunma.should == Proc.new{}.class.to_s+gunma; end
end
実際、Rubyの組み込みライブラリはもっと沢山ありますが、取り敢えず主要なオブジェクトに対するテストだけ。
実行します。
soplana ~/work/to_gunma $rspec -c spec/extention/object_spec.rb
.........
Finished in 0.00178 seconds
9 examples, 0 failures
全て通りました。Rubyはグンマーに制圧されました。
versionを上げて、commit & pushしておく
取り敢えず、ここまでの作業で一旦まともに動く様になったという意味でgemをマイナーバージョンアップさせておきます。
バージョンアップはやってもやらなくてもいいのですが、pec/extention/object_spec.rbなど、新しく作成されたファイルはcommitしておかないとビルドされない現象が確認でき、しばらく悩みました。
まずlib/to_gunma/version.rbを開き、versionを0.0.1から0.0.1.1にあげておきます。
module ToGunma
VERSION = "0.0.1.1"
end
commitしてpush。
soplana ~/work/to_gunma $git add .
soplana ~/work/to_gunma $git commit -m "v0.0.1.1"
[master 5a4737e] v0.0.1.1
1 files changed, 1 insertions(+), 1 deletions(-)
soplana ~/work/to_gunma $git push origin master
Counting objects: 28, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (19/19), done.
Writing objects: 100% (22/22), 2.09 KiB, done.
Total 22 (delta 4), reused 0 (delta 0)
To git@github.com:soplana/to_gunma.git
c1dd106..5a4737e master -> master
ローカルにインストールしてみる
はい、してみましょう。
bndlerを使ってまずはビルドをします。
soplana ~/work/to_gunma $gem build to_gunma.gemspec
WARNING: description and summary are identical
Successfully built RubyGem
Name: to_gunma
Version: 0.0.1.1
File: to_gunma-0.0.1.1.gem
warning出てますけど、ビルドは成功しました。
インストールしてみます。
soplana ~/work/to_gunma $rake install
to_gunma 0.0.1.1 built to pkg/to_gunma-0.0.1.1.gem
to_gunma (0.0.1.1) installed
完了しました。これでローカルでto_gunmaのgemが使えるはずです!
soplana ~/work/to_gunma $irb
irb(main):001:0> require "to_gunma"
=> true
irb(main):002:0> Time.now.to_gunma
=> "Timeは群馬県になりました。"
キタ━━━━(゚∀゚)━━━━ッ!!
Time.nowして現在時刻を取得したにも関わらずグンマーに支配される感じが堪らないですね。
rubygemsにgemを登録する
rubygemsに自分で作ったgemを登録することで、gem install to_gunmaとか出来るようになるので登録します。
まずはrubygemsに自分のアカウントを作成しておきます。
ここで入力するemailとpasswordはrubygemsにgemをpushする際、必要になってくるので覚えておきましょう。
本来なら、ビルドしてpushという流れになるのですが、ローカルでの動作確認の為、さきほどビルドはしていますのでそこは省略します。
soplana ~/work/to_gunma $gem push pkg/to_gunma-0.0.1.1.gem
Enter your RubyGems.org credentials.
Don't have an account yet? Create one at http://rubygems.org/sign_up
Email:
Password:
Pushing gem to https://rubygems.org...
Signed in.
Pushing gem to https://rubygems.org...
Successfully registered gem: to_gunma (0.0.1.1)
出来たっぽいですね。
さっそく試してみましょう!
先ほどローカルに入れたgemは削除してrubygemsから入れてみます。
soplana ~/work/to_gunma $gem uninstall to_gunma
Successfully uninstalled to_gunma-0.0.1.1
soplana ~/work/to_gunma $gem install to_gunma
Fetching: to_gunma-0.0.1.1.gem (100%)
Successfully installed to_gunma-0.0.1.1
1 gem installed
Installing ri documentation for to_gunma-0.0.1.1...
Installing RDoc documentation for to_gunma-0.0.1.1...
おおおお!!
入ったのでは!?
確認してみます。
soplana ~/work/to_gunma $irb
irb(main):001:0> require "to_gunma"
=> true
irb(main):002:0> [].to_gunma
=> "Arrayは群馬県になりました。"
irb(main):003:0> exit
やった!!グンマーがRubyのオブジェクトを制圧してる!
はい。rubygemsをこんなネタgemで汚してすみませんでした。
今後、to_gunma!メソッドも作りたいです。
一度呼び出すと、それ以降どのメソッド呼んでも群馬に制圧されてる感じの。
お疲れさまでした。