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