Rails」カテゴリーアーカイブ

Railsのgem「annotate」の使い方(Docker環境)

annotateとは?

モデルにデータベースのスキーマ構造をannotate(注釈)してくれるGem

テーブルを確認する時にDBに潜る必要がなくなるよ。

Dockerでの使い方

Gemfileのgroup :development doに

gem 'annotate'

を記述します。

docker-compose down でコンテナを削除

docker-compose build でコンテナを作り直し

docker-compose up -dで起動してから次のコマンドをする

docker-compose exec web bundle exec annotate 

変わらなかったときは、次のコマンドをする。

docker-compose exec web bundle exec rails g annotate:install

(次回のマイグレーション以降、自動でアノテーションしてくれるコマンド)

もう一度↓をコマンド

docker-compose exec web bundle exec annotate

Annotated (7): app/models/task.rb, test/models/task_test.rb, test/fixtures/tasks.yml, test/fixtures/tasks.yml, app/models/user.rb, test/models/user_test.rb, test/fixtures/users.yml

↑のようにAnnotetedとなっていれば成功していて、次のように

例:app/models/user.rb

# == Schema Information
#
# Table name: users
#
#  id              :bigint           not null, primary key
#  admin           :boolean          default(FALSE)
#  email           :string(255)
#  name            :string(255)
#  password_digest :string(255)
#  remember_digest :string(255)
#  created_at      :datetime         not null
#  updated_at      :datetime         not null
#
# Indexes
#
#  index_users_on_email  (email) UNIQUE
#
class User < ApplicationRecord
  has_many :tasks, dependent: :destroy
  attr_accessor :remember_token
  before_save { email.downcase! }
  validates :name, presence: true, length: {maximum: 15}
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
  validates :email, presence: true, length: {maximum: 100},
            format: {with: VALID_EMAIL_REGEX},
            uniqueness: {case_sensitive: false}
  has_secure_password
  validates :password, presence: true, length: {minimum: 5}, allow_nil: true

# == Schema Informationから始まるテーブル情報が記述されていればOKだよ

Rails+Docker環境下で(モデルにカラムを追加する)

Rails+Docker環境下でモデルにカラムを追加するには?

docker-compose run web bundle exec rails g migration AddYYYToXXX カラム名:データ型 (XXXはテーブル名、YYYはカラム名)

docker-compose run web bundle exec rails g migration AddDetailsToLists event:text

複数追加のときはAddDetailsToListsのようにカラム名は任意で良い(単体の場合は追加したいカラム名)

↑のようにマイグレーションファイルが生成される

マイグレートする

docker-compose run web bundle exec rake db:migrate

↑のようにマイグレート成功していればOK!

Docker環境下のpry-byebugの使い方(デバッグ)

Gemfileに以下を記述します。

group :development do
  gem 'pry-byebug' ←追加
end

docker-compose buildでコンテナを作る。

docker-compose build

docker-compose up -dでコンテナを起動する

docker-compose up -d

docker psでNAMESを確認

docker ps

docker attach lifehack_web_1

newメソッドにbinding.pryを追加

class TasksController < ApplicationController
  def new
    @task = Task.new
    binding.pry
  end
end

http://localhost:3000/tasks/newを開くかリロードして

ターミナルが↑のような表示になったらOK

@taskにいろいろ値を入れてみる

@task.title = 'task1'

@task.content = 'content'

@task.priority = 3

exitでデバッガーから抜けると

先程入力したオブジェクトがフォームにセットされた!

使い終わったらbinding.pryを消して

コントロールキー^を押しながらp,qキーの順に押すとアタッチが解除できる!

Docker環境でrails cするには?

Docker環境でrails cするには?

1:まずdocker-compose ps で確認する

docker-compose ps

赤枠の部分が重要

コンテナに接続する

#赤枠の部分(各々違うので注意)を↓に
docker exec -it lifehack_web_1 bash
bundle exec rails c

irb(main):001:0>というふうに表示されたらOKです。

もとに戻りたい時(コンテナの接続を外す)

