hiyoko-programingの日記

プログラミングを勉強したてのひよっ子。   エンジニア目指して勉強中。

Pictweetの作成

必要なファイルを準備

アプリケーションの以下のディレクトリにある同名のファイルと入れ替える方法。

  •  app
    •  views
      •  layouts
        •  application.html.erb

入れ替える際は、以下の動画のようにすると便利。まずはfinderを開き、その状態で別タブを開きます。別タブを開くためには、command + tキーを押す。その後、

pictweet/app/views/layoutsディレクトリまで開く。

https://tech-master.s3.amazonaws.com/uploads/curriculums//b386eca19114e6719616ea732f82c08b.gif

続いて元からあった方のタブに移動し、ダウンロードしたapplication.html.erbをドラッグ&ドラッグして先ほどの方に移動させ、置き換える。

https://tech-master.s3.amazonaws.com/uploads/curriculums//af6a6dee630d52860623f02107dba99e.gif

「style.scss」と「setting.css」を下記のディレクトリに配置

style.scss

setting.css

※ファイルを追加する際、既に同じ名前のファイルが存在する場合は上書き保存する

  •  app
    •  assets
      •  stylesheets
        •  style.scss
        •  setting.css

 リンク先のファイルをダウンロードして「arrow_top.png」を下記のディレクトリに配置

arrow_top.png

  •  app
    •  assets
      •  images
        •  arrow_top.png

 

デバッグツールの使い方

pry-railsというデバッグツールの使い方

 pry-rails

pry-railsRails向けに開発されたデバッグツール。デバッグツールとは、作業の際にバグの有無を確認したり、処理を止めてソースコードが正しいかを確認する際に使用するツール。デバッグツールを使いこなすことで、より開発がしやすくなる。

pry-railsはGemとして提供されており、本開発では最初にGemのインストールを行った際にインストールしてある。

また、Gemをインストールした際は、サーバーを立ち上げ直す必要がある。それはインストールしたGemが反映されるタイミングがサーバーを立ち上げるときだから。

Gemfile
41
  gem 'pry-rails'

pry-railsの機能の中で最も使用するのはbinding.pry(バインディングプライ)という機能。これは、binding.pryという文字列をソースコードの中に記述することで、binding.pryという文字列が存在する部分でRailsの処理を止めることができるというもの。

app/controlers/tweets_controller.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  class TweetsController < ApplicationController

    def index
      @tweets = Tweet.all
    end

    def new
    end

    def create
      Tweet.create(tweet_params)
      binding.pry
    end

    private
    def tweet_params
      params.permit(:name, :image, :text)
    end

  end

以上のようにbinding.pryという文字列を記述した上でローカルサーバーを立ち上げる。binding.pryを記述したのはツイートを作成する部分の処理。つまり、PicTweetでツイートの投稿を行うと処理が止まる。

処理が止まると、以下のような画面がターミナルに表示される。

