こんな人におすすめ
- プログラミング初心者でポートフォリオの作り方が分からない
- Rails Tutorialをやってみたが理解することが難しい
前回:#4 System spec導入編
次回:#6 ユーザー登録画面, エラー日本語化編
今回の流れ
- 完成のイメージを理解する
- Userモデルを作る
- Userモデルのテストを作る
- Herokuのエラーを解決する
- showビューを仮実装する
※ この記事は、ポートフォリオを作る理由をweb系自社開発企業に転職するためとします。
※ 2020年3月30日、記事を大幅に更新しました。
完成のイメージを理解する
まずは、登録機能・ログイン機能を実装するためのUserモデルを作ります。
続いて、UserモデルのテストをModel specで作ります。
その後、マイグレーションの際のHerokuエラーを解決します。
最後に、次回以降に必要な仮ビューを作ります。
以上です。
Userモデルを作る
ユーザー情報を管理する、Userモデルを作ります。
ここでの手順は以下の通りです。
- Userモデルを生成する
- バリデーションを追加する
- インデックスを追加する
- パスワードを追加する
Userモデルを生成する
ジェネレーターでUserモデルを生成します。
現段階では、Userモデルにユーザーネームとアドレスの属性を与えます。
$ rails g model User name:string email:string
この際、自動的にID属性も付与されます。
このIDは、showビューを仮実装にも使われます。
バリデーションを追加する
このままでは、どんな情報でもUser登録を受け付けてしまいます。
よって、以下のバリデーションを加えます。
- ① ユーザーネームを必須にする
- ② アドレスを必須にする
- ③ 51文字以上の名前を無効にする
- ④ 256文字以上のアドレスを無効にする
- ⑤ アドレスでない文字列を無効にする
- ⑥ 一意性のないアドレスを無効にする
- ⑦ 一意性のないアドレスの大文字小文字を区別しないようにする
- ⑧ アドレスを全て小文字にする
class User < ApplicationRecord
before_save :downcase_email #⑧
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :name,
presence: true, #①
length: { maximum: 50 } #③
validates :email,
presence: true, #②
length: { maximum: 255 }, #④
format: { with: VALID_EMAIL_REGEX }, #⑤
uniqueness: { case_sensitive: false } #⑥⑦
private
def downcase_email #⑧のメソッド
email.downcase!
end
end
downcase_emailメソッドは、user.rb内でのみ使います。
よって、プライベートにします。
インデックスを追加する
アドレスを検索する際は、ユーザーとの紐付けがあると高速です。
そのためのインデックスをマイグレーションに追加します。
$ rails g migration add_index_to_users_email
class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
def change
add_index :users, :email, unique: true
end
end
データベースレベルでの一意性も確保するために、uniqueをtrueにしています。
最後に、データベースを更新します。
$ rails db:migrate
パスワードを追加する
Userモデルにパスワードを追加します。
ここでの手順は以下の通りです。
- has_secure_passwordを理解する
- 必要なGemを入れる
- password_digest属性を追加する
- has_secure_passwordとバリデーションを追加する
has_secure_passwordを理解する
パスワードの実装は、モデルにhas_secure_passwordメソッドを加えることで可能になります。
これによって、以下の機能が使えます。
- passwordとpassword_confirmation(再入力)の仮属性が作られる
- セキュア化したパスワードをpassword_digest属性に保存する
- パスワードの正誤を表すauthenticateメソッドが使える
has_secure_passwordを使うには、Gemとpassword_digest属性が必要です。
必要なGemを入れる
bcryptというGemを加えます。
+ gem 'bcrypt', '3.1.12'
$ bundle install
password_digest属性を追加する
password_digest属性の追加には、同じくマイグレーションを使います。
$ rails g migration add_password_digest_to_users password_digest:string
$ rails db:migrate
has_secure_passwordとバリデーションを追加する
Userモデルにhas_secure_passwordメソッドを追加します。
パスワードの一意性と5文字以下を無効にするバリデーションも追加します。
class User < ApplicationRecord
before_save :downcase_email
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :name,
presence: true,
length: { maximum: 50 }
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 }
private
def downcase_email
email.downcase!
end
end
以上でモデルの作成が完了です。
Userモデルのテストを作る
Model specでUserモデルのテストを作ります。
Rails Tutorial 6.2のテスト内容とほぼ同じです。
require 'rails_helper'
RSpec.describe User, type: :model do
let(:user) { User.new(
name: "Example User",
email: "user@example.com",
password: "foobar",
password_confirmation: "foobar"
) }
describe "User" do
it "should be valid" do
expect(user).to be_valid
end
end
describe "name" do
it "gives presence" do
user.name = " "
expect(user).to be_invalid
end
context "50 characters" do
it "is not too long" do
user.name = "a" * 50
expect(user).to be_valid
end
end
context "51 characters" do
it "is too long" do
user.name = "a" * 51
expect(user).to be_invalid
end
end
end
describe "email" do
it "gives presence" do
user.email = " "
expect(user).to be_invalid
end
context "254 characters" do
it "is not too long" do
user.email = "a" * 243 + "@example.com"
expect(user).to be_valid
end
end
context "255 characters" do
it "is too long" do
user.email = "a" * 244 + "@example.com"
expect(user).to be_invalid
end
end
it "should accept valid addresses" do
user.email = "user@example.com"
expect(user).to be_valid
user.email = "USER@foo.COM"
expect(user).to be_valid
user.email = "A_US-ER@foo.bar.org"
expect(user).to be_valid
user.email = "first.last@foo.jp"
expect(user).to be_valid
user.email = "alice+bob@baz.cn"
expect(user).to be_valid
end
it "should reject invalid addresses" do
user.email = "user@example,com"
expect(user).to be_invalid
user.email = "user_at_foo.org"
expect(user).to be_invalid
user.email = "user.name@example."
expect(user).to be_invalid
user.email = "foo@bar_baz.com"
expect(user).to be_invalid
user.email = "foo@bar+baz.com"
expect(user).to be_invalid
user.email = "foo@bar..com"
expect(user).to be_invalid
end
it "should be unique" do
duplicate_user = user.dup
duplicate_user.email = user.email.upcase
user.save!
expect(duplicate_user).to be_invalid
end
it "should be saved as lower-case" do
user.email = "Foo@ExAMPle.CoM"
user.save!
expect(user.reload.email).to eq 'foo@example.com'
end
end
describe "password and password_confirmation" do
it "should be present (nonblank)" do
user.password = user.password_confirmation = " " * 6
expect(user).to be_invalid
end
context "5 characters" do
it "is too short" do
user.password = user.password_confirmation = "a" * 5
expect(user).to be_invalid
end
end
context "6 characters" do
it "is not too short" do
user.password = user.password_confirmation = "a" * 6
expect(user).to be_valid
end
end
end
end
テストのUserモデルにはインスタンス変数を使わず、letを使います。
望ましいテストにするため、しきい値を確認しています。
参考になりました↓
RSpec入門 - Dotinstall
RSpecで書かれたRailsチュートリアル 第6章のテストコードをレビューしてみた
Herokuのエラーを解決する
gitのpushを済ませ、Herokuを起動しようとすると以下のエラーが発生します。
$ heroku run rails db:migrate
Running rails db:migrate on ⬢ lantern-lantern-app... up, run.2705 (Free)
rails aborted!
PG::ConnectionBad: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
これはPostgreSQLの問題です。アドオンを足すことで使えます。
$ heroku addons:add heroku-postgresql
$ heroku run rails db:migrate
参考になりました↓
【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】
【Rails】「heroku run rake db:migrate」を実行しようとすると発生するエラーについて
showビューを仮実装する
次回以降に必要な、ログイン・登録後に遷移するshowビューを仮実装します。
ここでの手順は以下の通りです。
- RESTfulにルーティングを行う
- showアクションを編集する
- showビューを作る
RESTfulにルーティングを行う
まずはルーティングを行います。
もっと後になりますが、各ユーザーには様々なページを用意します。
各ユーザーのページURLをいい感じ(RESTful)に作るには、resourcesを使います。
Rails.application.routes.draw do
# 中略
resources :users
end
何が生成されたかを、Rails Tutorial 表7.1で確認します。
そのうちの一つである、showビューを仮実装します。
showアクションを編集する
Usersコントローラーのshowアクションを編集します。
showビューは、ログイン・登録画面から送信されたIDでユーザーを特定します。
それを実装したのが、以下の記述です。
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
def new
end
end
以下の部分を解説します。
@user = User.find(params[:id])
変数userに各Userモデル入れることで、各ユーザーの情報を画面に与えます。
例えば、初めての登録者のIDは1になります。
その際、ログイン・登録画面からshowビューに向けてIDが送信されます。
よって、ユーザーのshowビューのURLは/users/1になります。
ここの、/1というパラメーター部分が、/:idに当たります。
それを、railsで取得する際の書き方が、params[:id]になります。
各ユーザーを特定するには、UserモデルからIDが1のものを探します。
それを、Userモデルから探すのが、User.find(引数)になります。
showビューを作る
仮でshowビューを作ります。
先ほどshowアクションに、インスタンス変数@userを追加しました。
@user.nameのように属性をドットで繋ぐことで、ユーザーネームが呼び出せます。
<% provide(:title, @user.name) %>
<div class="container">
<div class="row">
<div class="col bg-primary">
form
</div>
<div class="col bg-secondary">
figure
</div>
</div>
<div class="row">
<div class="col bg-warning">
log
</div>
</div>
</div>
プレビューにあるデバッグを実装したい方は、Rails Tutorial 7.1.3をご覧ください。
(この記事では、言及しません。)
今回は以上です。