Rakeタスクとwheneverを使用して、定期実行したい処理を回す方法

そもそもの定義

rakeタスクとは

Rakeとは:rubyで処理内容を定義できるビルドツールのこと。
Rakeタスクとは:Rakeが実行する処理内容を「Rakeタスク」と呼ぶ。Rakeタスクを定義する場所を「Rakefile」と呼ぶ。

wheneverとは

wheneverとは:cronと呼ばれる、UNIX系のOSでは標準で備わってある仕組みを、rubyの簡単な文法で設定できるようにしたライブラリのこと。
cronとは:「○時になったら○○のコマンドを実行」などといった具合に、定期的にコマンドを実行するためにメモリ場で常に命令を待機しているプロセス(=デーモンプロセス)のこと。

Rakeタスクの実装方法

1. Rakeタスクのファイルを作成

以下のコマンドを実行する

% rails g task <タスクの名前>

コマンドを実行することでlib/tasks/<タスクの名前>.rakeという新しいファイルが生成される。このファイルにRakeタスクを記入していく。

2. タスクに処理を記入する

今回は「「公開待ち」の記事に対して、公開日時が過去になっているものがあれば、状態を「公開」に変更する」といったRakeタスクを作成する。以下が実装したコードになる。

namespace :publish_article do
  desc "記事の公開日が過去の日時になったら、ステータスが「公開待ち」の記事を「公開」にする"
  task change_published: :environment do
    Article.where(status: :publish_wait).find_each do |article|
      if article.published_at <= Time.current
        article.update( state: :published )
      end
    end
  end
end

task change_published:で区切られたブロック内にRakeタスクの内容を記述する。chenge_published:の部分の名前は適切な名前に変更可能です。
:environmentの記述はデータベースに接続する場合必要になる。今回はArticleクラスのモデルに接続する必要があるので記載してある。

Articleモデルのスキーマ情報は以下の通り

# models/article.rb

# == Schema Information
#
#  id           :bigint           not null, primary key
#  author_id    :bigint
#  uuid         :string(255)
#  title        :string(255)
#  description  :text(65535)
#  body         :text(65535)
#  state        :integer          default("draft"), not null
#  published_at :datetime
#  created_at   :datetime         not null
#  updated_at   :datetime         not null
#  deleted_at   :datetime
#
# Indexes
#
#  index_articles_on_author_id     (author_id)
#  index_articles_on_deleted_at    (deleted_at)
#  index_articles_on_published_at  (published_at)
#  index_articles_on_uuid          (uuid)
#

class Article < ApplicationRecord
  belongs_to :author
  
  enum state: { draft: 0, published: 1, publish_wait: 2 }
end

3. 作成したRakeタスクが存在するか確認する

以下のコマンドを実行する

% bundle exec rake -T

# 出力結果の中に2.で作成したRakeタスクの名前(今回だとchange_publishedの部分)が出力されているばOK

・・・
rake publish_article:change_published
・・・

4. Rakeタスクを実行する

% bundle exec rake publish_article:change_published

ログを確認してエラーが発生しておらず、Rakeタスクが実行されていればOK。

wheneverの使用方法

1. wheneverをインストールした後、以下のコマンドを実行する

# Gemfile

gem 'whenever'
=> bundle installコマンドを実行

以下のコマンドを実行することで、config配下にschedule.rbというファイルが生成される。

% bundle exec wheneverize .

2. schedule.rbに設定と実行する処理内容を記載する

今回は「日付が変わる毎に先ほど記述したRakeタスクを実行する」ことにした。以下が実装したコードになる。

# 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"

every 1.day, at: '0:00' do
  rake 'publish_article:change_published'
end

requireの部分はRails.rootを使用するためのコードです。
wheneverはRailsとは切り離されたもので単なるrubyのファイルとなっており、Railsとは関係のない単なるrubyのファイルの中でRailsのメソッドを使いたい場合、今回のような記述が必要となってきます。

rails_envの部分はENV['RAILS_ENV']で環境を判断し、何も入っていなければ:developmentをrails_envに代入するようにしています。

処理内容の書き方については、今回は「Rakeタスクを毎日実行する」ようにしてあるのでrakeを使用しました。他の書き方については

runner "Rails内のメソッドを実行"
command "bashコマンドを実行"

といったものがある。

3. crontabコマンドでcronを更新する

schedule.rbを変更しただけでは変更は反映されないので、以下のコマンドを実行することでcronにデータを反映させる。

% bundle exec whenever --update-crontab

次にcrontab -lコマンドを実行して、先ほど反映させた定期実行タスクが存在するかを確認できる。

% crontab -l

# Begin Whenever generated tasks for: (Rails_ROOT)/config/schedule.rb at: 2021-05-07 20:06:17 +0900
0 * * * * /bin/bash -l -c 'cd (Rails_ROOT) && RAILS_ENV=development bundle exec rake publish_article:change_published --silent >> (Rails_ROOT)/log/cron.log 2>&1'

# End Whenever generated tasks for: (Rails_ROOT)/config/schedule.rb at: 2021-05-07 20:06:17 +0900