railsチュートリアル魔改造編 第16章 新規登録魔改造
第16章 新規登録魔改造編
新規登録画面を魔改造していきます。
いつものようにトピックブランチを作成します。
git checkout -b rails-makaizou-16
16.1 リンクのおさらい
リンクについておさらいします。
新規登録画面に飛ぶためのコードは、以下のようになっています。
<%# 新規登録ボタンを赤色(danger)にする %>
<%= link_to "新規登録", signup_path, class: "btn btn-lg btn-danger signup " %>
16.1.1 どこから新規登録画面に飛ぶのか
signup_pathに飛びますが、
では一体、signup_pathはなにを表しているのでしょう?
rails routesコマンドを実行すると、以下のようになっています。
ログ
----------------------------
ec2-user:~/environment/sample_app (rails-makaizou-16) $ rails routes
Prefix Verb URI Pattern Controller#Action
root GET / static_pages#home
help GET /help(.:format) static_pages#help
about GET /about(.:format) static_pages#about
contact GET /contact(.:format) static_pages#contact
signup GET /signup(.:format) users#new
login GET /login(.:format) sessions#new
POST /login(.:format) sessions#create
logout DELETE /logout(.:format) sessions#destroy
following_user GET /users/:id/following(.:format) users#following
followers_user GET /users/:id/followers(.:format) users#followers
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
edit_account_activation GET /account_activations/:id/edit(.:format) account_activations#edit
password_resets POST /password_resets(.:format) password_resets#create
new_password_reset GET /password_resets/new(.:format) password_resets#new
edit_password_reset GET /password_resets/:id/edit(.:format) password_resets#edit
password_reset PATCH /password_resets/:id(.:format) password_resets#update
PUT /password_resets/:id(.:format) password_resets#update
microposts POST /microposts(.:format) microposts#create
micropost DELETE /microposts/:id(.:format) microposts#destroy
relationships POST /relationships(.:format) relationships#create
relationship DELETE /relationships/:id(.:format) relationships#destroy
ec2-user:~/environment/sample_app (rails-makaizou-16) $
----------------------------
プレフィックス+"_path"とすることで、そのパスが文字列で取得できます。
つまり、/signupが取得できるという訳ですね。それでまず一番最初にどこに飛ぶか。
新規登録ボタンを押すと、まずusers_controller.rbのnewアクションが実行されます。
その後、app/views/users/new.html.erbに飛びます。
ここで、1行ずつ、どの行がどのルーティングとなっているかを確認します。
全コメントアウト
----------------------------
rails routes
You don't have any routes defined!
Please add some routes in config/routes.rb.
For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
----------------------------
root 'static_pages#home'のみ
----------------------------
Prefix Verb URI Pattern Controller#Action
root GET / static_pages#home
----------------------------
get '/help', to: 'static_pages#help'のみ
----------------------------
Prefix Verb URI Pattern Controller#Action
help GET /help(.:format) static_pages#help
----------------------------
resources :users
----------------------------
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
----------------------------
resources :account_activations
----------------------------
Prefix Verb URI Pattern Controller#Action
account_activations GET /account_activations(.:format) account_activations#index
POST /account_activations(.:format) account_activations#create
new_account_activation GET /account_activations/new(.:format) account_activations#new
edit_account_activation GET /account_activations/:id/edit(.:format) account_activations#edit
account_activation GET /account_activations/:id(.:format) account_activations#show
PATCH /account_activations/:id(.:format) account_activations#update
PUT /account_activations/:id(.:format) account_activations#update
DELETE /account_activations/:id(.:format) account_activations#destroy
----------------------------
resourcesはルーティングがスッキリして便利ですが、どのようなルーティングになるか知らずに使うと何してるのか分からないというデメリットがあるので使用する際は注意が必要です。
16.2 文字魔改造
目に見えている文字列をどんどん変更していく。
16.2.1 Signup
見出しのSign upを新規登録にする。
ここで、一つ問題が発生する。
railsチュートリアルで作成したminitestにより、rails testが失敗するはずである。
以下のように変更する
リスト16.01: 見出しに対するテストをしないようにする
/sample_app/test/integration/site_layout_test.rb
----------------------------
require 'test_helper'
class SiteLayoutTest < ActionDispatch::IntegrationTest
test "layout links" do
get root_path
assert_template 'static_pages/home'
assert_select "a[href=?]", root_path, count: 2
assert_select "a[href=?]", help_path
assert_select "a[href=?]", about_path
assert_select "a[href=?]", contact_path
# コンタクトパス
get contact_path
assert_select "title", full_title("Contact")
#サインアップパス
get signup_path
#assert_select "title", full_title("Sign up")
end
end
あとで表示文字列変更したいと思った時の手間を考えて、コメントアウトにした。
これでrails testが通るはずである。
あとは、h1に囲まれたSign upを新規登録に変更するだけである。
16.2.2 Name
/sample_app/app/views/users/new.html.erb
<%= f.label :name %>
を
<%= f.label :name, "名前" %>
にするだけでおk
16.2.3 Email
/sample_app/app/views/users/new.html.erb
メールアドレスにする
16.2.4 Password
/sample_app/app/views/users/new.html.erb
パスワードにする
16.2.5 Confirmation
/sample_app/app/views/users/new.html.erb
パスワード(再入力)にする
16.3 エラー文魔改造
エラー文を全部魔改造します。
できるのか?
https://qiita.com/Ushinji/items/242bfba84df7a5a67d5b
のように、それ用のgemがあるが、今回は手打ちで全部エラー文を処理で分岐して表示する。
非効率だけどね。
16.3.1 どこにエラー文が出るか
下記パーシャルを呼び出して、実現している。
/sample_app/app/views/shared/_error_messages.html.erb
any?とは?(復習)
any?は、要素の中に一つでもnilでないものがある場合を指す
div id="error_explanation"は何を指す?
explanationは説明を指す
custom.cssに#error_explanationがある。
16.3.2 英語のエラー文を全て日本語に
/sample_app/app/views/shared/_error_messages.html.erb
で、msgとエラーメッセージを比較して、対応する日本語のエラーを表示する
16.4 ボタン文字列魔改造
ボタンを赤色にする。
16.4.1 Create my account
<%= f.submit "魔改造アカウントを作成する", class: "btn btn-danger" %>にする
16.5 最後に
git add -A
git status
ログ
----------------------------
modified: app/assets/stylesheets/custom.scss
modified: app/controllers/static_pages_controller.rb
modified: app/controllers/users_controller.rb
modified: app/views/shared/_error_messages.html.erb
modified: app/views/users/new.html.erb
modified: config/routes.rb
modified: test/integration/site_layout_test.rb
----------------------------
git commit -m "add 16"
git checkout master
修正前ファイルをブログ用に保存しておく
git merge rails-makaizou-16
修正後ファイルをブログ用に保存しておく
git push origin master
source <(curl -sL https://cdn.learnenough.com/heroku_install)
git push heroku master
16.5.1 修正後
リスト16.02: app/assets/stylesheets/custom.scss
----------------------------
@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;
}
/* universal */
/* 共通設定 */
/* 背景 */
body {
/* 文字の種類 */
font-family: "游明朝";
/* 文字色 */
color: #555555;
/* 文字の幅 */
letter-spacing: 1px;
/* 背景色 */
background-color:#FFEEEE;
/* 上からの空白 */
padding-top: 100px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
/* typography */
h1, h2, h3, h4, h5, h6 {
/* 行の高さ */
line-height: 1;
}
h1 {
font-size: 3em;
/* letter-spacing: -2px;*/
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: $gray-light;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
/* home中央文字 */
.center {
/* 相対位置 */
position: relative;
/* 文字の種類 */
font-family: "游明朝";
/* テキスト変換 */
text-transform: uppercase;
/* 文字の色 */
color: #EEEEEE;
/* 中央揃え */
text-align: center;
/* タイトル */
h1
{
/* 文字の幅 */
letter-spacing: 5px;
/* 透明度 */
opacity: 0.8;
/* 絶対位置 */
position: absolute;
/* 位置 */
top: 20%;
left: 50%;
/* X方向、Y方向にどれだけずらすか */
-ms-transform: translate(-50%,-50%);
-webkit-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
/* 改行させない */
white-space: nowrap;
}
/* サブタイトル */
h2{
/* 文字の色 */
color: #AAAAAA;
/* 文字の幅 */
letter-spacing: 5px;
/* 透明度 */
opacity: 0.9;
/* 絶対位置 */
position: absolute;
/* 上からの位置 */
top: 70%;
/* 左からの位置 */
left: 50%;
/* X方向、Y方向にどれだけずらすか */
-ms-transform: translate(-50%,-50%);
-webkit-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
}
img{
/* 背景のサイズ */
width: 100%;
}
/* サインアップボタン */
.signup{
/* 絶対位置 */
position: absolute;
/* 上からの位置 */
top: 90%;
/* 左からの位置 */
left: 50%;
/* X方向、Y方向にどれだけずらすか */
-ms-transform: translate(-50%,-50%);
-webkit-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
}
}
/* 画面が小さいとき */
@media(max-width:1000px){
/* home中央文字 */
.center {
/* タイトル */
h1 {
/* 文字サイズ */
font-size: 1.5em;
}
/* サブタイトル */
h2 {
/* 文字サイズ */
font-size: 0.6em;
}
/* 新規登録ボタン */
.signup{
/* 文字サイズ */
font-size: 1em;
/* 内側空白 */
padding:3px;
}
}
}
/* header */
header{
/* 文字の種類 */
font-family: "游ゴシック";
/* ロゴの文字列に対するCSSです */
#logo {
/* 寄せ方 */
float: left;
/* 線の外側の右側の余白 */
margin-right: 20px;
/*線の内側の上側の余白 */
padding-top: 10px;
/* フォントの大きさ */
font-size: 1.6em;
/* 文字色 */
color: #444444;
/* テキスト変換 */
text-transform: lowercase;
/* 文字の幅 */
letter-spacing: 5px;
/* 文字の太さ */
font-weight: normal;
/* マウスカーソルを乗せたときのスタイル */
&:hover {
color: #AAAAAA;
text-decoration: none;
}
}
}
/* footer */
footer {
/* 文字の種類 */
font-family: "游ゴシック";
/* 線の外の上からの空白 */
margin-top: 45px;
/* 線の中の上からの空白 */
padding-top: 5px;
/* 線 */
border-top: 1px solid #AAAAAA;
/* 文字色 */
color: $gray-light;
/* リンク */
a {
/* 文字色 */
color: $gray;
/* マウスを乗せたら */
&:hover {
/* 文字色 */
color: $gray-darker;
}
}
/* 左下 */
small {
/* 左寄せ */
float: left;
}
/* 右下 */
ul {
/* 右寄せ */
float: right;
/* 黒点なし */
list-style: none;
li {
/* 左寄せ */
float: left;
/* 間隔 */
margin-left: 30px;
}
}
}
.debug_dump {
clear: both;
float: left;
width: 100%;
margin-top: 45px;
@include box_sizing;
}
/* sidebar */
aside {
section.user_info {
margin-top: 20px;
}
section {
padding: 10px 0;
margin-top: 20px;
&:first-child {
border: 0;
padding-top: 0;
}
span {
display: block;
margin-bottom: 3px;
line-height: 1;
}
h1 {
font-size: 1.4em;
text-align: left;
letter-spacing: -1px;
margin-bottom: 3px;
margin-top: 0px;
}
}
}
.gravatar {
float: left;
margin-right: 10px;
}
.gravatar_edit {
margin-top: 15px;
}
.stats {
overflow: auto;
margin-top: 0;
padding: 0;
a {
float: left;
padding: 0 10px;
border-left: 1px solid $gray-lighter;
color: gray;
&:first-child {
padding-left: 0;
border: 0;
}
&:hover {
text-decoration: none;
color: blue;
}
}
strong {
display: block;
}
}
.user_avatars {
overflow: auto;
margin-top: 10px;
.gravatar {
margin: 1px 1px;
}
a {
padding: 0;
}
}
.users.follow {
padding: 0;
}
/* forms */
input, textarea, select, .uneditable-input {
border: 1px solid #bbb;
width: 100%;
margin-bottom: 15px;
@include box_sizing;
}
input {
height: auto !important;
}
/* 新規登録とかのエラー文字列 */
#error_explanation {
/* 文字色:赤 */
color: red;
/* リスト */
ul {
/* 文字色:オレンジ */
color: #E66D00;
/* 空白:下に30px */
/* margin: 0 0 30px 0;*/
}
}
.field_with_errors {
@extend .has-error;
.form-control {
color: $state-danger-text;
}
}
.checkbox {
margin-top: -10px;
margin-bottom: 10px;
span {
margin-left: 20px;
font-weight: normal;
}
}
#session_remember_me {
width: auto;
margin-left: 0;
}
/* Users index */
.users {
list-style: none;
margin: 0;
li {
overflow: auto;
padding: 10px 0;
border-bottom: 1px solid $gray-lighter;
}
}
/* microposts */
.microposts {
list-style: none;
padding: 0;
li {
padding: 10px 0;
border-top: 1px solid #e8e8e8;
}
.user {
margin-top: 5em;
padding-top: 0;
}
.content {
display: block;
margin-left: 60px;
img {
display: block;
padding: 5px 0;
}
}
.timestamp {
color: $gray-light;
display: block;
margin-left: 60px;
}
.gravatar {
float: left;
margin-right: 10px;
margin-top: 5px;
}
}
aside {
textarea {
height: 100px;
margin-bottom: 5px;
}
}
span.picture {
margin-top: 10px;
input {
border: 0;
}
}
リスト16.03: app/controllers/static_pages_controller.rb
----------------------------
class StaticPagesController < ApplicationController
# ルートページ
def home
# ログイン中?
if logged_in?
# 現在ログイン中のユーザーのマイクロポストを取得する
@micropost = current_user.microposts.build
# ページネーション用の準備を行う
@feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
end
def about
end
def contact
end
end
リスト16.04: app/controllers/users_controller.rb
----------------------------
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
:following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def index
@users = User.where(activated: true).paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end
# /signup で呼ばれるアクション
# app/views/users/new.html.erbへ
def new
#ユーザーを新規に作成する
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
@user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
end
def update
if @user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to @user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
def following
@title = "Following"
@user = User.find(params[:id])
@users = @user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
@title = "Followers"
@user = User.find(params[:id])
@users = @user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# beforeアクション
# 正しいユーザーかどうか確認
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
# 管理者かどうか確認
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
リスト16.05: app/views/shared/_error_messages.html.erb
----------------------------
<%# objectはform_forの|f|からf.objectで取り出したもの %>
<%# エラーあり? %>
<% if object.errors.any? %>
<%# エラー説明文 %>
<div id="error_explanation">
<%# エラーの数を表す %>
<div class="alert alert-danger">
入力いただいた情報に<%= object.errors.count %>つ誤りがあります。
<%# エラーリスト %>
<ul>
<%# エラーの数だけループ %>
<% object.errors.full_messages.each do |msg| %>
<li>
<% if msg == "Name can't be blank" %>
名前が空白のようです。
<% elsif msg == "Email can't be blank" %>
メールアドレスが空白のようです。
<% elsif msg == "Email is invalid" %>
無効なメールアドレスのようです。
<% elsif msg == "Password can't be blank" %>
パスワードが空白のようです。
<% elsif msg == "Password confirmation doesn't match Password" %>
2つのパスワードが一致していないようです。
<% elsif msg == "Password is too short (minimum is 6 characters)" %>
パスワードが短いようです。6文字以上にお願いします。
<% elsif msg == "Name is too long (maximum is 50 characters)" %>
名前が長いようです。50文字以内にお願いします。
<% elsif msg == "Email is too long (maximum is 255 characters)" %>
メールアドレスが長いようです。255文字以内にお願いします。
<% elsif msg == "Email has already been taken" %>
既に登録いただいているアカウントのようです。
<% else %>
<%= msg %>
<% end %>
</li>
<% end %>
</ul>
</div>
</div>
<% end %>
リスト16.06: app/views/users/new.html.erb
----------------------------
<%#
# /signup で呼ばれるアクション
# app/views/users/new.html.erbへ
def new
#ユーザーを新規に作成する
@user = User.new
end
%>
<%# 見出しを新規登録にする %>
<% provide(:title, '新規登録') %>
<%# 見出しを新規登録にする %>
<h1>新規登録</h1>
<%# Bootstrapの機能で、12列に何を割り当てるか決める %>
<div class="row">
<%# 空白3-入力スペース6-空白3の構成にする %>
<div class="col-md-6 col-md-offset-3">
<%# フォームを作成し、@userにデータを入れていく %>
<%= form_for(@user) do |f| %>
<%# エラー文を出力する %>
<%= render 'shared/error_messages', object: f.object %>
<%# 名前 %>
<%= 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, "パスワード(再入力)" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%# パスワード(再入力) %>
<%= f.submit "魔改造アカウントを作成する", class: "btn btn-danger" %>
<% end %>
</div>
</div>
リスト16.07: config/routes.rb
----------------------------
Rails.application.routes.draw do
# ルートページ
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
# 新規登録ページは、/signup
# app/controllers/users_controller.rbのnewアクションへ
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users do
member do
get :following, :followers
end
end
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
end
リスト16.08: test/integration/site_layout_test.rb
----------------------------
require 'test_helper'
class SiteLayoutTest < ActionDispatch::IntegrationTest
test "layout links" do
get root_path
assert_template 'static_pages/home'
assert_select "a[href=?]", root_path, count: 2
assert_select "a[href=?]", help_path
assert_select "a[href=?]", about_path
assert_select "a[href=?]", contact_path
# コンタクトパス
get contact_path
assert_select "title", full_title("Contact")
#サインアップパス
get signup_path
#assert_select "title", full_title("Sign up")
end
end
16.5.2 本章のまとめ
ルーティングについての理解を深めることができた。
pathについての理解を深めることができた。
resourcesを使わないと、ルーティングがどのようになるか理解できた。
rails testを習慣的に行うことによって、minitestの修正を漏れなく行うことができた。
アナログなやり方だが、英語のエラー文を自分好みのエラー文に差し替えることができた。
Bootstrapを使うと、エラーを表示する時に使う枠組みはたぶん変えられない。
修正前
修正後
この記事が気に入ったらサポートをしてみませんか?