書き捨てのRubyスクリプトをgemにするときの育て方の一例 - 2016-09-08 - ククログ

ククログ

株式会社クリアコード > ククログ > 書き捨てのRubyスクリプトをgemにするときの育て方の一例

書き捨てのRubyスクリプトをgemにするときの育て方の一例

Rubyでちょっとしたこと、例えばテキスト処理などをしたくてスクリプトを書くことはよくあります。そんなスクリプトは意外と再利用したくなるものです。しかし、作業用ディレクトリに適当なファイル名で保存していたりすると探し出すのは困難ですし、コマンドラインにワンライナーで書いたものだとそもそも残っていないこともあります1。別のPCでの再利用も、それらの保存方法では当然ながら不可能でしょう。

そこで、どこか整理された場所に保存しておいて再利用に備えたいのですが、保存先が問題になります2。そんなとき、私は次のような理由からgem(RubyGems)にしてしまうことにしています。手元ですぐに実行できますし、別の環境へのインストールも簡単です。また、他のRubyプログラムへの組み込みもコピペせずに実現できます。もちろん他の人が使えるようになるというメリットもあるのですが、自分の用途に限っても長い目で見れば作業時間の元は取れると思います。3

ここから、実際にスクリプトをgemにする手順を見ていきます。おおまかな手順は以下です。

  1. gemの雛形を作る

  2. スクリプトをexe/かbin/にコピーする

  3. lib/に切り出す

gemの雛形を作る

Bundlerのbundleコマンドでgemの雛形を作ることができます。実行ファイルを含めるgemの場合、--exeオプションを指定すると、実行ファイルを配置するディレクトリを生成してくれます。

% bundle gem GEM_NAME --exe

オプションの詳細は以下のコマンドで見られます。

% bundle help gem

雛形にはいくつかTODOコメントが埋め込まれています。これらが残っているとgemを作成する際にエラーになってしまうので、埋められるところはこの段階で埋めておきましょう。

スクリプトをexe/かbin/にコピーする

スクリプトをexeディレクトリ以下に生成されたファイルにコピーします。例えば、GEM_NAMEをpiyoとした場合、exe/piyoというファイルが生成されているはずです4。このファイルの名前がコマンド名になります。コマンド名は基本的にgemと同じ名前にします。もし複数のコマンドを含めたい場合、gem名-XXXのようにgem名をプレフィックスとして付けるようにすることが多いです5

例えば、画面にランダムで1〜10回piyoと表示させるスクリプトの場合は以下のようになります。

% cat exe/piyo
#!/usr/bin/env ruby

require "piyo"

puts "piyo" * (rand(10) + 1)

それでは実行してみましょう。手元で実行するときは、bundle installしてからbundle execを付けて呼び出します6

% bundle install
...
% bundle exec piyo
piyopiyopiyo

これで、スクリプトをgemにすることができました。

もう少しgemっぽくするのであれば、以下のようにRubyの標準添付ライブラリであるOptionParserを導入することで、バージョン番号を表示するオプションを追加することもできます。

#!/usr/bin/env ruby

require "piyo"

require "optparse"

option_parser = OptionParser.new
option_parser.on("-v", "--version",
                 "Show version number") do
  puts Piyo::VERSION
  exit
end
option_parser.parse!(ARGV)

puts "piyo" * (rand(10) + 1)

lib/に切り出す

せっかくgemにしたので、もう少しgemらしくしてみます。主な処理をlibディレクトリに移すことで、コマンドとしてだけでなく、ライブラリとしても使うことができるようになります。

piyoと表示する処理をChickクラスに切り出したコミットは以下です。

diff --git a/exe/piyo b/exe/piyo
index 8259679..3f483a1 100755
--- a/exe/piyo
+++ b/exe/piyo
@@ -12,4 +12,5 @@ option_parser.on("-v", "--version",
 end
 option_parser.parse!(ARGV)
 
-puts "piyo" * (rand(10) + 1)
+chick = Piyo::Chick.new
+chick.chirp
diff --git a/lib/piyo.rb b/lib/piyo.rb
index 835a236..efe8efa 100644
--- a/lib/piyo.rb
+++ b/lib/piyo.rb
@@ -1,3 +1,4 @@
+require "piyo/chick"
 require "piyo/version"
 
 module Piyo
diff --git a/lib/piyo/chick.rb b/lib/piyo/chick.rb
new file mode 100644
index 0000000..33e9084
--- /dev/null
+++ b/lib/piyo/chick.rb
@@ -0,0 +1,7 @@
+module Piyo
+  class Chick
+    def chirp
+      puts "piyo" * (rand(10) + 1)
+    end
+  end
+end

GitHubでも同じ内容を公開しています。こちらからご覧いただけます。

libディレクトリに切り出すことで、ユニットテストがしやすくなるというメリットもあります。

おわりに

ちょっとしたスクリプトをgemにする手順の一例を紹介しました。gemにすることで、埋もれていた便利スクリプトを再利用しやすくなります。今後も使いそうなスクリプトが書けたときにはぜひ試してみてください。7

  1. 私の環境だとコマンドラインにワンライナーで書いたものでもヒストリーに残っている(参考:おすすめzsh設定 - ククログ(2011-09-05))ため残ってはいるのですが、名前が付いていないスクリプトの山から探し出すのは大変です。

  2. 部分的なプログラムということで、まずGistが思い浮かびます。ちょっと参考にしたいという程度ならこれで十分なのですが、手元で実行したり、別のプログラムに組み込んだりしたいときには、一旦手元にまるっとコピーする必要があり、不便です。

  3. gemを作る過程で、似たようなgemがないか自然と探すことになり、便利gemを見つけることができるというのもメリットの一つと言えるかもしれません。ちょっとしたスクリプトだと、ついつい調べずに自分で書いてしまうことが多いのですが、ちょっと調べればもっと便利なgemが見つかることも多いです。

  4. 以前はbinディレクトリがこの用途で使われていたのですが、現在はbinディレクトリには開発用コマンドのファイルを置き、公開するコマンドはexeディレクトリに置くのが標準となっているようです。公開するコマンドの場所としてbinディレクトリを使いたい場合は、gemspecファイルの spec.bindir を bin に変更します。その場合は自動生成された開発用コマンドは削除するか別のディレクトリに移動するかしましょう。

  5. 他のコマンドとの名前の衝突を避けるためです。

  6. 他のディレクトリで実行してみたい場合は rake install でインストールできます。

  7. このやり方を別の観点で見ると、常に動く状態を保つことができるというメリットもあります。最終的にライブラリにするのであっても、動かない状態で書き進めるのは大変なので、最初は実際に動くスクリプトから気軽に書き始めるというのが私のやり方です。