0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Railsチュートリアル7章学習まとめ

Posted at

ユーザー登録

さていよいよ中盤の7章 ユーザー登録です。
この章はなかなか苦い思い出がある章で手順通りに作っているはずなのに
なぜかrails testを実行してもなぜかエラーがでる!!!と時間を要した章です。
チュートリアルやっている方は何かしらどこかでつまづくと思いますが
私も5カ所くらい大きくコケました。

というわけで、改めて本章を理解して、どんなエラーがきても対処できるようになりたいと思います。
では本編へ行きます。

本編
それでは本編を見ていきます。前回までにUserモデルを作成したのでユーザー登録機能を作りましょう
というのが7章のメインです。

ユーザーを表示する

ここでは、まずユーザーの名前とプロフィール写真を表示するためのページを作成とのこと。

ここでの完成モックアップを見ていると完成から逆算して分解化して構造化していかなければいけないと感じました。逆算思考が大事。

デバッグとRails環境

app/views/layouts/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>

本編はじめての動的ページのためdebugメソッドと``params`メソッドを挿入するようです。
これでプロフィールに各ページの情報が表示されるとのこと。

if Rails.env.development?
さらにこの記述をしておくと本番環境は避け、開発環境のみにdebug情報を表示する。
また開発が終わったら削除するべきコードのようです。

app/assets/stylesheets/custom.scss
@import "bootstrap-sprockets";
@import "bootstrap";
.
.
.
/* miscellaneous */

.debug_dump {
  clear: both;
  float: left;
  width: 100%;
  margin-top: 45px;
}

デバッグ情報をみやすいように整えます。

#<ActionController::Parameters {"controller"=>"static_pages", "action"=>"home"}
permitted: false>

現時点ではこちらが表示されています。

7.1.2Usersリソース

ユーザープロフィールを表示するにや先にユーザーデータの登録が必要とのこと。

config/routes.rb
Rails.application.routes.draw do
  root "static_pages#home"
  get  "/help",    to: "static_pages#help"
  get  "/about",   to: "static_pages#about"
  get  "/contact", to: "static_pages#contact"
  get  "/signup",  to: "users#new"
  resources :users
end

resources :usersを追加
RESTfulなUsersリソースで必要となるすべてのアクションが利用できるようになった。

スクリーンショット 2024-04-23 12.18.54.png
この辺りがRailsすげーって思うところですね。

route設定はしたが現在ページがない状態なので/users/1と入力してもユーザー情報はでてきません。

そんなわけで先ほどの画像をみてshowアクションである特定のユーザーを表示するページを
作成する必要があります。

app/views/users/show.html.erb
<%= @user.name %>, <%= @user.email %>

仮ページです、@user変数から名前とEメールを取得して表示する感じですね。

ただ現状のページだと何も設定してないので

app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
  end
end

user_controllerで@userの情報を探してidを取得する
取得したIDを特定ユーザーのページで表示するって感じですかね

なにはともあれこれで最低限の表示はできるようになりました。

route設定→ページビュー作成→モデル設定って感じですね。

7.1.3debuggerメソッド

debugメソッドで読み取った情報がアプリケーションで何が起きているのかをより直接的に取り出す方法
これエラー出た時にchatGPTにも提案されたのですが、チュートリアルの構築優先していたので
あんまり理解していなかったところです。

改めて理解していきます。

app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
    debugger
  end

  def new
  end
end

このようにdebuggerをねじ込むんですね。

そうするとターミナルのrgbgが立ち上がってるとのこと。
そしてrails consoleのようにコマンド入力ができるわけですね。

トラブルが起こってそうなコードで差し込むのがコツらしいので2週目構築の時に改めて
チャレンジしたいと思います。

7.2ユーザー登録フォーム

続いてユーザー登録フォーム作成です。

7.2.1form_withを使用する

ユーザー登録ページで重要な要素は、ユーザー登録に不可欠な情報を入力するフォームです。
Railsではform_withヘルパーメソッドを使い
Active Recordのオブジェクトを取り込み、そのオブジェクトの属性を使ってフォームを構築するとのこと

というわけで見ていきます。

form_withの引数で必要となるUserオブジェクトを作成する

app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end
end

ユーザー登録ページなのでusers_controllerにnewアクションを定義下って感じですね。
すでにrouteにはusers設定しており、userアクションまわりは使用できているので
次のコントローラーに設定したという感じで解釈してます。

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_with(model: @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>

そしてこれがViewページであるnewです。
パッと見た感じこのフォームに入力した内容をform_withで実行ということでしょうか。

app/assets/stylesheets/custom.scss
@import "bootstrap-sprockets";
@import "bootstrap";

/* mixins, variables, etc. */

$gray-medium-light: #eaeaea;

@mixin box_sizing {
  -moz-box-sizing:    border-box;
  -webkit-box-sizing: border-box;
  box-sizing:         border-box;
}
.
.
.
/* forms */

input, textarea {
  border: 1px solid #bbb;
  width: 100%;
  margin-bottom: 15px;
  @include box_sizing;
}

input {
  height: auto !important;
}

custom.sccsで見た目を整えます。

7.3ユーザー登録失敗

無効なデータ送信を受け付けるユーザー登録フォームを作成し、
ユーザー登録フォームを更新してエラーの一覧も考える。

無効なデータが表示された際エラーメッセージとフォームがどうなっているのかを検討する必要があるってことですね。

7.3.1正しいフォーム

resources :usersroutes.rbファイルに追加すると自動的にRailsアプリケーションがRESTful URIに応答するようになった。
/usersへのPOSTリクエストはcreateアクションに送られます。

ここでは、createアクションでフォーム送信を受け取り、User.newを使って新しいユーザーオブジェクトを作成し、ユーザーを保存(または保存に失敗)し、再送信できるようにユーザー登録ページを表示するという方法で機能を実装するとのこと。

つまり先ほど作ったフォームページに送信実行機能を実装するということですね。

app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(params[:user])    # 実装は終わっていないことに注意!
    if @user.save
      # 保存の成功をここで扱う。
    else
      render 'new', status: :unprocessable_entity
    end
  end
end

ここでは@user.newでユーザー情報を作成し
成功したら保存

失敗したらstatus: :unprocessable_entity レスポンスを送るって感じでしょうか。

7.3.2Strong Parameters

@user = User.new(params[:user]) # 実装は終わっていないことに注意!
これは、ユーザーが送信したデータをまるごとUser.newに渡していることになる為
admin属性とかもあったら管理権限も渡してしまう為危険である。

Rails 4.0ではStrong Parametersというテクニックをコントローラ層で使うことが推奨されています。Strong Parametersでは、必須パラメータと許可済みパラメータを指定できます。さらに、上のようにparamsハッシュを丸ごと渡すとエラーが発生するので、Railsはデフォルトでマスアサインメントの脆弱性から守られるようになりました。
とのこと、params渡してもセキュリティは安心ですね。

paramsハッシュでは:user属性を必須とし、名前、メールアドレス、パスワード、パスワードの確認の属性をそれぞれ許可し、それ以外は許可しないようにしたいと思います。これを行うには、次のように記述します。

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

app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      # 保存の成功をここで扱う。
    else
      render 'new', status: :unprocessable_entity
    end
  end

  private

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

ここでprivateを使うことで外部から使えないようになるんですね。
初めて意味を実感しました。

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_with(model: @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>
```app/views/shared/_error_messages.html.erb
ここでは、'shared/error_messages'というパーシャルをrender(描画)している点に注目してください。Rails全般の慣習として、複数のビューで使われるパーシャルは専用のディレクトリ「shared」によく置かれるtのこと。

