Ryota400’s blog

エンジニアを目指して書いてます。

記事ステータスの追加

実装したこと

・記事を投稿するアプリの編集画面で、記事のステータスを「下書き」「公開」「公開待ち」に分類したい。

・ステータスと公開日時は編集時に選択可能。ただし、公開日時によって記事のステータスを「公開」「公開待ち」に自動で判定して変更する。

・ステータスを「下書き」に指定したときは、「下書き」のままにする。

コントローラー

artcles_controller.rb(記事更新用)

def update
    authorize(@article)

    @article.assign_attributes(article_params) ①
    @article.adjust_state #3でステータスを調整

 ②
    if @article.save ③
      flash[:notice] = '更新しました'
      redirect_to edit_article_path(@article.uuid)
    else
      render :edit
    end
  end

① assign_attributesメソッドで変更を受け取る #引数で指定したattributes(属性)を変更するメソッド
②現在時刻が公開日を過ぎているか? いないかでstateを公開、公開待ちと振り分ける。
③ saveメソッドで保存 #DBへの保存はされないので必要。

published_controller.rb(記事公開用)

def update
    @article.published_at = Time.current unless @article.published_at?
    @article.state = @article.publishable? ? :published : :publish_wait
    #上記の一文は三項演算子

    if @article.valid?
      Article.transaction do
        @article.save!
      end
      flash[:notice] = @article.message_on_published
      redirect_to edit_admin_article_path(@article.uuid)
    else
      flash.now[:alert] = 'エラーがあります。確認してください。'
      @article.state = @article.state_was if @article.state_changed?
      render 'admin/articles/edit'
    end
  end

@article.publishable?が真ならpublishedを 偽ならpublish_waitを@article.stateに代入

モデルへ切り分ける

①日時で公開可能か判定
②判定結果ごとのメッセージ分け
③draft?はstateがdraftかどうか判定する trueならreturnによりここで処理が終わる(nilが返る)

article.rb

def publishable?    ①
    Time.current >= published_at
  end

  def message_on_published ②
    if published?
      '公開しました'
    elsif publish_wait?
      '公開待ちにしました。'
    end
  end

  def adjust_state  ③
    return if draft? 

    self.state = if publishable?  # stateがpublishedなら:published(公開)を返す
                              :published
                        else                        # それ以外(publish_waitしか残ってないのでpublish_wait)なら:publish_wait(公開待ち)を返す
                              :publish_wait
                        end
  end

公開日を1時間ごとにしか設定できないように変更する

admin.js

format: 'YYYY-MM-DD HH:00'

Rakeタスク

Rakeタスクとは
アプリケーションを起動せず、行いたい処理をCUIコマンドプロンプトやターミナル)から実行できます。CSVデータのインポートなど、サーバーを起動せず任意の処理を実行する際にこの機能がよく利用されます。

今回でいうと、公開待ちの中で、公開日時が過去になっているものがあれば、ステータスを「公開」に変更されるようにする。
上記の処理をrakefileに記述し、後ほど紹介するwhenneverと組み合わせることにより、自由なタイミングで処理を走らせることができます。

前提
app/models/article.rb enumで記事の状態を「下書き」「公開」「公開待ち」に分類しています。
今回のRakeタスクでは、「公開待ち」の記事について、公開日時を越えたら記事の状態を「公開」に変える処理を行います。
app/models/article.rb

# draft: 下書き, published: 公開, publish_wait: 公開待ち
enum state: { draft: 0, published: 1, publish_wait: 2 }

Rakeファイルの作成

rails g task article_state
# rails g task ファイル名

lib/tasks/article_state.rake

namespace :article_state do
  desc '公開日時が過去の日付の「公開待ち」記事があれば、ステータスを「公開」に変更する'   # desc = description(説明)
  task update_article_state: :environment do     # environmentはDBとのやりとりが必要な際に記述します
     Article.publish_wait.past_published.find_each(&:published!)
  end
end

Articleから「公開待ち」の状態で公開日時が現在〜過去のものを取ってきてから、find_eachでループさせます。 必要なデータだけ先に抽出してから繰り返し処理をしています。 (&:メソッド名)でpublished!を実行

公開日時が現在〜過去の記事を取得するscopeを準備
app/models/article.rb

scope :past_published, ->{ where('published_at <= ?', Time.current) }

Rakeタスクの実行

bundle exec rake article_state:update_article_state

cronとは

UNIX系のOSには、cronと呼ばれる仕組みが標準で備わっています。

cronとは、多くのUNIX系OSで標準的に利用される常駐プログラム(デーモン)の一種で、利用者の設定したスケジュールに従って指定されたプログラムを定期的に起動してくれるもの。

そして、このcronに対して命令を行うには、crontabというコマンドを実行します。

crontab -l で現在設定されている定期実行タスクの一覧を表示させたり、

crontab -r でcronを削除できたりなど、様々な命令を行うことが可能となっております。

gem wheneverの導入

cronjobsを実行してくれるgem

gem 'whenever', require: false

require: falseとするのはこのGem自体がRailsアプリケーションに反映するものではなく、 ターミナル(言わばOS)に反映させるため

$ bundle exec wheneverize

config/schedule.rbファイルが生成
schedule.rb

# Rails.rootを使用する
require File.expand_path(File.dirname(__FILE__) + "/environment")

# cronを実行する環境変数(RAILS_ENVが指定されていないときはdevelopmentを使用)
rails_env = ENV['RAILS_ENV'] || :development

# cronの実行環境を指定(上記で作成した変数を指定)
set :environment, rails_env

# cronのログファイルの出力先指定
set :output, "#{Rails.root}/log/cron.log"

#一時間毎に実行する&タスク名の指定
every 1.hours do
  rake 'article_state:update_article_state'
end

Crontabへの書き込み

$ bundle exec whenever --update-crontab

参考文献

qiita.com

ログイン - はてな