Rails Tutorial 5 Sign up
Rails Tutorial 5 Sign up
Chapter 7/ Sign up | Ruby on Rails Tutorial (Rails 5) | Softcover.io に沿って、今回はユーザー登録機能を追加していく。
HTMLフォームを使用して登録情報をWebアプリケーションに送信し、ユーザーを新規作成して情報をデータベースに保存する。
Showing users
ユーザーの名前とプロファイル写真を表示するためのページを作成する。 最初にトピックブランチを作成する。
$ git checkout -b sign-up
アプリケーションのデータベースから取り出した情報を使用して各プロファイルの表示をカスタマイズするという、初の真に動的なページ作成となる。 準備としてデバッグ情報を追加する。
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>
if Rails.env.development?
を入れることで、開発環境以外ではこのデバッグ情報は表示されない。
A Users resource
6章で作成したユーザ情報を、RESTアーキテクチャの慣習にしたがって表示する。
すなわち 、/users/1
にアクセスしてユーザ情報を表示できるようにする。
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
表示のためにビューファイルを作成する。
app/views/users/show.html.erb
<%= @user.name %>, <%= @user.email %>
ビューでは埋め込みRubyを使用し、ユーザ名とメールアドレスを表示する。ここで参照している@user
変数を Users コントローラ show アクション内に定義する。
app/controllers/users_controller.rb
class UsersController < ApplicationController def show @user = User.find(params[:id]) end def new end end
これでusers/1
にアクセスすることでユーザ情報を表示することができるようになる。
Debugger
byebug gem を使ったデバッグ手法についての解説。
debugger
メソッドを差しこむだけでブレークポイントをはることができる。
app/controllers/users_controller.rb
class UsersController < ApplicationController def show @user = User.find(params[:id]) debugger end def new end end
これでusers/1
にアクセスすると、プロンプトが表示され、変数の中身を確認したりできる。
デバッグが終わったら該当行を削除しておくだけ。
A Gravatar image and a sidebar
A Gravatar is a Globally Recognized Avatar. You upload it and create your profile just once, and then when you participate in any Gravatar-enabled site, your Gravatar image will automatically follow you there.
ということでユーザのプロフィール写真をGravatarを使って表示する。
Let's Chat でデフォルトで表示されているユーザアイコン「なんじゃこりゃ」と思っていたがこれだったのか。デファクトを知らないのが恥ずかしい。
gravatar_forをUsersコントローラに関連付けられているヘルパーファイルに置く。
app/helpers/users_helper.rb
を以下のように書き換える。
module UsersHelper # Returns the Gravatar for the given user. def gravatar_for(user) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end end
上記を使用してGravatarの画像を利用できるように表示ビューを書き換える。
app/views/users/show.html.erb
<% provide(:title, @user.name) %> <h1> <%= gravatar_for @user %> <%= @user.name %> </h1>
これでユーザ情報のメールアドレスに紐付いた Gravatar 画像が表示されるようになる。
ここからさらにユーザーのサイドバーを作っていく。
app/views/users/show.html.erb
を以下のように書き換える。
<% provide(:title, @user.name) %> <div class="row"> <aside class="col-md-4"> <section class="user_info"> <h1> <%= gravatar_for @user %> <%= @user.name %> </h1> </section> </aside> </div>
app/assets/stylesheets/custom.scss
に以下を追記する。
. . . /* sidebar */ aside { section.user_info { margin-top: 20px; } section { padding: 10px 0; margin-top: 20px; &:first-child { border: 0; padding-top: 0; } span { display: block; margin-bottom: 3px; line-height: 1; } h1 { font-size: 1.4em; text-align: left; letter-spacing: -1px; margin-bottom: 3px; margin-top: 0px; } } } .gravatar { float: left; margin-right: 10px; } .gravatar_edit { margin-top: 15px; }
Signup form
ユーザ情報を表示することができるようになったので、今度はユーザ登録フォームを作成していく。
ユーザー登録ページで重要な点は、ユーザー登録に欠かせない情報を入力するためのformです。Railsでform_forヘルパーメソッドを使用します。このメソッドはActive Recordのオブジェクトを取り込み、そのオブジェクトの属性を使ってフォームを構築します。
なんと便利な。
@user 変数を new
アクションに追加する。
app/controllers/users_controller.rb
class UsersController < ApplicationController def show @user = User.find(params[:id]) end def new @user = User.new end 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>
また、app/assets/stylesheets/custom.scss
に以下を追記する。
. . . /* forms */ input, textarea, select, .uneditable-input { border: 1px solid #bbb; width: 100%; margin-bottom: 15px; @include box_sizing; } input { height: auto !important; }
Unsuccessful signups
無効なデータ送信に対してエラーの一覧を表示する。
間違った送信をして何も表示されなければユーザが困惑する原因となる。
マスアサインメント脆弱性対策に Strong Parameters を使うことなどが解説されている。
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
というパーシャルをrenderする。
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 %>
エラーメッセージにスタイルを与えるための id や、単数形、複数形を適切に変更してくれるヘルパーpluralize
などが盛り込まれている。
エラーメッセージにスタイルを与えるためのCSS id error_explanation
を追記する。
app/assets/stylesheets/custom.scss
#error_explanation { color: red; ul { color: red; margin: 0 0 30px 0; } } .field_with_errors { @extend .has-error; .form-control { color: $state-danger-text; } }
これでエラーが表示されるようになる。
A test for invalid submission
Rails ではフォーム用のテストを書いて自動化が可能。
まずユーザ新規登録の統合テストを作成する。
$ rails generate integration_test users_signup Running via Spring preloader in process 2839 invoke test_unit create test/integration/users_signup_test.rb
test/integration/users_signup_test.rb
が生成されるので以下のように書き換える。
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' assert_select 'div#error_explanation' assert_select 'div.field_with_errors' end end
以下を実行してあるので上記を保存した時点で自動テストが走り、成功する。
$ bundle exec guard
上記テストでは、無効なユーザ登録リクエスト前後で登録されているユーザ数が変化していないことを確かめている。
更に、セレクタを使って特定のHTMLタグが存在するかどうかをテストしている。(エラーメッセージが意図通り表示されていることのテスト)
演習で名前付きルートと、デフォルトのRESTfulなルーティングについてもう少しあるが、以降の章を見る限り上記までにとどめてそれ以降は元に戻しておくのが良さそう。
Successful signups
次はユーザを細ンできるようにする。
- 正常なリクエストならユーザを保存し、ユーザ情報の表示とウェルカムメッセージの表示
- そうでなければ先ほど作成したエラーメッセージの表示
となるよう実装していくとのこと。
app/controllers/users_controller.rb
に以下を追記する。
redirect_to @user
するとredirect_to @user
というコードから、Rails がuser_url(@user)
というコードを推察してくれる。なんと便利な。
The flash
ユーザ登録完了時に一度だけ表示し、次回以降表示しない、と言った機能を実現するためにRailsでは flash という変数を使えばいいとのこと。 これまた便利。
app/controllers/users_controller.rb
に以下を追記する。
if @user.save flash[:success] = "Welcome to the Sample App!" redirect_to @user else
app/views/layouts/application.html.erb
にいかを追記することで flash の内容を表示する。
<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>
これだけでユーザ登録後初回のみウェルカムメッセージを表示することができる、と。
The first signup
まず以下を実行して DB をリセットする。
$ rails db:migrate:reset Dropped database 'db/development.sqlite3' Dropped database 'db/test.sqlite3' Created database 'db/development.sqlite3' Created database 'db/test.sqlite3' == 20161023011602 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0015s == 20161023011602 CreateUsers: migrated (0.0021s) ============================= == 20161106170056 AddIndexToUsersEmail: migrating ============================= -- add_index(:users, :email, {:unique=>true}) -> 0.0015s == 20161106170056 AddIndexToUsersEmail: migrated (0.0017s) ==================== == 20161109220735 AddPasswordToUsers: migrating =============================== -- add_column(:users, :password_digest, :string) -> 0.0010s == 20161109220735 AddPasswordToUsers: migrated (0.0012s) ======================
サーバを起動してユーザ登録を行ってみる。
ユーザ表示ページにウェルカムメッセージが表示されていることが確認できた。
リロードしても次回以降はウェルカムメッセージが表示されないことも確認。
A test for valid submission
ここでユーザ登録成功パターンのテストを追加する。
test/integration/users_signup_test.rb
に以下を追記する。
test "valid signup information" do get signup_path assert_difference 'User.count', 1 do post users_path, params: { user: { name: "Example User", email: "user@example.com", password: "password", password_confirmation: "password" } } end follow_redirect! assert_template 'users/show' assert_not flash.empty? end
- 登録後に、登録前よりユーザ数が1増加していること
- Userのshowアクションが正しく動作していること
- show.html.erbビューが正しく動作していること
- flash が空でないこと
が確認できる。
00:16:08 - INFO - Running: test/integration/site_layout_test.rb Running via Spring preloader in process 5607 Started with run options --seed 46326 19/19: [============================================================] 100% Time: 00:00:01, Time: 00:00:01 Finished in 1.03830s 19 tests, 40 assertions, 0 failures, 0 errors, 0 skips
実行結果は上記の通り、すべて成功。
Professional-grade deployment
ここでデプロイを進めるが、今回からメールアドレスやらパスワードやらのやりとりが入るので SSL 対応を入れ込む、とのこと。
config/environments/production.rb
が本番環境設定なので、こちらに以下の行のコメントアウトを解除する。
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true
Heroku のサブドメインで動作させているうちは、なんとこれだけで SSL 対応完了だ。便利
独自ドメインで SSL 対応させたければ以下を参考に作業すれば良いらしい。
Heroku SSL | Heroku Dev Center
また、Webサーバも本番環境向けに変更する。 - デフォルトでは WEBrick - 構築が簡単 - 反面、多数のリクエストを捌くのは苦手 - 本番環境ではPuma に変更する - 多数のリクエストを捌くことができる - しかも Rails 5 からは 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
を作成し、以下を記載する。
web: bundle exec puma -C config/puma.rb
Heroku にデプロイすれば SSL で動作するようになる。
その他
Rebuild 聞きながらやってたらそっちが面白くてすぐに手が止まる。
Rebuild: 169: Your Blog Can Be Generated By Neural Networks (omo)
- 本読んだ感想として、「新人に読ませたい」じゃなくてあなたはどう思ったの
- 「達人プログラマー」は精神論的な部分と技術的な部分の2パート構成
- 前者はいいが、後者が陳腐化しつつある
みたいな感じで対談が進んでいく。
「達人プログラマーをディスる」との宣言で始まったが根底に流れるのはこの本への愛だ(と勝手に解釈した)。
精神論がキリッとしているのにその実例がしょぼいというのは良くない。ちゃんと書き直さないといけない。誰かが。
たしかに、せっかく新装版出たのにな、というところではありますが。
精神論的な部分はちょっとまとめて社内でも共有してみようかな。