0
0

More than 3 years have passed since last update.

Railsチュートリアル 第7章

Posted at

デバッグ

debugメソッド

debugメソッドとparams変数を使い、各ページにデバッグ情報が表示されるようにする。

application.html.erb
<!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コントローラーに差し込む

users_controller.rb
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

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_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を使うようにする」という設定をするだけです。

config/enviroments/production.rb
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が使える。

config/puma.rb
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のプロセスを走らせる設定ファイルを作成する。

./Procfile
web: bundle exec puma -C config/puma.rb
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