一時間ごとにRakeタスクを実行する
今回行いたいことは、公開待ちになっているブログの記事を公開日時になったら自動で公開してくれるような実装をすることです。
ここで使う機能として3つあります。この3つを使って実装していくので少しややこしく感じますが、一つずつ説明していきたいと思います。
Rakeタスク
whenever
cron
Rakeタスクとは
Rakeは、Ruby および Rails アプリケーションで人気のあるタスクランナーです。例: Rails は、データベースの作成、移行の実行、テストの実行のための事前定義された Rake タスクを提供します。カスタムタスクを作成して特定のアクションを自動化することもできます。コード分析ツールの実行、データベースのバックアップなど。
そしてこのRakeが実行する処理内容を「Rakeタスク」と呼び、定義する場所を「Rakefile」と呼びます。
Rakeタスクの使用方法
Rakeタスクに「公開日時になっている公開待ちの記事を公開に変更する」を定義していきます。enumで記事の状態を「公開(published)」「公開待ち(future_publish)」に分類しています。
models/article.rb enum state: { published: 0, future_publish: 1 }
まず最初にタスクファイルを生成します。
ファイル名は何を行うファイルなのかわかりやすい名前をつけると良いです。今回は「article_state」にしました。
$ rails g task ファイル名
lib/tasks/配下にarticle_state.rakeというファイルが生成されました。
ファイルの中身は以下のようになっていると思います。
namespace :article_state do end
作成されたファイルに実行したい処理を記入していきます。
lib/tasks/article_state.rake namespace :article_state do desc '公開待ちの中で、公開日時が過去になっているものがあれば、ステータスを「公開」に変更されるようにする。' task change_to_be_published: :environment do Article.future_publish.past_published.find_each(&:published!) end end
desc
には処理の説明を記入します。
task
にはタスク名をつけます。
:environment
はデータベースに接続する必要がある場合につけます。今回はstateカラムのデータが変わるので必要です。
do
以下に処理内容を記入します。
do
以下のコードは少し難しいので一つずつ読み解いていきます。
past_published
は下記のarticleモデルで定義したscopeのメソッドです。
事前に公開日時が現在からみて過去の記事を取得するためのscopeを準備します。
参考 : 部分一致とか日付の範囲で検索したい - Qiita
models/article.rb scope :past_published, -> { where('published_at <= ?', Time.current) }
find_each
は大量のデータを一度に取得してループするのではなく決まった単位(デフォルト1,000件)ごとに取得してループしてくれる機能です。データを分割して取得することで少ないメモリで処理することができます。
参考 : [Rails]find_eachが無限ループして本番環境のメモリを食いつぶした話 - Qiita
&:
は配列(find_eachやmap、select、pluckなど)の中身を受け取るようなメソッドで使用することができます。
この二つのコードは同じ意味です。 Article.find_each(&:published!) Article.find_each { |article| article.published! }
published!
はenumのメソッドです。以下でenumのメソッドについて説明しておきます。
article = Article.new(state: :future_publish) article.state => future_publish article.future_publish? => true article.published? => false article.published! => stateをpublishedに変更することができます。 article.state = 'published'と同じ意味ですが、コード量が多くなるのでNGです。 Article.published => publishedになっている記事をまとめて全件検索することができます。 Article.where(state: :published)と同じ意味になります。
ファイルの中身が書き終わったら作ったRakeファイルを確認します。
$ rails -T
このようにコマンド上で確認することができます。
これで「公開日時になっている公開待ちの記事を公開に変更する」を定義できました。次は一時間ごとに自動で「公開待ちを公開にする」設定を行っていきたいと思います。
wheneverを導入する
wheneverはcrontab管理ライブラリです。
Gemfileにwheneverのgemを追加してbundle install
を行います。
gem 'whenever', require: false
require: false
はRailsの内部でwheneverを使うわけではないため、Railsの実行時に読み込まないようにするためです。
wheneverの設定ファイルを作成します。
$ bundle exec wheneverize .
このコマンドによってconfig/schedule.rbファイルが生成されました。このファイルの中で、cronを設定していきます。
config/schedule.rb # Rails.rootを使用するために必要。なぜなら、wheneverは読み込まれるときにrailsを起動する必要がある 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" # 1時間ごとにarticle_state.rakeのchange_to_be_publishedを実行する every 1.hour do rake "article_state:change_to_be_published" end
参考 : [初学者]whenever を使って定期的にバッチ処理を行う(公開設定編) - Qiita
crontabを実行する
cronとは「○時になったら○○のコマンドを実行」などといった具合に、定期的にコマンドを実行するためにメモリ場で常に命令を待機しているプロセスです。このcronに対して命令を行うには、crontabというコマンドを実行して登録・確認・管理しています。
# wheneverの設定更新 $ bundle exec whenever --update-crontab
このコマンドを使うことでcronにデータを反映して、schedule.rbファイルに記載したコードを実行することができます。
他にも下記のようなcrontabコマンドがあります。
# 設定内容にエラーがないか確認 $ bundle exec whenever # 設定されているcronを見る $ crontab -l # crontabの設定削除 $ bundle exec whenever --clear-crontab