Edited at

railsのバッチの共通処理について考えてみた


この記事のライセンス



この記事はCC BY 4.0(クリエイティブ・コモンズ 表示 4.0 国際 ライセンス)の元で公開します。


前提・背景

railsにはrails runnerとてもよくできたバッチ機構が存在します。

rails runner 'p "hoge"' のように、rails runnerの後ろにrubyでプログラムが書けます。

大体、サービスのバッチで使用する場合は、下記のようにして、

Batchクラスに実装したrunメソッドを実行することが多いようです。

$ rails runner Batch.run

もちろん、Batch.runの部分はただのrailsですので、

$ rails r User.actives.map(&:kick)

のように、書くこともできます。

ただ、大体はバッチの実行用のクラスを用意し、処理をファイルの中に書き、それを実行すると思います。


自分が考えていること


少しでもDRYにしたいにしたい

バッチを毎回毎回普通に作るのはそれでいいんだけど、

バッチの実行時間や実行ログなどは、大体同じ処理になるはず。

失敗したときの通知とか、正常に終了した時の通知とかもあるはず。


少しでもインターフェースを統一したい

バッチファイルの置き場所をつくっても、人によって実行の仕方が違うと混乱する。

runで実行するひととexecで実行する人とか色々いそう。

バッチを書いた時に、自分がチームのインターフェースと違う形で実行していると気付けるようにしたい。

「BaseBatchクラスを作って、それを継承すればいいじゃん!」って話なのですが、rubyはfinalがないので、自分がオレオレで実装すると、そのオレオレが優先されて、チームのルールから外れていることに気づきにくい。


今回考えたこと

バッチを実行するとき大体は事前処理と事後処理があるはずです。

その内容はサービスやバッチによっていろいろだとは思いますが、

今回は実行時間を計測するために、起動時と終了時の時刻をログに書くものを統一するシンプルな共通処理を書いてみました。

こういうbaseのモジュールを作っておき、

module Batch

module Base
def before
Rails.logger(Time.current)
end

def run
before
execute
after
end

def after
Rails.logger(Time.current)
end
end
end

実行するバッチファイルの方には、prependで、共通処理を上書きしないように書いておきます。

module Batch

class Example
prepend Batch::Base

def execute

end
end
end

一応これで完成。

$ rails r "Batch::Example.new.run"

で実行できるようになります。

runメソッドを実装すればいいんだ!と思ってrunを実装しても、Exampleの方にexecuteがないと言って怒られるので、個人的には気付けると思います。

rubyにはfinalがないので、prependで上書きはできないようにしています。


番外編 (もやもや)

コマンドの書き方が気に食わなかったので、

module Batch

class Executer
def self.run(klass_name)
klass_name.constantize.new.run
end
end
end

というラッパーを作ってインターフェースを統一してみました。

$ rails r "Batch::Executer.run('Batch::Example')"

あんまり嬉しくないかな?

なんとなく名前が悪い気がするから、gem化してもいいかもね。

コマンドレベルで、とかすればいいか?

$ rake batch run 'Batch::Example'

うーん。微妙か。