初めてのgem作成

僕はJavaメインでずっと仕事してきたこともありRubyはほとんど書けないし、仕事でRubyのコードを書く機会も今のところ少ないです。ただRubyで実装されたアプリ(例:fluentd)を使うことはあるしchef, vagrantなど興味あるアプリはRubyベースなことも多いので環境周りなんかは少し知っといた方が良いなと思っているわけです。

まあRubyに限らずLL言語で実装されたアプリは(C拡張とか出てくると特に)インストールは大変だという先入観というか偏見を持っているのですが慣れの問題でしょうねえ。その辺ではまらないようにしときたいなと思っている訳です。

Rubyアプリはgemでインストールするのが一般的なことは分かっているので、その辺を理解するためにもいっちょ自分で簡単なtoyプログラムを作ってgemで公開してみましたというのが本エントリです。

gemの作り方に関してはWEB+DB vol74の記事も良いですが無料で読めるものとしては下記のチュートリアルが素晴らしいです。英語ですが。
tech.pro - このウェブサイトは販売用です! -&nbsptech リソースおよび情報

僕が今回作ったgemはea_sports_ppiというものです。

https://rubygems.org/gems/ea_sports_ppi

インストール方法および使い方は下記の通り。

$ gem install ea_sports_ppi
$ ea_sports_ppi
Robin van Persie 790
Gareth Bale 628
Juan Mata 611
Luis Suarez 611
Leighton Baines 579
Eden Hazard 563
Santi Cazorla 562
Steven Gerrard 558
Patrice Evra 556
Wayne Rooney 532
Yaya Toure 529
Michael Carrick 525
Carlos Tevez 522
Marouane Fellaini 513
David Silva 505
Mikel Arteta 500
Steven Pienaar 486
Dimitar Berbatov 467
Rickie Lambert 465
Aaron Lennon 433

内容としては
http://www.premierleague.com/en-gb/players/ea-sports-player-performance-index.html
をparseしてPLAYERとINDEXの値を抜き出しているだけです。

これは今シーズンのプレミアリーグの選手のパフォーマンスを数値化したもので今のところ一位がファンペルシーで790です。これは蓄積された値です。このINDEXというのがEA SPORTS PLAYER PERFORMANCE INDEXでea_sports_ppiというgem名はそこから取りました。

EA SPORTS PLAYER PERFORMANCE INDEXの詳細はこちら
http://www.premierleague.com/en-gb/players/ea-sports-player-performance-index/what-is-the-ea-sports-ppi.html

詳細といっても統計値を求めるアルゴリズムが公開されているわけではなくて以下の6つの観点から数値を出しているみたいです。

1. Winning Performance
2. Player's Performance per match
3. Appearances
4. Goals scored
5. Assists
6. Clean sheets

ちょっと面白いのは上記1でこれは勝ち試合にフィールドにいた時間が長いほど良いようです。それだけ勝利に貢献したってことでしょうね。

このランキングを見るとまあ納得というか上位6人、ファンペルシー、ベイル、マタ、スアレス、ベインズ、アザールはそのままPFA Team of the Year、要は今季プレミアリーグベストイレブンにも入っています。ちなみにベストイレブンはデヘア、サバレタフェルトンゲン、ファーディナント、ベインズ、キャリック、マタ、ベイル、スアレスアザールファンペルシーです。こちらもまあ納得な人選ですね。


閑話休題。では本題のgem作成に入りましょう。


まず環境構築としてbundlerをインストールします。

$ gem install bundler

その前にそもそもsystem rubyの環境を使いたくないよねって場合はrvm, rbenv使っても良いですしxbuildで下記のように別途Rubyをインストールしても良いと思います。

$ git clone git://github.com/tagomoris/xbuild.git
$ cd xbuild/
$ ./ruby-install 1.9.3-p392 ~/local/ruby-1.9.3-p392

Ruby環境を構築したらbundle gemでgemのひな形を作成します。

$ bundle gem ea_sports_ppi
      create  ea_sports_ppi/Gemfile
      create  ea_sports_ppi/Rakefile
      create  ea_sports_ppi/LICENSE.txt
      create  ea_sports_ppi/README.md
      create  ea_sports_ppi/.gitignore
      create  ea_sports_ppi/ea_sports_ppi.gemspec
      create  ea_sports_ppi/lib/ea_sports_ppi.rb
      create  ea_sports_ppi/lib/ea_sports_ppi/version.rb
Initializating git repo in /home/vagrant/temp/ea_sports_ppi

こうするとgitのindexにも追加された状態になります。言い忘れましたがgitはインストールされていて、GitHubおよびrubygems.orgにアカウントを持っているものとします。

$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#	new file:   .gitignore
#	new file:   Gemfile
#	new file:   LICENSE.txt
#	new file:   README.md
#	new file:   Rakefile
#	new file:   ea_sports_ppi.gemspec
#	new file:   lib/ea_sports_ppi.rb
#	new file:   lib/ea_sports_ppi/version.rb
#


2013/5/2追記
rake releaseのときにgit pushするのでremoteリポジトリにも追加しておきます。

$ git remote add origin git@github.com:wyukawa/ea_sports_ppi.git


では、まずは軽くHello, Worldしてみましょう。lib/ea_sports_ppi.rbがアプリの実装を書く部分なのでここを編集して下記のようにします。

module EaSportsPpi
  # Your code goes here...
  def self.run()
    puts "Hello"
  end
end

動作確認しましょう。

$ bundle console
Resolving dependencies...
irb(main):001:0> EaSportsPpi.run
Hello
=> nil

