野良 Gem の作り方(Ruby)

野良 GemRubyGems.org に登録していない Gem を、ここではこう呼びます)が何とか作れるようになったので、自分用にメモしておきます。初心者なので、理解していないところがたくさんあります。あくまでも備忘録なので、そのつもりで。OS は Linux Mint 17.2 です。


自分の環境はちょっと特殊で、Documents ディレクトリの下に Ruby ディレクトリを作って、その中に rbenv と bundler で Ruby の環境を作っています。まず、このディレクトリから始めます。


基本的には、ここのとおりにやりました。つまり、bundler を使って Gem も作ります。作る Gem は Utils という名前にします。まず

$ bundle gem utils

をやると、ディレクトリ内に utils というディレクトリが自動的にできます。この中にある、utils.gemspec というファイルの中身を変更します。具体的には上のリンク先を見て下さい。spec.summaryspec.description は必ず中身を記述しなくてはなりません。また、依存する Gem があれば spec.add_dependency も記述します(標準添付ライブラリについては必要ありません)。


次に、さらに lib というディレクトリに入って、中の utils.rb を書きます。これが require される Gem の本体になります。utils ディレクトリに上がって

$ bundle install

をすると、既にこの内部で(だけ)実行できます。$ bundle exec irbirb に入り、require 'utils' で true が返ればとりあえずよしです。そして

$ gem build utils.gemspec
$ gem install utils

で 1 gem installed が出れば一段落です。gem list の入力でずらずらっと出てきた Gem の一覧に utils の名前があれば成功です。これで utils ディレクトリの中でだけなら require も出き、Gem として使えますが、まだシステムの Gem としては使えません。


