デバッグ
debugメソッド
debugメソッドとparams変数を使い、各ページにデバッグ情報が表示されるようにする。
<!DOCTYPE html>
<html>
.
.
.
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
<%= debug(params) if Rails.env.development? %> ⇦ここに追加
</div>
</body>
</html>
Railsにはテスト環境(test)、開発環境(development)、本番環境(production)の3つのデフォルト環境が用意されている。
上記では、開発環境にのみデバッグ情報を表示されるようにしている。
debuggerメソッド
上記ではdebugメソッドを書きましたが、もっと直接的にデバッグする方法として、
byebug gemによるdebuggerメソッドがあります。
debuggerメソッドをUsersコントローラーに差し込む
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
debugger
end
def new
end
end
debuggerメソッドを差し込んだら、ブラウザから/users/1にアクセスすると、
Railsサーバーを立ち上げたターミナルにはbyebugプロンプトが表示される。
(byebug)
このプロンプトに対して、Railsコンソールのように状態を確認することが可能となる。
(byebug) @user.name
"Example User"
(byebug) @user.email
"example@railstutorial.org"
(byebug) params[:id]
"1"
フォームHTML
<% 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_confirmaiton , "Confirmation" %>
<%= f.password_field :password_confirmaiton %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
</div>
</div>
form_forメソッド
form_forとは、Railsでフォームタグを作成するメソッド。
同じようなヘルパーとして、form_tagもあるが、基本的にはユーザーの登録や更新はform_forを、検索機能にはform_tagを使用する。
<%= form_for(モデル,[オプション]) do |f| %>
フォームの中身
<% end %>
例えば、次のコードを実行すると
<%= f.label :name %>
<%= f.text_field :name %>
Userモデルはstring型のnameというカラムを持っているので、f.とすることで、Userモデルとlabelやテキストボックス(text_field)を紐づけることができる。
f.labelは、nameのラベルタグを生成し、f.text_fieldは、nameのテキストボックスを生成する。
実際に生成されたHTMLソースは以下のようになる。
<label for="user_name">Name</label>
<input id="user_name" name="user[name]" type="text" />
また、下記を実行すると
<%= f.submit "Create my account", class: "btn btn-primary" %>
f.submitがこのフォームの送信ボタンを生成される。
<form action="/users" class="new_user" id="new_user" method="post">
上記は、/usersに対して、HTTPのPOSTリクエストを送信している。
Strong Parameters
マスアサインメント脆弱性を回避するための機能。
Rails4以降に提供されるようになった。
マスアサインメント機能とは
以下のようにRailsではDBの更新系処理で複数のカラムを一括で指定できること。
@user = User.new(params[:user])
上記は実際には下記とほぼ同じです。
@user = User.new(name: "Foo Bar", email: "foo@invalid",
password: "foobar", password_confirmation: "foobar")
マスアサインメントの脆弱性とは
マスアサインメントはハッシュをそのまま利用します。
例えばユーザーからのリクエストはparams変数にハッシュとして保存されます。
この内容をそのままマスアサインメント機能を利用して設定していた場合に、想定しないカラムを更新されてしまう可能性があります。
例えばUserクラスにname、age、adminの3カラムがあり、adminはユーザーの画面からは更新させない管理者権限だとします。
Userの新規登録処理で、以下のようなコードを書いていた場合
@user = User.new(params[:user])
@user.save
不正リクエストなどによってparamsにadminが含まれていた場合、管理者権限を任意の値に設定されてしまいます。
マスアサインメントの脆弱性対策
この場合、user_paramsという外部メソッドを使うのが慣習になっている。
このメソッドは適切に初期化したハッシュを返し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
このuser_paramsメソッドはUsersコントローラの内部でのみ実行され、Web経由で外部ユーザーに晒される必要はないため、privateキーワードを使って外部から使えないようにしている。
def <Model名>_params
params.require(:<Model名>).permit(:<カラム名1>, :<カラム名2>)
end
ユーザー登録失敗時のテスト
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
assert_no_differenceメソッド
assert_no_differenceのブロックを実行する前後で引数の値(User.count)が変わらない事をテストしている。
ブロック内でpostを使い、登録が失敗するユーザ情報をリクエストしている。
ユーザー登録成功時
redirect_toメソッド
redirect_toメソッドは、他のURLにリダイレクトする際に使用します。
下記は全て同じ挙動をしますが、下にいくほど省略形になっています。
redirect_to "/users/#{@user.id}" ⇨原形
redirect_to user_path(@user.id)
redirect_to user_path(@user) ⇨オブジェクトのデフォルトとして、idを渡す場合@userに省略可能
redirect_to @user ⇨redirect_toのデフォルトとして、userオブジェクトが渡されると、user_pathに変える
データベースリセット
$ rails db:migrate:reset
登録成功のテスト
test "valid signup information" do
get signup_path
assert_difference 'User.count', 1 do
post signup_path, params: { user: { name: "foo",
email: "foo@example.com",
password: "foobar",
password_confirmation: "foobar" } }
end
follow_redirect!
assert_template 'users/show'
end
end
assert_differenceメソッド
assert_no_differenceはブロック内の処理実行後にユーザーの数は変わらない事を確認しましたが、assert_differenceはその逆で今回は登録前後で差異(1件)がある事を確認している。
follow_redirect!メソッド
users_pathにPOSTリクエスト(users#create)を送信した後に、follow_redirect!メソッドをすると、POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッド。
content_tagメソッド
HTMLコードの要素を生成することが可能。
構文
content_tag name, content, option
<div class="alert alert-<%= message_type %>">
<%= message %>
</div>
上記をcontent_tagで書くと、下記のようになります。
<%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
デプロイ
本章で開発したユーザー登録フォームで送信すると、名前やメールアドレス、パスワードといったデータがネットワーク越しに流されてしまう。
このようなネットワークに流れるデータは途中で捕捉できるため、扱いには注意が必要。
これを修正するためにSSLを使います。これはローカルのサーバからネットワークに流れる前に、大事な情報を暗号化する技術です。
SSLの有効化
production.rbという本番環境の設定ファイルの1行を修正するのみです。
具体的には、configに「本番環境ではSSLを使うようにする」という設定をするだけです。
Rails.application.configure do
.
.
# Force all access to the app over SSL, use Strict-Transport-Security,
# and use secure cookies.
config.force_ssl = true ⇦変更
.
.
end
WEBrickをPumaというWebサーバに置き換える
Rails5ではデフォルでPumaが使える。
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count
preload_app!
rackup DefaultRackup
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || 'development'
on_worker_boot do
# Worker specific setup for Rails 4.1+
# See: https://devcenter.heroku.com/articles/
# deploying-rails-applications-with-the-puma-web-server#on-worker-boot
ActiveRecord::Base.establish_connection
end
Procfile作成
Heroku上でPumaのプロセスを走らせる設定ファイルを作成する。
web: bundle exec puma -C config/puma.rb