exit

exit

でもとに戻ることができます。

Docker+Rails環境下でのユーザー削除の実装(退会機能の実装)

サンプルデータ生成タスクにadminユーザーを1人追加します。

db/seeds.rb

User.create!([{ name: "suzutuki",
  email: "example@railstutorial.org",
  password: "suzu",
  password_confirmation: "suzu",
  admin: true },
  { name: "suzu",
  email: "suzu1@railstutorial.org",
  password: "suzu",
  password_confirmation: "suzu"},
  { name: "suzu",
  email: "suzu2@railstutorial.org",
  password: "suzu",
  password_confirmation: "suzu"},
  { name: "suzu",
  email: "suzue3@railstutorial.org",
  password: "suzu",
  password_confirmation: "suzu"}])

seedファイルを使う時にはデータベースをリセットします。

docker-compose run web bundle exec rails db:migrate:reset

データベース上にサンプルユーザーを生成します。

docker-compose run web bundle exec rails db:seed

users_contorollerに以下の記述を追加します。

before_action :admin_user, only: [:show]

 def destroy
  @user = User.find(params[:id])
  @user.destroy if correct_user || admin_user
 end
  
private
 def user_params
  params.require(:user).permit(:name, :email, :password,
                          :image, :password_confirmation)
  end

# 正しいユーザーかどうか確認する
  def correct_user
    @user = User.find(params[:id])
    redirect_to(root_url) unless current_user?(@user)
  end

# 管理者かどうか確認する
  def admin_user
    redirect_to(root_url) unless current_user.admin?
  end

任意のユーザーが自分自身にアプリケーションの管理者権限を与えることを防止できます。

続きを読む

debugのやり方(Better Errors)

自分用にエラーが出たときの対応の仕方をメモしておく。

1:エラーをGemで見やすくする

Better_errorsの使い方(Docker)からBetter Errorsを導入

2:どこでエラーが起きたか調べる

エラーログの左側を見てどこでエラーが起きたか調べてみる

Application Frames: 自分が書いたコード

All Frames:                   ライブラリなど全部あわせたもの

またはdocker-compose up などでエラーログを見る

続きを読む

画像投稿機能の実装(Docker+rails+carrierwave+fog-aws+rmagick)

目標:ユーザーのプロフィールに画像をアップロードして設定できるようにしたい

以下のGemをGemfileに記述します。

コンテナを再起動する

docker-compose stop

docker-compose down

docker-compose build
#アップローダーを生成する
docker-compose exec web rails g uploader Image

userモデルに画像をアップロードするためにimageカラムを追加します。

docker-compose run web bundle exec rails g migration AddImageToUsers user image:string

マイグレートします

続きを読む

RailsでSass変数で色の管理をする方法

動機

Sassで変数を定義する練習のため。

使い方

  1. app/assets/stylesheets/variables.scssを作成する。
$color-nobleblack:   #1d1d1f;
$color-white:        #fff;
$color-coffee_brown: #614226;
$color-thin_grey: rgba(250, 240, 230, 0.1);

2.変数を使うscssファイルで@import “color”;を記述

app/assets/stylesheets/base.scss

@import "variables";
//全体の設定
html {
  position: relative;
  min-height: 100%;
}

/* bodyからの設定 */
body {
  margin-bottom: 60px;
  border-top: 6px solid $color-coffee_brown;
  font-family: "SF Pro JP","SF Pro Text","SF Pro Icons","Hiragino Kaku Gothic Pro","ヒラギノ角ゴ Pro W3","メイリオ","Meiryo","MS Pゴシック","Helvetica Neue","Helvetica","Arial",sans-serif;
  line-height: 1.47059;
  background-color: $color-white;
  letter-spacing: -.022em;
  color: $color-nobleblack;
  overflow-x: hidden;
  font-style: normal;
}

変数の部分を変更するとインポートしている部分がすべて変わるので、便利な半面、気をつけて利用しよう!

Rails-tutorialのまとめ(14.3 ステータスフィード)

