営業職の俺がエンジニアになる〜群馬Web化計画編〜

2017年に未経験からエンジニアに転職をした私が群馬でイノベーションを起こすまでの備忘録です。

Railsチュートリアルの返信機能を実装する〜その1〜

※長編になりそうでしたので各項ごとに書いて最終的にマージするようにします。

昨日に時間はかかりましたがようやくRailsチュートリアルの2周目を完走しきりました。本から掲げている通り、Railsチュートリアルの2周目を完走したら、予備演習の機能追加を2〜3個進める予定でしたので、そちらを黙々と進めています。

今回は機能追加演習の中の「返信機能」の追加をすることに成功したので、そちらの開発方法について記載をしていきます。ただし、先に申し上げると自分のレベル感の問題から、そもそもの要件とは少し異なった内容となっているので、アレンジバージョンとして見て頂けると幸いです。

全体的な開発の流れ

返信機能を実装するにあたって、下記の順で実装を進めていきました。

  1. 一意のユーザー名の追加
  2. インターフェースの変更
  3. ユーザー名に対応した返信機能の実装
  4. 返信内容を表示させるためのフィードの改善

自分の開発に対しての振り返りのためにも一つ一つ細かく解説を入れながら書いていきたいと思います。もしかしたら書いている最中に自分でツッコミを入れながら進めるかもしれませんが、ご容赦くださいませ。

1 一意のユーザー名の追加

返信機能を実装するにおいて、まず「誰」宛に返信をするのかを明示する必要があります。そのためには一意性を持った要素が必要となります。Sample AppのUserオブジェクトではidやemailに一意性を持たせていたか思います。しかしながら、今回の返信機能を実装するにあたってidでは短すぎて、かつどのユーザーなのか区別が付きづらく、またemailでは逆に長く、返信機能を使うのにいちいちメールアドレスを入力しなければならないのは大変です。

そこでusersテーブルに新しくユーザー名(user_name)のカラムを実装することにします。

1.1 usersテーブルへのuser_nameカラムの追加

早速usersテーブルへuser_nameカラムを追加するためのコマンドを実行します。

$ rails generate migration add_user_name_to_users user_name:string

上記のコマンドを実行することにより新たにmigrationファイルが作成されます。この後にuser_nameからUserオブジェクトを検索するコードを追加するので、indexも追加している点に注意ください。(indexを追加する必要があるかどうかは正直自身はないので飛ばしてもOKかもです)また、一意性を保つ必要もあるので、この時点でunique: trueを指定しています。

class AddUserNameToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :user_name, :string
    add_index :users, :user_name, unique: true
  end
end

この設定をした上で、マイグレーションの適用をしましょう。

$ rails db:migrate

1.2 user_nameのバリデーション

上記の流れでuser_nameがusersテーブルへ追加されたので、続いてはuser_nameにバリデーションを追加していきます。今回は「存在性」「一意性」「フォーマット(英数字のみ)」この3つのバリデーションを追加することにします。せっかくなのでテスト駆動開発(TDD)を意識して進めるため、先にテストを書いてから実装に移りたいと思います。

1.2.1 バリデーションのテスト

上記の通り「存在性」「一意性」「フォーマット(英数字のみ)」の3つのバリデーションを追加するので、それぞれについてのテストを書いていきます。

user_test.rbにテストコードを追加

require 'test_helper'

class UserTest < ActiveSupport::TestCase
  
  def setup
    @user = User.new(name: "Example User", email: "user@example.com",
                     password: "foobar", password_confirmation: "foobar",
                     user_name: "example")
  end
・
・
・
  test "ユーザー名:存在性に対するバリデーション" do
    @user.user_name = ""
    assert_not @user.valid?
  end
  
  test "ユーザー名:一意性に対するバリデーション" do
    duplicate_user = @user.dup
    duplicate_user.user_name = @user.user_name.upcase
    @user.save
    assert_not duplicate_user.valid?
  end
  
  test "ユーザー名:使用可能な文字列のバリデーション" do
    valid_user_names = %w[yutachan taro2 3Shiro Boys2Men]
    valid_user_names.each do |valid_user_name|
      @user.user_name = valid_user_name
      assert @user.valid?, "#{valid_user_name.inspect}は使用可能"
    end
  end
  
  test "ユーザー名:使用不可な文字列のバリデーション" do
    invalid_user_names = %w[yuta-chan Taro@San 3.sHiro boY.2/meN あいう]
    invalid_user_names.each do |invalid_user_name|
      @user.user_name = invalid_user_name
      assert_not @user.valid?, "#{invalid_user_name.inspect}は使用不可"
    end
  end
end

上記の通り記載をし$rails testREDになることを確認してください。確認ができたら実際にuser.rbにバリデーションを追加していきます。Railsチュートリアル本編でのメールアドレスのバリデーションとほぼ同じなので、その内容に習って記載をしていきます。

user.rbにバリデーションを追加

class User < ApplicationRecord
・
・
・
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
  VALID_USER_NAME_REGEX = /\A(\w+)\z/i
  validates :user_name, presence: true,
                        format: { with: VALID_USER_NAME_REGEX },
                        uniqueness: { case_sensitive: false }
・
・
・

上記のコードを見た時にメールアドレスのバリデーションとの大きな違いとしては正規表現のところくらいでしょうか。

VALID_USER_NAME_REGEX = /\A(\w+)\z/i

すごくシンプルではありますがフォーマットを英数字のみに指定するためにVALID_USER_NAME_REGEXという定数に正規表現での検証を追加しています。ここはもしかしたらもっといい正規表現があるかもしれません。

これらのコードを追加し改めて$ rails testを実行するとGREENになる、と思いきやまだREDのままです。何故かと言うとuser_nameへ「存在性」を追加したことで過去のテストに影響が出てしまっているためです。統合テスト「users_signup_test.rb」を下記の通り修正することで、テストがGREENになるはずです。

user_signup_test.rbにuser_nameの値を追加

・
・
・
  test "valid signup infromation with account activation" do
    get signup_path
    assert_difference "User.count", 1 do
      post signup_path, params: { user: { name: "Example User",
                                          email: "user@example.com",
                                          user_name: "example",
                                          password: "password",
                                          password_confirmation: "password" } }
    end
・
・
・

これでusersテーブルへuser_nameカラムを追加する作業が完了です。続きについてはものすごく長くなる可能性があるので別の記事にまとめようかと思います。一旦ここまでご拝読ありがとうございました。