Ruby gemでのコマンドライン・ツールの作り方
たまにgemでツールを作りたくなるのですが、毎回イチから調べ回る羽目になるので、ひと通りの作り方をまとめておきます。
環境
bundler 1.7.6
thor 0.19.1
Ruby 2.1.2p95
Mac OS X Yosemite 10.10.2
gemの雛形を作成
gemの雛形を作るには bundle gem
コマンドを実行します。
コマンドで実行するgemの場合は、-b
オプションを付けて bin
にコマンドファイルを生成させます。
※以降では、DiskSizeRec というツールを作った時の例を示します。ファイル名等は適宜読み替えてください。
$ bundle gem disksizerec -b create disksizerec/Gemfile create disksizerec/Rakefile create disksizerec/LICENSE.txt create disksizerec/README.md create disksizerec/.gitignore create disksizerec/disksizerec.gemspec create disksizerec/lib/disksizerec.rb create disksizerec/lib/disksizerec/version.rb create disksizerec/bin/disksizerec Initializing git repo in /tmp/disksizerec
他のオプションは bundle help gem
で確認できます。
このコマンドを実行すると雛形が生成され、Gitリポジトリも作成されます。
.gitignoreの設定
必要に応じて、.gitignore
に以下の項目を追加します:
vendor/bundle/ .idea
1つ目は依存モジュールを格納するディレクトリ、2つ目はRubyMineの設定情報のディレクトリです。
この時点でGitリポジトリにコミットしておきます。
リモートリポジトリの作成
ソースをGitHubやBitbucketなどで管理する場合は、リモートのGitリポジトリを作成します。
GitHubの手順:
- GitHubにログインして上部の「+」ボタンから「New repository」を選択。
- 各項目を入力して作成するが、README等のファイルは追加しないようにする。
- 「Quick setup」の説明が表示されるので「…or push an existing repository from the command line」の内容に従ってコマンドを実行。
※僕が作る時には、SSHの方のパスを使っています。 - ページをリロードすると、リポジトリのページが表示されるはず。
参考:
Adding an existing project to GitHub using the command line - User Documentation
依存モジュールのインストール
僕は依存gemを vendor/bundle
以下に格納しているので、そのために以下のコマンドを実行します:
$ bundle install --path vendor/bundle
この時、以下のようなエラーが表示されますが、気にしなくてよいです:
disksizerec at /tmp/disksizerec did not have a valid gemspec. This prevents bundler from installing bins or native extensions, but that may not affect its functionality. The validation message from Rubygems was: "FIXME" or "TODO" is not a description
gemspecファイルの設定
TODO:
となっている個所を記述します。
GitHub等でソースを公開している場合は、そのURLを spec.homepage
に記述します。
その他の必要な項目を記述します。
Thorの導入
コマンドライン・ツールを作る場合、Thor を使うのが一般的です。
gemspecファイルに以下の記述を追加します:
spec.add_dependency "thor"
そして、Thorをインストールするために bundle install
を実行します。
※他のgemを追加する場合も同様にします。
コマンドライン・インターフェース用のクラスを記述
コマンドライン・インターフェース用のファイル lib/discsizerec/cli.rb
を作成し、以下のようなクラスを記述します:
# coding: utf-8 require 'thor' module Disksizerec class CLI < Thor end end
lib/disksizerec.rb
ファイルでcli.rb
ファイルをロードします:
require "disksizerec/cli"
bin/discsizerec
の最後に、CLI
を起動するコードを追加します:
Disksizerec::CLI.start
コマンドを記述
ツールのコマンドは、CLI
クラスの中に以下のように記述します:
desc "hello NAME", "say hello to NAME" def hello(name) puts "Hello #{name}" end
この場合、ビルドすると、以下のようにコマンドを指定できるようになります:
$ discsizerec hello NAME
NAME
はパラメーターです。
動作チェック
$ bundle exec bin/disksizerec hello world Hello world
オプションの記述
特定のコマンドに対してオプションを設定するには option
で記述します:
desc "hello NAME", "say hello to NAME" option :from, type: :string, default: "none" def hello(name) puts "Hello #{name} from #{options[:from]}" end
実行例:
$ bundle exec bin/disksizerec hello world --from=John Hello world from John
すべてのコマンドに共通のオプションを設定する場合は class_option
で記述します:
class_option :silent, type: :boolean, default: false, desc: 'Not write log messages', aliases: '-s'
参考:
Thor - Home
デフォルト・コマンドの設定
コマンド名を指定しない場合に実行されるデフォルト・コマンドを設定するには default_command
を使います:
default_command :hello
なお、デフォルト・コマンドを使う場合(コマンドを指定しない場合)はパラメーターを指定できないので、デフォルト・コマンドにはパラメーターのないコマンドを設定するべきでしょう。
また、ヘルプの表示では、どれがデフォルト・コマンドかわからないので、その点も注意が必要です。
参考:
サブコマンドのないコマンドをthorで管理する方法 - sonots:blog
コマンド内で別のコマンドを呼び出す
invoke
を使います:
desc "greeting", "greeting" def greeting invoke :hello, ["everyone"] end
パラメーターは配列で指定します。
なお、メンバー変数は渡らないので、データはパラメーターで渡す必要があります。
配置
最も手軽に配置するには、Gitリポジトリからプロジェクトをクローンして、上記の「動作チェック」の項目で記述したように、bundle exec
で実行します。
gem install
でインストールできるようにするには、RubyGems.org に登録する必要がありますが、今回は行わなかったので省略します。
cronで実行した場合のエラーの対処
作ったツールをcronで実行した場合に(bundle exec
で実行)、以下のようなエラーが発生することがあります。
There was a Errno::ENOENT while loading disksizerec.gemspec: No such file or directory - git from /usr/local/disksizerec/disksizerec.gemspec:16:in ``'
これは git
コマンドが見つからないというエラーです。
調べてみると、僕の環境では git
が /usr/local/bin
に入っており、cronのデフォルトの状態ではパスに /usr/local/bin
が含まれていませんでした。
この場合、cronファイルの最初に PATH=/usr/local/bin:/bin:/usr/bin
などと追加すると、解決できます。