deviseを用いたユーザーまわりの機能の実装

多くのウェブサイトにはユーザーログイン機能がある。 この機能はサインアップしたユーザーに対してアカウントを発行し、そのアカウントで情報を管理することができる機能である。 Railsの場合、ログイン機能は「devise」というGemを使用することで簡単に実装することができる。

devise

deviseは、ログイン機能を簡単に作成することができるGem。ログイン機能をGem無しで実装するのは非常に大変だが、このGemを使うことで比較的簡単に実装することができる。

deviseのインストール

・Gemfileの最終行に以下のように追記 Gemfile

〜
  gem 'devise'  # 最終行に追記してください

・bundle installを実行 ターミナル

  $ pwd
  #現在のディレクトリが~/projects/pictweetであることを確認
  $ bundle install
  #bundle install の実行

本記事では、ログイン機能を実装することで、ログインしたユーザーのみが投稿を行えるようにするところまで説明する。 閲覧に関してはログインにかかわらずできるようにします。

作業内容

(①Gemをインストールしてサーバーを立ち上げ直す) ②コマンドを利用してdeviseの設定ファイルを作成する ③コマンドを利用してUsersモデルを作成する ④未ログイン時にはログインと新規登録ボタンを表示する ⑤コントローラにリダイレクトを設定する

①Gemをインストールしてサーバーを立ち上げ直す ログイン機能を実装する際には「devise」というGemをインストールして使用する。またGemをインストールした後はrails sをし直しサーバーを立ち上げ直す必要がある。これはインストールしたGemが反映されるタイミングがサーバーを立てるときだから。

②コマンドを利用してdeviseの設定ファイルを作成する deviseを使用するためには、Gemのインストールに加えてdevise専用のコマンドを利用して設定ファイルを作成する必要がある。 まず、ターミナルから下記のコマンドを実行する。 ターミナル

  $ rails g devise:install
  # deviseの設定ファイルを作成

これにより、以下のファイルが新規作成される

config/initializers/devise.rb config/locales/devise.en.yml

③コマンドを利用してUserモデルを作成する deviseを利用する際にはアカウントを作成するためのUserモデルを新しく作成する。作成には通常のモデルの作成方法ではなく、deviseのモデルの作成用コマンドを使用するので注意。

rails g deviseコマンド

deviseで、ログイン機能をつける概念のモデルを作成する際に利用するコマンド。 モデルに加えて、ログイン機能のために必要なカラムが追加されるマイグレーションファイルなどが生成される。

次に、下記の指示に従い、ログイン機能を持つuserクラスを作成する。

rails g deviseコマンドでuserモデルを作成してください ターミナル

  $ rails g devise user
  # deviseコマンドでモデルを作成

これにより以下のファイルが新規作成される

app/models/user.rb db/migrate/2014XXXXXXXXXX_devise_create_users.rb test/fixtures/users.yml test/models/user_test.rb

また、config/routes.rbに以下の様な記述が自動的に追記される。

config/routes.rb

Rails.application.routes.draw do
  devise_for :users
#以下略
devise_for

devise_forはログインまわりに必要なルーティングを一気に生成してくれるdeviseのヘルパーメソッド。

例えばdevise_for :usersの記述により、以下のように「ログイン・新規登録」で必要なルーティングが生成される。 また、後ほど登場するcurrent_userやuser_signed_in?などのヘルパーメソッドも利用できるようになる。

ターミナル

                  Prefix Verb   URI Pattern                    Controller#Action
                    root GET    /                              tweets#index
              tweets_new POST   /tweets/new(.:format)          tweets#new
                  tweets POST   /tweets(.:format)              tweets#create
                         GET    /users/:user_id(.:format)      users#show
                         DELETE /tweets/:id(.:format)          tweets#destroy
                         GET    /tweets/:id/edit(.:format)     tweets#edit
                         PATCH  /tweets/:id(.:format)          tweets#update
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
       user_registration POST   /users(.:format)               devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)       devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
                         PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy

・rake db:migrateを実行 ターミナル

  $ rake db:migrate
  # 作成されたマイグレーションファイルを実行

user.rbに「:trackable」の記述がある場合、「:trackable」を削除してください。

app/models/user.rb(修正前)

lass User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
 devise :database_authenticatable, :registerable,
       :recoverable, :rememberable, :trackable, :validatable
end

         ↓

app/models/user.rb(修正後)

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
 devise :database_authenticatable, :registerable,
      :recoverable, :rememberable, :validatable
end

④未ログイン時にはログインと新規登録ボタンを表示する 誰かが未ログイン時にツイート一覧画面を表示した際に、ヘッダーに投稿ボタンの代わりにログインと新規登録ボタンを表示するようにする。

【補足】Rubyタグ

<%=と%>で囲まれた部分をRubyタグと言う。Rubyタグは拡張子が「erb」のビューファイルで使用することができる。Rubyタグを使用して記述されたコードはビューファイルが読み込まれる際にHTMLコードとなって読み込まれる。また-%>のように閉じるタグに-をつける形もある。このようにすることで、余計な改行を取り除くことができる。

※<%= %>と<% %>の違いは、Rubyタグに囲まれた処理の返り値をビューに出力するか、しないかという点。

例1) <%= tweet.text %>

この場合、計算結果を出力したいので<%= %>を使用。

例2) <% if user.name == "たなかたろう" %>

この場合、条件分岐の処理に使いたいだけで、user_signed_in? の返り値は特に出力する必要はないため<% %>を使用。

link_toメソッド

link_toはRubyタグの中で使用することができるメソッド。このメソッドは引数を指定することで様々なリンクを生成する。 通常HTMLコード内でリンクを生成する際にはaタグを使用する。link_toメソッドを使って記述を行うと、HTMLコードが読み込まれる際にaタグに変換されるため、サイトを表示した際にはaタグと同様に、リンクとして表示される。今回の実装で追加するログインや新規登録ボタンもこのメソッドを利用して生成する。

