これから、PicTweetにログイン機能を追加していく。
実装にはDeviseというGemを使用する。
このGemは広く使われているもので、オリジナルでアプリケーションを作る際に活用できまるため理解しながら進めていきたい。
作業の準備する
必要なファイルを準備する
機能の追加を学習を始める前に、必要なファイルの準備を行う。
リンク先のファイルをダウンロードして「arrow_top.png」を下記のディレクトリに配置する。arrow_top.png
app/asset/simages/arrow_top.png
ルートパスを追加する
ツイート一覧のページを、アプリケーションのトップページに設定する。
ルートパス
ウェブサイトへアクセスする際などに使用するURLには、ホスト名とパスの2つの部分が存在する。「
www.yahoo.co.jp」や「pictweet.me」のように、パスを付けないホスト名だけのURLのことをルートパスという。
現在のPicTweetは「/tweets」にアクセスするとツイートの一覧画面が表示される。
今回はルーティングを追記してルートパスにアクセスした際にツイート一覧画面が表示されるようにしたい。
そのためには、routes.rbにルートパス専用のルーティングを設定する必要がある。
以下の例のように、root コントローラ名#アクション名
と書く。
【例】
config/routes.rb
Rails.application.routes.draw do root 'コントローラ名#アクション名' end
ルートパスを設定する
routes.rbを編集してルートパスの設定を行う。
config/routes.rb
Rails.application.routes.draw do root 'tweets#index' #ルートパスの指定 get 'tweets' => 'tweets#index' #ツイート一覧画面 get 'tweets/new' => 'tweets#new' #ツイート投稿画面 post 'tweets' => 'tweets#create' #ツイート投稿機能 end
ルートパスにアクセスする
トップページにアクセスして、ルートパスががきちんと反映されているか確認する。
こちらにアクセスすると、「トップページアドレス/tweets」にアクセスした画面と同じ画面になっているのが確認できる。
これは、ルートパスによってtweets_controllerのindexアクションが動いているということである。
ログイン機能とは
Twitterなどのウェブサイトにはログイン機能がある。
この機能はサインアップしたユーザーに対してアカウントを発行し、そのアカウントで情報を管理することができる機能である。
Railsの場合、ログイン機能は「devise」というGemを使用することで簡単に実装することができる。
以下の動画のように、ヘッダーにログイン/新規登録のボタンを置き、実際にログインするとその部分が登録したユーザー名と投稿するボタンになる。
今回はログイン機能を実装することで、ログインしたユーザーのみがツイートの投稿を行えるようにする。
ツイートの閲覧に関してはログインにかかわらずできるようにする。
ツイート閲覧時
ツイート投稿時
作業内容
1.Gemをインストールしてサーバーを立ち上げ直す
2.コマンドを利用してdeviseの設定ファイルを作成する
3.コマンドを利用してUserモデルを作成する
4.未ログイン時にはログインと新規登録ボタンを表示する
5.コントローラにリダイレクトを設定する
1.Gemをインストールしてサーバーを立ち上げ直す
ログイン機能を実装する際には「devise」というGemをインストールして使用する。
またGemをインストールした後はrails s
をし直しサーバーを立ち上げ直す必要がある。
これはインストールしたGemが反映されるタイミングがサーバーを立てるときだからである。
devise
deviseは、ログイン機能を簡単に作成することができるGemである。
ログイン機能をGem無しで実装するのは非常に大変である。
しかし、このGemを使うことで比較的簡単に実装することができる。
以下に従い、deviseをインストール
- ①Gemfileの最後の行に下記の記述を追記
Gemfile
gem 'devise'
- ②bundle installを実行
ターミナル
$ pwd #現在のディレクトリが/home/ec2-user/environment/pictweetであることを確認 $ bundle install #bundle install の実行
- ③ローカルサーバーを再起動
ターミナル
$ rails s
これでインストールしたGemが反映された。
この後ターミナルで作業をするため1回controlキー+cでサーバーをシャットダウンする。
2.コマンドを利用してdeviseの設定ファイルを作成する
deviseを使用するためには、Gemのインストールに加えてdevise専用のコマンドを利用して設定ファイルを作成する必要がある。
ターミナルから下記のコマンドを実行する。
$ rails g devise:install # deviseの設定ファイルを作成
ターミナルに上のように表示されていれば成功。
新規作成されるファイル
- config/initializers/devise.rb
- config/locales/devise.en.yml
3.コマンドを利用してUserモデルを作成する
deviseを利用する際にはアカウントを作成するためのUserモデルを新しく作成する。
作成には通常のモデルの作成方法ではなく、deviseのモデルの作成用コマンドを使用する。
rails g devise コマンド
deviseで、ログイン機能をつける概念のモデルを作成する際に利用するコマンド。
モデルに加えて、ログイン機能のために必要なカラムが追加されるマイグレーションファイルなどが生成される。
下記の指示に従い、ログイン機能を持つuserクラスを作成する
1.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
2.マイグレートを実行する
ターミナル
$ rails db:migrate # 作成されたマイグレーションファイルを実行
phpMyAdminにusersテーブルが作成されているか確認する
phpMyAdminをリロードし、以下のようにusersテーブルが作成されていれば成功。
マイグレーションファイルを実行してもusersテーブルが表示されない場合は、ブラウザをリロードしてみる。
phpMyAdminの起動が出来ない方向け
usersテーブルが作成されているかどうか、コンソールから確認してみる。
以下のコマンドを実行する
$ rails c # コンソールの起動 pry(main)>ActiveRecord::Base.connection.tables => ["schema_migrations", "tweets", "users"] # 上記コマンドの返り値に、"users"が含まれていれば、usersテーブルの作成に成功しています pry(main)>exit # コンソールの終了
user.rbを開き、5行目の「:trackable」を削除する
:trackable
のみを削除。
バージョンによる差異
使用している環境によって、つくられるファイルの内容が異なる。
もともと「;trackable」の記述がない場合は作業不要。
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, :trackable, :validatable end
class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable end
4.未ログイン時にはログインと新規登録ボタンを表示する
誰かが未ログイン時にツイート一覧画面を表示した際に、ヘッダーに投稿ボタンの代わりにログインと新規登録ボタンを表示するようにする。
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 'ツイート一覧へ', '/tweets' %> # link_toメソッドを使ってリンクを生成
また、htmlの要素に指定できるclass
などの属性は、以下の例のように続けてclass: 'sample'
などと書くことで付与することができる。
sample.html.erb
<%= link_to 'ツイート一覧へ', '/tweets', class: 'sample' %> # 作成したaタグに`class="sample"`属性を付与
link_toメソッドがビューファイルとして読み込まれる際には、以下の様なHTMLコードになる。
sample.html.erb
<a class="sample" href="/tweets">ツイート一覧へ</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コマンドの実行 $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
とつける必要があります。
ここまでの説明を踏まえて、以下の問題に挑戦してみる。
ヘッダー部分のhtmlは、全てのビューの共通のテンプレートであるapplication.html.erbに書きます。
問題1:ログインしている時とそうでない時で表示するビューを変更しましょう
作業ファイル:app/views/layouts/application.html.erb
ヒント:下記の例をもとに、user_signed_in?メソッドを利用してください。
【例】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<header class="header">
<div class="header__bar row">
<h1 class="grid-6"><a href="/">PicTweet</a></h1>
<% if ユーザーがサインインしている場合 %>
<div class="user_nav grid-6">
<%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
<a class="post" href="/tweets/new">投稿する</a>
</div>
<% サインインしていない場合 %>
<div class="grid-6">
<%= link_to "ログイン", new_user_session_path, class: 'post' %>
<%= link_to "新規登録", new_user_registration_path, class: 'post' %>
</div>
<% 閉じタグ %>
</div>
</header>
|
トップページを開きログインしている時としていない時でヘッダー部分の表示が変わることを確認する
ログインしていない時
「ログイン」「新規登録」ボタンの2つが表示されていることが確認できる。
ログインしている時
「新規登録」ボタンより、ユーザーの新規登録(作成)を行う。
無事、登録ができれば、自動的にログインされ、以下のように「ログアウト」「投稿する」ボタンの2つが表示されていることが確認できる。
5. コントローラにリダイレクトを設定する
上記の作業で未ログインユーザーがツイートの一覧画面を表示しても投稿ボタンが表示されなくなりった。
しかし、未ログインユーザーであっても直接/tweets/new
というパスにアクセスすることで投稿ができてしまうという問題が残る。
トップページを開き下記のアドレスを追記しする
トップページアドレス/tweets/new
ログインしていない状態でも「トップページアドレス/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/tweets_controller.rb
class TweetsController < ApplicationController def index @tweets = Tweet.all 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/tweets_controller.rb
class TweetsController < ApplicationController before_action :hoge, except: :index # indexアクション以外が実行される前にhogeが実行される。 def index @tweets = Tweet.all end private def hoge end end
問題2:ログインしていないときに新規投稿しようとすると、indexアクションにリダイレクトされるようにしましょう
作業ファイル:app/controllers/tweets_controller.rb
ヒント:before_actionは全てのアクションの実行前に強制的に実行されるメソッドを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class TweetsController < ApplicationController
before_action :move_to_index, indexアクションの場合は実行しない条件をつける
def index
@tweets = Tweet.all
end
def new
end
def create
Tweet.create(tweet_params)
end
private
def tweet_params
params.permit(:name, :image, :text)
end
def move_to_index
# ログインしていなかった場合、「index」アクションを実行する
end
end
|
【 問題2の解答を確認する 】