#ユーザー登録
■第7章
Userモデルができあがったので、ユーザー登録機能を追加していく。
この第7章からは難易度が少しずつ上がっていくっぽい。
##7.1 ユーザーを表示する
ユーザーの名前とプロフィール写真を表示するためのページを作成していく。
###7.1.1 デバッグとRails環境
debug
メソッドとparams
変数を使って、各プロフィールページにデバッグ用の情報が表示されるようにする。
application.html.erb
に追記。
<%= debug(params) if Rails.env.development? %>
if以降のコードは「開発環境?」という意味。
デバッグ出力をきれいに整形するために、cssを弄る。
@import "bootstrap-sprockets";
@import "bootstrap";
/* mixins, variables, etc. */
$gray-medium-light: #eaeaea;
@mixin box_sizing {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.
.
.
/* miscellaneous */
.debug_dump {
clear: both;
float: left;
width: 100%;
margin-top: 45px;
@include box_sizing;
}
Sassのミックスイン機能 (ここではbox_sizing
) を使っている。ミックスイン機能を使うことで、CSSルールのグループをパッケージ化して複数の要素に適用できる。
###7.1.2 Usersリソース
6章での操作で、データベースに1人のユーザーがいる。データの作成、表示、更新、削除をリソース (Resources) として扱う。
/users/1 のURLを有効にするために、routesファイル (config/routes.rb
)に次の1行を追加します。
resources :users
サンプルアプリケーションにこの1行を追加すると、ユーザーのURLを生成するための多数の名前付きルートと共に、RESTfulなUsersリソースで必要となるすべてのアクションが利用できるようになる。便利すぎ。
show用のファイルを手動で作成し、ユーザー表示ビューが正常に動作するためには、Usersコントローラ内のshow
アクションに対応する@user
変数を定義する。
User.find(params[:id])
このコードでid=1のユーザーを検索できる。
###7.1.3 debuggerメソッド
byebug
gemによるdebugger
メソッドでもっと直接的にデバッグできる。
今後Railsアプリケーションの中でよく分からない挙動があったら、上のようにdebugger
を差し込んで調べてみる。
###7.1.4 Gravatar画像とサイドバー
今度は各ユーザーのプロフィール写真のあたりをもう少し肉付けし、サイドバーも作り始める。
gravatar_for
ヘルパーメソッドを使ってGravatarの画像を利用できるようにする。show.html.erb
を書き換え。
<% provide(:title, @user.name) %>
<h1>
<%= gravatar_for @user %>
<%= @user.name %>
</h1>
gravatar_for
ヘルパーメソッドを定義する。
module UsersHelper
# 引数で与えられたユーザーのGravatar画像を返す
def gravatar_for(user)
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
end
Gravatarの画像タグにgravatar
クラスとユーザー名のaltテキストを追加したものを返す。
ユーザーのshow
ビューにサイドバーを追加する。またshow.html.erb
を書き換え。
<% provide(:title, @user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for @user %>
<%= @user.name %>
</h1>
</section>
</aside>
</div>
HTML要素とCSSクラスを配置したおかげで、プロフィールページにスタイルを与えられるように。
##7.2ユーザー登録フォーム
今度はユーザー登録フォームを作成。
###7.2.1 form_forを使用する
フォームを作るために、form_for
ヘルパーメソッドを使う。
@user
変数の定義。
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
end
def new
@user = User.new
end
end
新規ユーザーのためのユーザー登録フォーム。
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
###7.2.2 フォームHTML
フォームを理解していく。
<%= form_for(@user) do |f| %>
.
.
.
<% end %>
変数fは “form” のf。このfオブジェクトは、HTMLフォーム要素に対応するメソッドが呼び出されると、@user
の属性を設定するために特別に設計されたHTMLを返す。
ユーザーの作成で重要なのはinput
ごとにある特殊なname
属性。params
変数経由で取りに行くため。
次に重要な要素は、form
タグ自身。action
とmethod
が重要。
##7.3ユーザー登録失敗
入力が失敗したときにエラーを表示するようにする。
###7.3.1 正しいフォーム
ユーザー登録フォームを動かす。
以下のコードを追加。
def create
@user = User.new(params[:user]) # 実装は終わっていないことに注意!
if @user.save
# 保存の成功をここで扱う。
else
render 'new'
end
end
実装の出発点は完了。
###7.3.2 Strong Parameters
実装していく。Strong Parametersを使う。これで、必須のパラメータと許可されたパラメータを指定することができる。
@user = User.new(params[:user])
これだとセキュリティ上かなり危険らしいので、次のようにする。
class UsersController < ApplicationController
.
.
.
def create
@user = User.new(user_params)
if @user.save
# 保存の成功をここで扱う。
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
private
キーワードを使って外部から使えないようにする。
やっぱり難易度が上がったこともあって、結構何言ってるかわかんないとこがチラホラ出てきました。
###7.3.3 エラーメッセージ
エラーメッセージを追加していく。ブラウザで表示するために、ユーザーのnew
ページでエラーメッセージのパーシャル (partial) を出力する。
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
shared/error_messages
というパーシャルをrenderする。
複数のビューで使われるパーシャルは専用のディレクトリshared
によく置かれる。ただ、ディレクトリが存在しないので作る必要がある。
$mkdir app/views/shared
パーシャルを作っておく。
<% if @user.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>.
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
count
メソッドはいくつあるか数え、any?
メソッドはempty
?メソッドと逆で存在していればtrue。
###7.3.4 失敗時のテスト
Railsではフォーム用のテストを書くことができる。無効な送信をしたときの正しい振る舞いについてテストを書いていく。
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
end
end
このコードと等価になるらしい。
before_count = User.count
post users_path, ...
after_count = User.count
assert_equal before_count, after_count
ブロックの実行前後で引数が変わらないことをテストしている。またget
メソッドを使っておらず、ユーザー登録ページにアクセスしなくても、直接post
メソッドを呼び出してユーザー登録ができることを意味している。
##7.4 ユーザー登録成功
新規ユーザーを実際にデータベースに保存できるようにし、ユーザー登録フォームを完成させていく。
###7.4.1 登録フォームの完成
ーザー登録に成功した場合はページを描画せずに別のページにリダイレクト するようにしてみる。
def create
@user = User.new(user_params)
if @user.save
redirect_to @user
else
render 'new'
end
end
redirect_to
メソッドに着目する。次のコードと等価。
redirect_to user_url(@user)
###7.4.2 flash
登録完了後に表示されるページにメッセージを表示し、2度目以降にはそのページにメッセージを表示しないようにする。Railsではこういった情報の表示のためにflashという変数を用いる。
def create
@user = User.new(user_params)
if @user.save
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
flash
変数に代入したメッセージは、リダイレクトした直後のページで表示できるようになる。
<!DOCTYPE html>
<html>
.
.
.
<body>
<%= render 'layouts/header' %>
<div class="container">
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
<%= yield %>
<%= render 'layouts/footer' %>
<%= debug(params) if Rails.env.development? %>
</div>
.
.
.
</body>
</html>
これでflash
変数の内容をWebサイトのレイアウトに追加できる。
###7.4.3実際のユーザー登録
ここまでの変更の確認のため、データベース初期化。
$ rails db:migrate:reset
そしてサーバーを起動して、ユーザー登録してみる。無事成功。フラッシュメッセージも表示されました。
###7.4.4 成功時のテスト
有効な送信に対するテストを書いてみる。これでアプリケーションの振る舞いを検証し、もし今後バグが埋め込まれたらそれを検知できるようになる。
test "valid signup information" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user@example.com",
password: "password",
password_confirmation: "password" } }
end
follow_redirect!
assert_template 'users/show'
end
##7.5 プロのデプロイ
初めてデータを操作できるようにするデプロイを行う。
###7.5.1 本番環境でのSSL
SSLとはローカルのサーバーからネットワークに流れる前に、大事な情報を暗号化する技術。
本番環境ではSSLを使うように修正する。以下のコードを追記。
config.force_ssl = true
###7.5.2 本番環境用のWebサーバー
Herokuのデフォルトサーバーの、Rubyだけで実装されたWEBrickでなくPumaに置き換える。
###7.5.3 本番環境へのデプロイ
いつものプッシュしてからマイグレーション。
SSLの設定をしたので鍵付きに。
##感想
さらに難しくなった感じがしました。なんとなくサーバー上のやりとりについて書いてるんだろうなーくらいしかわかんないままでしたが、それでもエラーなく進めることができました。