その14.25から続く

14.3 ステータスフィード

最後の難関、ステータスフィードの実装に取りかかりましょう。
本書の中でも最も高度なものです。完全なステータスフィードは、
13章で扱ったプロトフィードをベースにします。

現在のユーザーにフォローされているユーザーのマイクロポストの配列を作成し、現在のユーザー自身のマイクロポストと合わせて表示します。
このセクションを通して、複雑さを増したフィードの実装に進んでいきます。
これを実現するためには、RailsとRubyの高度な機能の他に、
SQLプログラミングの技術も必要です。

14.3.1 動機と計画

フィードに必要な3つの条件を満たすことです。具体的には、

1) フォローしているユーザーのマイクロポストがフィードに含まれていること
2) 自分自身のマイクロポストもフィードに含まれていること。
3) フォローしていないユーザーのマイクロポストがフィードに含まれていないこと

の3つです。

まずはMichaelがLanaをフォローしていて、
Archerをフォローしていないという状況を作ってみましょう。
この状況のMichaelのフィードでは、Lanaと自分自身の投稿が見えていて、Archerの投稿は見えないことになります

先ほどの3つの条件をアサーションに変換して、
Userモデルにfeedメソッドがあることに注意しながら、
更新したUserモデルに対するテストを書いてみましょう。
結果を↓に示します。

ステータスフィードのテスト
test/models/user_test.rb

演習

マイクロポストのidが正しく並んでいると仮定して
(すなわち若いidの投稿ほど古くなる前提で)、データセットで
user.feed.map(&:id)を実行すると、
どのような結果が表示されるでしょうか? 考えてみてください。
たdefault_scopeを思い出してください。

user.feed.map($:id)
=>[1,2,7,8,10]

このように、引数として受け取った自分のidと、
フォローしているidが組み合わさって表示される。

14.3.2 フィードを初めて実装する

早速フィードの実装に着手してみましょう。最終的なフィードの実装はやや込み入っているため、細かい部品を1つずつ確かめながら導入していきます。

このフィードで必要なクエリについて考えましょう。
ここで必要なのは、micropostsテーブルから、
あるユーザー (つまり自分自身) がフォローしているユーザーに対応するidを持つマイクロポストをすべて選択 (select) することです。
このクエリを模式的に書くと次のようになります。

SELECT * FROM microposts
WHERE user_id IN (<list of ids>) OR user_id = <user id>

SQLがINというキーワードをサポートしていることを前提にしています

このキーワードを使うことで、
idの集合の内包 (set inclusion) に対してテストを行えます。

Active Recordのwhereメソッドを使っていることを思い出してください。このときに選択すべき対象はシンプルで、
現在のユーザーに対応するユーザーidを持つマイクロポストを選択すればよかったのでした。

Micropost.where("user_id = ?", id)

今回必要になる選択は、上よりも少し複雑で、例えば次のような形になります。

Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)

フォローされているユーザーに対応するidの配列が必要であることが
わかってきました。これを行う方法の1つは、Rubyのmapメソッドを使うことです。
このメソッドはすべての「列挙可能 (enumerable)」なオブジェクト
(配列やハッシュなど、要素の集合で構成されるあらゆるオブジェクト)で使えます。なお、このメソッドは前にもも出てきました。
mapメソッドを使って配列を文字列に変換すると、次のようになります。

rails console
>> [1, 2, 3, 4].map { |i| i.to_s }
=> ["1", "2", "3", "4"]

& と、メソッドに対応するシンボルを使った短縮表記が使えます。
この短縮表記であれば、変数iを使わずに済みます。

>> [1, 2, 3, 4].map(&:to_s)
=> ["1", "2", "3", "4"]

>> [1, 2, 3, 4].map(&:to_s).join(', ')
=> "1, 2, 3, 4"

上のコードを使えば、user.followingにある各要素のidを呼び出し、
フォローしているユーザーのidを配列として扱うことができます。

>> User.first.following.map(&:id)
=> [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,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]

