Railsで定期処理を回す(rakeタスク, cron)
ブログアプリでは、記事の状態を「下書き」「公開待ち」「公開」などで分けて管理していることが多いと思います。
今回はこの「公開待ち」を指定時間になったら「公開」する、いわゆる予約投稿の処理についてまとめます。
予約投稿は、「公開待ち」状態の記事のうち、「公開日が現在の日時より過去になっている記事」を「公開」状態にする処理を定期的に実行することによって実現します。
これは以下の2つの要素で実装することになります。
- 現在時刻と公開日を比較して状態を変更する処理(バッチ)
- バッチを定期的に実行する仕組み(cron)
railsでバッチ処理を書く際によく使われるものにrake taskとRails runnerがあります。
今回はrake taskで実装します。
Rakeタスクとは
まずrakeとはrubyで書いたビルドツールのことです。
makeでいうMakefileの代わりに、Rakefileというファイルを作成して実行することができます。
そしてRakefileで定義される処理を「Rakeタスク」と呼びます。
cronとは
UNIX系のOSに標準で備わっていて、定期的にコマンドを実行するデーモンプロセスです。 このcronに対して命令を行うには、crontabというコマンドを実行します。
そして、このcronの命令の記述を簡単に行うのがwheneverというgemになります。
Rakeタスクの実装
1.rakefileを作成する
# rails g task [namespace] [task_name]
$ rails g task article_state update
2. rakefileにタスクを記述する
# 名前空間 namespace :article_state do # タスクの説明 desc '公開日時を過ぎた「公開待ち」状態の記事を「公開」状態にする' #タスクの名前 task update: :environment do # 処理内容 Article.publish_wait.find_each do |article| if article.published_at <= Time.current( article&.published! end end end
task update: :environment
はupdateがタスク名になります。
:environment はupdateタスクを実行される前にrailsアプリケーションを呼び出してくれます。
これがないとRails側のモデルやクラスを扱えません。
3. タスクが登録されているかを確認する。
$ bundle exec rake -T rake article_state:update #公開日時を過ぎた「公開待ち」状態の記事を「公開」状態にする #これで表示される中に作成したタスクが表示されていればOK #パイプライン処理で検索かけるが吉
4. Rakeタスクを実行できるか確認
$ bundle exec rake article_state:update
エラーが出なければとりあえずOK
実際の処理内容は目視なりRSpecなりで確認する。
Rakeタスクをcronで定期実行する
cronの管理にwheneverを使用するのでそのインストールからやっていきます。
1. whenever インストール
gem 'whenever', require: false
$ bundle install
以下のコマンドで、初期のconfig/schedule.rbファイルが作成されます。
$ bundle exec wheneverize .
2. schedule.rbで定期処理を設定する
今回は30分毎に先程のタスクを走らせたいので
# Rails.rootを使用するために必要 require File.expand_path(File.dirname(__FILE__) + '/environment') # cronを実行する環境変数 rails_env = ENV['RAILS_ENV'] || :development # cronを実行する環境変数をセット set :environment, rails_env # cronのログの吐き出し場所 set :output, "#{Rails.root}/log/cron.log" #30分ごとにrakeタスクを実行する every 30.minutes do rake "article_state:update" end
require File. ~~
はRails.rootを使用するための記述です。
wheneverはあくまでrubyの機能なので使いたい場合はこれが必要になります。
実行する処理(job)については3つ標準で用意されています。
rake: #rake taskを実行する runner: #rails runnerを実行する(rails内のメソッドを実行できる) command: #シェルコマンドを実行する。
3. crontabを更新する
$bundle exec whenever --updete-crontab [write] crontab file updated
このコマンドはconfig/schedule.rbを探すためプロジェクトのルートで実行する必要があります。
これでcrontabが更新されました。
確認は以下
$crontab -l # Begin Whenever generated tasks for: (project_root)/config/schedule.rb at: 2021-06-14 01:47:15 +0900 0,30 * * * * /bin/bash -l -c 'cd ( :path ) && RAILS_ENV=development bundle exec rake article_state:update --silent >> (Rails.root)/log/cron.log 2>&1' # End Whenever generated tasks for: (project_root)/config/schedule.rb at: 2021-06-14 01:47:15 +0900
:pathには特に指定しない限りwheneverを実行した時のカレントパスが入ります。 つまりRails.rootと同じになります。
余談
cron これは、「クロン」または「クーロン」と呼ばれます日本では。
英語圏の方は「クローン」と発音してますね。
lとrの区別ができない日本人にはcloneと区別できないので「クーロン」になったとかなんですかね。