ターミナル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  Started POST "/tweets" for ::1 at 2012-03-22 09:18:00 +0900
  Processing by TweetsController#create as HTML
    Parameters: {"utf8"=>"✓", "authenticity_token"=>"FONntu/x3bU5uJ6hJmWtv792TnR7qaOUL7Vv42ObM8OBm3ETF61mgvbaXzEwrby2RDlxQmZApmmR5KpuIgC+tQ==", "name"=>"hoge", "image"=>"http://sample.jpg", "text"=>"Hello World!"}
    User Load (0.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 6  ORDER BY `users`.`id` ASC LIMIT 1
  Unpermitted parameters: utf8, authenticity_token, name
     (0.1ms)  BEGIN
    SQL (0.2ms)  INSERT INTO `tweets` (`image`, `text`, `created_at`, `updated_at`) VALUES ('http://sample.jpg', 'Hello World!', '2015-03-31 05:32:52.087499', '2015-03-31 05:32:52.087499')
     (0.4ms)  COMMIT

  From: /Users/hoge/projects/pictweet/app/controllers/tweets_controller.rb @ line 12 TweetsController#create:

      10: def create
      11:   Tweet.create(tweet_params)
   => 12:   binding.pry
      13: end

  [1] pry(#<TweetsController>)>

ここでは、この時点での変数の値を出力したりすることができる。仮に実装を行った際にエラーが出た場合、複数個のbinding.pryを記述することで処理が正しく行われているのかを確認することができる。

binding.pryで処理を止めた際には、コンソールとほぼ同じことができるので、変数を出力したい際などには出力したい変数を入力することで内容を出力できる。

ターミナル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  From: /Users/hoge/projects/pictweet/app/controllers/tweets_controller.rb @ line 12 TweetsController#create:

      10: def create
      11:   Tweet.create(tweet_params)
   => 12:   binding.pry
      13: end

  [1] pry(#<TweetsController>)> tweet_params
  Unpermitted parameters: utf8, authenticity_token
  => {"name"=>"hoge", "image"=>"http://images.sample.jpg", "text"=>"Hello World!"}
  [2] pry(#<TweetsController>)>

処理を再開させたい場合にはexitというコマンドを入力する。exitコマンドを入力すると止まっていた処理が再開し、処理が再開される。また、exit!とコマンドを入力するとローカルサーバーを終了させることもできる。

ターミナル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  From: /Users/hoge/projects/pictweet/app/controllers/tweets_controller.rb @ line 12 TweetsController#create:

      10: def create
      11:   Tweet.create(tweet_params)
   => 12:   binding.pry
      13: end

  [1] pry(#<TweetsController>)> tweet_params
  Unpermitted parameters: utf8, authenticity_token
  => {"name"=>"hoge", "image"=>"http://images.sample.jpg", "text"=>"Hello World!"}
  [2] pry(#<TweetsController>)> exit
    Rendered tweets/create.html.erb within layouts/application (0.0ms)
  Completed 200 OK in 356078ms (Views: 69.0ms | ActiveRecord: 3.0ms)


  Started GET "/assets/setting-6304eaa3803ad71fa1d106214a7dcf02.css?body=1" for ::1 at 2012-03-22 09:18:00 +0900


  Started GET "/assets/users-bfd9b813b3d5121ad480bb8a65fd9bbc.css?body=1" for ::1 at 2012-03-22 09:18:00 +0900

 

binding.pryを利用してparamsを取得

では、binding.pryを使ったデバックを体験するために、実際にbinding.pryを利用して処理を止め、ビューから送られてくるパラメータであるparams(パラムス)を表示する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
  class TweetsController < ApplicationController

    def index
      @tweets = Tweet.all
    end

    def new
    end

    def create
      Tweet.create(tweet_params)
      binding.pry
    end

    private
    def tweet_params
      params.permit(:name, :image, :text)
    end

  end
localhost:3000/tweets/newにアクセスし、右上の「投稿する」ボタンを押す。

 項目を入力して「SENT」ボタンを押す

「imageURL」「text」各欄を入力し、「SENT」ボタンを押す。すると、ページ遷移せずに現在の画面にとどまり続ける。

https://tech-master.s3.amazonaws.com/uploads/curriculums//288984b774ade9bb7eb76029e8178680.png

 サーバーを立ち上げているターミナル画面を開き、paramsと入力する

この時、サーバーを立ち上げているターミナル(rails sコマンドを実行したターミナル画面)を見ると、以下のようにコマンドが入力できるようになっている。

binding.pry1

中断した部分までで定義されている変数やメソッドを呼び出すことができるので、paramsと入力する。以下のようなハッシュが表示されれば成功。

ターミナル
1
2
3
4
5
6
7
8
[1] pry(TweetsController)> params
#paramsと入力し、実行
=> {"utf8"=>"✓",
 "authenticity_token"=>"/3QchQigdEcpc2VaOn+6IGIV14x1iJ5bDsM7GI5NA6k=",
 "image"=>"http://sample/aaaabbbbccc.jpg",
 "text"=>"hello!",
 "action"=>"create",
 "controller"=>"tweets"}

投稿フォームではhtmlのinput要素を使う。
「SENT」ボタンが押されリクエストされると、input要素の属性としてname="〇〇"と書いた部分がparams内のキーに、入力欄に入力された文字がそのキーの値になり送信される。

binding.pryで処理を中断している間はその時点で使える変数やメソッドを呼び出せるので、paramsはこのcreateアクションの中で使うことができるハッシュのようなもの、ということになります。

 ターミナルからparams[:text]と入力し、実行

続いて、paramsはハッシュなのでのキーを指定してそのキーの値を表示してみる。そのままターミナルにparams[:text]と入力し、実行する。

ターミナル
1
2
3
[1] pry(TweetsController)> params[:text]
#params[:text]と入力し、実行
=> "hello!"

このように、先ほど投稿ページの「text」欄で入力したテキストが表示される。

 ターミナル上でexitコマンドを実行

このままではいつまで経っても、サーバーの処理がbinding.pryで中断されたまま。
exitコマンドを実行し、処理を再開する。

ターミナル
1
2
TweetController>exit
#処理を再開

 先ほどtweetsコントローラに追記したbinding.pryを削除する。

binding.pryが残っていると、また同じ操作をした際に処理が中断してしまう。忘れずに消去する。

params/ストロングパラメーターに関する解説動画

paramsやストロングパラメーターに関しては処理の流れが複雑ですので、以下の動画を見て学習内容を整理する。

  参考書参照

「プロになるためのWeb技術入門」p70 ~, 項3.4

1:ルートパスを追加

ツイート一覧のページを、アプリケーションのトップページに設定。

 ルートパス

ウェブサイトへアクセスする際などに使用するURLには、ホスト名とパスの2つの部分が存在。「localhost:3000」や「pictweet.me」のように、パスを付けないホスト名だけのURLのことをルートパスと言う。

現在のPicTweetは「/tweets」にアクセスするとツイートの一覧画面が表示される。今回はルーティングを追記してルートパスにアクセスした際にツイート一覧画面が表示されるようにする。そのためには、routes.rbにルートパス専用のルーティングを設定する必要がある。以下の例のように、root コントローラ名#アクション名と書く。

config/routes.rb
1
2
3
  Rails.application.routes.draw do
    root  'コントローラ名#アクション名'
  end

 問題1:ルートパスにアクセスした際にツイート一覧画面が表示されるようにする

作業ファイル:config/routes.rb
ヒント:ルートパスにアクセスした際にツイートの一覧画面を表示するためには、ルートパスに「/tweets」へアクセスした際と同じアクションを定義する。

ルートパスにアクセス

http://localhost:3000にアクセスして、ルートパスががきちんと反映されているか確認する。

こちらにアクセスすると、http://localhost:3000/tweetsにアクセスした画面と同じ画面になっているのが確認できる。これは、ルートパスによってtweets_controllerのindexアクションが動いているということ。

ルートパス確認

  

2:ツイートを最新順に並び替える

現在のPicTweetは、新しく投稿したツイートが1番下に表示されている。この表示の順番を最新のツイートが一番上に来るように表示させる

ツイート最新順説明

 orderメソッド

orderメソッドは、テーブルから取得してきたインスタンスたちを並び替えるメソッド。例えば「contents」という名前のテーブルがあり、以下のようにContentクラスからallメソッドを利用して全てのレコードを取得してきたとする時、orderメソッドを利用して取得してきたレコードを並び変えることができる。

app/controllers/contents_controller.rb
1
2
3
  def index
    @contents = Content.all.order("id DESC")
  end

allメソッドを利用した場合、通常であればレコードはid順に取得されるが、上記のようにorderメソッドの引数として("id DESC")とすれば、レコードは逆順に並び替えられる。
orderメソッドは引数として("テーブルのカラム名 並び替える順序")という形を取る。並び替える順序には、ASC(昇順)DESC(降順)の2種類がある。

 問題2:最新のツイートが一番上に来るように表示させる

作業ファイル:app/controllers/tweets_controller.rb
ヒント:ツイートが作成された日時は、「created_at」カラムに記録される。

 実際にhttp://localhost:3000でツイートを投稿して確認。

このサンプル画像のURLを使って投稿してみる。

最新順になっていれば以下の画像のようにトップページの一番上に投稿したツイートが表示される。

投稿の順番確認

 

3:ページネーションを実装

ツイートの一覧画面にページネーションを実装して、1ページに表示されるツイートの数を5件にする。5件以降は次ページを作成して、そこに表示されるようにする。

 ページネーション

ページネーションとは長い文章を複数のページに分割して、各ページへのリンクを並べアクセスしやすくすること。検索サイトのGoogleなども検索結果の表示にはページネーションを使用している。

ページネーション

 作業内容

1.Gemをインストールしてサーバーを立ち上げ直す
2.コントローラファイルを編集する
3.ビューファイルを編集する

1.Gemをインストールしてサーバーを立ち上げ直す

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

 kaminari

kaminariはRubyのGemの一種。このGemをインストールすることによって、簡単にページネーションを実装することができる。

①Gemfileの最後の行に下記の記述を追記

kaminariをインストールするために、Gemfileの最終行に以下のように追記する。

Gemfile
1
2
(省略)
  gem 'kaminari'  # 最終行に追記してください

②bundle installを実行

ターミナル
1
2
3
4
  $ pwd
  #現在のディレクトリが~/projects/pictweetであることを確認
  $ bundle install
  #bundle install の実行

③ローカルサーバーを再起動

ターミナル
1
  $ rails s

2.コントローラファイルを編集

1ページに表示されるツイートの情報を5件にするということは、コントローラからビューに送るツイート情報も5件のみになる。その数を制御するために、kaminariをインストールすると使える2つのメソッドを利用する。

 kaminari:pageメソッド

kaminariを導入すると、モデルクラスにpageメソッドが定義される。このメソッドは、ページネーションにおけるページ数を指定する。
ビューのリクエストの際paramsの中にpageというキーが追加されて、その値がビューで指定したページ番号となる。なので、pageの引数はparams[:page]となる。

 kaminari:perメソッド

perメソッドもpageメソッドと同様、kaminariというgemをインストールすることで利用できるメソッド。1ページあたりに表示する件数を指定する。perメソッドに引数として渡した数字が、ページネーションが実装されたビューで1ページあたりに表示する件数になる。

上記2つのメソッドは、よく以下のように使われる。

app/controllers/tweets_controller.rb
1
  変数名 = クラスを利用して取得したレコードのインスタンス.page(params[:page]).per(ここに1ページで表示したい件数を入力)

今回ページネーションを実装したいのはindexアクションのビューであり、コントローラで定義しビューに渡すのはtweetsテーブルのレコードである。

 問題3:「page」と「per」メソッドを利用して@tweetsを定義する

作業ファイル:app/controllers/tweets_controller.rb
ヒント:当該ファイルに既にある、@tweetsへ代入している箇所(右辺)を書き換えましょう。orderメソッドの後ろに書き加える形。

3.ビューファイルを編集

ページネーションを実装する際にはコントローラだけでなくビューにも編集を加える必要がある。

 kaminari:paginateメソッド

ページネーションのリンクを表示したいときに使用するメソッド。kaminariをインストールすると利用できる。paginateメソッドの引数は、コントローラで定義した変数を指定する。

app/views/tweets/index.html.erb
1
2
3
4
5
6
7
8
9
  <div class="contents row" >
    <% @tweets.each do |tweet| %>
      <div class="content_post" style="background-image: url(<%= tweet.image %>);">
        <%= simple_format(tweet.text) %>
        <span class="name"><%= tweet.name %></span>
      </div>
    <% end %>
  <%= paginate(@tweets) %>
</div>

これで、ページネーションを実装することができた。

このときデータベースのtweetsテーブルにレコードが6個以上ないとページネーションが表示されない。

 tweetsテーブルにレコードを6個以上作成する。

tweetsテーブルにレコードが6つ以上あるか確認し、なければ、レコード数が6個以上になるようレコードを作成する。

 http://localhost:3000にアクセス

下の方に、以下のようにページネーションが表示されていれば成功。

pagination

  

4:ログイン機能を作成

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

以下の動画のように、ヘッダーにログイン/新規登録のボタンを置き、実際にログインするとその部分が登録したユーザー名と投稿するボタンになる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//9edb5446444521a3db10df75ad3168c5.gif

今回はログイン機能を実装することで、ログインしたユーザーのみがツイートの投稿を行えるようにする。ツイートの閲覧に関してはログインにかかわらずできるようにする。

  • ツイート閲覧時

ログイン機能説明1

  • ツイート投稿時

ログイン機能説明2

 作業内容

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

1.Gemをインストールしてサーバーを立ち上げ直す

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

 devise

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

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

Gemfile
1
2
(省略)
  gem 'devise'  # 最終行に追記してください

②bundle installを実行

ターミナル
1
2
3
4
  $ pwd
  #現在のディレクトリが~/projects/pictweetであることを確認
  $ bundle install
  #bundle install の実行

③ローカルサーバーを再起動

ターミナル
1
  $ rails s

2.コマンドを利用してdeviseの設定ファイルを作成

deviseを使用するためには、Gemのインストールに加えてdevise専用のコマンドを利用して設定ファイルを作成する必要がある。

ターミナル
1
2
  $ rails g devise:install
  # deviseの設定ファイルを作成

 新規作成されるファイル

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

3.コマンドを利用してUserモデルを作成

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

 rails g devise コマンド

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

rails g deviseコマンドでuserモデルを作成する

ターミナル
1
2
  $ 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
1
2
3
Rails.application.routes.draw do
  devise_for :users
#以下略

 devise_for

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

例えばdevise_for :usersの記述により、以下のように「ログイン・新規登録」で必要なルーティングが生成される。

また、後ほど登場するcurrent_useruser_signed_in?などのヘルパーメソッドも利用できるようになる。

ターミナル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
                  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を実行

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

 SequelProを開き、usersテーブルが作成されているか確認する

以下のように、usersテーブルが作成されていれば成功。

user_idにid保存

マイグレーションファイルを実行してもusersテーブルが表示されない場合は、データベース選択から「データベースを再読み込み」を押すか、左下の再読み込みボタンを押してみる。

 user.rbを開き、5行目の「:trackable」を削除する。

:trackableのみを削除する。
:trackableの記述がない場合はこの作業を行う必要ない。

app/models/user.rb(修正前)
1
2
3
4
5
6
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(修正後)
1
2
3
4
5
6
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_toRubyタグの中で使用することができるメソッド。このメソッドは引数を指定することで様々なリンクを生成する。

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

sample.html.erb
1
2
  <%= link_to 'ツイート一覧へ', '/tweets' %>
  # link_toメソッドを使ってリンクを生成

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

sample.html.erb
1
2
  <%= link_to 'ツイート一覧へ', '/tweets', class: 'sample' %>
  # 作成したaタグに`class="sample"`属性を付与

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

sample.html.erb
1
  <a class="sample" href="/tweets">ツイート一覧へ</a>

 user_signed_in?

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

sample.html.erb
1
2
3
  <% if user_signed_in? %>
    # ユーザーがサインインしている場合に実行する処理
  <% end %>

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

 prefix

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

これを確認するには、ターミナルからrake routesコマンドを実行する。すると、以下のような表示になる。

【例】ターミナル
 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
26
27
#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に書く。

 問題4:ログインしている時とそうでない時で表示するビューを変更する

作業ファイル:app/views/layouts/application.html.erb
ヒント:下記の例をもとに、user_signed_in?メソッドを利用。
【例】
application.html.erb
 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>

 localhost:3000でログインしている時としていない時でヘッダー部分の表示が変わることを確認する

ログインしていない時

「ログイン」「新規作成」ボタンの2つが表示されていることが確認できる。

008

ログインしている時

「ログアウト」「投稿する」ボタンの2つが表示されていることが確認できる。

009

5. コントローラにリダイレクトを設定

上記の作業で未ログインユーザーがツイートの一覧画面を表示しても投稿ボタンが表示されなくなった。しかし、未ログインユーザーであっても直接/tweets/newというパスにアクセスすることで投稿ができてしまう。

localhost:3000/tweets/new にアクセスする

ログインしてない状態でもlocalhost:3000/tweets/newにアクセスできるのが確認できる。

アクセス可能

そこで、未ログインユーザーが投稿画面など直接アクセスしてきた際にはルートパスに遷移するように設定を行う。

 unless文

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

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

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

1
2
3
4
5
6
  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
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  class TweetsController < ApplicationController

    def index
      @tweets = Tweet.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 :メソッド名と記述することで、コントローラのアクションが実行される前にそのメソッドを実行することができる。
また、オプションonlyexceptを使うことにより、before_actionを実行することをアクションごとに制限をかけることができる。

indexアクション以外でbefore_actionを実行したい場合

app/controllers/tweets_controller.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
  class TweetsController < ApplicationController

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

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

    private
    def hoge
    end
  end

 問題5:ログインしていないときに新規投稿しようとすると、indexアクションにリダイレクトされるようする

作業ファイル:app/controllers/tweets_controller.rb
ヒント:before_actionは全てのアクションの実行前に強制的に実行されるメソッドを設定。
tweets_controller.rb
 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.page(params[:page]).per(5).order("created_at DESC")
  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
  

 

5:ログイン・サインアップ画面のビューを変更

deviseを使用してログイン機能を実装すると自動的にログイン画面とサインアップ画面が生成される。元のままの見た目だと味気ないため、以下画像のようなサインイン/サインアップ画面に変更する。

https://tech-master.s3.amazonaws.com/uploads/curriculums//133f5392c783d33e1dc64846a1e3ed74.png

 作業内容

1.devise用のビューファイルを作成する
2.ログイン・サインアップ画面のビューファイルを編集する

devise用のビューファイルを作成

deviseでログイン機能を実装すると、ログイン・サインアップ画面は自動的に生成されるがビューファイルとしては生成されない。これは、deviseのGem内に存在するビューファイルを読み込んでいるため。

ビューファイルに変更を加えるためには、deviseのコマンドを利用してビューファイルを生成する必要がある。

ターミナル
1
  $ rails g devise:views

 新規作成されるファイル

  • app/views/devise以下のディレクトリにあるビューファイル各種

ログイン・サインアップ画面のビューファイルを編集

devise用の生成されたビューファイルを編集してレイアウトを変更していく。

  • サインアップ画面はapp/views/devise/registrations/new.html.erb、ログイン画面のビューはapp/views/devise/sessions/new.html.erbというビューファイルが対応している。
app/views/devise/registrations/new.html.erb
 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
26
27
28
29
  <div class="contents row">
    <h2>Sign up</h2>
    <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= devise_error_messages! %>

      <div class="field">
        <%= f.label :email %><br />
        <%= f.email_field :email %>
      </div>


      <div class="field">
        <%= f.label :password %>
        <% if @validatable %>
        <em>(<%= @minimum_password_length %> characters minimum)</em>
        <% end %><br />
        <%= f.password_field :password, autocomplete: "off" %>
      </div>

      <div class="field">
        <%= f.label :password_confirmation %><br />
        <%= f.password_field :password_confirmation, autocomplete: "off" %>
      </div>

      <div class="actions">
        <%= f.submit "Sign up" %>
      </div>
    <% end %>
  </div>
app/views/devise/sessions/new.html.erb
 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
26
27
  <div class="contents row">
    <h2>Log in</h2>

    <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>

      <div class="field">
        <%= f.label :email %><br />
        <%= f.email_field :email, autofocus: true %>
      </div>

      <div class="field">
        <%= f.label :password %><br />
        <%= f.password_field :password, autocomplete: "off" %>
      </div>

      <% if devise_mapping.rememberable? -%>
        <div class="field">
          <%= f.label :remember_me %><br />
          <%= f.check_box :remember_me %>
        </div>
      <% end -%>

      <div class="actions">
        <%= f.submit "Log in" %>
      </div>
    <% end %>
  </div>

 変更できたか確認

localhost:3000/users/sign_inにアクセスし、
以下の画面になっていることを確認。

confirm_sign_in_view

localhost:3000/users/sign_upにアクセスし、
以下の画面になっていることを確認。

confirm_sign_in_view 

6:サインアップ時にニックネームを登録

現在、サインアップ時に登録する情報はメールアドレスとパスワードの2つ。これに加えてニックネームを登録できるようにする。

 作業内容

1.ユーザーテーブルにカラムを追加する
2.登録画面のビューを編集する
3.ストロングパラメーターを編集する

1.ユーザーテーブルにカラムを追加

ニックネームを登録するために、usersテーブルにニックネームを保存するためのカラムを追加する。追加したカラムに、サインアップ時に登録するニックネームを保存する。

 カラムを追加するマイグレーションファイルの作成

カラムを追加するマイグレーションファイルを作成するためには、下記のコマンドを実行する。

$ rails g migration Addカラム名To追加先テーブル名 追加するカラム名:型

userの自己紹介を保存するためのカラム、introductionカラムをusersテーブルにtext型で追加したい時は、以下の例のように書く。

ターミナル
1
2
  $ rails g migration AddIntroductionToUsers introduction:text
  # usersテーブルにintroductionカラムをtext型で追加するマイグレーションファイルの作成

この時、「Add」「Introduction」「To」「Users」と、単語の頭文字が大文字になっている。この表記方法について。

 スネークケースとキャメルケース

プログラムが単語の区切りを認識する際、2通りの方法がある。それらをそれぞれ「スネークケース、キャメルケース」と呼ぶ。
スネークケースは、例えば「sample_hoge_huga」のように「_」で単語を区切る方法。対してキャメルケースは、「SampleHogeHuga」のように単語の頭文字を大文字にすることで単語を区切る

 問題6:usersテーブルに、nicknameカラムをstring型で追加しましょう

作業ファイル:なし
ヒント1:マイグレーションファイルを作成しても、実行しなければカラムは追加されない。
ヒント2:もし誤ったマイグレーションファイルを実行してしまった場合はロールバックする。

Addカラム名To追加先テーブルカラム名の部分は、必ずしも厳密なカラム名を入力する必要はない。
今回はnicknameというカラムを追加するので今回はNicknameと記述する。

 実際にカラムが追加された確認

SequelProを起動し、usersテーブルを開きます。一番右にスクロールして新しく追加されたnicknameカラムを確認する。

nickname

2.登録画面のビューを編集

これでDB側の準備は整ったので、続いてユーザーの新規登録画面にニックネームを登録するためのフォームを追加する。ユーザーの新規登録画面のビューは、app/views/devise/registrations/new.html.erbです。その際、オプションをつけることで入力できる文字数を6文字以下にする。

text_field:maxlengthオプション

maxlengthは、text_fieldというinputタグを生成するヘルパーメソッドにつけることができるオプション。

app/views/devise/registrations/new.html.erb
6
7
8
  <div class="field">
    <%= f.text_field :nickname, autofocus: true, maxlength: "6" %>
  </div>

この場合、生成されたフォームに7文字以上入力すると、エンターキーを押した瞬間に6文字までカットされる。

app/views/devise/registrations/new.html.erb
 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
26
27
28
29
30
31
32
33
  <div class="contents row">
    <h2>Sign up</h2>
    <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= devise_error_messages! %>

      <div class="field">
        <%= f.label :nickname %> <em>(6 characters maximum)</em><br />
        <%= f.text_field :nickname, autofocus: true, maxlength: "6" %>
      </div>

      <div class="field">
        <%= f.label :email %><br />
        <%= f.email_field :email %>
      </div>

      <div class="field">
        <%= f.label :password %>
        <% if @validatable %>
        <em>(<%= @minimum_password_length %> characters minimum)</em>
        <% end %><br />
        <%= f.password_field :password, autocomplete: "off" %>
      </div>

      <div class="field">
        <%= f.label :password_confirmation %><br />
        <%= f.password_field :password_confirmation, autocomplete: "off" %>
      </div>

      <div class="actions">
        <%= f.submit "Sign up" %>
      </div>
    <% end %>
  </div>

3.ストロングパラメーターを編集

サインアップ時に入力する情報はパラメーターとしてサーバーに送信される。通常のリクエストの場合は、コントローラに記述してあるストロングパラメーターで受け取れるパラメーターを制限する。

しかし、deviseでログイン機能を実装した場合のパラメーターの受け取り方は通常とは異なる。ログイン時に送られてくるパラメーターを制限するストロングパラメーターは、deviseのGem内に記述されているため編集することはできない。

https://tech-master.s3.amazonaws.com/uploads/curriculums//da0f7d41792819f24a70ff434e2f56e3.png今回追加したニックネームを受け取れるようにするにはdeviseが提供しているconfigure_permitted_parametersメソッドを利用する。

 configure_permitted_parametersメソッド

deviseでは初期状態でサインアップ時にメールアドレスとパスワードのみを受け取るようにストロングパラメーターが設定してあるので、追加したキーのパラメーターは許可されていない。追加のパラメーターを許可したい場合は、application_controllerにおいてbefore_actionconfigure_permitted_parametersメソッドを設定する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  class ApplicationController < ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception
    before_action :configure_permitted_parameters, if: :devise_controller?

    def configure_permitted_parameters
      devise_parameter_sanitizer.permit(:ストロングパラメーターを追加したいアクション名, keys: [:追加するキー])
    end
  end

sign_upアクションに対してnicknameというキーのパラメーターを追加で許可する。

app/controllers/application_controller.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  class ApplicationController < ActionController::Base
    # Prevent CSRF attacks by raising an exception.
    # For APIs, you may want to use :null_session instead.
    protect_from_forgery with: :exception
    before_action :configure_permitted_parameters, if: :devise_controller?

    def configure_permitted_parameters
      devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])
    end
  end

 登録画面のビューを確認してみる

localhost:3000/users/sign_upにアクセスして、nicknameを入力するフォームができていることを確認。

ビューの確認

ここまでできたら、実際にユーザーを作成してニックネームが登録できるか確かめてみる

 pictweetのトップページから、新規でユーザーを作成してみる。

localhost:3000/users/sign_upにアクセスして、nicknameを保存する。

registration_nickname

きちんと保存できているかどうかSequelProで確認してみましょう。

confirm_nickname