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
を見てみる