実際、この手法は実に便利なので、Active Recordでは次のようなメソッドも用意されています。

>> User.first.following_ids
=> [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,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51]

following_idsメソッドは、has_many :followingの関連付けをしたときにActive Recordが自動生成したものです。
これにより、user.followingコレクションに対応するidを得るためには、関連付けの名前の末尾に_idsを付け足すだけで済みます。
結果として、フォローしているユーザーidの文字列は、
次のようにして取得することができます。

>> User.first.following_ids.join(', ')
=> "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,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51"

実際にSQL文字列に挿入するときは、このように記述する必要はありません。
実は、?を内挿すると自動的にこの辺りの面倒を見てくれます。
さらに、データベースに依存する一部の非互換性まで解消してくれます。
つまり、ここではfollowing_idsメソッドをそのまま使えばよいだけなのです。結果、最初に想像していたとおり、

Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
とりあえず動くフィードの実装 green
app/models/user.rb
class User < ApplicationRecord
  .
  .
  .
  # パスワード再設定の期限が切れている場合はtrueを返す
  def password_reset_expired?
    reset_sent_at < 2.hours.ago
  end

  # ユーザーのステータスフィードを返す
  def feed
    Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  end

  # ユーザーをフォローする
  def follow(other_user)
    following << other_user
  end
  .
  .
  .
end
 green
rails test

いくつかのアプリケーションにおいては、この初期実装だけで目的が達成され、十分に思えるかもしれません。しかしにはまだ足りないものがあります。
それが何なのか、次に進む前に考えてみてください。
(フォローしているユーザーが5,000人もいたらどうなるでしょうか?)。

演習

1:現在のユーザー自身の投稿を含めないようにするにはどうすれば良いでしょうか? また、そのような変更を加えると、どのテストが失敗するでしょうか?

OR user_id = 1で、自分自身のユーザーidを渡している点に注目。
(user_id IN (3,..,51) OR user_id = 1)

2:フォローしているユーザーの投稿を含めないようにするにはどうすれば良いでしょうか? また、そのような変更を加えると、どのテストが失敗するでしょうか?

def feed
  Micropost.where("user_id = ?", following_ids, id)
