★Rails Tutorial 学習メモ
第1章 ゼロからデプロイまで
①Railsのインストール
$ printf "install: --no-document \nupdate: --no-document\n" >> ~/.gemrc
$ gem install rails -v 5.1.6
※-vでバージョン指定
②新しいアプリを作成する。
$ rails _5.1.6_ new hello_app
※自動で基本的なディレクトリやファイルが生成される。
⇒各フォルダ内にあるデータの役割は「1.3」の表1.2を参照
③アプリに必要なgemのインストール
gemファイルの内容を指定の内容に置き換える。(gemファイルはRubyで書かれている。)
gem 'uglifier', '>= 1.3.0'
↑のように、範囲をもたせたバージョン指定も可能。
その後、Bundlerを実行して、gemをインストール。
$ cd hello_app/ $ bundle install
※うまくいかない場合、bundle update
④ページが見れるか確認
$ rails server
「Preview」⇒「Preview running application」で確認。
⑤MVC
MVC = Model-View-Controller
Railsは、MVCというアーキテクチャパターンを採用している。
⑥Hello, World の表示
ルータ:ブラウザからのリクエストをコントローラに振り分ける役割。
ブラウザとコントローラの間に入るイメージ。
⇒ルーティングファイル(routes.rb)により設定
root application#hello
←ルートのルーティング
アプリケーション名#アクション名
⑦gitによるバージョン管理
・開発現場で必要不可欠
・削除したファイルの復旧、修正履歴の追跡が行える。
※cloud9ではインストールの必要はない。
各コンピュータにつき1回だけ下記設定が必要(一度やれば必要なし)
$ git config --global user.name "Your Name" $ git config --global user.email your.email@example.com
新しいリポジトリの初期化(リポジトリ=保管場所)
$ git init
←新しいリポジトリの初期化
$ git add -A
←リポジトリにファイルを追加
$ git commit -m "作業内容"
←リポジトリへ変更を反映(-mを使うことでコミットメッセージを指定できる)
このあとgit pushでリモートリポジトリにも反映することになる。(後に解説)
変更前の情報に戻したいときは、下記を実行
git checkout -f
←以前のコミットの状態へ強制的に上書きして戻す(checkout -f)
⑧Bitbucket
・リモートリポジトリを行うツール。
メリット1:ソースコードとすべての変更履歴の完全なバックアップを作成
メリット2:ほかの開発者との共同作業を便利に行う。
【使い方】
①公開鍵をターミナル上で取得。
$ cd ~/.ssh $ ls authorized_keys2 id_dsa known_hosts config id_dsa.pub
↑
id_dsa あるいは id_rsa というファイルと、同名で .pub という拡張子を持つファイルの組み合わせを探します。もし見つかったら、.pub がついているほうのファイルがあなたの公開鍵で、もう一方があなたの秘密鍵です。 そのようなファイルがない (あるいはそもそも .ssh ディレクトリがない) 場合は、ssh-keygen というプログラムを実行してそれを作成します。このプログラムは Linux/Mac なら SSH パッケージに含まれており、Windows では Git for Windows に含まれています。
②公開鍵の作成
$ ssh-keygen
→Enter→パスワード設定
③公開鍵の確認
$ cat ~/.ssh/id_rsa.pub
ssh-rssa含めすべての文字をbitbucketの「SSH鍵」追加でコピペ
④Bitbucket上でリポジトリの作成
非公開のリポジトリにチェック+READMEに含めるか?をNO
⑤ターミナル上でBitbucketと接続する。
Bitbucket上にコードが書いてあるのでそれに従う。
⑨ブランチ、編集、コミット、マージ
①ブランチの作成
・マスターブランチ=親リポジトリ
・トピックブランチ=一時的に使うブランチ
【例】
$ git checkout -b modify-README
←ブランチの作成(checkout -b)
Switched to a new branch 'modify-README'
$ git branch
←すべてのローカルブランチを表示する
master
* modify-README
←*がついているものが今作業中のブランチ
②編集
コードの変更
③Commit
$ git commit -a -m "Improve the README file"
←すべての変更をコミットする。
※新しいファイルを作成した場合、git addを実行する必要がある。
※コミットメッセージは、現在形かつ命令形が望ましい(英語)
日本語の場合には、体言止め。
④マージ
マスターブランチにマージ(変更を反映)する。
$ git checkout master
←マスターブランチへ移動
$ git merge modify-README
←マージ
$ git branch -d modify-README
←トピックブランチの削除
⑤push
$ git push
⑩デプロイ
・本番環境にデプロイする。
Herokuを使用する。
Herokuはsqliteがサポートされていないため、下記をgemfileに追加する。
group :production do gem 'pg', '0.20.0' end
group :development, :test do gem 'sqlite3', '1.3.13' gem 'byebug', '9.0.6', platform: :mri end
gemのインストール
$ bundle install --without production
←本番用のgemをローカルに反映させないために、本番用以外のgemをインストール$ git commit -m "Initialize repository"
→gemfile.lockにはgemfileの変更内容を反映させるため、上記を実行。
git commit -a -m "Update Gemfile for Heroku"
←gemfileの変更をコミット
heroku --version
←herokuが入っているか確認
入っていなかったら、下記コマンドを実行(IDEの場合)
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)
$ heroku login --interactive
←ログイン
$ heroku keys:add
←SSHキーの追加
$ heroku create
←新しいアプリケーションをherokuに追加
$ git push heroku master
←herokuにデプロイ(リポジトリをプッシュ)
第2章 Toyアプリケーション
scaffoldを使って、とても簡単にRailsでアプリケーションを作成する。
次の章でひとつひとつ自力で作っていく。
①アプリケーションの作成
①toy_appの作成
$ rails _5.1.6_ new toy_app
②gemfileの更新
③$ bundle install --without production
←インストール
④gitで管理
$ git init
git add -A
$ git commit -m "Initialize repository"
⑤Bitbucketでリポジトリを作成する。
⑥ターミナルで操作
⑦hello, world!を表示させる。
⑧herokuにデプロイする
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)
$ git commit -am "Add hello"
$ heroku create
$ git push heroku master
②モデルの設計
・userモデルはid,name,emailの属性をもつ。
id:integer型
name,email:string型
・micropostモデルはid,content,user_idの属性を持つ
id,uesr_id:integer型
content:text型
・このデータモデルとWebインターフェイスが組み合わさってUsersリソースとなる。
→HTTPプロトコル経由で自由にユーザーを作成・更新・削除できるようになる。
まずはRailsのscaffoldを使って、簡単にUsersリソースを生成してみる。
rails generate scaffold User name:string email:string
これでrails serverを実行すれば、簡易機能を備えたページがすでに出来上がっている。
【ページ構成】
/users・・indexアクション 全てのユーザーを一覧するページ
/users/1・・showアクション id=1のユーザーを表示するページ
/users/new・・newアクション 新規ユーザーを作成するページ
/users/1/edit・・editアクション id=1のユーザーを編集するページ
③MVCの挙動
①ブラウザから「/users」というURLのリクエストをRailsサーバーに送信する。
②「/users」リクエストは、Railsのルーティング機構 (ルーター) によってUsersコントローラ内のindexアクションに割り当てられる。
③indexアクションが実行され、そこからUserモデルに、「すべてのユーザーを取り出せ」(User.all)と問い合わせる。
④Userモデルは問い合わせを受け、すべてのユーザーをデータベースから取り出す。
⑤データベースから取り出したユーザーの一覧をUserモデルからコントローラに返す。
⑥Usersコントローラは、ユーザーの一覧を@users変数 (@はRubyのインスタンス変数を表す) に保存し、indexビューに渡す。
⑦indexビューが起動し、ERB (Embedded RuBy: ビューのHTMLに埋め込まれているRubyコード) を実行して HTMLを生成 (レンダリング) する。
⑧コントローラは、ビューで生成されたHTMLを受け取り、ブラウザに返す。
・RailsアプリケーションにおけるRESTとは、アプリケーションを構成するコンポーネント(ユーザーやマイクロポストなど)を「リソース」としてモデル化することを指す。
・@記号で始まる変数:インスタンス変数
→Railsのコントローラ内で宣言したインスタンス変数は、ビューでも使えるようになる。
④scaffoldで作成したUsersリソースの欠点
・データの検証が行われていない。ユーザ名やメールアドレスが空欄でも通る。
・ユーザー認証が行われていない。ログイン、ログアウトが行われていないので、誰でもアクセスできる。
・テストが十分に書かれていない。(データ検証やユーザー認証など)
・レイアウトやスタイルが整っていない。
・scaffoldのコードは理解が困難。
⑤Micropostsリソース
scaffoldでMicropostsリソースを作成。
rails generate scaffold Micropost content:text user_id:integer
rails db:migrate
・app/models/user.rb
has_many :microposts
・app/models/micropost.rb
belongs_to :user
→上記内容を追加することで、RailsとActiveRecordがマイクロポストとユーザーを関連付けることができるようになる。下記イメージ。
first_user.microposts:最初のユーザーの投稿一覧を意味する。
⑥クラスの継承
ActiveRecord::Baseというクラスは、RailsのActiveRecordが提供する基本クラス。
コントローラも同様に継承しており、コントローラの場合、ActionController::Baseが基本のコントローラ。
⑦デプロイ
git add -A
git commit -m "Finish toy app"
git push
git push heroku
←heroku(本番環境)にプッシュ
heroku run rails db:migrate
←本番データベースを動作させる。
→modelを使って、Heroku上のデータベースが更新される。(テーブル作成やカラム追加等。)
→マイグレーションが完了したら、toy_appをPostgreSQLデータベースを配置した本番環境で利用できるようになる。
第3章 ほぼ静的なページの作成
①セットアップ
アプリケーションの作成→gemファイルの更新→bundle install→gitにコミット→READMEファイルの更新→gitにコミット→bitbucket上にリポジトリ作成→bitbucketにプッシュ→hello,worldを表示するコードを書く(controller,route)→gitにコミット→herokuにリポジトリ作成→herokuにプッシュ→「hello,world」が表示されていることを確認
②静的なページの生成
masterブランチではなく、都度トピックブランチを作成して作業する。
$ git checkout -b static-pages
generateスクリプトで静的ページを扱うためだけのコントローラを作成する。
$ rails generate controller StaticPages home help
←StaticPagesコントローラ+homeアクション・helpアクションの作成
※コントローラ名をキャメルケース(単語の頭文字を大文字にしてつなぎあわせる)で渡すのは、Rubyの慣習。
【ターミナル操作の短縮形】
gitに追加しておく。
$ git add -A
git commit -m "Add a Static Pages controller"
git push -u origin static-pages
←トピックブランチを最初にbitbucketにプッシュするときは、このコマンドが必要。そのあとは「git push」のみでOK。
controller内にhomeアクションとhelpアクション、route内にURLが自動で入力される。
homeアクションとhelpアクションにはコードが書かれていないが、Railsの機能により、
「/static_pages/home」→homeアクション→view(home.html.erb)が返されるようになっている。
②テスト
①期待する動作をするテストを作成する。
test "should get about" do
get static_pages_about_url
assert_response :success
end
→Aboutページのテスト。GETリクエストをaboutアクションに対して発行せよ。
そうすれば、リクエストに対するレスポンスは[成功]になるはず。という意味。
②一度rails testを実行→RED→エラー内容確認
NameError: undefined local variable or method
static_pages_about_url'`
→AboutページへのURLが見当たらないよ。
→routeに追加しなきゃいけない!
→routeに追加(get 'static_pages/about')
③再度rails testを実行→RED→エラー内容確認
AbstractController::ActionNotFound:
The action 'about' could not be found for StaticPagesController
→aboutアクションが見当たらないよ。
→controllerにaboutアクションを追加
④再度rails testを実行→RED→エラー内容確認
ActionController::UnknownFormat: StaticPagesController#about
is missing a template for this request format and variant.
→テンプレートが見当たらないよ。テンプレートとは、「ビュー」のこと。
→about.html.erbファイルの作成
⑤再度rails testを実行→GREEN
⑥リファクタリングを行う。
操作が変わらないが、きれいなコードに整える。
コラム:元に戻す方法
■controllerの生成失敗
$ rails generate controller StaticPages home help
$ rails destroy controller StaticPages home help
←戻すコード
■modelの生成失敗
$ rails generate model User name:string email:string
$ rails destroy model User
←戻すコード
■migrateの失敗
$ rails db:migrate
$ rails db:rollback
←戻すコード
$ rails db:migrate VERSION=0
←指定したバージョンへ戻すコード(0の場合最初)
③少しだけ動的なページ
タイトル(Home | Ruby on Rails Tutorial Sample App)の「Home」の部分がページによって自動で変わるようにする。
①まずはテストを書く。
static_pages_controller_test.rbに下記コードを追加。
assert_select "title", "Home | Ruby on Rails Tutorial Sample App"
=
②テストを実行(RED)
③viewにタイトルを追加する。
<title>Home | Ruby on Rails Tutorial Sample App</title>
←
④テスト実行(GREEN)
⑤about,helpも①~④を行う。
⑥provideメソッド、yieldメソッドを使うことで、自動化
<% provide(:title, "Home") %>
←「:title」に"Home"を代入する。
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
→yield(:title)に"Home"が代入される。
※<%%>はhtmlで出力されない。<%=%>は出力される。
⑦レイアウトview(application.html.erb)内で各ページのタイトルが出力されるようにする。
■application.html.erb
<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
↑
■home.html.erb,about.html.erb,help.html.erb
`<% provide(:title, "Home") %>←"About","Help"も同様。
→各ページのhtmlファイルは、レイアウトviewと重複する部分をすべてそぎ落とす。
ルートの設定
root 'static_pages#home'
←homeページをルートに設定する。
・ルートのテストを行う。
get root_url
assert_response :success
第4章 Rails風味のRuby
目的
・Rubyの基礎知識を学ぶ
まずはトピックブランチの作成
カスタムヘルパー
Railsでは膨大な数の組み込み関数が使用できる。
新しく自分でメソッドを作ることも可能。→作ったメソッドをカスタムヘルパーという。
ページごとにタイトルが変わるようなカスタムヘルパー(full_titleメソッド)を作成する。
(application_helper.rb)
def full_title(page_title = '')
←メソッド定義(引数にデフォルト値nilを入れている)
base_title = "Ruby on Rails Tutorial Sample App"
←変数への代入
if page_title.empty?
←論理値テスト
base_title
←暗黙の戻り値
else
page_title + " | " + base_title
←文字列の結合
end
end
(application.html.erb)
<title><%= full_title(yield(:title)) %></title>
if s.nil?
←戻り値がtrue,falseになるメソッドは「?」をつけるのが慣習
配列と範囲演算子
配列:["foo", "bar", "baz"]`
範囲:(0..9)
"fooxbarxbaz".split('x')
←xで文字列を分割して、配列に変換する。
=> ["foo", "bar", "baz"]
(0..9).to_a
←0~9の文字列の配列を作成
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
('a'..'e').to_a
←a~eの文字列の配列を作成
=> ["a", "b", "c", "d", "e"]
ブロック
①(1..5).each { |i| puts 2 * i }
←{}部分がブロック。
↓同じ働きで、別の記法
②(1..5).each do |i|
puts 2 * i
end
※1行で収まるブロックの場合①を使用し、複数行になる場合は②
(1..5).map { |i| i**2 }
←1~5のそれぞれの2乗
%w[a b c].map { |char| char.upcase }
←[a,b,c]⇒["A","B","C"]
('a'..'z').to_a.shuffle[0..7]
←a~zの文字列の配列作成、ランダムな8つの文字列を返す。
ハッシュとシンボル
ハッシュは{}で表す。
user = { "first_name" => "Michael", "last_name" => "Hartl" }
←例
↑同じ意味
user = {first_name: "Michael", last_name: "Hartl"}
⇒{key: value, key: value}
CSS読み込みコードの解読
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
・{media: 'all', 'data-turbolinks-track': 'reload'} の波かっこが省略されている。
⇒最後の引数がハッシュで終わる場合、波かっこを省略可能。
・第一引数がapplication(スタイルシートへのパス)、第二引数がハッシュ(メディアタイプを示している+turbolinkをオンにしている。)
クラス
・コンストラクタ:オブジェクトを作成するもの
(0..9)⇒リテラルコンストラクタ(文字列のオブジェクトを暗黙で作成する。)
s = String.new("foobar")
←文字列の名前付きコンストラクタ
⇒StringクラスはRailsの組み込みクラス
⇒Stringクラスを継承して、新たにクラスを作成する場合、下記の通り。
class Word < String
←継承。これでWordクラスでStringクラスのメソッドを使用できる。
Stringクラス(組み込みクラス)も自分でメソッドを追加したりできるが、積極的に行うものではない。
ユーザークラス
attr_accessor :name, :email
←アクセサーを作成すると、データを取り出すメソッドと代入するメソッドをそれぞれ定義してくれる。
⇒インスタンス変数@nameとインスタンス変数@emailにアクセスするためのメソッドが用意される。
第5章 レイアウトを作成する。
CSS、Bootstrapを活用してきれいにする。
ナビゲーション
Bootstrapのnav-barなどを活用する。
目立ったコード
<li><%= link_to "Home", '#' %></li>
←引数1:表示文字、引数2:リンク先
<%= link_to image_tag("rails.png", alt: "Rails logo"), 'http://rubyonrails.org/' %>
←image_tag(app/assets/images/ディレクトリから画像をもってくる。
$ curl -o app/assets/images/rails.png -OL railstutorial.jp/rails.png
←画像をダウンロードするコード。
Bootstrapの導入
gemファイルに追加
gem 'bootstrap-sass', '3.3.7'
↓
$ bundle install
↓
$ touch app/assets/stylesheets/custom.scss
←custom.scssの作成
↓
custom.scssに下記コードを追加。
@import "bootstrap-sprockets"; @import "bootstrap";
↓
custom.scssにcssコードを追加していくと、すべてのページに反映される。
パーシャル
パーシャルという機能を使うことによって似たようなコードを1つにまとめることができる。
なお、パーシャルのファイル名の頭にはアンダースコア(アンダーバー)を付加。
よくviewで使うことになる。またrenderというメソッドを使用することが多い。
<%= render 'layouts/shim' %>
↑代入される
app/views/layouts/_shim.html.erb
アセットパイプライン
・CSS、JavaScript、画像などの静的コンテンツの生産性と管理を大幅に強化する機能。
■アセットディレクトリ
app/assets: 現在のアプリケーション固有のアセット
lib/assets: あなたの開発チームによって作成されたライブラリ用のアセット
vendor/assets: サードパーティのアセット
■マニフェストファイル
アセットディレクトリのそれぞれ適切なディレクトリにファイルを配置されていれば、
マニフェストファイルにより、どのように1つのファイルにまとめるのかをRailsに指示を出すことができる。
⇒app/assets/stylesheets/application.css※マニフェストファイル
■プリプロセッサエンジン
Sass用の.scss、CoffeeScript用の.coffee、埋め込みRuby (ERb) 用の.erb
↑を実行する機能を持つ。
アセットディレクトリにファイルを配置⇒プリプロセッサエンジンで各種実行⇒マニフェストファイルの指示のとおりに結合して、ブラウザへ返す。
foobar.js.erb.coffee
←coffeeとerbをつなげて実行できる。
⇒CoffeeScript(javascript)を実行したのち、erb(ruby)が実行される。(右から順番に)
■本番環境での働き
アセットパイプラインが、CSSファイルを1ファイル(application.css)にまとめ、javascript(javascripts.js)を1つのファイルにまとめ、不要な空欄などを削除してファイルサイズを最小化してくれる。
⇒効率的な実装。
SCSS
■ネスト
【ネスト化前】
.center {
text-align: center;
}
.center h1 {
margin-bottom: 10px;
}
【ネスト化後】
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
【ネスト化前】
#logo { float: left; margin-right: 10px; font-size: 1.7em; color: #fff; text-transform: uppercase; letter-spacing: -1px; padding-top: 9px; font-weight: bold; }
#logo:hover { color: #fff; text-decoration: none; }
【ネスト化後】
#logo { float: left; margin-right: 10px; font-size: 1.7em; color: #fff; text-transform: uppercase; letter-spacing: -1px; padding-top: 9px; font-weight: bold; &:hover { color: #fff; text-decoration: none; } }
■変数
$light-gray: #777; h2 { color: $light-gray; } footer { color: $light-gray; }
レイアウトのリンク
<%= link_to "About", about_path %>
↓route設定を下記に変更
get '/about', to: 'static_pages#about'
↓〇〇_path,〇〇_urlの表現
about_path -> '/about'
about_url -> 'http://www.example.com/about'
↓
各ページのルート表記を変更する。
(test,controller,view)
■統合テスト
今回のテスト手順
①ルートURL (Homeページ) にGETリクエストを送る.
②正しいページテンプレートが描画されているかどうか確かめる.
③Home、Help、About、Contactの各ページへのリンクが正しく動くか確かめる.
assert_template 'static_pages/home'
←テストするテンプレページ
assert_select "a[href=?]", root_path, count: 2
←href=?:今回は自動でroot_pathが入る。
root_pathへのリンクが2つあるかチェックする。という動作。
$ rails test:integration
←統合テストの実行
第6章 ユーザーのモデルを作成する
Userモデル
$ rails generate model User name:string email:string
←モデルの作成
⇒migrationファイルも生成される。(データベースに変更を指示するファイル)
※ブロックの最後の行t.timestampsは特別なコマンドで、created_atとupdated_atという2つの「マジックカラム (Magic Columns)」を作成する。
$ rails db:migrate
生成されたモデルは、requireしなくとも、各ファイルと紐づいている
⇒継承が行われているため。
user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
←データの生成
user.valid?
←userオブジェクトが有効かどうかの確認。
user.save
←ここでやっとデータベースに書き込まれる
User.create(name: "A Nother", email: "another@example.org")
←モデルの生成と保存が同時に行える。
foo.destroy
←データの削除
User.find(1)
←データの検索(IDが1のデータ)
User.find_by(email: "mhartl@example.com")
←emailを指定した検索
User.all
←Userテーブルのすべてのデータ
user.email = "mhartl@example.net"
オブジェクトの変更
user.save
←saveの必要あり
user.update_attributes(name: "The Dude", email: "dude@abides.org")
←オブジェクトの変更および保存
ユーザーの検証
・現状だとログインしていなくても投稿にアクセスできたりしてしまう。
⇒アクセスの制限をかける。
⇒Active Record では検証 (Validation) という機能がある。
⇒存在性 (presence)の検証、長さ (length)の検証、フォーマット (format)の検証、一意性 (uniqueness)の検証
⇒最終検証として確認 (confirmation)
■具体的な方法
①有効なモデルのオブジェクトを作成
②その属性のうちの1つを有効でない属性に意図的に変更
③バリデーションで失敗するかどうかをテストする
■user_test.rb
def setup
@user = User.new(name: "Example User", email: "user@example.com")
end
⇒インスタンス変数(@user)を作成。
test "should be valid" do
assert @user.valid?
end
⇒userの有効性をテスト(valid?メソッドにより、存在すればtrue、存在しなければfalseが返る)
⇒trueがassertに返れば、success
rails test:models
←モデルだけのテストを行う。
存在性の検証
①テストを書く(RED)
test "name should be present" do
@user.name = " "
assert_not @user.valid?
end
⇒nameが空欄になっても、userが生成されてしまう。
②@user.valid?がfalseを返すように修正するために、validateメソッドを使用する。(GREEN)
validates :name, presence: true
③emailについても同様の①②を行う。
長さの検証
・nameの長さは50文字まで
・emailの長さは255文字まで
①テストを書く(user_test.rb)
test "name should not be too long" do
@user.name = "a" * 51
←aaaaa...aaaaという名前
assert_not @user.valid?
←@user.valid?がfalseを返せばsuccess
end
②長さの上限を設定する
validates :name, presence: true, length: { maximum: 50 }
validates :email, presence: true, length: { maximum: 255 }
フォーマットを検証する
・有効なメールアドレス、無効なメールアドレスの例を作成する。
■有効なメールアドレスが通るか検証するテスト
test "email validation should accept valid addresses" do valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org first.last@foo.jp alice+bob@baz.cn] valid_addresses.each do |valid_address| @user.email = valid_address assert @user.valid?, "#{valid_address.inspect} should be valid" end end end
・%w[]でemailの配列を作成。
・eachメソッドで、各emailひとつひとつを検証する。
・assertメソッドの第二引数は、エラーメッセージ。
・#{valid_address.inspect}には、エラーが出たemailが表示される。
■無効なメールアドレスが通らないか検証するテスト
test "email validation should reject invalid addresses" do invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. foo@bar_baz.com foo@bar+baz.com] invalid_addresses.each do |invalid_address| @user.email = invalid_address assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" end end
■検証コード(user.rb)
class User < ApplicationRecord
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
←正規表現
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX }
←フォーマットの指定(正規表現)
end
・↓正規表現に対して、正しく通るか、はじかれるかの確認ができる
https://rubular.com/
一意性を検証する
・同じemailの登録を拒否する
■テスト(user_test.rb)
test "email addresses should be unique" do
duplicate_user = @user.dup
←userの複製
duplicate_user.email = @user.email.upcase
←emailを大文字にする
@user.save
←保存
assert_not duplicate_user.valid?
←複製したuserは有効じゃないよね?
end
■validationの追加(user.rb)
uniqueness: { case_sensitive: false }
←ユニークだったら、trueを返す
現状だと、2回連続で登録をクリックすると、重複したユーザーが作られてしまう。
⇒emailのカラムにインデックスを追加することで解決する。
■emailのカラムにインデックスを追加
$ rails generate migration add_index_to_users_email
←migrationファイルを作成
マイグレーションファイルにコードを追加
class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
def change
add_index :users, :email, unique: true
←usersテーブルのemailカラムにインデックスを追加する。一意性を強制する。
end
end
test/fixtures/users.ymlの中身を消す
⇒重複したユーザがあらかじめ入ってしまっているため。
■大文字と小文字を区別しない(user.rb)
before_save { self.email = email.downcase }
←保存する前に、emailを小文字に変換する。
セキュアなパスワードを追加する
・パスワードの送信、ハッシュ化、データベース内のハッシュ化された値との比較、という手順。
・比較の結果が一致すれば、送信されたパスワードは正しいと認識され、そのユーザーは認証されます。
・ハッシュ化されたパスワード同士を比較する。
password_digest属性を追加するためのマイグレーションファイルを作成
$ rails generate migration add_password_digest_to_users password_digest:string
作成されたマイグレーションファイルを確認後、下記を実行
$ rails db:migrate
bcrypt(パスワードハッシュ化関数)を使えるようにするために、gemfileを更新+インストール
gem 'bcrypt', '3.1.12'
bundle install
has_secure_passwordを使えるようになった。
■user.rb
has_secure_password
←パスワードのハッシュ化~合致チェックを行ってくれる
↓
■user_test.rb
def setup @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") end
パスワードの最小文字数を設定
■テストを書く(テストが空白ではない。5文字以上)
`test "password should be present (nonblank)" do
@user.password = @user.password_confirmation = " " * 6
assert_not @user.valid?
end
test "password should have a minimum length" do
@user.password = @user.password_confirmation = "a" * 5
assert_not @user.valid?
end`
■user.rb
validates :password, presence: true, length: { minimum: 6 }
第7章 ユーザー登録
・ユーザー名、基本ユーザーデータ、マイクロポストの一覧を表示するページを作成する
■デバック情報を各ページで表示させる。(開発環境のみ)
<%= debug(params) if Rails.env.development? %>
←開発環境だったら、デバック情報を表示する。
※Railsにはテスト環境 (test)、開発環境 (development)、そして本番環境 (production) の3つの環境がデフォルトである。Rails consoleのデフォルトの環境はdevelopment。
■scssの機能
@mixin box_sizing { -moz-box-sizing: border-box; -webkit-box-sizing: border-box;
box-sizing: border-box;
} .debug_dump { clear: both; float: left; width: 100%; margin-top: 45px;
@include box_sizing;
←@mixinで指定された「box_sizing」の内容がそのまま代入される。
}
■ユーザーページを表示する
ルートが通っていないので、通す
・routes.rb
resources :users
※routesファイルに上記コードを追加するだけで、「HTTPリクエストに対応するアクションの設定」「アクションの使用」「名前付きルートの使用」が可能になる。
試しにviewを追加する
・show.html.erb
<%= @user.name %>, <%= @user.email %>
userコントローラにshowアクションを追加する。
@user = User.find(params[:id])
■debuggerメソッド
・バグを見つけるメソッド
バグがありそうなコードの近くに挿入する。
@user = User.find(params[:id])
debugger
ブラウザから/users/1 にアクセス
railsサーバを立ち上げたターミナルを見てみると下記のように表示される。
(byebug) @user.name "Example User" (byebug) @user.email "example@railstutorial.org" (byebug) params[:id] "1"
※システムの状態の調査、バグの追跡ができる。
■gravater画像
Gravatarは無料のサービスで、プロフィール写真をアップロードして、指定したメールアドレスと関連付けることができる。
・show.html.erb
<%= gravatar_for @user %>
・users_helper.rb
# 引数で与えられたユーザーのGravatar画像を返す def gravatar_for(user) gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
←小文字化したemailをidとする。
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end
ユーザーの登録フォーム
・form_forヘルパーメソッドを使用する。
fオブジェクトは、HTMLフォーム要素 (テキストフィールド、ラジオボタン、パスワードフィールドなど) に対応するメソッドが呼び出されると、@userの属性を設定するために特別に設計されたHTMLを返す。
<%= f.label :name %>
<%= f.text_field :name %>
↓ fオブジェクトを使用することで、HTML上は下記の表示になる。
※:passwordで使用すると、HTML上は、type=passwordが設定され、*表記になったりする。
自動で属性に適したフォームを生成してくれる。
<label for="user_name">Name</label> <input id="user_name" name="user[name]" type="text" />
・usersコントローラー
def new @user = User.new end
・railsは、nameの値を使って、params変数経由でハッシュを作る。
ハッシュはHTTPで転送される。(params変数を含んで)
@user = User.new(name: "Foo Bar", email: "foo@invalid", password: "foo", password_confirmation: "bar")
↑同じ意味
@user = User.new(params[:user])
ただし、セキュリティ上よろしくない。
・user_paramsメソッドはUsersコントローラの内部でのみ実行され、Web経由で外部ユーザーにさらされない。Rubyのprivateキーワードを使って外部から使えないようにする。
@user = User.new(user_params)
←user_paramsメソッド(paramsの内容は勝手に入ってる。)
params.require(:user).permit(:name, :email, :password, :password_confirmation)
⇒privateキーワード内のコード。許可する属性を限定している。
エラー時のメッセージ
<%= render 'shared/error_messages' %>
・Rails全般の慣習として、複数のビューで使われるパーシャルは専用のディレクトリ「shared」によく置かれます
class: 'form-control'
helper.pluralize(1, "error")
・新規ユーザー登録用の統合テストを生成
'$ rails generate integration_test users_signup'
・成功時のメッセージ
flash[:success] = "Welcome to the Sample App!"
第八章 基本的なログイン機構
・セッション:ログイン情報等を保持できる。ブラウザを閉じると情報を破棄する。
・cookies:ログイン情報等を保持できる。ブラウザを閉じても保持できる。
・ログインフォームはnewアクションで処理する。createアクションにPOSTリクエストを送信するとログインする。destroyアクションにDELETEリクエストを送信するとログアウトする。
・名前付きルート「login_path」
get /login・・newアクションへ
post /login・・createアクションへ
・セッションへ情報を渡す時には、下記のように具体的にリソースの名前とURLを指定する必要がある。
form_for(:session, url: login_path)
■セッションを実装するためのモジュールを使えるようにする
・セッションを実装するには、多くのメソッドを定義する必要があるが、Rubyの機能で、セッションコントローラーを生成したときに、自動でセッション用のヘルパーが読み込まれている。
⇒「application_controller.rb」に下記コードを埋めこむことでヘルパーを使用できる。
include SessionsHelper
・sessionメソッドで作成された一時cookiesは、ブラウザを閉じた瞬間に有効期限が終了する。
・sessionsヘルパーにlog_inメソッドを定義することで、同じログイン機能をどこでも使える状態にしておく。
・sessions_controller.rbのcreateアクションにlog_inメソッドを実装
⇒現時点だと、sessionにIDは保持されているが、画面上でログインできている確認することができない。
・sessionのIDから、ユーザー名を取り出すメソッドを定義する(current_userメソッドの定義)
8.2.4のfixtureの部分が謎
ログアウト
・ログアウトメソッドの定義
def log_out
session.delete(:user_id)
@current_user = nil
end
・destroyアクションの設定
def destroy
log_out
redirect_to root_url
end
第9章 発展的なログイン機能
第8章では、sessionを使って、ID情報を保存していたが、この情報はブラウザを閉じると消える。
⇒記憶トークンを生成して、cookiesメソッドによる永続的cookiesを作成する。
トークンとは、コンピュータが作成・管理する秘密情報のこと。⇔パスワードはユーザーが作成する。
永続cookiesを使用する場合、cookiesを盗み出して、乗っ取られる可能性がある。
【乗っ取りの方法】
・ネットワークパケットから、パケットスニッファ(特殊なソフトウェア)で直接cookiesを取り出す。
⇒Secure Sockets Layerをサイト全体に適用して、データを暗号化して保護する。
・データベースから記憶トークンを取り出す。
⇒記憶トークンのハッシュ値を保存するようにする。
・クロスサイトスクリプティングを使う
⇒Railsにより自動で対策されている。viewのテンプレで入力した内容を自動でエスケープする。
・ユーザーがログインしているパソコンやスマホを直接操作してアクセスを奪う。
⇒根本的な解決はできないが、ユーザーがログアウトしたときに、必ずトークンを変更するようにする。デジタル書面も行う。
【設計への落とし込み方針】
・記憶トークンにはランダムな文字列を生成して用いる。
・ブラウザのcookiesにトークンを保存するときには、有効期限を設定する。
・トークンはハッシュ値に変換してからデータベースに保存する。
・ブラウザのcookiesに保存するユーザーIDは暗号化しておく。
・永続ユーザーIDを含むcookiesを受け取ったら、そのIDでデータベースを検索し、記憶トークンのcookiesがデータベース内のハッシュ値と一致することを確認する。
9.2まで終わった。
【9章はもう一回見直したほうが良い】
第10章 ユーザーの更新・表示・削除
ユーザーを更新
ユーザの作成と似ている
newアクション⇔editアクション
createアクション⇔updateアクション
@user = User.find(params[:id])
editアクションを定義
viewファイル「edit.html.erb」のコーディング
⇒form_forでテキストエリアを用意
headerにリンクを掲載
<li><%= link_to "Settings", edit_user_path(current_user) %></li>
編集に失敗したときのテストを書く
$ rails generate integration_test users_edit
編集ページにアクセスし、editビューが描画されるか?
無効な情報を送信したときに、editビューが再描画されるか?
のテスト
編集に成功したときのテストを書く
認可
認証 (authentication) はサイトのユーザーを識別することであり、認可 (authorization) はそのユーザーが実行可能な操作を管理することです。
⇒ユーザーのみ、自分の投稿を編集できるなどなど(認可)
・ログインしていないユーザーには、ログインを要求する。
logged_in_userの定義、before_actionへの設定(edit,updateアクションを行う場合)
・正しいユーザーを要求する。
correct_userの定義、before_actionへの設定。
正しいユーザーではなかった場合、ルートURLへリダイレクト
・フレンドリーフォワーディング
ログインしていないユーザーが、編集ページへ行こうとした場合、ログインページにリダイレクトされる。
その後、ログインした場合、プロフィールページにリダイレクトされてしまう。
本来、編集ページへアクセスしたかったのだから、ログイン後は編集ページにリダイレクトされるべき。
⇒本来のリクエストを出しているページをsessionに保存しておいて、ログイン後にももともと行きたかったページにいけるように設定する。
sessions_helperで、redirect_back_or、store_locationの定義
store_locationで、もともといきたかったURLを保存
redirect_back_orで、↑で記憶したURLへリダイレクト
⇒users_controller.rbに実装する。