0
0

More than 3 years have passed since last update.

Railsチュートリアル学習 備忘

Last updated at Posted at 2019-11-18

備忘 1章~

メソッドの()は省略で書かれることが基本
Refactor(コードの可読性とか問題)

ページネーション(htmlを分けるやつ)
<%= render 'layouts/header' %> に対して
_header.html.erb

rails 5.1.6(バージョン) new apurimei(アプリ名)

Gemfileで設定したら保存してから
bundle install --without production
bundle update

Git
git config --global user.name "Your Name"
git config --global user.email your.email@example.com
(情報を合わせて書かないとGitHubは反応しない)

一連の流れ
git init 準備
git add -A 選択
git commit -m "メッセージ" 反映
git push

git log 確認

git checkout -f 前コミットに戻る
もっと戻すときはブランチ

Heroku
bundle install --without production テストや本番環境があるということ

source <(curl -sL https://cdn.learnenough.com/heroku_install) インストール
よく使う

heroku create 最初に作る
git push heroku master これで送る
heroku run rails db:migrate dbを動かす

REST
ルート
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/signup', to: 'users#new' (testで get signup_path
resources :users
HTTPリクエスト URL アクション 用途
GET /users index すべてのユーザーを一覧するページ
GET /users/1 show id=1のユーザーを表示するページ
GET /users/new new 新規ユーザーを作成するページ
POST /users create ユーザーを作成するアクション
GET /users/1/edit edit id=1のユーザーを編集するページ
PATCH /users/1 update id=1のユーザーを更新するアクション
DELETE /users/1 destroy id=1のユーザーを削除するアクション

ページ名 URL 名前付きルート
Home / root_path
About /about about_path
Help /help help_path
Contact /contact contact_path
Sign up /signup signup_path
Log in /login login_path

users
id name email c_at up_at
integer string string datetime datetime

関連付け userが1つでmicropostが複数の場合
models/user.rb
has_many :microposts

models/micropost.rb
belongs_to :user

modelsにはvalidates

継承は
class 子 < 親

ルート
コントローラ
ビュー

rails generate controller 名前 アクション
rails destroy controller 名前 アクション

rails generate model テーブル名 名前:型 名前:型
rails destroy model テーブル名

rails db:igrate
rails db:rollback

テスト
test "テスト名" do
get static_pages_about_url(get 'static_pages/about'ならば)
assert_response :success httpのステが200台 普通に表示で成功
redirect missing error がある
assert_select セレクタ, 内容 セレクタ部分が一致で成功
end

レイアウトのリンクテスト
get root_path
assert_template 'static_pages/home' (指定が出ていればOK)
assert_select "a[href=?]", root_path, count: 2(リンク個数も調べられる)
assert_select "a[href=?]", help_path
(Railsは?をhelp_pathに自動変換する)

Code マッチするHTML
assert_select "div"

foobar

assert_select "div", "foobar"
foobar

assert_select "div.nav"
foobar

assert_select "div#profile"
foobar

assert_select "div[name=yo]"
hey

assert_select "a[href=?]", '/', count: 1 foo
assert_select "a[href=?]", '/', text: "foo" foo
表 5.2: assert_selectのいくつかの使用例

assert @user.valid? バリデが通るか調べる。問題なければtrue
assert_not @user.valid? バリデが通らなければtrue
assert_equal a, b aとbが等しければtrue
assert_no_difference 'User.count' do 変わらなければtrue
(difference 差)

includeメソッド 含ませる
include ApplicationHelper(ヘルパーを使えるようにする)
include? インクルードしているか調べる

yieldメソッド 処理の塊。利回りじゃない
<% provide(:title, "中身") %>

<%= yield(:title) %>

ハッシュ
abc = {"fname" => "b", "lname" => "d"}
シンボル
:a

ERB 埋め込みrubyの意

bootstrap 機能が仕上がったらテンプレートの変更くらいはする。
メインがデザイン違うから
Bootstrapテンプレートで適当に好きなのを選ぶくらいでいいと思う。

DB関係

find
User.find(1)
User.find_by(:id)

user = User.new(email:"a@gmail.com")
user.save
一発 User.create(email:"a@gmail.com")

user.email = "a@gmail.com"
user.save
一発 user .update_attributes(email: "a@gmail.com")

セキュアなパスワード
has_secure_passwordメソッドをモデルファイルに設置
password_digest属性(列、カラム)追加
bcryptジェムが必要

debug(params) if Rails.env.development?
testとproductionもある

countメソッド dbの登録数を数えるのに使う

7章 ユーザー登録
これ以降は慎重に進まないと危険

HTTPリクエスト URL アクション 名前付きルート 用途
GET /users index users_path すべてのユーザーを一覧するページ
GET /users/1 show user_path(user) 特定のユーザーを表示するページ
GET /users/new new new_user_path ユーザーを新規作成するページ (ユーザー登録)
POST /users create users_path ユーザーを作成するアクション
GET /users/1/ edit edit edit_user_path(user) id=1のユーザーを編集するページ
PATCH /users/1 update user_path(user) ユーザーを更新するアクション
DELETE /users/1 destroy user_path(user) ユーザーを削除するアクション
表 7.1: リスト 7.3のUsersリソースが提供するRESTfulなルート

@user = User.find(params[:id]) /user/:idを調べてDBデータを入れている

debugger 止めて調べるやつ

YAML わりと読みやすい形式のファイル

Gravatar メアドと連動させる画像表示サービス

form_forヘルパーメソッド
<%= f.label :name %>
<%= f.text_field :name %>
↓html
Name

redirect_to ルーターから呼び出し
render view直呼び出し

7.3.2
@user = User.new(params[:user])はセキュリティ上問題があるため

params.require(:user).permit(:name, :email, :password, :password_confirmation)
requireメソッド オブジェクト指定
permitメソッド 変更キー指定
他の物がリクエストされても許可のない項目は変更されない。

user_paramsメソッドを作成(慣習)
privateをかけて外部から使えないようにする

@user = User.new(user_params)

def user_params
  params.require(:user).permit(:name, :email, :password,:password_confirmation)
end

要約

params[:user]

params.require(:user).permit(:name, :email, :password, :password_confirmation)

セーブで失敗したときのエラメ出し
user.errors.full_messages

エラメを表示する部分

<%= form_for(@user) do |f| %>
  <%= render 'shared/error_messages' %>

  <%= f.label :name %>
  <%= f.text_field :name, class: 'form-control' %>

shared/error_messagesにパーシャルがある。複数ビューで使うのがsharedフォルダに入る。慣習
app/views/shared/_error_messages.html.erb

pluralize 複数形で表示するテキストヘルパー
.any? 有でtrue empty?の対

7.4.2 flash
flash[:success] = "Welcome to the Sample App!"

div class="alert alert-success">Welcome to the Sample App!

follow_redirect!
POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッド
assert_difference 'User.count', 1 ひとつふえていることの確認
assert_not flash.empty? フラッシュが存在しないとOKかと思いきや、assert_notなので存在でOK

7.5
本番k南橋でのデプロイにはいろいろと手順がある。

8章 ログイン機構
認証システム (Authentification System
認可モデル (Authorization Model

session代わりに
cookies使用

RESTfulではなく
名前付きルーティングだけ使用。
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
HTTPリクエスト URL 名前付きルート アクション名 用途
GET /login login_path new 新しいセッションのページ (ログイン)
POST /login login_path create 新しいセッションの作成 (ログイン)
DELETE /logout logout_path destroy セッションの削除 (ログアウト)
表 8.1: リスト 8.2のセッションルールによって提供されるルーティング

8.1.2
Active Record自動生成メッセを使えない。
flashを使う。

<%= form_for(:session, url: login_path) do |f| %>
  <%= f.label :email %>
  <%= f.email_field :email, class: 'form-control' %>


Email
name="session[email]" type="text" />

form_for
新規はcreate 編集はupdateが呼び出される。
, url指定
第2要素でurl指定

8.1.3 ユーザーの検索と認証

最初に設定
 無効な場合の処理

def create
user = User.find_by(email: params[:session][:email].downcase) メアドを見つけて小さく整形
if user && user.authenticate(params[:session][:password]) DBのuserと比較
# ユーザーログイン後にユーザー情報のページにリダイレクトする
else
flash[:danger] = 'Invalid email/password combination' # 本当は正しくない
render 'new'

flash[:danger]
↓renderでもメッセージが残らない
flash.now[:danger]

8.2ログイン
application_controllerにセッションヘルパーを読み込ませる。
include SessionsHelper

セッションヘルパーにどんどんメソッドを定義する
# log_inメソッド 渡されたユーザーでログインする
def log_in(user)
session[:user_id] = user.id idをsessionに渡してブラウザ閉じるまで使う
end

8.2.2 現在のユーザ���
ユーザーIDをsessionに置いたので

# 現在ログイン中のユーザーを返す (いる場合)
def current_user
if session[:user_id] データがあったら
@current_user ||= User.find_by(id: session[:user_id])
# @が既にあればそのまま、なければ入れる
end

(
・currnt_user.name で名前が取り出したい
・redirect_to current_user でユーザープロフページに行きたい

User.find(session[:user_id]) ユーザーが存在しない場合「例外」になる。

User.find_by(id: session[:user_id]) ユーザーが存在しない場合「nil」を返す。

8.2.3レイアウトリンク変更

<% if logged_in? %>
# ログインユーザー用のリンク
<% else %>
# ログインしていないユーザー用のリンク
を作る

# ユーザーがログインしていればtrue、その他ならfalseを返す
def logged_in?
!current_user.nil?
end

jqueryとbootstrapをライブラリ追加 app/assets/javascripts/application.js
見た目をいい感じにする。 class dropdown-menuのところとdivider(仕切り)

8.2.4 レイアウト変更テスト
ログイン画面行く、ログインする、
ログイン画面 非表示確認、ログアウトリンク 表示確認、プロフリンク 表示確認

BCrypt::Password.create(string, cost: cost) stringがハッシュ化する文字列、costは高いとパス難度が上がる

models/user.rb
# 渡された文字列のハッシュ値を返す
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
テストで使うユーザー用のpassword_digestに対して使うのみ。

test/integration/users_login_test.rb
def setup
@user = users(:michael)
end
最初にセットするやつ

8.2.5 ユーザー登録時にログイン
def create のsaveの流れに
log_in @userを加えるのみ

test/test_helper.rb
# テストユーザーがログイン中の場合にtrueを返す
def is_logged_in?
!session[:user_id].nil?
end
で有効な登録をしたときのテストで
assert is_logged_in? がtrueならテスト成功

8.3ログアウト
セッションヘルパーにて
# 現在のユーザーをログアウトする
def log_out
session.delete(:user_id) セッションからユーザーidを削除
@current_user = nil
end
メソッドを作った後、
セッションコントローラーにて
def destroy
log_out
redirect_to root_url
end
でルートurlへ飛ばして完了

テストの流れは
ログアウト後、ログインしてない確認、root_urlへのリダイレクト確認、各リンク確認

有効なログインとログアウト完全版
test "login with valid information followed by logout" do
get login_path
post login_path, params: { session: { email: @user.email,
password: 'password' } }
assert is_logged_in?
assert_redirected_to @user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(@user), count: 0
end

※補足 (たぶんまたわからなくなる)
count: は数字分のリンクが存在するかどうか。
GET /users/1 show user_path(user) 特定のユーザーを表示するページ

まだ大丈夫だと思っていたが、怪しい
9章 発展的なログイン
9.1 remember me 機能
permanent cookies(永続)使う
チェックボックスでユーザーの意を汲む

9.2 記憶トークンと暗号化
remenber token作成 cookiesメソッドで永続的cookies作成、remember digestでトークン認証
パスはユーザーが作成管理。トークンはコンピュータが作成管理する情報。という違い

cookiesメソッドの安全性を確保する。SSLでパケットスニッファ対策、DBに直保存ではなくハッシュ値保存
クロスサイトスクリプティングはRails対策済み、ログアウト時にトークンを変更する。重要情報はデジタル署名

・記憶トークン ランダム文字列生成使用
・ブラウザにcookiesトークン保存有効期限設定
・トークンはハッシュ値変換でDB保存
・ブラウザのcookies保存はユーザーID暗号化
・永久ユーザーID含むcookies受け、IDをDB検索→記憶トークンのcookiesがDBハッシュ値と一致確認

まず
usersにremember_digest属性追加

何を記憶トークンにするか?
SecureRandom.urlsafe_base64を使う。ランダムの22文字出力
(モジュール.メソッド)

models/user.rb
# ランダムなトークンを返す
def User.new_token
SecureRandom.urlsafe_base64
end

attr_accessor :name, :email で @name@emailにアクセスするためのメソッド
(属性に対応するアクセサー)
attr_accessor :remember_token で @remember_tokenにアクセスするためのメソッド

仮想の属性

self.remember_token
selfがないとremember_token変数ができてしまう。(不要物

models/user.rb 
attr_accessor :remember_token 追加
# 永続セッションのためにユーザーをデータベースに記憶する
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end

9.1.2 ログイン状態の保持
user.rememberメソッド 暗号化済みID、記憶トークンを永続cookies保存 永久セッション作成準備完了
cookiesメソッド ハッシュ扱い value(値 expires(有効期限
cookies[:remember_token] = { value: remember_token,
expires: 20.years.from_now.utc } 有効期限20年

cookies[:user_id] = user.id
↓署名つきcookies
cookies.signed[:user_id] = user.id
↓cookieも永続化、signed(署名 とpermanent(恒久的 をメソッドチェーンで繋ぐ
cookies.permanent.signed[:user_id] = user.id

User.find_by(id: cookies.signed[:user_id]) 以後ビューでcookiesを取り出せる

渡したトークンとユーザー記憶ダイジェスト一致確認
BCrypt::Password.new(password_digest) == unencrypted_password
を参考に
BCrypt::Password.new(remember_digest) == remember_token
↓==演算子再定義
BCrypt::Password.new(remember_digest).is_password?(remember_token) 実際は==ではなくis_password?

models/user.rb
# 渡されたトークンがダイジェストと一致したらtrueを返す
def authenticated?(remember_token)
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end

セッションコントローラーのクリエイト
ifがtrueに追加
remember user

sessions_helper.rb
# ユーザーのセッションを永続的にする
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end

9.1.3ユーザーを忘れる
ログアウトのための
user.forgetメソッドでuser.rememberを消す

model/user.rb
nil更新する
# ユーザーのログイン情報を破棄する
def forget
update_attribute(:remember_digest, nil)
end

helpers/sessions_helper.rb
# 永続的セッションを破棄する
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end

# 現在のユーザーをログアウトする
def log_out
forget(current_user) 追加
session.delete(:user_id)
@current_user = nil
end

9.1.4 2バグ
・同タブ1ログアウト→もう片方ログアウト→エラー 解決はログイン中のみログアウト
・別種ブラウザログイン remember_digestが存在しないときにfalseを返すauthentiatd?追加
F G G
out in → ?
1.Fはログアウトでremember_digestがnil エラー
2.Gはsession[:user_id]nilだがブラウザ内にcookiesが残る エラー

セッションコントローラー
def destroy
log_out if logged_in? 追加
redirect_to root_url
end

2種ブラウザシミュレーション?
Userモデルで直接テストで対応

models/user.rb
# 渡されたトークンがダイジェストと一致したらtrueを返す
def authenticated?(remember_token)
return false if remember_digest.nil? 追加
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end

9.2 □ Rmember me on this computer 追加
check_box

※※※要注意。前に詰んだ部分※※※
セッションコントローラー

def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user) 追加

特に難しいことをしているわけではないので
前段階でセッティングがおかしかったと思われる。

※※※結※※※

三項演算子
これどう? ? あってる : 違う

9.3.1 Rmember me テスト

params[:session][:remember_me] == '1' ? remember(user) : forget(user)

params[:session][:remember_me] ? remember(user) : forget(user)

0も1も理論値はtrue→常にon状態の動作になるというミス

テスト内でユーザーがログインのヘルパーメソッド作る
log_in_asメソッド

test/test_helper.rb
# テストユーザーとしてログインする
def log_in_as(user)
session[:user_id] = user.id
end
end

class ActionDispatch::IntegrationTest

# テストユーザーとしてログインする
def log_in_as(user, password: 'password', remember_me: '1')
post login_path, params: { session: { email: user.email,
password: password,
remember_me: remember_me } }
end

integration/users_login_test.rb
test "login with remembering" do
log_in_as(@user, remember_me: '1')
assert_not_empty cookies['remember_token']
end

test "login without remembering" do
# クッキーを保存してログイン
log_in_as(@user, remember_me: '1')
delete logout_path
# クッキーを削除してログイン
log_in_as(@user, remember_me: '0')
assert_empty cookies['remember_token']

9.3.2 Rmemberme テスト

helpers/sessions_helper.rb
elsif (user_id = cookies.signed[:user_id])
raise # テストがパスすれば、この部分がテストされていないことがわかる

後で動画見ておいて

問題ないはず
10章 更新表示削除
REST完成する。

ユーザーコントローラ
def edit
@user = User.find(params[:id])
end

ビュー書いて
レイアウトにセッティングを追加

//メモ 発火不備・強制遷移のため 完了

10.1.2 失敗

def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to @user
else
render 'edit'
end
end

10.1.3 編集失敗時のテスト
統合テスト生成
rails generate integration_test users_edit
編集失敗 editビューの描画で確認

10.1.4 TDD(テスト駆動開発)成功時のテスト
allow_nil: true
nilでバリデをスキップ

10.2 認可
認証(authentication サイトのユーザーを識別
認可(authorization ユーザーが実行可能操作の管理

ログイン中かつ自分のみユーザー情報を編集できるようにする。

10.2.1 ユーザーにログインを要求
beforeフィルターでコントロールの頭にメソッド実行
before_action : メソッド, only: [:アクション名]
# ログイン済みユーザーかどうか確認
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end

10.2.2 正しいユーザー要求
before_action :correct_user, only: [:edit, :update]
# 正しいユーザーかどうか確認
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless @user == current_user
end

session_helper.rb
# 渡されたユーザーがログイン済みユーザーであればtrueを返す
def current_user?(user)
user == current_user
end

ユーザーコントローラ
# 正しいユーザーかどうか確認
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end

10.2.3 フレンドリーフォワーディング
親切設計
今  保護ページ→自分プロフ
親切 保護ページ→ログイン要求→ログインした後→さっきの保護ページ

sessions_helper.rb
# 記憶したURL (もしくはデフォルト値) にリダイレクト
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end

# アクセスしようとしたURLを覚えておく
def store_location
session[:forwarding_url] = request.original_url if request.get?
end

# ログイン済みユーザーかどうか確認
def logged_in_user
  unless logged_in?
    store_location 追加

セッションコントローラー
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user 追加
end

10.3 すべてのユーザー表示

def index
allとってきて

indexビューつくって

helpers/users_helper.rb
# 渡されたユーザーのGravatar画像を返す
def gravatar_for(user, options = { size: 80 })
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
size = options[:size]
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end

cssつけて
ヘッダーつけて

<%= link_to "Users", users_path %>
GET /users index users_path すべてのユーザーを一覧するページ

10.3.2 サンプルのユーザー
gemfile faker
seedsでサンプルユーザーを作る。

10.3.3 ページネーション
gem will_paginate
gem bootstrap-will_paginate
indexを will_paginateでくくって
コントローラで@User.paginate(page: params[:page])

10.3.4 ユーザー一覧のテスト
ok
10.3.5パーシャルフィルタリング
変更前後はtestしておく
<%= render user %>
users/_user.html.erb

10.4 ユーザーを削除する
管理ユーザーを設定して
admin権限が付与された人間のみが削除できる状態にする。

usersにadminカラムを追加。defaultはfalse
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
押したら即削除とならないようにする。

なんともいえない
11章 アカ有効化

1.有効化トークン、ダイジェスト関連付け
2.有効化リンクをメール送信
3.リンククリックで有効化

1.unactivated登録スタート
2.有効化トークン、有効化ダイジェスト生成
3.ダイジェストはDB トークンはメール
4.リンククリックでメアドをキーにユーザーを探してダイジェスト比較で有効化
5.activated

検索キー string digest authentication
email password password_digest authenticate(password)
id remember_token remember_digest authenticated?(:remember, token)
email activation_token activation_digest authenticated?(:activation, token)
email reset_token reset_digest authenticated?(:reset, token)
表 11.1: ログイン/記憶トークン/アカウントの有効化/パスワードの再設定で似ている点

11.1 AccountActivations アカ有効化

11.2 コントローラ

resources :account_activations, only: [:edit]

名前付きルートの中身
about_path -> '/about'
about_url -> 'http://localhost:3000/about'
よって
メールurlからアクセスする場合はURLである必要がある。

11.1.2 データモデル
メールとDBのトークン照合 ハッシュ化
パスワード実装、記憶トークン実装と同様
仮想的属性でハッシュ化してDB保存

user.activation_token ユーザー認証
user.authenticated?(:activation, token) メソッド改良必要

usersカラム追加
activation_digest:string activated:boolean activated_at:datetime

前実行(トークンとそれに対応するダイジェストを割り当て
before_create :create_activation_digest
create_activation_digestを探す Userモデル内のみ(privateっ設定

model/user.rb
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
.
.
.
private

# メールアドレスをすべて小文字にする
def downcase_email
  self.email = email.downcase
end

# 有効化トークンとダイジェストを作成および代入する
def create_activation_digest
  self.activation_token  = User.new_token
  self.activation_digest = User.digest(activation_token)
end

end

サンプルではtrueに
activated: true,
activated_at: Time.zone.now)

11.2 有効化メール送信
app/views/user_mailer/に2種類htmlとtxtで存在 有効化リンクURLを飛ばす
app/mailers/にメール系の物がある。

11.2.2 送信メールのプレビュー

development環境のメール設定
config/environments/development.rb
host = 'rails-tutorial-mhartl.c9users.io' # クラウドIDE
config.action_mailer.default_url_options = { host: host, protocol: 'https' }

test/mailers/previews/user_mailer_preview.rb で確認できる。
もとのURL/rails/mailers/user_mailer/account_activation
もとのURL/rails/mailers/user_mailer/account_activation.txt

11.2.3 送信メールのテスト
assert_match( regexp, string, [msg] ) stringは正規表現 (regexp) にマッチすると主張する。
テストのドメインホストを設定する
config/environments/test.rb
config.action_mailer.default_url_options = { host: 'example.com' }

11.2.4 ユーザーのcreateアクションを更新

def create
@user = User.new(user_params)
if @user.save
UserMailer.account_activation(@user).deliver_now 以前のようにログインしない。メール
flash[:info] = "Please check your email to activate your account." 追加
redirect_to root_url リダイレクト先をプロフィールページからルートURLに変更
else
render 'new'
end
end

11.3 アカウントを有効化
11.3.1 authenticated?メソッドの抽象化
有効化トークンとメールをそれぞれparams[:id]とparams[:email]で参照できる

メタプログラミング プログラムでプログラムを作成する

これから実装するauthenticated?メソッドでは、受け取ったパラメータに応じて呼び出すメソッドを切り替える

def authenticated?(remember_token)
digest = self.send("remember_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(remember_token)
end
↓引数を一般化、文字列の式展開
def authenticated?(attribute, token)
digest = self.send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
↓Rbuyらしく
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end

app/helpers/sessions_helper.rb
if user && user.authenticated?(:remember, cookies[:remember_token])

test/models/user_test.rb
assert_not @user.authenticated?(:remember, '')

11.3.2 editアクションで有効化
authenticated?を手直ししたのでeditが書ける。
!user.activated? 

controllers/account_activations_controller.rb
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.update_attribute(:activated, true)
user.update_attribute(:activated_at, Time.zone.now)
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
これでメールのURLを開くと有効化

controllers/sessions_controller.rb 有効じゃないユーザーのログイン阻止
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
if user.activated?
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user
else
message = "Account not activated. "
message += "Check your email for the activation link."
flash[:warning] = message
redirect_to root_url
end

11.3.3 有効化のテストとリファクタリング
11.4 本番環境でのメール送信
starter tier設定

11がイマイチなのでこちらも同様
12章 パス再設定
changeビュー1つ login
newフォーム2つ lorgot password, reset password

1.再設定リクでメアドをキーにDBから見つける
2.再設定トークンとリセットダイジェスト生成
3.ダイジェストはDB保存、再生っていトークンはメアドに入れとく
4.クリックでメアドからユーザーを探して、DB保存してた再設定用ダイジェストと比較
5.パスワード変更用フォーム

12.1 PasswordResetsリソース
12.1.1 PasswordResetsコントローラ
rails generate controller PasswordResets new edit --no-test-framework
(大文字にすると頭に_が付く)

resources :password_resets, only: [:new, :create, :edit, :update]
HTTPリクエスト URL Action 名前付きルート
GET /password_resets/new new new_password_reset_path
POST /password_resets create password_resets_path
GET /password_resets//edit edit edit_password_reset_url(token)
PATCH /password_resets/ update password_reset_url(token)
表 12.1: リスト 12.1のPasswordResetsリソースで提供されるRESTfulルーティング

login画面にパスワード再設定画面へのリンクを追加

12.1.2 新しいパスワードの設定
users
rest_digest:string
rest_sent_at:detetime
追加

12.1.3 createアクションでパスワード再設定
メールアドレスでDBからみつける。パス再設定トークンと送信時タイムスタンプでDB属性更新
ルートURLにリダイレクトし、フラッシュメッセージをユーザーに表示

12.2 パスワード再設定のメール送信
11章 password_resetメソッド

12.2.1 パスワード再設定のメールとテンプレート
11章 Userメイラー⇒Userモデル移行のリファクタリング
同様のリファクタリングをパスワード再設定にも

12.2.2 送信メールのテスト
11.20 同様にメイラーメソッドテスト

12.3 パスワードを再設定する
PasswordResetsコントローラのeditアクションの実装
11.3.3と同様に統合テスト行う。

12.3.1 editアクションで再設定
送信メールにパス再設定フォームのビュー
ユーザー検索にedit update メアド必要

12.3.2 パスワードを更新する
updateにて
・期限切れ
・無効パスで失敗
・新しいパスが空文字列
・正しい場合の更新

期限切れ確認メソッドcheck_expiration
password_reset_expired?でif

if params[:user][:password].empty? 新しい文字列の空チェック

elseif @user.update_attributes(user_params) 正しい場合と無効の場合

12.3.3 パスワードの再設定をテストする
テスト

12.4 本番環境でのメール送信 (再掲)
11.4と同じ

かなりしつこくやっているので理解度は高め
13章 ユーザーのマイクロポスト

app/models/micropost.rb
belongs_to :user
app/models/user.rb
has_many :microposts

メソッド 用途
micropost.user Micropostに紐付いたUserオブジェクトを返す
user.microposts Userのマイクロポストの集合をかえす
user.microposts.create(arg) userに紐付いたマイクロポストを作成する
user.microposts.create!(arg) userに紐付いたマイクロポストを作成する (失敗時に例外を発生)
user.microposts.build(arg) userに紐付いた新しいMicropostオブジェクトを返す
user.microposts.find_by(id: 1) userに紐付いていて、idが1であるマイクロポストを検索する
表 13.1: user/micropost関連メソッドのまとめ

13.1.4 マイクロポストを改良する
順序

13.2.1 マイクロポストの描画

1つのマイクロポストを表示するパーシャル
_micropost.html.erb

app/controllers/users_controller.rb
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end

マイクロポストの投稿数を表示
user.microposts.count

プロフィールにマイクロポストを表示
app/views/users/show.html.erb

13.2.2 マイクロポストのサンプル
サンプルデータにマイクロポストを追加してmigrate
CSS手直し

13.2.3 プロフィール画面のマイクロポストをテストする
テスト書き
プロフ画面テスト
タイトルなどの一致をみる

13.3 マイクロポストを操作する
作成と破棄
config/routes.rb
resources :microposts, only: [:create, :destroy]

HTTPリクエスト URL アクション 名前付きルート
POST /microposts create microposts_path
DELETE /microposts/1 destroy micropost_path(micropost)
表 13.2: Micropostsリソースが提供するリスト 13.30のRESTfulルート

13.3.2 マイクロポストを作成する
ホーム画面(ルートパス)にフォーム
現時点は Sing up now!のページになっているが

createアクション作る microposts_controller.rb
def create
@micropost = current_user.microposts.build(micropost_params)
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
render 'static_pages/home'
end
end

ビュー static_pages/home.html.erb
<% if logged_in? %>




<%= render 'shared/user_info' %>


<%= render 'shared/micropost_form' %>



<% else %>

・部分
サイドバーshared/_user_info.html.erb
投稿フォーム_micropost_form.html.erb

static_pages_controller.rb
def home
@micropost = current_user.microposts.build if logged_in? ろぐいんしてる場合
end

views/users/new.html.erb 登録時のエラー表示
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages', object: f.object %> 手直し

users/edit.html.erb
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages', object: f.object %> 手直し

views/password_resets/edit.html.erb パス再設定
<%= form_for(@user, url: password_reset_path(params[:id])) do |f| %>
<%= render 'shared/error_messages', object: f.object %> 手直し

13.3.3 フィードの原型
マイクロポスト表示部分実装
フィード(配信用の加工文書

feedメソッド作成
models/user.rb
# 試作feedの定義
# 完全な実装は次章の「ユーザーをフォローする」を参照
def feed
Micropost.where("user_id = ?", id)
end

?でセキュリティ上の何かよくなる。動画で確認
.where

controllers/static_pages_controller.rb
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end

views/shared/_feed.html.erb

Homeページにステータスフィードを追加

表示ok

13.3.4 マイクロポストを削除する
自分の投稿に対しての削除を作る

マイクロポストパーシャルで削除リンク追加して

destroyを書く
correct_userメソッドを作り
ユーザーが削除対象のマイクロポストを保有しているかどうかを確認

13.3.5 フィード画面のマイクロポストをテスト

test "should redirect destroy for wrong micropost" do 間違ったユーザーによる削除
log_in_as(users(:michael))
micropost = microposts(:ants)
assert_no_difference 'Micropost.count' do 差なしか比較
delete micropost_path(micropost)
end
assert_redirected_to root_url ホームに戻ったか確認
end

ここをやれればツイッター的なものからインスタ的なものになれる
13.4 マイクロポストの画像投稿
・画像うpフォーム
・画像

13.4.1 基本的な画像アップロード
gem carrierwave
gem mini_magick
gem fog

rails generate uploader Picture 画像アップローダ生成

rails generate migration add_picture_to_microposts picture:string 画像のファイル名格納

mount_uploaderメソッド
mount_uploader :属性名シンボル, 生成アップローダクラス名
mount_uploader :picture, PictureUploader

Micropostモデルに画像を追加する
マイクロポスト投稿フォームに画像アップローダーを追加する
pictureを許可された属性のリストに追加する
マイクロポストの画像表示を追加する

13.4.2 画像の検証
拡張子・サイズ問題にバリデ

13.4.3 画像のリサイズ
大きすぎると困る(ImageMagickで対応
sudo yum install -y ImageMagick

app/uploaders/picture_uploader.rb にコードを書く

13.4.4 本番環境での画像アップロード
fog gem
Simple Storage Service (S3)使用

ひとまず実装できればいいが
いずれは完全理解してもらう
14章 ユーザーフォロー
14.1 Relationshipモデル

14.1.1 データモデルの問題 (および解決策)
Calvin
fwer
fwed +1

Hobbes
fwer +1
fwed

user ⇒relationships ⇒user
follower_id followerd_id
throught フォロー側のID フォローされた側のID has_many

relationshipsテーブル作成
id follower_id followed_id created_at updated_at

マイグレーションファイル
add_indexでフォローとフォロワーの列に組み合わせをかける

14.1.2 User/Relationshipの関連付け

UserとRelationshipの関連付け belongs_toとhas_many
user
↓多
relationships
↓多
user

勝手に推測機能が使えない_id

app/models/user.rb
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy 削除の

app/models/relationship.rb
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"

メソッド 用途
active_relationship.follower フォロワーを返します
active_relationship.followed フォローしているユーザーを返します
user.active_relationships.create(followed_id: other_user.id) userと紐付けて能動的関係を作成/登録する
user.active_relationships.create!(followed_id: other_user.id) userを紐付けて能動的関係を作成/登録する (失敗時にエラーを出力)
user.active_relationships.build(followed_id: other_user.id) userと紐付けた新しいRelationshipオブジェクトを返す
表 14.1: ユーザーと能動的関係の関連付けによって使えるようになったメソッドのまとめ

メソッドさえわかればなんとかなる。

14.1.3 Relationshipのバリデーション
フォローとフォロワーidの存在バリデ

ここから本題
14.1.4 フォローしているユーザー
through(通して

Userモデルにfollowingの関連付けを追加する
app/models/user.rb
has_many :following, through: :active_relationships, source: :followed following配列の元はfollowed idの集合である

<<演算子 (Shovel Operator) で配列の要素追加

“following” 関連のメソッドをテスト
test "should follow and unfollow a user" do
michael = users(:michael)
archer = users(:archer)
assert_not michael.following?(archer) していない状態
michael.follow(archer) フォロー
assert michael.following?(archer) している状態
michael.unfollow(archer) 解除
assert_not michael.following?(archer) していない状態
end

メソッドを定義
models/user.rb
# ユーザーをフォローする
def follow(other_user)
following << other_user
end

# ユーザーをフォロー解除する
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end

# 現在のユーザーがフォローしてたらtrueを返す
def following?(other_user)
following.include?(other_user)
end

14.1.5 フォロワー
前でフォローを作ったのでフォロワーも同じような流れで作れるのではないか
受動的関係を使ってuser.followersを実装する
app/models/user.rb
今回はsourceを省略できる

テスト
assert archer.followers.include?(michael) フォロワーに含まれているか

14.2.1 フォローのサンプルデータ
db/seeds.rb

リレーションシップ

users = User.all
user = users.first
following = users[2..50]
followers = users[3..40]
following.each { |followed| user.follow(followed) }
followers.each { |follower| follower.follow(user) }
最初のユーザーにユーザー3からユーザー51までをフォローさせ、それから逆にユーザー4からユーザー41に最初のユーザーをフォロー

14.2.2 統計と [Follow] フォーム
プロフィールとhomeに
フォローとフォロワーを表示

routers.rbに追加
resources :users do
member do
get :following, :followers
end
end
HTTPリクエスト URL アクション 名前付きルート
GET /users/1/following following following_user_path(1)
GET /users/1/followers followers followers_user_path(1)
表 14.2: カスタムルールで提供するリスト 14.15のRESTfulルート

フォロワーの統計情報を表示するパーシャル
app/views/shared/_stats.html.erb
<% @user ||= current_user %>

Homeページにフォロワーの統計情報を追加する
app/views/static_pages/home.html.erb

<%= render 'shared/stats' %> パーシャルくっつけ

CSS変更
完成

フォロー/フォロー解除フォームのパーシャル
app/views/users/_follow_form.html.erb
<% unless current_user?(@user) %>


<% if current_user.following?(@user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>

<% end %>

Relationshipリソース用のルーティングを追加する
config/routes.rb
resources :relationships, only: [:create, :destroy]
ユーザーをフォロー・解除するフォーム
app/views/users/_follow.html.erb
app/views/users/_unfollow.html.erb

プロフィールページにフォロー用フォームとフォロワーの統計情報を追加する
app/views/users/show.html.erb

<%= render 'shared/stats' %> _stats.html.erbステータスのパーシャルだし



<%= render 'follow_form' if logged_in? %> _follow_form.html.erbフォームのパーシャル出し

14.2.3 [Following] と [Followers] ページ
フォローしているユーザーとフォロワーの両方を表示するshow_followビュー green
app/views/users/show_follow.html.erb
followingの場合、followersの場合、両方対応ビュー
コントローラーで@usersの中身を変えてrenderでビューに飛ばすことで対応している。

14.2.4 [Follow] ボタン (基本編)
rails generate controller Relationships

Relationshipsコントローラ
app/controllers/relationships_controller.rb
def create
user = User.find(params[:followed_id]) フォロワーidでみつけて
current_user.follow(user) ユーザーフォローして
redirect_to user 飛ばす
end

def destroy
user = Relationship.find(params[:id]).followed
current_user.unfollow(user)
redirect_to user
end
end

14.2.5 [Follow] ボタン (Ajax編)
非同期にする。
ポートフォリオに取り入れたい機能。

Ajaxを使ったフォローフォーム
app/views/users/_follow.html.erb
<%= form_for(current_user.active_relationships.build, remote: true) do |f| %> remote: true追加だけ

RelationshipsコントローラでAjaxリクエストに対応する
app/controllers/relationships_controller.rb
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format| respond_toメソッド if文を使った分岐処理に近い html形式でもjs形式でも返してくれる
format.html { redirect_to @user }
format.js
end
end

def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
end

JavaScriptが無効になっていたときのための設定
config/application.rb
module SampleApp
class Application < Rails::Application
.
.
.
# 認証トークンをremoteフォームに埋め込む
config.action_view.embed_authenticity_token_in_remote_forms = true

JavaScriptと埋め込みRubyを使ってフォローの関係性を作成する
app/views/relationships/create.js.erb
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>"); jsなので;ある
$("#followers").html('<%= @user.followers.count %>');
Ruby JavaScript (RJS) を使ってフォローの関係性を削除する
app/views/relationships/destroy.js.erb
$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
$("#followers").html('<%= @user.followers.count %>');

これらのコードにより、プロフィールページを更新させずにフォローとフォロー解除ができるようになった

14.2.6 フォローをテストする
xhr (XmlHttpRequest) というオプションをtrueに設定すると、Ajaxでリクエストを発行する
xhr: true

[Follow] / [Unfollow] ボタンをテストする
今はよい

14.3 ステータスフィード
14.3.1 動機と計画

ステータスフィードのテスト red
test/models/user_test.rb
test "feed should have the right posts" do
michael = users(:michael)
archer = users(:archer)
lana = users(:lana)
# フォローしているユーザーの投稿を確認
lana.microposts.each do |post_following|
assert michael.feed.include?(post_following)
end
# 自分自身の投稿を確認
michael.microposts.each do |post_self|
assert michael.feed.include?(post_self)
end
# フォローしていないユーザーの投稿を確認
archer.microposts.each do |post_unfollowed|
assert_not michael.feed.include?(post_unfollowed)
end

14.3.2 フィードを初めて実装する
取得したuser_idをうまく処理して表示に使う。
とりあえず動くフィードの実装 green
app/models/user.rb
class User < ApplicationRecord
.
# パスワード再設定の期限が切れている場合はtrueを返す
def password_reset_expired?
reset_sent_at < 2.hours.ago
end

# ユーザーのステータスフィードを返す
def feed
Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
end

# ユーザーをフォローする
def follow(other_user)
following << other_user
end
.
end

小規模ならok

14.3.3 サブセレクト

SQLで改善

SQLサブセレクトを使う
フィードの最終的な実装 green
app/models/user.rb
def feed
following_ids = "SELECT followed_id FROM relationships フォロワーidを選択して リレーションシップを検索
WHERE follower_id = :user_id" フォロワーidとユーザーidが同じ行を指定
Micropost.where("user_id IN (#{following_ids})
OR user_id = :user_id", user_id: id)
end

--20191121

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0