とりとめも

藻類が学んだり感じたりしたことを未来の自分のために書き留めるところ

Wheneverを使ってRailsタスクを定期実行する

はじめに

Railsタスクでバッチ処理を動かしたいと思いました。
要するにcronでRailsタスクをコマンド実行したかったのですが、Railsを介するcrontabを簡単に設定・管理できる「Whenever」というgemがあるそうです。

というわけで、試してみたメモです。

環境

Ruby:2.4.1
Rails:5.1.1
Whenever:0.9.7

タスクを作る

Wheneverの話の前に、Railsでタスクを作ったことがなかったので、まずはタスク作りから始めます。

タスクファイルは何処にあるのか

cron(Whenever)的には、実行さえできればそれでよいので、必要なメソッドを備えたclassやmoduleが何処かにあれば良さそうです。
もっと言えば、「モデル内のクラスメソッドを定期的に実行したい!」というような場合、今回のように個別のタスクファイルは作成せずに、Wheneverで該当のクラスメソッドを記述してあげればよいのだと思います。

置き場所はlib/tasksが良さそう?

今回はアプリ内で完結する機能ではない想定なので、専用のタスクファイルを作成します。

https://altarf.net/computer/rails/2493

こちらの記事を参考に、lib/tasksに作成しました。
たとえば以下のようなファイル「lib/tasks/marimotask.rb」を作ります。

require "#{Rails.root}/app/models/marimodel"
require "#{Rails.root}/app/lib/malib"
class Tasks::Marimotask
  def self.marimo
   #modelやlibのメソッド呼んだり
    Marimodel.hoge
    Malib::Marimo.nyanya 
    #適当な処理書いたり
    foo
    #putsしてみたり
    puts "成功しました!"
  end
end

各modelやlibのクラスを使いたい場合は、個別にrequireしてあげる必要があるようです。

タスクのパスをオートロードにする

また、Wheneverからlib/tasksを呼び出せるようにパスも通してあげます。
config/application.rbに以下を追記します。

config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.eager_load_paths += Dir["#{config.root}/lib/**/"]

development環境ではconfig.autoload_paths、production環境ではconfig.eager_load_pathsが必要となります。(設定次第ですが)
当初私はconfig.autoload_pathsしか設定しておらず、とてもエラーに悩まされました。
この点は別途記事にしたいと思います。

手動実行してみる

$ bundle exec rails runner Tasks::Marimotask.marimo
成功しました!

これでタスクの準備ができました。
あとはWheneverを使って、このタスクをcrontabに登録していきます。

Wheneverの使い方

gemインストー

これは当然Gemfileに記述。

(略)
gem "whenever"

Wheneverの設定ファイル作成

$ bundle exec wheneverize
[add] writing `./config/schedule.rb'
[done] wheneverized!

config/schedule.rbが作成されました。
これがcrontabの元になります。つまり、このファイルに「○時間ごとにxxを実行」みたいな処理を書いていきます。

schedule.rbの編集

先ほど作ったmarimoを登録したければ、以下のように書きます。

every 8.hours do
  runner "Tasks::Marimotask.marimo"
end

これで8時間ごとにmarimoが実行されることを意味します。
以下のコマンドで、schedule.rbをcrontab用の記法に変換した結果を出力してくれます。

$ bundle exec whenever
0 0,8,16 * * * /bin/bash -l -c (略)
## [message] Above is your schedule file converted to cron syntax; your crontab file was not updated.
## [message] Run `whenever --help' for more options.

これはあくまでもscheduleの中身をcrontab用に翻訳してくれるだけで、まだcrontabに登録されたわけではありません。お試しです。
実際に反映するには次のコマンドを使います。

crontabへの反映

$ bundle exec whenever --update-crontab
[write] crontab file updated

登録できた。ので、確認する。
あとはもういつものcrontabの話。

$ crontab -l
# Begin Whenever generated tasks for: (Rails_ROOT)/config/schedule.rb
0 0,8,16 * * * /bin/bash -l -c (略)
# End Whenever generated tasks for: (Rails_ROOT)/config/schedule.rb

できてた。

(補足と注意)wheneverコマンドのオプション

#schedule.rbをcrontabに反映(既存のcrontabは残る)
$ bundle exec whenever --update-crontab

#schedule.rbをcrontabに反映(既存のcrontabは消える!)
$ bundle exec whenever --write-crontab

#schedule.rbで定義したcrontabを削除
$ bundle exec whenever --clear-crontab

updateとwriteでは、whenever以外で定義したcronの扱いが異なります。
crontab中の#Begin〜から#End〜(Wheneverが書き込んだ範囲)のみを更新するのがupdateで、#Begin〜とか無視してcrontabをまるっと書き換えるのがwrite。
削除コマンドであるclearは、update同様に、BeginとEndで挟まれたWheneverによる定義部分のみを削除してくれます。