【Twitterクローン作成】2.ユーザ登録機能

プログラミング




 

 

この記事は【Twitterクローン作成】1.トップページ作成の記事の続きとなります。

 




ユーザ登録機能の実装

ユーザテーブルの作成

  1. 名前
  2. メールアドレス
  3. パスワード

以上、3つのカラムが必要になります。

 

Model

$ rails g model User name:string email:string password_digest:string

password_digestは文字列を暗号化して、元の文字列に戻せなくします。セキュリティ上、passwordを追加する際は password_digest としましょう。

 

マイグレーションファイル

db/migrate/年月_create_users.rb

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email
      t.string :password_digest

      t.timestamps
    end
  end
end

 

正常にカラムが作成できたことを確認したら、マイグレーションを実行します。

$ rails db:migrate

 

バリデーションとhas_secure_passwordを記述

app/models/user.rb

class User < ApplicationRecord
    before_save { self.email.downcase! }
    validates :name, presence: true, length: { maximum: 50 }
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i },
                    uniqueness: { case_sensitive: false }
    has_secure_password 
end

before_save{ self.email.downcase! } はUserのインスタンスレコードを保存する前に実行され、文字列の大文字は全て小文字に変換されます。

emailのバリデーションである、format:… は正規表現といいメールアドレスを正しい表記にするものです。特に覚える必要はありません。

uniquness:… は重複させないためのバリデーションです。その後ろに続く、{ case_sesitive: false }は大文字と小文字を区別しないためのものです。以上の2つのバリデーションを組み合わせることで例えば、Tech@gmail.com と tech@gmail.com は同じメールアドレスと認識されこれらのメールアドレスが同時に作成されることはありません。

has_secure_passwordは以下の機能を提供します。

  • usersテーブルに保存されるパスワードが暗号化される
  • ログインフォームに password と password_confirmation の2つの入力欄を設置することができます。ログインをする際にパスワード入力欄にある パスワード(確認用)の機能です。
  • ログイン機能のメソッド authenticate を追加します

※しかし、has_secure_passwordを定義するだけではpasswordの暗号化機能を使うことはできません。

 

bcryptのコメントを外す

Gemfile

#gem 'bcrypt', '~> 3.1.7'

gem 'bcrypt', '~> 3.1.7'

コメントを外すことで、has_secure_passwordの暗号化機能を使うことができるようになります。

 

ターミナル

$ bundle install

コメントを外した際にも、記述した時と同じく bundle install を実行します。

 

Router

config/routes.rb

Rails.application.routes.draw do
  root to: 'toppages#index'

  get 'signup', to: 'users#new'
  resources :users, only: [:index, :show, :new, :create]
end

prefixを signup のように指定していますが、ここの部分がURLの一部となるために指定することで見映えをよくしています。

※ここで注意していただきたいのは、必ずget signup, to: users#new‘をresources :users, only: [:index, :show, :new, :create]よりも上に記述することです。

上から順にRouterを検索する性質上、下に書いてしまうとsignup_pathを使用する際にエラーが起きてしまいます。

 

Controller

コントローラの作成

ターミナル

$ rails g controller users index show new create

 

index

app/controllers/users_controller.rb index

def index
    @users = User.all.page(params[:page])
end

 

ユーザー一覧は別の場所でも使用するのでパーシャルにします

app/views/users/index.html.erb

<%= render 'users', users: @users %>

 

app/views/users/_users.html.erb

<% if users.any? %>
  <ul class="media-list">
    <% users.each do |user| %>
      <li class="media">
        <div class="media-left">
          <img class="media-object img-rounded" src="<%= gravatar_url(user, { size: 50 }) %>" alt="">
        </div>
        <div class="media-body">
          <div>
            <%= user.name %>
          </div>
          <div>
            <p><%= link_to 'View profile', user_path(user) %></p>
          </div>
        </div>
      </li>
    <% end %>
  </ul>
  <%= paginate users %>
<% end %>

 

app/helpers/users_helper.rb

module UsersHelper
  def gravatar_url(user, options = { size: 80 })
      gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
      size = options[:size]
      "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
  end