end
ActiveRecord::PreparedStatementInvalid: wrong number of bind variables (2 for 1) in: user_id = ?
test/models/user_test.rb:15:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:14:in `block in <class:UserTest>'

3:フォローしていないユーザーの投稿を含めるためには
どうすれば良いでしょうか?
また、そのような変更を加えると、どのテストが失敗するでしょうか?
自分自身とフォローしているユーザー、そしてそれ以外という集合は、
いったいどういった集合を表すのか考えてみてください。

# ユーザーのステータスフィードを返す
def feed
  Micropost.all
end

Expected true to be nil or false
test/models/user_test.rb:23:in `block (2 levels) in <class:UserTest>'
test/models/user_test.rb:22:in `block in <class:UserTest>'
# フォローしていないユーザーの投稿を確認
archer.microposts.each do |post_unfollowed|
  assert_not michael.feed.include?(post_unfollowed)
end

フォローしていないものが含まれているのはダメです

14.3.3 サブセレクト

つまり、フォローしているユーザーが5,000人程度になるとWebサービス全体が遅くなる可能性があります。ステータスフィードを改善していきましょう。

following_idsでフォローしているすべてのユーザーをデータベースに
問い合わせし、さらに、フォローしているユーザーの完全な配列を作るために再度データベースに問い合わせしている点です。
SQLのサブセレクト (subselect) を使うと解決できます。

フィードをリファクタリングすることから始めましょう。
whereメソッド内の変数に、キーと値のペアを使う green
app/models/user.rb

class User < ApplicationRecord
  .
  .
  .
  # ユーザーのステータスフィードを返す
  def feed
    Micropost.where("user_id IN (:following_ids) OR user_id = :user_id",
     following_ids: following_ids, user_id: id)
  end
  .
  .
  .
end

これまでのコード

Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)

を次のように置き換えました。

Micropost.where("user_id IN (:following_ids) OR user_id = :user_id",
    following_ids: following_ids, user_id: id)

前者の疑問符を使った文法も便利ですが、同じ変数を複数の場所に挿入したい場合は、後者の置き換え後の文法を使う方がより便利です。

これからSQLクエリにもう1つのuser_idを追加します。特に、次のRubyコードは、

following_ids

このようなSQLに置き換えることができます。

following_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"

このコードをSQLのサブセレクトとして使います。つまり、「ユーザー1がフォローしているユーザーすべてを選択する」というSQLを既存のSQLに内包させる形になり、結果としてSQLは次のようになります。

SELECT * FROM microposts
WHERE user_id IN (SELECT followed_id FROM relationships
                  WHERE follower_id = 1)
OR user_id = 1

集合のロジックを (Railsではなく) データベース内に保存するので、
より効率的にデータを取得することができます。

もっと効率的なフィードを実装する準備ができました。
(ここに記述されているコードは生のSQLを表す文字列であり、
following_idsという文字列はエスケープされているのではなく、
見やすさのために式展開しているだけだという点に注意してください。)

フィードの最終的な実装 green
app/models/user.rb
class User < ApplicationRecord
  .
  .
  .
  # ユーザーのステータスフィードを返す
  def feed
    following_ids = "SELECT followed_id FROM relationships
                     WHERE follower_id = :user_id"
    Micropost.where("user_id IN (#{following_ids})
                     OR user_id = :user_id", user_id: id)
  end
  .
  .
  .
end

RailsとRubyとSQLのコードが複雑に絡み合っていて厄介ですが、
ちゃんと動作します。

green
rails test

大規模なWebサービスでは、バックグラウンド処理を使ってフィードを非同期で生成するなどのさらなる改善が必要でしょう。

ステータスフィードの実装は完了です。

rails test
git add -A
git commit -m "Add user following"
git checkout master
git merge following-users

コードをリポジトリにpushして、本番環境にデプロイしてみましょう。

git push
git push heroku
heroku pg:reset DATABASE
heroku run rails db:migrate
heroku run rails db:seed

演習

1:Homeページで表示される1ページ目のフィードに対して、
統合テストを書いてみましょう。

 test "feed on Home page" do
   get root_path
   @user.feed.paginate(page: 1).each do |micropost|
    assert_match CGI.escapeHTML(micropost.content), response.body
  end 
 end

2:期待されるHTMLをCGI.escapeHTMLメソッドでエスケープしています(このメソッドはCGI.escapeと同じ用途です)。
このコードでは、なぜHTMLをエスケープさせる必要があったのでしょうか?考えてみてください。
ヒント: 試しにエスケープ処理を外して、
得られるHTMLの内容を注意深く調べてください。
マイクロポストの内容が何かおかしいはずです。また、
ターミナルの検索機能 (Cmd-FもしくはCtrl-F) を使って
「sorry」を探すと原因の究明に役立つはずです。

contentをエスケープしている為、CGI.esapeHTMLを加える必要がある。

ステータスフィードが追加され、Ruby on Railsチュートリアルの
サンプルアプリケーションがとうとう完成しました。
このサンプルアプリケーションには、
Railsの主要な機能 (モデル、ビュー、コントローラ、テンプレート、パーシャル、beforeフィルター、バリデーション、コールバック、has_many/belongs_to/has_many through関連付け、セキュリティ、テスティング、デプロイ)が多数含まれています。

14.4.1 サンプルアプリケーションの機能を拡張する

Railsアプリケーションに何らかの機能を実装していて困ったときは、RailsガイドやRails APIをチェックしてみてください。
いずれも1,000ページを超える大型の公式ドキュメントなので、
今自分がやろうとしていることに関連するトピックがあるかもしれません。

できるだけ念入りにGoogleで検索し、自分が調べようとしているトピックに言及しているブログやチュートリアルがないかどうか、よく探すことです。
Webアプリケーションの開発には常に困難がつきまといます。
他人の経験と失敗から学ぶことも重要です。

14.4.1 サンプルアプリケーションの機能を拡張する

Twitterには、マイクロポスト入力中に@記号に続けてユーザーのログイン名を入力するとそのユーザーに返信できる機能があります。
このポストは、宛先のユーザーのフィードと、
自分をフォローしているユーザーにのみ表示されます。
この返信機能の簡単なバージョンを実装してみましょう。具体的には、@replyは受信者のフィードと送信者のフィードにのみ表示されるようにします。
これを実装するには、micropostsテーブルのin_reply_toカラムと、
追加のincluding_repliesスコープをMicropostモデルに追加する
必要があると思います。スコープの詳細については、
RailsガイドのActive Record クエリインターフェイスを参照してください。

このサンプルアプリケーションではユーザー名が重なり得るので、
ユーザー名を一意に表す方法も考えなければならないでしょう。
1つの方法は、idと名前を組み合わせて@1-michael-hartlのようにすることです

もう1つの方法は、ユーザー登録の項目に一意のユーザー名を追加し、@replyで使えるようにすることです。

メッセージ機能

Twitterでは、ダイレクトメッセージを行える機能がサポートされています。この機能をサンプルアプリケーションに実装してみましょう
(ヒント: Messageモデルと、新規マイクロポストにマッチする正規表現が必要になるでしょう)。

フォロワーの通知

ユーザーに新しくフォロワーが増えたときにメールで通知する機能を
実装してみましょう。続いて、メールでの通知機能をオプションとして選択可能にし、不要な場合は通知をオフにできるようにしてみましょう。
メール周りで分からないことがあったら、
RailsガイドのAction Mailerの基礎にヒントがないか調べてみましょう。

RSSフィード

ユーザーごとのマイクロポストをRSSフィードする機能を実装してください。次にステータスフィードをRSSフィードする機能も実装し、
余裕があればフィードに認証スキームも追加してアクセスを制限してみてください。

REST API

多くのWebサイトはAPI (Application Programmer Interface)を
公開しており、第三者のアプリケーションからリソースのget/post/put/deleteが行えるようになっています。
サンプルアプリケーションにもこのようなREST APIを実装してください。解決のヒントは、respond_toブロックをコントローラーの多くのアクションに追加することです。
このブロックはXMLをリクエストされたときに応答します。
セキュリティには十分注意してください。
認可されたユーザーにのみAPIアクセスを許可する必要があります。

検索機能

現在のサンプルアプリケーションには、ユーザーの一覧ページを端から探す、もしくは他のユーザーのフィードを表示する以外に他のユーザーを検索する手段がありません。
この点を強化するために、検索機能を実装してください。
続いて、マイクロポストを検索する機能も追加してください
(ヒント: まずは自分自身で検索機能に関する情報を探してみましょう。難しければ、@budougumi0617 さんの簡単な検索フォームの実装例を参考にしてください)。

他の拡張機能

上記の他にも、「いいね機能」「シェア機能」「minitestの代わりにRSpecで書き直す」「erbの代わりにHamlで書き直す」
「エラーメッセージをI18nで日本語化する」「オートコンプリート機能」といったアイデアがありそうです。

14.4.3 本章のまとめ

1:has_many :throughを使うと、複雑なデータ関係をモデリングできる

2:has_manyメソッドには、クラス名や外部キーなど、いくつものオプションを渡すことができる

3:適切なクラス名と外部キーと一緒に
has_many/has_many :throughを使うことで、
能動的関係 (フォローする) や受動的関係 (フォローされる)がモデリングできた

4:ルーティングは、ネストさせて使うことができる

5:whereメソッドを使うと、
柔軟で強力なデータベースへの問い合わせが作成できる

6:Railsは (必要に応じて) 低級なSQLクエリを呼び出すことができる

7:本書で学んだすべてを駆使することで、フォローしているユーザーのマイクロポスト一覧をステータスフィードに表示させることができた

has_many through
多対多の関係性を定義する関連付けメソッド。

source
has_manyに対してパラメータを与えるオプション。
sourceオブションで与えた値は配列の元を表しているので、実際の配列のインデックスは変わらない。

collection
コレクションルーティングを追加するメソッド。
idを指定せずに全てのメンバーを表示したりできる

Rails-tutorialのまとめ14.25(FollowingとFollowersページ)

その14.2から続く

14.2.3 [Following] と [Followers] ページ

フォローしているユーザーを表示するページと、フォロワーを表示するページは、
いずれもプロフィールページとユーザー一覧ページを合わせたような作りになるという点で似ています。
どちらにもフォローの統計情報などのユーザー情報を
表示するサイドバーと、ユーザーのリストがあります。
さらに、サイドバーには小さめのユーザープロフィール画像のリンクを格子状に並べて表示する予定です。

フォローしているユーザーのリンクとフォロワーのリンクを
動くようにすることでTwitterに倣って、
どちらのページでもユーザーのログインを要求するようにします。

フォローしているユーザーのリンクとフォロワーのリンクを動くようにすることです。 Twitterに倣って、どちらのページでもユーザーのログインを要求するようにします。そこで前回のアクセス制御と同様に、まずはテストから書いていきましょう。

フォロー/フォロワーページの認可をテストする red
test/controllers/users_controller_test.rb

require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest

 def setup
  @user = users(:michael)
  @other_user = users(:archer)
 end
.
.
.
 test "should redirect following when not logged in" do
  get following_user_path(@user)
  assert_redirected_to login_url
 end

 test "should redirect followers when not logged in" do
  get followers_user_path(@user)
  assert_redirected_to login_url
 end
end

この実装には1つだけトリッキーな部分があります。
それはUsersコントローラに2つの新しいアクションを追加する必要があるということです。

定義した2つのルーティングにもとづいており、これらはそれぞれfollowingおよびfollowersと呼ぶ必要があります。
それぞれのアクションでは、タイトルを設定し、ユーザーを検索し、@user.followingまたは@user.followersからデータを取り出し、
ページネーションを行なって、ページを出力する必要があります。

followingアクションとfollowersアクション red
app/controllers/users_controller.rb

class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
                                        :following, :followers]
  .
  .
  .
  def following
    @title = "Following"
    @user  = User.find(params[:id])
    @users = @user.following.paginate(page: params[:page])
    render 'show_follow'
  end

  def followers
    @title = "Followers"
    @user  = User.find(params[:id])
    @users = @user.followers.paginate(page: params[:page])
    render 'show_follow'
  end

  private
  .
  .
  .
end

Railsは慣習に従って、アクションに対応するビューを暗黙的に呼び出します。
例えば、showアクションの最後でshow.html.erbを呼び出す、といった具合です。

renderを明示的に呼び出し、show_followという同じビューを出力しています。つまり、作成が必要なビューはこれ1つです。
renderで呼び出しているビューが同じである理由は、
ERbはどちらの場合でもほぼ同じであり、で両方の場合をカバーできるためです。

↓フォローしているユーザーとフォロワーの両方を表示するshow_followビュー green
app/views/users/show_follow.html.erb

<% provide(:title, @title) %>
<div class="row">
 <aside class="col-md-4">
  <section class="user_info">
   <%= gravatar_for @user %>
   <h1><%= @user.name %></h1>
   <span><%= link_to "view my profile", @user %></span>
   <span><b>Microposts:</b> <%= @user.microposts.count %></span>
  </section>
 <section class="stats">
  <%= render 'shared/stats' %>
   <% if @users.any? %>
    <div class="user_avatars">
     <% @users.each do |user| %>
      <%= link_to gravatar_for(user, size: 30), user %>
   <% end %>
    </div>
   <% end %>
  </section>
 </aside>
<div class="col-md-8">
 <h3><%= @title %></h3>
  <% if @users.any? %>
   <ul class="users follow">
    <%= render @users %>
   </ul>
  <%= will_paginate %>
 <% end %>
 </div>
</div>

 

続きを読む