いよいよ解禁
2/28
23:30 そわそわする
23:40 耐えられず、男友達と電話を始める(ホモ)
23:50 気が気じゃなくなる。自宅で縦揺れを始める。
3/1
0:00 目にも留まらぬ速さでマイナビを開く。
0:01 御社「サイトが混み合っています」
0:10 御社「サイトが混み合っています」
0:30 御社「サイトが混み合っています」
0:45 とりあえずツイッターを開く
2:00 何もせず寝る
結果、あけおめ挨拶を友達としてツイッターに興じただけの人
マイナビ、就活解禁あんなに煽っといて結局アクセス過多で死んでるの、限定品薄商法しか脳がない道玄坂のアイドル事務所を思い出してしまうな
てか、律儀に3月解禁信じてマジでそれまで何もやってない人結構いて仰天優莉奈になってしまった いいよね、未来の無職候補 まぁ僕もなんですけど
これから4ヶ月、毎日死ぬ気で頑張りましょう(解禁後3日目にしてアイドルさん観に行った)。てか企業側も経団連ルール守るなや、6月から面接始めて同寸年
Rails Tutorial 9章 remember_meチェックボックス
よくあるやつ。「次回以降自動でログインする」みたいな。
ログインフォームに追記
app/views/sessions/new.html.erb . . <%= f.label :remember_me, class: "checkbox inline" do %> <%= f.check_box :remember_me %> <span>Remember me on this computer</span> <% end %> .
bootstrapが、checkbox, inlineの二つを認識して、テキストRemember ~~ と同じ行に配置してくれる
もうちょっとスタイルを整える
app/assets/stylesheets/custom.scss . . /* forms */ . .checkbox { margin-top: -10px; margin-bottom: 10px; span { margin-left: 20px; font-weight: normal; } } #session_remember_me { width: auto; margin-left: 0; }
これでログインフォームの編集終わり。
次に、チェックの有無でユーザーの記憶をするかどうかを書く。
実は、ログインフォームから送信されたparamsハッシュには、すでにチェックボックスの値が含まれている。(チェックボックスがオンの時は「1」になり、オフの時は「0」になる。
なので、このparamsハッシュの値を調べて判定すれば良い。
if params[:session][:remember_me] == '1' remember(user) else forget(user) end
この式、「三項演算子」というのを使えば一行で書けるっぽい。
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
非常にコンパクト
これを、createアクションに追記
app/controllers/sessions_controller.rb . def create . log_in user params[:session][:remember_me] == '1' ? remember(user) : forget(user) .
三項演算子
「論理値? ? 何かをする : 別のことをする
if boolean?
var = foo
else
var = bar
endvar = boolean? ? foo : bar
Rails Tutorial 8章 ログイン処理(current_user等)
現在のユーザー
ユーザーIDをセッションの中に安全におけるようになったので、これを別ページで取り出せるようにしたい。
current_userメソッドを定義して、セッションIDに対応するユーザー名をDBから取り出せるようにする。
例えば、こんなことをしたい
<%= current_user.name %>
redirect_to current_user
現在のユーザーを検索する方法としては、 findメソッドが挙げられる。
しかし、ユーザーIDが存在しない状態でfindメソッドを使うと例外が発生してしまう。(ActiveRecord::RecordNotFoundが出ちゃう)
そこでfind_byメソッドを使いたい。
User.find_by(id: session[:user_id])
find_byメソッドによって、IDが無効な場合(=ユーザーが存在しない場合)には、メソッドは例外を発生せず nil を返す。
この手法を使ってcurrent_userを定義
def current_user if session[:user_id] User.find_by(id:session[:user_id]) end end
nilが返されれば、DBへの問い合わせ回数を抑えられ、負担も減る。
また、Rubyの慣習に従い、実行結果をインスタンス変数に保存している
if @current_user.nil? @current_user = User.find_by(id: session[:user_id:) else @current_user end
or演算子「||」を使えばこんな簡単に
@current_user = @current_user || User.find_by(id: session[:user_id])
これ、論理値自体は常にTrueになる。
よって、@current_userに何も代入されてない時だけfind_byメソッドが呼び出され、無駄な読み出しが防げる。
これを短縮して、
@current_user ||= User.find_by(id: session[:user:id])
「||=」とは何か?
例)>>@foo =>nil >>@foo = @foo || "bar" =>"bar" >>@foo = @foo || "baz" =>"bar"
以上のコードを適用した結果がこちら
app/helpers/sessions_helper.rb . . #現在ログイン中のユーザーを返す(いる場合) def current_user if session[:user_id] @current_user ||= User.find_by(id: session[:user_id]) end end .
レイアウトリンクを変更する
ユーザーがログインしている時とそうでないときでレイアウトを変更したい。
まず、ログインしているかどうかを判別するメソッドを作成(ERBコードの中で条件分岐を使う)
<% if logged_in? %> #ログインユーザー用のリンク <% else %> #ログインしていないユーザー用のリンク <% end %>
これを機能させるため、logged_in?を作る
ユーザーがログイン中の状態とは、「sessionにユーザーIDが存在している」こと。
つまり、current_userがnilではないということ。
つまり、否定演算子「!」を使用して判定。
app/helpers/sessions_helper.rb . . #ユーザーがログインしていればtrue、その他ならfalseを返す def logged_in? !current_user.nil? end
これでユーザーのログイン時にレイアウトを変えられる準備が整った。
んで、書いたビューがこれ
app/views/layouts/_header.html.erb <header class="navbar navbar-fixed-top navbar-inverse"> <div class="container"> <%= link_to "sample app", root_path, id: "logo" %> <nav> <ul class="nav navbar-nav navbar-right"> <li><%= link_to "Home", root_path %></li> <li><%= link_to "Help", help_path %></li> <% if logged_in? %> <li><%= link_to "Users", '#' %></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> Account <b class="caret"></b> </a> <ul class="dropdown-menu"> <li><%= link_to "Profile", current_user %></li> <li><%= link_to "Settings", '#' %></li> <li class="divider"></li> <li> <%= link_to "Log out", logout_path, method: :delete %> </li> </ul> </li> <% else %> <li><%= link_to "Log in", login_path %></li> <% end %> </ul> </nav> </div> </header>
レイアウトに新しいリンクを追加したので、Bootstrapのドロップダウンメニュー機能を適用できる状態になった。(dropdown-menu, dropdownとか)
これらの機能を有効にするため、Railsのapplication.jsファイルに指示出す。
app/assets/javascripts/application.js . //= require jquery //= require bootstrap .
ユーザー登録時にログイン
log_in @user これだけ
app/controllers/users_controller.rb . . def create . if @user.save log_in @user . .
ログアウト
セッションからユーザーIDを削除ればいい。
session.delete(:user_id)
これで、現在のユーザーをnilにできる。
ログイン処理と同じようにsessionヘルパーに記載
app/helpers/sessions_helper.rb . . #現在のユーザーをログアウトする def log_out session.delete(:user_id) @current_user = nil
ここで定義したlog_outメソッドをsesisonコントローラのdestroyアクションでも使う。
app/controllers/sessions_controller.rb . . def destroy log_out redirect_to root_url end
Rails Tutorial 8章 ログイン処理(session)
セッション
HTTPはステートレスなプロトコルであり、リクエスト一つ一つが独立したトランザクションとなっている。
つまり、ユーザーIDとかはHTTPプロトコル内には保存できない。
その代わりに状態を保存しているのがセッション。
セッションを作成する
セッションをRESTfulなリソースとしてモデリングしたい。そのほうが便利。new→create→destroyみたいな。
まず、セッションコントローラを作る。
$ rails g controller Sessions new
リソースベースルーティングは無駄が多いため、今回は個別に指定(名前付きルーティングのみ)
. . get '/login' => 'sessions#new' post '/login' => 'sessions#create' delete '/logout' => 'sessions#destroy'
ちなみに、 rails routesコマンドで今使用しているルーティングを確認できる
$ rails routes Prefix Verb URI Pattern Controller#Action root GET / static_pages#home help GET /help(.:format) static_pages#help . .
ログインフォームの作成
新規作成ではActiveRecordがエラーメッセージを持っていたけど、今回のセッションはActiveRecordではないた、エラーメッセージを自分で作成する必要がある。
(セッションはDBであれこれするものではないため、ActiveRecordに関連づけられたエラーメッセージは使えない)
セッションフォームとユーザー登録ページの最大の違いは、セッションはモデルを持たないこと。
つまり、セッションは、@userのようなインスタンス変数を持たない。
→新しいセッションフォームを作成するときには、form_forヘルパーに追加の独自の情報を渡さなければいけない。
form_for(@user)
Railsは通常、この記述だけで「フォームのactionは/usersというURLへのPOSTである」と判定するが、
セッションの場合は、リソースの名前と対応するURLを具体的に指定しなければならない。
form_for(:session, url: login_path)
これを用いて作成したログインフォームがこれ
app/views/sessions/new.html.erb <% provide(:title, "Log in") %> <h1>Log in</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_for(:session, url: login_path) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.submit "Log in", class: "btn btn-primary" %> <% end %> <p>New user? <%= link_to "Sign up now!", signup_path %></p> </div> </div>
そして、生成されたHTMLフォーム
<form accept-charset="UTF-8" action="/login" method="post"> <input name="utf8" type="hidden" value="✓" /> <input name="authenticity_token" type="hidden" value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" /> <label for="session_email">Email</label> <input class="form-control" id="session_email" name="session[email]" type="text" /> <label for="session_password">Password</label> <input id="session_password" name="session[password]" type="password" /> <input class="btn btn-primary" name="commit" type="submit" value="Log in" /> </form>
これを詳しく見るに、paramsハッシュに入る値は
params[:session][:email]
params[:session][:password]
になる。
ユーザーの検索と検証
ログイン処理
↓
ログイン失敗
↓
ログイン成功
の順で作成
まず、最小限のcreateアクションを作成
app/controllers/sessions_controller.rb . def new end def create render 'new' end def destroy end
ここで、ログインフォームのデバック情報
--- session: email: 'user@example.com' password: 'foobar' commit: Log in action: create controller: sessions
paramsを見ると
{ session: { password: "foobar", email: "user@example.com" } }
つまり、
params[:session]
に、
{ password: "foobar", email: "user@example.com" }
が含まれていて、
結果として、
params[:session][:email] params[:session][:password]
これにアクセスすることになる。
ネストした(入れ子になった)ハッシュになっている。
つまり、createアクションの中では、ユーザー認証に必要な情報をparamsから簡単に取り出せる。
そこで、
app/controllers/sessions_controller.rb . . def create user = User.find_by(email: params[:session][:email].downcase if user && user.authenticate(params[:session][:password] #ユーザーログイン後にユーザー情報のページにリダイレクトする else #エラーメッセージを表示する render 'new' end end
以下、解説
・if user && ~~~~
&&(論理積)は取得したユーザーが有効かどうかを決定するためのもの。
つまり、
左辺:ユーザーがDBに存在する
右辺:DBのユーザーとparams値の認証に成功する
この場合のみ、if が true になる。
フラッシュメッセージを表示する
セッションではActiveRecordを使ってないので、ログインに失敗した時は、エラーメッセージの代わりにフラッシュメッセージを使用する。
app/controllers/sessions_controller.rb def create . . else flash[:danger] = 'Invalid email/password combination' . .
※フラッシュメッセージはWebサイトのレイアウトに表示されるので、flash[:danger]で設定したメッセージは自動的に表示される。(スタイルもBootStrapで整えられている)
しかし、これはrenderで強制的に再レンダリングされているため、メッセージが消えない。
んで、こう修正
app/controllers/sessions_controller.rb def create . . else flash.now[:danger] = 'Invalid email/password combination' . .
flash.nowとすることで、その後リクエストが発生するとメッセージが消滅する。
ログイン
有効な値の送信をフォームで扱う。
そのため、cookieを使った一時セッションでユーザーをログインできるようにする。(ブラウザを閉じると消滅する)
セッションを一から実装するには、大量のコードを書かなければいけない(らしい)
それを一箇所にパッケージ化するのがモジュール。
セッションコントローラを生成した時点で、セッション用ヘルパーモジュールも自動生成されている。しかも、ビューに自動的に読み込まれる。(Applicationコントローラにモジュールを読み込ませれば、どのコントローラにも使える)
Applicationコントローラにモジュールを読み込ませる
app/controllers/application_controller.rb class ApplicationController < ActionController::Base . include SessionsHelper end
設定が完了したので、ユーザーログインのコードへ。
log_inメソッド
railsで事前定義済みのsessionメソッドを使って、単純なログインを行えるようにする。(さっき作ったsessionコントローラとは無関係)
session[:user_id] = user,id
この処理を実行すると、ブラウザ内の一時cookieに暗号化済みのユーザーIDが自動で作成される。(ブラウザを閉じると破棄される)
→この後のページで、session[:user_id]を使ってユーザーIDをもとどおりに取り出すことができる。
これを、様々な場所で使えるように、ヘルパーにメソッドを定義
app/helpers/sessions_helper.rb module SessionsHelper #渡されたユーザーでログインする def log_in(user) session[:user_id] = user.id end end
sesisonメソッドで作成された一時cookieは自動的に暗号化され、このコードは保護される。
!攻撃者がこの情報をcookieから盗み出しても、それを使って本物のユーザーとしてはログインできない!
→しかし、永続cookieは別
このlog_inメソッドを使い、createアクションに追記。
app/controllers/sessions_controller.rb def create user ~~~~ if ~~~~~ log_in user redirect_to user . .
以下のコード
redirect_to user
を、Railsは自動的に変換して、次のように、プロフィールページへのルーティングにする
user_url(user)
Rails Tutorial 7章 編集中 ユーザー登録フォーム(エラーメッセージ)
エラーメッセージ
errors.full_messagesオブジェクトはエラーメッセージの配列を持ってる。
例えば
$ rails console . >>user.save =>false >>user.error.full_message => ["Email is invalid", "Password is too short (minimum is 6 characters)"]
ここでは、保存に失敗した際、newページに戻って、エラーメッセージのパーシャル出力して、エラーメッセージを表示する。
app/views/users/new.html.erb <% 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'てやつ
複数のビューで使用されるパーシャルは、sharedってディレクトリによく置かれるらしい。
sharedを作る
$ mkdir app/views/shared
中身 (_error_messages.html.erb)を作成
app/views/shared/_error_messages.html.erb <% 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メソッド
>>user.errors.count =>2
any? empty? メソッド
>>user.errors.any? =>true >>user.errors.empty? =>false
Rails Tutorial 7章 ユーザー登録フォーム(フォームの作成・送信)
ユーザー登録フォーム
1、form_forを使う
Railsに元々あるヘルパーメソッド。
まず、登録フォームのアクションから設定
app/controllers/users_controller.rb def new @user = User.new end
んで、これがフォーム
app/views/users/new.html.erb <% 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>
2、フォームHTML
先ほど作成したフォームを理解するため、細かく見ていく。
<%= form_for(@user) do |f| %> . . <% end %>
・do は、form_forが一つの変数を取るブロックを表す。
・fオブジェクトは、HTMLフォーム要素(textfieldとか)に対応するメソッドが呼び出されると、@userの属性を指定するために特別に設計されたHTMLを返す。
つまり、これでは
<%= f.label :name %> <%= f.text_field :name %>
Userモデルのname属性を設定する、ラベル付きテキストフィールドを返す。
こんなHTMLを生成してるよ
<label for="user_name">Name</label> <input id="user_name" name="user[name]" type="text" />
重要なのが、inputごとにあるname属性
これ
<input id="user_name" name="user[name]" - - - /> . . <input id="user_password" name="user[password]" - - - />
これは、Railsがnameの値を使って初期化したハッシュをparams変数経由で構成するもの。nameの値がparamsで受け取れる。んだと思う
次に、formタグ自身
<form action="/users" class="new_user" id="new_user" method="post">
action = " /users "
method = " post " この二つが重要。
form_forはこれを勝手に生成してくれる。
3.1、ユーザー登録失敗
こんな感じで作る
createアクションでフォーム送信を受け付ける
↓
User.newで新しいオブジェクトを作成
↓
ユーザーを保存(または失敗)
↓
再度、送信用のユーザー登録ページを表示
まず、createアクションを作る(途中まで)
app/controllers/users_controller.rb def create @user = User,new(params[:user]) if @user.save #保存の成功をここで扱う else render 'new' end end
深く理解するため、デバック情報を参照してみる
"user" => { "name" => "Foo Bar", "email" => "foo@invalid", "password" => "[FILTERED]", "password_confirmation" => "[FILTERED]" }
ここでは、params[:user]に複数のハッシュが含まれている。フォーム送信の結果が、送信された値に対応する属性とともにuserハッシュに保存されている。
ハッシュのキーは、inputタグにあったnameの値。
ちなみに、この二つは同義
@user = User.new(params[:user])
@user = User.new(name: "Foo Bar", email: "foo@invalid", password: "foo", password_confirmation: "bar")
けど、前者のコードではセキュリティ面で懸念があるっぽい。
そこで登場するのがStrongParameters
3.2、Strong Parameters
まず、マスアサインメントってやつ
次のコードみたいに、値のハッシュを使ってRubyの変数を初期化するもの
@user = User.new(params[:user])
マスアサインメントでは、name, emailとかの他に、actionやcontrollerといった全ての値が飛んでくる。
paramsハッシュ全体を初期化するのは危険すギル。マスアサインメント脆弱性(admin=管理者権限 があればparamsの中身を見れるし自由にシステムを操作できるっぽい)。
今は、マスアサインメントを使うとRailsが怒る。
そこで、StrongParametersを使用
これを使うことで、必須のパラメーターと許可されたパラメーターを指定することができる。って何?
→簡単に言うと、DBへ入れたり更新したりする値を制限できるらしい
<使い方>
①requireでPOSTで受け取る値の設定
②permitで許可するカラムを設定
例えば、「user属性を必須にして、名前・アドレス・パスワード・パスワードの確認だけ更新許可したい!」ってとき
params.require(:user).permit(:name, :email, :password, :password_confirmation)
ここで、このパラメーターを使いやすくするため、巷では user_paramsとかいう外部メソッドが使われているらしい。
主に params[:user] の代わりとして使われる
@user = User.new(user_params)
これを、外部ユーザーに漏れないよう、privateに隠す
app/controllers/users_controller.rb . . def create @uer = 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