例) sample.html.erb

  <%= link_to '作品一覧へ', '/products' %>
  # link_toメソッドを使ってリンクを生成

また、htmlの要素に指定できるclassなどの属性は、以下の例のように続けてclass: 'sample'などと書くことで付与することができる。

sample.html.erb

  <%= link_to '作品一覧へ', '/products', class: 'sample' %>
  # 作成したaタグに`class="sample"`属性を付与

link_toメソッドがビューファイルとして読み込まれる際には、以下の様なHTMLコードになる。

例) sample.html.erb

  <a class="sample" href="/products">作品一覧へ</a>
user_signed_in?

deviseでログイン機能を実装すると、user_signed_in?というメソッドを使用することができる。 これは、ユーザーがサインインしているかどうか検証するメソッド。サインインしている場合にはtrueを返し、サインインしていない場合にはfalseを返す。

例) sample.html.erb

  <% if user_signed_in? %>
    # ユーザーがサインインしている場合に実行する処理
  <% end %>

user_signed_in?が返す値は最終的にtrueかfalseになるので、上記の例のようにif文または、unless文とともに使用する。

prefix

Prefix(プレフィックス)とは、ルーティングのパスが入る変数のこと。コントローラやビューなどで呼び出すことで、prefixに入っているパスやURL情報を取得できるようになる。Prefixは、routes.rbの各リクエストにオプションとして設定しますが、記述によっては自動的に作成される場合もある。 これを確認するには、ターミナルからrake routesコマンドを実行するといい。 実行例は、以下のような表記。

例)ターミナル

$rake routes
#実行結果

                  Prefix Verb   URI Pattern                    Controller#Action
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
       user_registration POST   /users(.:format)               devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)       devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
                         PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy
                    root GET    /                              tweets#index
              tweets_new GET    /tweets/new(.:format)          tweets#new
                  tweets POST   /tweets(.:format)              tweets#create
                         GET    /users/:id(.:format)           users#show
                         DELETE /tweets/:id(.:format)          tweets#destroy
                         GET    /tweets/:id/edit(.:format)     tweets#edit
                         PATCH  /tweets/:id(.:format)          tweets#update

上記の表示でわかる通り、Prefixが設定されている場合は表の一番左に示される。

deviseを導入した場合、ユーザーの新規登録やログインに関するprefixは予め決まっており、以下のようになる。

例)deviseによって設定されるprefixの一部 | リクエスト | prefix | パス | | ----- | ----- | ----- | | devise/sessions#new | new_user_session | /users/sign_in | | devise/sessions#create | user_session | /users/sign_in | | devise/sessions#destroy | destroy_user_session | /users/sign_out |

実際にprefixを利用する場合は、new_user_session_pathのように、最後に_pathとつける必要がある。

⑤コントローラにリダイレクトを設定する 今のままだと、未ログインユーザーがツイートの一覧画面を表示しても投稿ボタンが表示されなくなっても、未ログインユーザーが直接/tweets/newというパスにアクセスすることで投稿ができてしまう状態である。 そこで、未ログインユーザーが投稿画面など直接アクセスしてきた際にはルートパスに遷移するように設定を行う。

unless文

ここまで条件分岐にはif文を使用してきたが、似た制御構造を持つものとしてunless文がある。unless文は条件式が偽(false)の場合の処理を記述するのに使われる。今回はユーザーがログインしていない場合の条件分岐にunless文を使用してみる。基本的なunless文の書き方は以下の通り。

例)

  unless 条件式
    # 条件式が偽(false)のときに実行する処理
  end

また条件分岐の中が一行で記述できる場合は、if/unless文は以下のように一行で記述することができる。 記述が簡単で見やすくなるので、一行で記述できる場合は一行で記述すると良い。

例)

  puts 'ログインをしてください' unless user_signed_in?
  # 以下と同義

  unless user_signed_in?
    puts 'ログインをしてください'
  end
redirect_toメソッド

Railsでは通常、アクション内の処理が終了すると自動的にアクション名と同名のビューが表示される。 ただしredirect_toメソッドをアクション内で利用すると、そこからさらに別のアクションを実行したり、ビューに遷移させたりできる。

引数にはaction: :indexという形で、キーがaction:バリューが:indexであるハッシュを指定する。このようにバリューにはアクションの名前のシンボル型を利用する。丁寧に書くのであれば{ action: :index }となるが、Railsの内部では特別にハッシュの括弧{}を省略することができる。

今回はindexアクションを実行させるために、redirect_to action: :indexとする。

例)app/controllers/products_controller.rb

  class ProductsController < ApplicationController

    def index
      @products = Product.page(params[:page]).per(5).order("created_at DESC")
    end

    private
    def move_to_index
      redirect_to action: :index
      # indexアクションを強制的に実行する
    end
  end
before_action

Railsではコントローラでbefore_action :メソッド名と記述することで、コントローラのアクションが実行される前にそのメソッドを実行することができる。 また、オプションonlyやexceptを使うことにより、before_actionを実行することをアクションごとに制限をかけることができる。

例)indexアクション以外でbefore_actionを実行したい場合 app/controllers/products_controller.rb ``` class ProductsController < ApplicationController

before_action :hoge, except: :index
# indexアクション以外が実行される前にhogeが実行される。

def index
  @products = Product.page(params[:page]).per(5).order("created_at DESC")
end

private
def hoge
end

end

以上でログイン機能の実装は完了している。
ただ、あくまで最低限のものなので、ビューや細かい登録内容の変更を行う必要がある。