end

ここでhelperにgravatar_urlをメソッドとして定義するのは、ViewでHTMLの記述を多くすると表示されるサイトの動きが重くなってしまうためです。

 

show

Controller

app/controllers/users_controller.rb

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

 

View

app/views/users/show.html.erb

<div class="row">
  <aside class="col-xs-4">
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 class="panel-title"><%= @user.name %></h3>
      </div>
      <div class="panel-body">
        <img class="media-object img-rounded img-responsive" src="<%= gravatar_url(@user, { size: 500 }) %>" alt="">
      </div>
    </div>
  </aside>
  <div class="col-xs-8">
    <ul class="nav nav-tabs nav-justified">
      <li><a href="#">Twitter</a></li>
      <li><a href="#">Followings</a></li>
      <li><a href="#">Followers</a></li>
    </ul>
  </div>
</div>

 

new

Controller

app/controllers/users_controller.rb

  def new
    @user = User.new
  end

 

View

app/views/users/new.html.erb

<div class="text-center">
  <h1>Sign up</h1>
</div>

<div class="row">
  <div class="col-md-6 col-md-offset-3">

    <%= form_for(@user) do |f| %>

      <div class="form-group">
        <%= f.label :name, 'Name' %>
        <%= f.text_field :name, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :email, 'Email' %>
        <%= f.email_field :email, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :password, 'Password' %>
        <%= f.password_field :password, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :password_confirmation, 'Confirmation' %>
        <%= f.password_field :password_confirmation, class: 'form-control' %>
      </div>

      <%= f.submit 'Sign up', class: 'btn btn-primary btn-block' %>
    <% end %>
  </div>
</div>

 

create

Controller

  def create
    @user = User.new(user_params)

    if @user.save
      flash[:success] = 'ユーザを登録しました。'
      redirect_to @user
    else
      flash.now[:danger] = 'ユーザの登録に失敗しました。'
      render :new
    end
  end

  private

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

 

View

createに対応するViewはありませんが、エラーメッセージを表示するためにエラーメッセージをrenderします。

app/views/users/new.html.erb 

<div class="text-center">
  <h1>Sign up</h1>
</div>

<div class="row">
  <div class="col-md-6 col-md-offset-3">

    <%= form_for(@user) do |f| %>
      <%= render 'layouts/error_messages', model: f.object %>

      <div class="form-group">
        <%= f.label :name, 'Name' %>
        <%= f.text_field :name, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :email, 'Email' %>
        <%= f.email_field :email, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :password, 'Password' %>
        <%= f.password_field :password, class: 'form-control' %>
      </div>

      <div class="form-group">
        <%= f.label :password_confirmation, 'Confirmation' %>
        <%= f.password_field :password_confirmation, class: 'form-control' %>
      </div>

      <%= f.submit 'Sign up', class: 'btn btn-primary btn-block' %>
    <% end %>
  </div>
</div>

 

トップページにSign upボタンを追加

app/views/toppages/index.html.erb

<div class="center jumbotron">
  <div class="text-center">
    <h1>Twitter</h1>
    <%= link_to 'Sign up now!', signup_path, class: 'btn btn-lg btn-primary' %>
  </div>
</div>

 

ナビバー元々あったSignupの部分をリンクボタンにします

app/views/layouts/_navbar.html.erb 

<header>
  <nav class="navbar navbar-inverse navbar-static-top">
    <div class="container">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="/">Twitter</a>
      </div>
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav navbar-right">
          <li><%= link_to 'Signup', signup_path %></li>
          <li><a href="#">Login</a></li>
        </ul>
      </div>
    </div>
  </nav>
</header>

 

ユーザ登録機能の実装は以上です!

まとめ

ここで何を行ったのかをまとめると、

  • ユーザ作成を可能に
  • 作成したユーザをデータベースに記録

となります。

しかし、単にユーザを作成しただけでは特に意味はありません。

ログイン機能を実装して、データベースに保存したユーザー達を確認できるようにしましょう。

次はログイン機能の実装です。

【Twiiterクローン作成】3.ログイン機能作成