Rakeタスクの引数の取り扱いまとめ
RakeタスクはRailsのプロジェクトでよく作るのだけれど、どうも引数の受け渡しが面倒でイライラする。
Rakeタスクで引数を扱うには主に以下の2パターンのやり方があるのだが・・・・。
1. 引数を定義する方法
task :hoge, [:a, :b] do |task, args|
p args.a
p args.b
end
$ bin/rake hoge[yo,hey]
"yo"
"hey"
$ bin/rake "hoge[yo,hey]" # zsh拡張している場合
"yo"
"hey"
実行時に[]を付けたり、自分はzshユーザーなのでタスクを「"」でくくる必要があったりと面倒。
第2引数だけ指定したい場合は bin/rake hoge[,'hey!'] などと微妙な書き方をしなければならず面倒。
2. 環境変数を経由する方法
task :hoge do |task|
p ENV['a']
p ENV['b']
end
$ bin/rake hoge a=yo b=hey
"yo"
"hey"
実行時に環境変数名を書いてやらねばならず、やはり面倒。
Rakeタスクの引数がウザいのでThorを使う
ならRakeを使わなければいい。Thorを使おう。
Thorはコマンドラインツールを作る為のgemで、引数の受け渡しは当然簡潔に書けるし、オプションのパースやUsage Messageの表示などを簡単に作成でき、おまけにシンタックスがRake-likeなのでRakeを理解しているエンジニアなら違和感なく移行できるCLIツールです。
インストール
普通にインストールします。
% gem i thor
Thorで書くとどうなるか
先ほどのRakeタスクをThorを使って書くとこうなる。
class Test < Thor
desc 'hoge [a] [b]', 'parameter test'
def hoge a, b
p a
p b
end
end
問題のパラメータは・・・。
$ thor test:hoge yo hey
"yo"
"hey"
これだ!こう書きたかったんだ!!234567890
Thor入門
というわけで引数の問題は解決しましたが、それ以外にも機能が豊富なThorの使い方を解説します。
基本
では試しに超基本的なコードを書いてみましょう。
現在のディレクトリにtest.thorという名前のファイルを作成します。内容は以下のとおり。
class Test < Thor # Thorクラスを継承
desc 'say [Name]', 'say task' # Usage Message, 詳細な説明
def say(name) # publicで定義したメソッドが実行するタスク
puts "Hello, #{name}!"
end
end
実行してみましょう。
$ thor test:say # パラメータ無しだとUsage Massageを出力
ERROR: "thor say" was called with no arguments
Usage: "thor test:say [NAME]"
$ thor test:say Thor
Hello, Thor!
簡単ですね。
名前空間
基本を理解出来たら次は名前空間の説明です。
Thorのタスク名はデフォルトだとRubyの名前空間を元に決まります。
これはそのままなので分かりやすいでしょう。
class App < Thor
desc 'say', '言う'
def say
puts 'Yo!'
end
end
$ thor app:say # thor downcaseしたクラス名:メソッド名
Yo!
当然moduleをAppの上に追加すると、そのままタスクの名前に追加されます。
module Livesense
class App < Thor
desc 'say', '言う'
def say
puts 'Yo!'
end
end
end
$ thor livesense:app:say
Yo!
名前空間を変更することもできます。
module Livesense
class App < Thor
namespace :yo # namespace :任意の名前 で指定
desc 'say', '言う'
def say
puts 'Yo!'
end
end
end
$ thor yo:say
Yo!
個人的にはnamespaceをあまり多用されると辛くなるかなと思っている。
正しく使えば問題ないのだが。
実行可能なコマンドを作る
ThorをRakeタスクの代替として使うのを前提に説明をしてきましたが、実行可能なコマンドも作成可能です。
#!/usr/bin/env ruby
require 'thor'
class YoCommand < Thor
desc 'say', '言う'
def say
puts 'hey!'
end
end
YoCommand.start
上記のようにSheBangを書いてthorをrequireして、実行権限を付加し、実行します。
$ chmod a+x ./yo
$ ./yo say
hey!
しかしこれだとパラメータを毎回指定しなければならないので、startメソッドのパラメータに配列でメソッド名を書いておけば実行時にパラメータを省略できます。
YoCommand.start %w(say)
$ chmod a+x ./yo
$ ./yo
hey!
PATHに通して普通のUNIXコマンドみたいに使うならこんな感じですね。
メソッドオプション
実行可能なコマンドを作るとなると、今度は実行時にオプションを付けたくなる。
method_optionを使えばよくあるオプションの挙動を簡単に定義できます。
コマンドに実行時オプションを追加する
とりあえずは超基本的なサンプルコードです。
#!/usr/bin/env ruby
require 'thor'
class YoCommand < Thor
desc 'say', '言う'
method_option :force
def say
if options.force # options[:force]でもOK
# optionsはhash形式で値が入っている => {"force"=>"force"}
puts 'yo!hey!'
else
puts 'yo!'
end
end
end
YoCommand.start %w(say) + ARGV
$ ./yo
yo!
$ ./yo --force
yo!hey!
メソッドオプションのオプション
コマンドにオプションを追加すると、色々とお決まりの処理を書かなければならなくなる。
オプションのエイリアスを設定したいとか、デフォルト値を設定しておけばロジックが綺麗になるな・・などなど。
#!/usr/bin/env ruby
require 'thor'
class YoCommand < Thor
desc 'say', '言う'
method_option :number, aliases: '-n', default: 1, type: :numeric
def say
puts 'yo!hey!' * options.number
end
end
YoCommand.start %w(say) + ARGV
当然その手の機能も上記のようにオプションを指定するだけ。
% ./yo --number 2
yo!hey!yo!hey!
% ./yo -n 2 # aliasesオプション
yo!hey!yo!hey!
% ./yo --number stirng # typeオプション
Expected numeric value for '--number'; got "stirng"
% ./yo # defaultオプション
yo!hey!
他にもありますが、使用頻度が高そうなのはこのぐらいですかね。
Thorコマンドをインストールしたい
実行可能なコマンドであればbinに置くなりPATHを通せばいいのだけど、もう少し手軽に使えればそれでいいという場合、Thor installでシステム領域にスクリプトをインストールし、使うことができます。
ローカルのファイルは当然ですが、Thorのinstallはウェブ上にアップしたファイルからでもインストールOKなので、例えばGistに公開しているファイルからでもインストールできます。
$ thor install https://gist.githubusercontent.com/yatmsu/d156000dd6eefb9c52bd/raw/4fe831ce6e987e46833abee0444ef1505c1251f5/yo.thor
$ thor list
gist
----
thor gist:yo # gist sample task
$ thor gist:yo
hey!
更にスクリプトがアップデートされても、update一発で更新できます。
$ thor installed # インストール済みのスクリプトを表示
Modules Namespaces
------- ----------
yo.thor gist
gist
----
thor gist:yo # gist sample task
$ thor update yo.thor # モジュール名を指定することによって更新
これは便利。
ThorをRailsで使いたい
「Rubyやってます!」って人は大体Railsを使っていることが殆どなので、RailsのRakeタスクをThorに置き換えたいと思うはず。手順は以下のとおり。
インストール
RailsはGeneratorでThorを既に使っています。scaffoldとかファイルを生成するやつでお馴染みのアレです。なので、特別インストールする必要はありません。
Thorfileを作成
次はThorfileを作成します。これはRakefileと同じようなファイルです。
これがないとActiveRecordにアクセス出来ません。
require File.expand_path('../config/environment', __FILE__)
各タスクでrequireしている例を見かけるんですが、冗長的なのでこっちの方がDRYでおすすめ。
Thorでタスクを作成
で、普通にThorでタスクを作成。
class Sample < Thor
desc 'users', 'user名を表示'
method_option :limit, default: 10
def users
User.all.limit(options.limit).each do |user|
puts user.first_name
end
end
end
実行してみます。
$ bundle exec thor sample:users --limit 3
洋平
陽平
遥平
よし動いた・・!
これでRakeのウザい引数を気にせず年を越せる!
(∩´∀`)∩ワーイ
参考URL
- https://github.com/erikhuda/thor/wiki - もっと詳細を知りたい人はここを見るのが一番。