次に、Gem を GitHub からダウンロードして、bundler で管理できる野良 Gem にします。こうすれば誰でも GitHub からダウンロードできるようになります。(ここからが試行錯誤でやったので、問題があります。


まずは GitHub 上に新しいリポジトリ obelisk68/utils を作ります。そこは解説しないので、他をあたって下さい。

GitHub実践入門 ~Pull Requestによる開発の変革 (WEB+DB PRESS plus)

GitHub実践入門 ~Pull Requestによる開発の変革 (WEB+DB PRESS plus)



また utils ディレクトリに入ります。
さて、参考にした上のサイトではここで

$ git add . -A && git commit -m 'first commit'

を実行するように書いてあります。他のサイトではちがうことが書いてあったりします。ひとまず、最初のようにやってみます。そして

$ git remote add origin git@github.com:obelisk68/utils.git
$ rake release

なのですが、たぶんこれで終わりません。というか、自分は終わりませんでした。rake aborted! そして utils-0.1.0 contains itself とか出ました。


さて、ここでこのサイトの記述を参考にして、

$ git rm utils-0.1.0.gem
$ git status
$ git commit -m 'rm "utils-0.1.0.gem"'

[master bfa4c95] rm "utils-0.1.0.gem"
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 utils-0.1.0.gem

とやってみます。いい感じです。さらに

$ rake build
$ rake install
$ rake release

でどうかと思ったら、Untagging v0.1.0 due to error. だそうです。

rake aborted!
Couldn't git push. `git push  2>&1' failed with the following output:

No refs in common and none specified; doing nothing.
Perhaps you should specify a branch such as 'master'.
error: failed to push some refs to 'git@github.com:obelisk68/utils.git'

なそうなので、git status をやってみると、

On branch master
nothing to commit, working directory clean

とか何とか。


ここで、このサイトを参考にします。

$ git remote rm origin
$ git remote add origin git@github.com:obelisk68/utils.git
$ git push -u origin master

とすると

Counting objects: 282, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (270/270), done.
Writing objects: 100% (282/282), 6.32 MiB | 734.00 KiB/s, done.
Total 282 (delta 20), reused 0 (delta 0)
To git@github.com:obelisk68/utils.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

で、いい感じです。さてどうかなと

$ rake release

で、

utils 0.1.0 built to pkg/utils-0.1.0.gem.
Tagged v0.1.0.
Pushed git commits and tags.
rake aborted!
ERROR:  ....

とエラーがでているのだが、どうもこれで一応上手くいったっぽい。野良 Gem が出来たかな?


最後に GitHub からここで作った野良 Gem をインストールしてみます。ディレクトリはいつも Ruby を使う環境に入って下さい。あとは以前の記事のとおりで、野良 Gem が bundler でインストールできると思います。つまり、Gemfile に以下を追加して$ bundle install

gem 'utils', github: 'obelisk68/utils'

お疲れ様でした。


GitHub のまったくの初心者なので、おかしなことをやっている筈です。


この Gem の使い方

require 'bundler/setup'
require 'utils'


使い方というか、いつも僕が使っているコードの寄せ集めなので、あんまり他人には役に立たないかと。

Utils.imgexists?("http://www.ninjintei.jp/onetyan/image1203.jpg")    #=>[1016, 1476]

で url の先が画像かどうかを判断します。画像が存在すればそのサイズが配列で返り、存在しなければ nil が返ります。

"http://www.ninjintei.jp/onetyan/image1203.jpg".imgsuffix    #=>".jpg"

は String#imgsuffix で、文字列の最後が画像の識別子ならそれを返し、そうでなければ空文字列を返します。

Utils.getfile(url, "image.jpg")

は url の先をダウンロードします。後の引数は保存されるファイル名です。バイナリ・ファイルも DL 可能です。

[2, 4, 3].nest_loop {|i, j, k| print "#{[i, j, k]}  "}; puts

は簡単に多重ループを作ります。ここを参照。

p "327865".pickup(2, nil)    #=>"7865"
p [3, 2, 7, 8, 6, 5].pickup(-1, -4, -1)    #=>[5, 6, 8]

Python のスライスの Ruby 実装です。ここを参照。


※追記
さらに機能を追加しました。

p "12345678".separate(3)

は String#separate(n) で、文字列をあらゆる組合せで n 分割し、すべての組合せを配列に入れて返します。ここを参照。(※追記 ブロックがある場合はそれを実行、ない場合は Enumerator を返すようにしました。)

loop_with_index {|i| print "\e[1G#{i}  "; sleep(0.5)}

は Kernel#loop_with_index で、インデックス付き無限ループです。ここを参照。

p ("10.(952)".to_r + "5.26(3)".to_r).to_rec_decimal    #=>"16.21(628)"

は String#to_r のオーバーライドと、Rational#to_rec_decimal で、循環小数を扱います。ここを参照。

p 150.divisors_int    #=>[1, 2, 3, 5, 6, 10, 15, 25, 30, 50, 75, 150]

は Integer#divisors_int で、約数を配列で返します。ここを参照。

機能追加(2/27)

p 3.0.integer?    #=>true

は Float#integer? で、レシーバーの小数点以下が 0 なら true、それ以外は false を返します。

p Array.make([2, 3], "a")    #=>[["a", "a", "a"], ["a", "a", "a"]]

は Array.make(ar, ob) で、多重配列を簡単に作成します。ar は Array、ob は初期値(省略可能)ですが、初期値は Array.new とちがって、ob のコピーが入ります。ここを参照。

機能追加(3/11)

p Utils.key_wait

はキーボードからの一文字入力です。

[[1, 2], [3, 4]].deep_copy

は Object#deep_copy で、オブジェクトのいわゆる「ディープコピー」をします。

p 5.factorial    #=>120

は Integer#factorial で、階乗を返します。

p Utils.time_lexic    #=>"kimgfpxtuzl"

はマイクロ秒まで採った現在時刻を、アルファベットの辞書順になるように変換して、String で出力します。ここを参照。

機能追加(4/23)

a = [1, 5, 9, 1, 6]
a.change(1, "a")    #=>["a", 5, 9, "a", 6]
a.change!(1, "a")

は Array#change(a, b) (change! は破壊的メソッド)で、配列のすべての要素 a を b に置き換えます。

p Utils.permutation(4, 2)    #=>12
p Utils.combination(4, 2)    #=>6

は順列と組み合わせを求めます。

機能追加(7/29)
末尾再帰を回避するメソッドを2つ、Utils.trcall と Module#tco を追加しました。詳細はここを参照して下さい。

機能追加(11/10)

p "test".flow {|x| x.upcase}.chars    #=>["T", "E", "S", "T"]

Object#flow は self をブロック内で処理して最後の値を返します。

p "te*st".fname_filter   #=>"test"

String#fname_filter は self に含まれているフォルダ名・ファイル名に使っていけない文字を取り除いて返します。

p nil.nil_trans([])    #=>[]

Object#nil_trans(obj = "") は self が nil ならば obj(デフォルトは "")を返し、それ以外なら self 自身を返します。

Counter.make(:count, 100, -4)
10.times {puts count}

Counter.make はカウンターを作ります。詳細はここを参照して下さい。


※追記
この Gem の使い方だけ下に移動させました。そちらを見て下さい。
野良 Gem 'Utils' - Camera Obscura