<% if @user.errors.any? %>

The form contains <%= pluralize(@user.errors.count, "error") %>.
    <% @user.errors.full_messages.each do |msg| %>
  • <%= msg %>
  • <% end %>
<% end %> ```

続いてパーシャルを作成します。

そしてエラー検証用コードが紹介されてました。

・user.errors.count: ユーザーの検証エラーの数を取得します。このメソッドを使用することで、特定のユーザーに関連する検証エラーの数を取得できます。
・user.errors.empty?: ユーザーの検証エラーが空かどうかを確認します。エラーがない場合はtrueを返し、エラーがある場合はfalseを返します。
・user.errors.any?: ユーザーの検証エラーがあるかどうかを確認します。エラーがある場合はtrueを返し、エラーがない場合はfalseを返します。
・helper.pluralize(1, "error"): ヘルパーメソッドpluralizeを使用して、単数形の単語を複数形に変換します。第一引数には数を、第二引数には単語を指定します。これにより、1つのエラーに対しては"1 error"、複数のエラーに対しては"5 errors"のように、エラーメッセージを適切に表示することができます。

そして使い分け用途です。
user.errors.count: 特定のユーザーの検証エラーの数を調べたい場合に使用します。たとえば、フォームの送信後にエラーメッセージを表示する際に、エラーの数をカウントして表示するために利用されます。
user.errors.empty?およびuser.errors.any?: ユーザーに関連する検証エラーがあるかどうかを確認するために使用します。これにより、フォームの送信後にエラーメッセージを表示する際に、エラーがあるかどうかを確認し、適切な処理を行うことができます。
helper.pluralize(1, "error"): エラーメッセージを表示する際に、単数形と複数形の両方を適切に扱うために使用されます。これにより、エラーが1つの場合と複数の場合の両方に対応したメッセージを生成することができます。

ユーザー登録の成功

フォームが有効な場合に新規ユーザーを実際のデータベースに保存する。
現時点で基本的には有効な情報で送信するとエラーが発生
これは、Railsはデフォルトのアクションに対応するビューを表示しようとした際に、
createアクションに対応するビューのテンプレートがないことが原因とのこと。

というわけでcreateのアクションが必要みたいです。

app/controllers/users_controller.rb
#保存とリダイレクトを行う、userのcreateアクション
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render 'new', status: :unprocessable_entity
    end
  end

  private

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

redirect_to @user=redirect_to user_url(@user)とのこと。
つまりリダイレクトしたらそのままユーザーページにいくみたいです。

7.4.2flash

ユーザー登録フォームが実際に動くようになりました。これでブラウザから正しいユーザー情報を登録できるようになったが、その前にWebアプリケーションに常識的に備わっている機能を追加していく。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new', status: :unprocessable_entity
    end
  end

  private

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

Railsでこういった情報を表示するには、flashという特殊な変数を使う。
Railsの一般的な慣習に沿って、:successというキーには成功時のメッセージを代入。

つまりuser.saveが成功したら表示させる機能のようです。

#コンソールでflashハッシュをイテレート(each do ... end)する
rails console
>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", danger: "It failed."}
>> flash.each do |key, value|
?>   puts "#{key}"
?>   puts "#{value}"
>> end
success
It worked!
danger
It failed.

イテレートってなんだ?と思い調べてみました。

flash = { success: "It worked!", danger: "It failed." }: flashという名前のハッシュを定義し、それぞれのキーに成功と失敗のメッセージが格納されています。
flash.each do |key, value|: flashハッシュの各要素に対して繰り返し処理を行うためのeachメソッドが呼び出されています。|key, value|はブロックパラメーターであり、各要素のキーと値を受け取ります。

puts"#{key}"がsuccessにくる箇所
puts"#{value}"がdangerに来る部分のようですね。
これでuser.saveしたときの結果によって条件が分岐されるようです。

app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  .
  .
  .
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
    .
    .
    .
  </body>
</html>

flashを埋め込みました。

7.5.1本番環境でのTLS

TLSは、ローカルのサーバーからネットワークにデータを送信する前に大事な情報を暗号化する技術。
Railsでは1行変更するだけで適用できる優れもの。

config/environments/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

config.force_ssl = trueこれをtrue設定にしていればいいだけです。

7.5.2本番環境用のWebサーバー

config/puma.rb
# Pumaの設定ファイル
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count
port        ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
workers ENV.fetch("WEB_CONCURRENCY") { 4 }
preload_app!
plugin :tmp_restart


こちらのソースコードへ置き換え

Rendersetting command`bundle exec puma -C config/puma.rb`へ書き換え

#Gemfile
.
.
.
group :production do
gem "pg", "1.3.5"
end

Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります

gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Gemfileを追加

 $ bundle config set --local without 'production'
 $ bundle install

bundle config setという見慣れないコードがあったので調べました。

具体的には、bundle config setは、Bundleの構成を設定するためのコマンドです。--localオプションは、プロジェクトのディレクトリ内にのみ適用されるローカルな構成を設定することを意味します。そして、without 'production'は、プロジェクトの依存関係のインストール時に、productionグループに含まれるgemをインストールしないようにするための設定です。

つまり、このコマンドは、プロジェクト内の依存関係のインストール時に、productionグループに含まれるgemをインストールしないようにするためのローカルなBundleの設定を行っています。

config/database.yml
# SQLite. Versions 3.8.0 and up are supported.
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem "sqlite3"
#
default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  adapter: postgresql
  encoding: unicode
  url: <%= ENV['DATABASE_URL'] %>

データベース設定ファイルconfig/database.ymlのproductionセクションを更新

以下割愛

本章のまとめ(引用)

・debugメソッドを使うことで、役立つデバッグ情報を表示できる
・Sassのmixin機能を使うと、CSSのルールをまとめたり他の場所で再利用できるようになる
・Railsには標準で3つの環境が備わっており、それぞれdevelopment(開発)環境、test(テスト)環境、production(本番)環境と呼ぶ
・標準的なRESTful URLを通して、ユーザー情報をリソースとして扱えるようになった
・Gravatarを使うと、ユーザーのプロフィール画像を簡単に表示できるようになる
・form_withヘルパーは、Active Recordのオブジェクトに対応したフォームを生成する
・ユーザー登録に失敗した場合はnewビューを再びレンダリングするようにした。その際、Active Recordが自動的に検知したエラーメッセージを表示できるようにした
・flash変数を使うと、一時的なメッセージを表示できるようになる
・ユーザー登録に成功すると、「データベース上にユーザーを追加」「プロフィールページにリダイレクト」「ウェルカムメッセージの表示」の順で処理が進む
・統合テストを使うことで送信フォームの振る舞いを検証したり、バグの発生を検知したりできる

感想

debugメソッドの入れ方はすごい便利だと感じた。またtest環境の概念等使い分けの部分でイメージが湧いた。
user createアクションなどuserの追加により広がる拡張性とセキュリティの危険性を認識
仕様設計の上で注意していかなければいけないと感じた。

引き続き習得していきたい。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?