OKだったのでea_sports_ppi.gemspecを編集しましょう。ea_sports_ppi.gemspecはgemのメタ情報を書くところですね。TODOの部分を埋めればよいです。

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'ea_sports_ppi/version'

Gem::Specification.new do |spec|
  spec.name          = "ea_sports_ppi"
  spec.version       = EaSportsPpi::VERSION
  spec.authors      = ["TODO: Write your name"]
  spec.email         = ["TODO: Write your email address"]
  spec.description   = %q{TODO: Write a gem description}
  spec.summary       = %q{TODO: Write a gem summary}
  spec.homepage      = ""
  spec.license       = "MIT"

  spec.files         = `git ls-files`.split($/)
  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.3"
  spec.add_development_dependency "rake"
end

ちなみにgit config --global user.nameとgit config --global user.emailを設定しているとそれがそれぞれspec.authorsとspec.emailになります。spec.descriptionとspec.summaryにgemの説明を書きます。

spec.descriptionに書かれた説明がrubygems.orgに載ります。またspec.homepageに書いたURLがrubygems.orgのLinks Homepageになります。

rake buildでpkgディレクトリ以下にgemができます。rake installでgemがインストールされます。rake releaseするとgitのtag打ち、rubygems.orgへのリリースまで行います。

$ rake build
ea_sports_ppi 0.0.1 built to pkg/ea_sports_ppi-0.0.1.gem.
$ rake install
ea_sports_ppi 0.0.1 built to pkg/ea_sports_ppi-0.0.1.gem.
ea_sports_ppi (0.0.1) installed.
$ gem list | grep ea_sports_ppi
ea_sports_ppi (0.0.1)

バージョン情報はlib/ea_sports_ppi/version.rbに書かれています。

module EaSportsPpi
  VERSION = "0.0.1"
end

ではirbで試してみましょう

$ irb
irb(main):001:0> require 'ea_sports_ppi'
=> true
irb(main):002:0> EaSportsPpi.run
Hello
=> nil

おー、できてますね。

実行コマンドもあったほうがいいのでそれも作ります。
bin/ea_sports_ppiを作って下記のようにします。

require 'ea_sports_ppi'
EaSportsPpi.run

ただしこのままだとbin以下がgemに含まれません。gemspecのspec.filesにあるようにgit ls-filesに含まれていないとダメなようなのでgit addします。

$ git ls-files
.gitignore
Gemfile
LICENSE.txt
README.md
Rakefile
ea_sports_ppi.gemspec
lib/ea_sports_ppi.rb
lib/ea_sports_ppi/version.rb
$ git add .
$ git ls-files
.gitignore
Gemfile
LICENSE.txt
README.md
Rakefile
bin/ea_sports_ppi
ea_sports_ppi.gemspec
lib/ea_sports_ppi.rb
lib/ea_sports_ppi/version.rb

そうやって再びrake installすると、おー、コマンド使えますね。

$ rake install
ea_sports_ppi 0.0.1 built to pkg/ea_sports_ppi-0.0.1.gem.
ea_sports_ppi (0.0.1) installed.
$ ea_sports_ppi
Hello


では以下のページをparseしてPLAYERとINDEXを取得してみましょう。
http://www.premierleague.com/en-gb/players/ea-sports-player-performance-index.html

parseするにはHTML parserが必要なのでnokogiriを使いましょう。

gemspecに下記を追加します。

spec.add_runtime_dependency "nokogiri"

そしてbundleを実行してnokogiriをインストールします。

$ bundle
Fetching gem metadata from https://rubygems.org/.........
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Using rake (10.0.4)
Using bundler (1.3.5)
Installing nokogiri (1.5.9)
Using ea_sports_ppi (0.0.1) from source at .
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

ただちょっと注意点なのはCentOSの場合は以下も必要です。nokogiriが依存しているからですね。

sudo yum install libxml2-devel libxslt-devel

そしてlib/ea_sports_ppi.rbを以下のようにすればPLAYERとINDEXを取得できます。

require "ea_sports_ppi/version"

require 'nokogiri'
require 'open-uri'

module EaSportsPpi
  # Your code goes here...
  def self.run()
    doc=Nokogiri::HTML(open("http://www.premierleague.com/en-gb/players/ea-sports-player-performance-index/"))
    tables=doc.xpath('/html/body/div/div/div/div/div/div/div/form/div/table')
    tables[0].xpath('tbody/tr').each do |tr|
      print(tr.xpath('td[4]').inner_text.strip, " ", tr.xpath('td[14]').inner_text.strip, "\n")
    end
  end
end

あとはrake installして動作確認して問題無ければrake releaseしてrubygems.orgにアップします。

$ rake release
ea_sports_ppi 0.0.4 built to pkg/ea_sports_ppi-0.0.4.gem.
Tagged v0.0.4.
Enter passphrase for key '/home/vagrant/.ssh/id_rsa':
Enter passphrase for key '/home/vagrant/.ssh/id_rsa':
Pushed git commits and tags.
Pushed ea_sports_ppi 0.0.4 to rubygems.org.

ソースはこちらにあります。
https://github.com/wyukawa/ea_sports_ppi


やってみて思ったのは、思ったよりも簡単でしたね。。。自分の書いたプログラムをこうやって気軽に全世界に公開できるって素晴らしいですね。Java文化だとこういうの無いですしね。や、まあmave repositoryへの登録ってどうやるか知らないですけど。。。ともあれgem素晴らしい!


2013/5/2追記
ruportを使って出力をきれいにしたバージョン0.0.5をリリースしました。

gemをリリースするとTwitterでもrubygemsアカウントがつぶやいてくれます。

なおこのgemは以下にインスパイアされて作りました。Thank you!

http://rubygems.org/gems/live_soccer