Shinjuku.rb #43
Wheneverとは、明快なシンタックスとcron jobを提供するgemである。
グローバルにinstall
$ gem install whenever
or Gemfileに書く
+ gem 'whenever', require: false$ wheneverize .→ config/schedule.rb に設定ファイルができる
書き方
set :output, "/path/to/my/cron_log.log"
every 2.hours do
command "/usr/bin/some_great_command"
runner "MyModel.some_method"
rake "some:great:rake:task"
endみたいなDSLで書ける。
$ wheneverize --update-crontab直感的なDSLでcronを書けるよ!
大体以下のようにできている
- wheneverize
config/shedule.rbを直で出力- テンプレートは
bin/wheneverizeに直接書いてある
- テンプレートは
- whenever
- オプションのparse
Whenever::CommandLine(self.execute->initialize->run) ->Whenever.cron->Whenever::JobList(initialize->generate_cron_output)の順でたどる。- ユーザー定義の設定を読み込む
- マージ
instance_eval(setup, setup_file)とある。
読み込んだ中身をそのままevalしている -> lib/whenever/job_list.rbのメソッドがそのまま、DSLとして使える
- set
- env
- every
- job_type
この中でもset, job_typeはメソッドの中でさらにメソッドを定義するというメタプロなことをしている
- set : https://github.com/javan/whenever/blob/c7675c81d34131f7107b2930c207c9b994895a08/lib/whenever/job_list.rb#L28-L35
- job_type : https://github.com/javan/whenever/blob/c7675c81d34131f7107b2930c207c9b994895a08/lib/whenever/job_list.rb#L28-L35
job_type はどういうテンプレートを使うか定義できるものだが、defaultのDSLsetup.rb内で下記4つが動的に定義されている
- command
- rake
- script
- runner
everyメソッドはブロックをスコープのように扱うことでcronの時間情報を閉じ込めている(@current_time_scopeに時間情報を持っている)
つまり、
every 2.days do
command 'echo "hello, world!!"'
endのようなDSLは、@current_time_scope = 2.daysとして、その後のparseが行われる。
- 実際のcrontab形式への変換は
generate_cron_outputメソッド内、cron_jobsメソッドで行っている- generate_cron_outputでは、環境変数の変換も別に行っている(environment_variables)
- さらに深掘りすると、
Whenever::Output::Cron.outputというのが出てくる - Whenever::Output::Cron.output -> Whenever::Output::Cron.enumerate -> Whenever::Output::Cron#output で追う。
Whenever::Output::Cron#time_in_cron_syntaxが、wheneverの記法から、cronの記法に必要あれば変換しているところ(時間情報) - 場合分けのところが勉強になる(case文: https://gist.github.com/treby/78a1c6bf9c145970ea8f1e4af2cbc73d#gistcomment-1931738)- コマンド部分は
Whenever::Job#outputにてやっている- templateに変数をあてはめることをしている
- これら二つを組み合わせてDSLからcronの記述ができる
lib/whenever/command_line.rb内でやっている - 特定のマジックコメントでどの範囲を更新するか制御している
- プロセス内ロック - 単一のプロセス内では重複しないことが保証されている
- プロセス間ロック・サーバ内ロック - 同一のサーバ内では処理が重複しないことが保証されている
- サーバ間ロック - サーバ群の中で処理が重複しないことが保証されている
wheneverはあくまでcrontabを更新するためのgem。上の内では「プロセス間ロック・サーバ内ロック」にあたる。単一のサーバでのみ稼働するシステムでは有用だが、複数サーバを使うようなケースでは、どのサーバで稼働させるかを考慮しなければならない。
Whenever::JobListを見てみる