【動画付き】Railsでenumの状態を変更するリンクの作り方


はじめに

Qiitaを見ているとこちらの記事が目に入りました。

本当の初心者のためのenumを使った状態管理(クリック一つで状態変遷させるやつ)の実装 rails - Qiita

「ふんふん、なるほど~」と思いながら読んでいたら、GETでデータを変更しようとしていることに気づきました。

データを変更するのであれば、GET以外のHTTPメソッド(POST、PATCH、PUT、DELETE)を使う方がWebアプリケーションとして適切です。

最初は「GETを使わない方がいいですよ~」とコメントして終わろうかと思ったのですが、記事の最後の方に「初心者はview controller model route 全て具体例がないと実装できません(泣)」と書いてあります。

たしかに。
上級者なら自力で埋められる情報の溝(不足している情報)も、初心者の人にとってはなかなか乗り越えるのが難しかったりします。

それならば、ということで「こうやれば実装できますよ」というのをrails newするところから動画に撮って説明してみようと思いました。

この記事ではその動画のポイントを説明していきます。

動画はこちらです

サンプルアプリケーションを作成する様子はこちらに載せています。

Railsでenumの状態を変更するリンクの作り方 - YouTube

良くも悪くもノーカットです

ちなみに、完全ノーカットでトータル36分あります。
最初は10分ぐらいあればできるでしょー、と高をくくっていたんですが、途中でenumを使ったフォームの作り方で試行錯誤してしまい、時間を食ってしまいました。
(言い訳すると、僕は普段enumerize gemを使うことが多く、Rails標準のenumは使ったことがないんですよね。。)

試行錯誤の時間が結構長いので、さっさと先に進みたい方は11:30~19:50のあたりをスキップしてください。

あと、どっちみち長いので動画は1.5倍速ぐらいで見た方が良いと思います。

RubyMineを使ってます!

動画の中ではRubyMineを使って開発しています。
RubyMineを使ったRails開発に興味がある人も参考にしてみてください。

今回作成する画面

こんな感じでStatusをクリックすると"draft"と"published"が切り替わるリンクを作成します。

コードはこちらです

今回作成したサンプルアプリケーションは以下のGitHubリポジトリに置いています。

ここを見れば、ViewもControllerもModelもRoutesも全部チェックできます。

アプリの簡単な解説

Migration

statusがenumになるカラムです。
カーディナリティが低く(=データの散らばりが少ない)、インデックスは付けても効果がなさそうなので、付けていません。

db/migrate/20170926222713_create_articles.rb
class CreateArticles < ActiveRecord::Migration[5.1]
  def change
    create_table :articles do |t|
      t.string :title, null: false
      t.text :content, null: false
      t.integer :status, null: false, default: 0

      t.timestamps
    end
  end
end

Model

モデルの実装はこんな感じです。
statusを切り替えるためのtoggle_status!メソッドを用意しました。

app/models/article.rb
class Article < ApplicationRecord
  enum status: { draft: 0, published: 1 }
  validates :title, :content, :status, presence: true
  validates :status, inclusion: { in: Article.statuses.keys }

  def toggle_status!
    if draft?
      published!
    else
      draft!
    end
  end
end

Routes

既存のデータを書き換えるので、PATCHでtogle_statusアクションを定義しました。

config/routes.rb
Rails.application.routes.draw do
  resources :articles do
    patch :toggle_status
  end
end

View

今回はshowの画面でstatusを変更できるようにしました。
link_toメソッドにmethod: :patchのオプションが付いている点に注目してください。

app/views/articles/show.html.erb
<!-- 省略 -->

<p>
  <strong>Status:</strong>
  <%= link_to @article.status, article_toggle_status_path(@article), method: :patch %>
</p>

<!-- 省略 -->

Controller

Scaffoldで用意されるアクションに加えて、toggle_statusアクションを追加しています。
before_action:toggle_statusを追加することと、set_articleメソッドの中で|| params[:article_id]を追加することも必要になるので注意してください。

class ArticlesController < ApplicationController
  before_action :set_article, only: [:show, :edit, :update, :destroy, :toggle_status]

  # 省略

  def toggle_status
    @article.toggle_status!
    redirect_to @article, notice: 'Article was successfully updated.'
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_article
      @article = Article.find(params[:id] || params[:article_id])
    end

    # 省略
end

System test

念のため、簡単なテストも書いておきました。
リンクをクリックしたらstatusが切り替わることを検証しています。

test/system/articles_test.rb
require "application_system_test_case"

class ArticlesTest < ApplicationSystemTestCase
  test 'toggle status' do
    visit article_path(articles(:one))
    assert_link 'published'

    click_link 'published'
    assert_link 'draft'

    click_link 'draft'
    assert_link 'published'
  end
end

まとめ

こんな感じにすると、enumの状態を変更するリンクを作ることができます。

データを変更する場合はGET以外のHTTPメソッドを使う!

これがWebアプリケーションのセオリーですので、あまり意識していなかった人は気をつけるようにしてください。