Ruby on Rails チュートリアル
-
第3版 目次
- 第1章 ゼロからデプロイまで
- 第2章 Toyアプリケーション
- 第3章 ほぼ静的なページの作成
- 第4章 Rails風味のRuby
- 第5章 レイアウトを作成する
- 第6章 ユーザーのモデルを作成する
- 第7章 ユーザー登録
- 第8章 ログイン、ログアウト
- 第9章 ユーザーの更新・表示・削除
- 第10章 アカウント有効化とパスワード再設定
- 第11章 ユーザーのマイクロポスト
- 第12章 ユーザーをフォローする
|
||
第3版 目次
|
||
最新版を読む |
Ruby on Rails チュートリアル
プロダクト開発の0→1を学ぼう
下記フォームからメールアドレスを入力していただくと、招待リンクが記載されたメールが届きます。リンクをクリックし、アカウントを有効化した時点から『30分間』解説動画のお試し視聴ができます。
メール内のリンクから視聴を開始できます。
第3版 目次
- 第1章 ゼロからデプロイまで
- 第2章 Toyアプリケーション
- 第3章 ほぼ静的なページの作成
- 第4章 Rails風味のRuby
- 第5章 レイアウトを作成する
- 第6章 ユーザーのモデルを作成する
- 第7章 ユーザー登録
- 第8章 ログイン、ログアウト
- 第9章 ユーザーの更新・表示・削除
- 第10章 アカウント有効化とパスワード再設定
- 第11章 ユーザーのマイクロポスト
- 第12章 ユーザーをフォローする
第12章 ユーザーをフォローする
この章では、他のユーザーをフォロー (およびフォロー解除) できるソーシャルレイヤーを追加し、各ユーザーのHomeページに、現在フォロー中のユーザーのステータスフィードを表示できるようにして、サンプルアプリケーションのコアを完成させます。まずは、ユーザー間の関係性をどうモデリングするかについて学びます (12.1)。その後、 モデリング結果に対応するWebインターフェースを実装していきます (12.2)。このとき、Webインターフェースの例としてAjaxについても紹介します。最後に、ステータスフィードの完成版を実装します (12.3)。
この最終章では、本書の中で最も難易度の高い手法をいくつか使用しています。その中には、ステータスフィード作成のためにRuby/SQLを「だます」テクニックも含まれます。この章の例全体にわたって、これまでよりも複雑なデータモデルを使用しています。ここで学んだデータモデルは、今後自分用のWebアプリケーションを開発するときに必ず役に立ちます。また、本書を卒業して実際の開発に携わるときのために、12.4で役立つリソース集 (読み物ガイド) についても紹介します。
この章で扱っている手法は本書全体の中で最も難易度が高いので、理解を助けるため、コードを書く前にはいったん立ち止まってインターフェイスを探検することにします。これまでの章と同様、最初にモックアップを示します1。ページ操作の全体的なフローは次のようになります。あるユーザー (John Calvin) は自分のプロファイルページを最初に表示し (図12.1)、フォローするユーザーを選択するためにUsersページ (図12.2) に移動します。Calvinは2番目のユーザーThomas Hobbes (図12.3) を表示し、[Follow] ボタンを押してフォローします。これにより、[Follow] ボタンが [Unfollow] に変わり、Hobbes の [followers] カウントが1人増えます (図12.4)。CalvinがHomeページに戻ると、[following] カウントが1人増え、Hobbesのマイクロポストがステータスフィードに表示されるようになっていることがわかります (図12.5)。この節では、以後このフローの実現に専念します。
12.1 Relationshipモデル
ユーザーをフォローする機能を実装する第一歩は、データモデルを構成することです。ただし、これは見た目ほど単純ではありません。素朴に考えれば、has_many
(1対多) リレーションシップはこんな感じでできそうな気がします。1人のユーザーが複数のユーザーをhas_many
としてフォローし 、1人のユーザーに複数のフォロワーがいることをhas_many
で表す、という具合です。この後で説明しますが、この方法ではたちまち壁に突き当たってしまいます。これを解決するためのhas_many through
(多対多の関係を表すのに使用) についてもこの後で説明します。
Gitユーザーはこれまで同様新しいトピックブランチを作成してください。
$ git checkout master
$ git checkout -b following-users
12.1.1 データモデルの問題 (および解決策)
ユーザーをフォローするデータモデル構成のための第一歩として、典型的な場合を検討してみましょう。あるユーザーが、別のユーザーをフォローしているところを考えてみましょう。具体例を挙げると、CalvinはHobbesをフォローしています。これを逆から見れば、HobbesはCalvinからフォローされています。CalvinはHobbesから見ればフォロワー (follower)であり、HobbesはCalvinによってフォローされている (followed) ことになります。Railsにおけるデフォルトの複数形の慣習に従えば、あるユーザーをフォローしているすべてのユーザーの集合はfollowersとなり、user.followers
はそれらのユーザーの配列を表すことになります。残念なことに、この名前付けは逆についてはうまくいきません (Railsのというより英語の都合ですが)。フォローされているすべてのユーザーの集合は、このままではfollowedsとなってしまい、英語の文法からも外れるうえに非常に見苦しいものになってしまいます。そこで、今Twitterの慣習にならい、本チュートリアルではfollowingという呼称を採用します (例: “50 following, 75 followers”)。したがって、あるユーザーがフォローしているすべてのユーザーの集合はcalvin.following
となります。
これにより、図12.6のようにfollowing
テーブルと has_many
関連付けを使用して、フォローしているユーザーのモデリングができます。user.following
はユーザーの集合でなければならないため、following
テーブルのそれぞれの行は、followed_id
で識別可能なユーザーでなければなりません (これはfollower_id
の関連付けについても同様です)2。さらに、それぞれの行はユーザーなので、これらのユーザーに名前やパスワードなどの属性も追加する必要があるでしょう。
図12.6のデータモデルの問題点は、非常に無駄が多いことです。各行には、フォローしているユーザーのidのみならず、名前やメールアドレスまであります。これらはいずれもusersテーブルに既にある
ものばかりです。さらによくないことに、フォロワー (followers) の方をモデリングするときにも、同じぐらい無駄の多いfollowers
テーブルを別に作成しなければならなくなってしまいます。結論としては、このデータモデルはメンテナンスの観点から見て悪夢です。ユーザー名を変更するたびに、users
テーブルのそのレコードだけでなく、following
テーブルとfollowers
テーブルの両方について、そのユーザーを含むすべての行を更新しなければならなくなります。
この問題の根本は、必要な抽象化を行なっていないことです。正しいモデルを見つけ出す方法の1つは、Webアプリケーションにおけるfollowingの動作をどのように実装するかをじっくり考えることです。7.1.2において、RESTアーキテクチャは、作成されたり削除されたりするリソースに関連していたことを思い出してください。ここから、2つの疑問が生じます。1. あるユーザーが別のユーザーをフォローするとき、何が作成されるのでしょうか。2. あるユーザーが別のユーザーをフォロー解除するとき、何が削除されるのでしょうか。この点を踏まえて考えると、この場合アプリケーションによって作成または削除されるのは、つまるところ2人のユーザーの「関係 (リレーションシップ)」であることがわかります。つまり、1人のユーザーは1対多の関係を持つことができ、さらにユーザーはリレーションシップを経由して多くのfollowing
(またはfollowers
) と関係を持つことができるということです。
このデータモデルには他にも解決しなくてはいけない問題があります。Facebookのような友好関係 (Friendships) では本質的に左右対称のデータモデルが成り立ちますが、Twitterのようなフォロー関係では左右非対称の性質があります。すなわち、CalvinはHobbesをフォローしていても、HobbesはCalvinをフォローしていないといった関係性が成り立つのです。このような左右非対称な関係性を見分けるために、それぞれを能動的関係 (Active Relationship)と受動的関係 (Passive Relationship)と呼ぶことにします。たとえば先ほどの事例のような、CalvinがHobbesをフォローしているが、HobbesはCalvinをフォローしていない場合では、CalvinはHobbesに対して「能動的関係」を持っていることになります。逆に、HobbesはCalvinに対して「受動的関係」を持っていることになります3。
まずは、フォローしているユーザーを生成するために、能動的関係に焦点を当てていきます (受動的関係については12.1.5で考えていきます)。先ほどの図12.6は実装のヒントになりました。フォローしているユーザーはfollowed_id
があれば識別することができるので、先ほどのfollowing
テーブルをactive_relationships
テーブルと見立ててみましょう。ただしユーザー情報は無駄なので、ユーザーid以外の情報は削除します。そして、followed_id
をとおして、users
テーブルのフォローされているユーザーを見つけるようにします。このデータモデルの模式図にすると、図12.7のようになります。
能動的関係も受動的関係も、最終的にはデータベースの同じテーブルを使うことになります。したがって、テーブル名にはこの「関係」を表す「relationship」を使いましょう。モデル名も同様にして、Relationshipモデルとします。作成したRelationshipデータモデルを図12.8に示します。1つのrelationshipテーブルを使って2つのモデル (能動的関係と受動的関係) をシミュレートする方法については、12.1.4で説明します。
このデータモデルを実装するために、まずは次のように図12.8に対応したマイグレーションを生成します。
$ rails generate model Relationship follower_id:integer followed_id:integer
このリレーションシップは今後follower_id
とfollowed_id
で頻繁に検索することになるので、リスト12.1に示したように、それぞれのカラムにインデックスを追加します。
relationships
テーブルにインデックスを追加する db/migrate/[timestamp]_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :followed_id
t.timestamps null: false
end
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
リスト12.1では複合キーインデックスという行もあることに注目してください。これは、follower_id
とfollowed_id
の組み合わせが必ずユニークであることを保証する仕組みです。これにより、あるユーザーが同じユーザーを2回以上フォローすることを防ぎます。(リスト6.28でメールアドレスの一意性を保証したり、リスト11.1で使った複合キーインデックスと比較してみてください。) もちろん、このような重複 (2回以上フォローすること) が起きないよう、インターフェイス側の実装でも注意を払います(12.1.4)。しかし、ユーザーが何らかの方法で (たとえばcurl
などのコマンドラインツールを使用して) Relationshipのデータを操作するようなことも起こり得ます。そのような場合でも、一意なインデックスを追加していれば、エラーを発生させて重複を防ぐことができます。
relationships
テーブルを作成するために、いつものようにデータベースのマイグレーションを行います。
$ bundle exec rake db:migrate
12.1.2 User/Relationshipの関連付け
フォローしているユーザーとフォロワーを実装する前に、UserとRelationshipの関連付けを行います。1人のユーザーにはhas_many
(1対多) のリレーションシップがあり、このリレーションシップは2人のユーザーの間の関係なので、フォローしているユーザーとフォロワーの両方に属します (belongs_to
)。
11.1.3のマイクロポストのときと同様、以下のようなユーザー関連付けのコードを使用して新しいリレーションシップを作成します。
user.active_relationships.build(followed_id: ...)
この時点で、アプリケーションコードは11.1.3のようになるのではないかと予測した方もいるかもしれません。実際似ているのですが、2つの大きな違いがあります。
まずは1つ目の違いについてです。以前、ユーザーとマイクロポストの関連付けをしたときは、次のように書きました。
class User < ActiveRecord::Base
has_many :microposts
.
.
.
end
引数の:microposts
シンボルから、Railsはこれに対応するMicropostモデルを探し出し、見つけることができました4。しかし今回のケースで同じように書くと、
has_many :active_relationships
となってしまい、(ActiveRelationshipモデルを探してしまい) Relationshipモデルを見つけることができません。このため、今回のケースでは、Railsに探して欲しいモデルのクラス名を明示的に伝える必要があります。
2つ目の違いは、先ほどの逆のケースについてです。以前はMicropostモデルで
class Micropost < ActiveRecord::Base
belongs_to :user
.
.
.
end
このように書きました。microposts
テーブルにはuser_id
属性があるので、これを辿って対応する所有者 (ユーザー) を特定することができました (11.1.1)。データベースの2つのテーブルを繋ぐとき、このようなidは外部キー (foreign key)と呼びます。すなわち、Userモデルに繋げる外部キーが、Micropostモデルのuser_id
属性ということです。この外部キーの名前を使って、Railsは関連付けの推測をしています。具体的には、Railsはデフォルトでは外部キーの名前を<class>_id
といったパターンとして理解し、 <class>
に当たる部分からクラス名 (正確には小文字に変換されたクラス名) を推測します5。ただし、先ほどはユーザーを例として扱いましたが、今回のケースではフォローしているユーザーをfollower_id
という外部キーを使って特定しなくてはなりません。また、followerというクラス名は存在しないので、ここでもRailsに正しいクラス名を伝える必要が発生します。
先ほどの説明をコードにまとめると、UserとRelationshipの関連付けはリスト12.2とリスト12.3のようになります。
has_many
) の関連付けを実装する app/models/user.rb
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
.
.
.
end
(ユーザーを削除したら、ユーザーのリレーションシップも同時に削除される必要があります。そのため、関連付けにdependent: :destroy
も追加しています。)
belongs_to
の関連付けを追加する app/models/relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
end
なお、follower
の関連付けについては、12.1.5に入るまでは使いません。しかしfollowerとfollowedを対称的に実装しておくことで、構造に対する理解は容易になるはずです。
リスト12.2とリスト12.3で定義した関連付けにより、表11.1で以前紹介したような多くのメソッドが使えるようになりました。今回使えるようになったメソッドを表12.1に示します。
メソッド | 用途 |
active_relationship.follower |
フォロワーを返します |
active_relationship.followed |
フォローしているユーザーを返します |
user.active_relationships.create(followed_id: user.id) |
user を紐付けて能動的関係を作成/登録する
|
user.active_relationships.create!(followed_id: user.id) |
user を紐付けて能動的関係を作成/登録する (失敗時にエラーを出力) |
user.active_relationships.build(followed_id: user.id) |
user と紐付けた新しいRelationshipオブジェクトを返す
|
12.1.3 Relationshipのバリデーション
先に進む前に、Relationshipモデルの検証を追加して完全なものにしておきましょう。テスト (リスト12.4)とアプリケーションコード (リスト12.5) は素直な作りです。ただし、ユーザー用のfixtureファイル (リスト6.29) と同じように、生成されたリレーションシップ用のfixtureでは、マイグレーション (リスト12.1) で制約させた一意性を満たすことができません。ということで、ユーザーのときと同じで (リスト6.30でfixtureの内容を削除したように)、今の時点では生成されたリレーションシップ用のfixtureファイルも空にしておきましょう (リスト12.6)。
require 'test_helper'
class RelationshipTest < ActiveSupport::TestCase
def setup
@relationship = Relationship.new(follower_id: 1, followed_id: 2)
end
test "should be valid" do
assert @relationship.valid?
end
test "should require a follower_id" do
@relationship.follower_id = nil
assert_not @relationship.valid?
end
test "should require a followed_id" do
@relationship.followed_id = nil
assert_not @relationship.valid?
end
end
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
# empty
これにより、テストはGREENになるはずです。
$ bundle exec rake test
12.1.4 フォローしているユーザー
いよいよRelationshipの関連付けの核心、following
とfollowers
に取りかかります。今回はhas_many through
を使用します。図11.7のように、1人のユーザーにはいくつもの「フォローする/される (多対多)」のリレーションシップがあります。デフォルトのhas_many through
という関連付けでは、Railsはモデル名(単数形)に対応する外部キーを探します。つまり、次のコードでは、
has_many :followeds, through: :active_relationships
Railsは“followeds”というシンボル名を見て、これを“followed”という単数形に変え、 relationships
テーブルのfollowed_id
を使って対象のユーザーを取得してきます。しかし、12.1.1で指摘したように、user.followeds
という名前は英語として不適切です。代わりに、user.following
という名前を使いましょう。そのためには、Railsのデフォルトを上書きする必要があります。ここでは:source
パラメーター (リスト12.10) を使用し、「following
配列の元はfollowed
idの集合である」ということを明示的にRailsに伝えます。
following
の関連付けを追加する app/models/user.rb
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
.
.
.
end
リスト12.8で定義した関連付けにより、フォローしているユーザーを配列の様に扱えるようになりました。たとえば、include?
メソッド (4.3.1) を使ってフォローしているユーザーの集合を調べてみたり、関連付けを通してオブジェクトを探しだせるようになります。
user.following.include?(other_user)
user.following.find(other_user)
following
メソッドで配列のように扱えるだけでも便利ですが、Railsは単純な配列ではなく、もっと賢くこの集合を扱っています。たとえば、次のようなコードでは
following.include?(other_user)
フォローしている全てのユーザーをデータベースから取得し、その集合に対してinclude?
メソッドを実行しているように見えますが、しかし実際にはデータベースの中で直接比較をするように配慮しています。(11.2.1でも説明したように、次のようなコードは
user.microposts.count
データベースの中で合計を計算したほうが高速になることを思い出してください。)
次に、followingで取得した集合をより簡単に取り扱うために、follow
やunfollow
といった便利メソッドを追加しましょう。これらのメソッドは、たとえばuser.follow(other_user)
といった具合に使います。さらに、これに関連するfollowing?
論理値メソッドも追加し、あるユーザーが誰かをフォローしているかどうかを確認できるようにします6。
今回は、こういったメソッドはテストから先に書いていきます。というのも、Webインターフェイスなどで便利メソッドを使うのはまだ先なので、すぐに使える場面がなく、実装した手応えを得にくいからです。一方で、Userモデルに対するテストを書くのは簡単かつ今すぐできます。そのテストの中で、これらのメソッドを使っていきます。具体的には、following?
メソッドであるユーザーをまだフォローしていないことを確認、follow
メソッドを使ってそのユーザーをフォロー、 following?
メソッドを使ってフォロー中になったことを確認、 最後にunfollow
メソッドでフォロー解除できたことを確認、といった具合でテストをしてきます。このテストをコードにすると、リスト12.9のようになります。
require 'test_helper'
class UserTest < ActiveSupport::TestCase
.
.
.
test "should follow and unfollow a user" do
michael = users(:michael)
archer = users(:archer)
assert_not michael.following?(archer)
michael.follow(archer)
assert michael.following?(archer)
michael.unfollow(archer)
assert_not michael.following?(archer)
end
end
表12.1のメソッドを参考にしながら、following
による関連付けを使ってfollow
、unfollow
、following?
メソッドを実装していきましょう (リスト12.10)。このとき、可能な限りself
(user自身を表すオブジェクト) を省略している点に注目してください。
class User < ActiveRecord::Base
.
.
.
def feed
.
.
.
end
# ユーザーをフォローする
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# ユーザーをアンフォローする
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# 現在のユーザーがフォローしてたらtrueを返す
def following?(other_user)
following.include?(other_user)
end
private
.
.
.
end
リスト12.10のコードを追加することで、テストスイートはGREENになるはずです。
$ bundle exec rake test
12.1.5 フォロワー
リレーションシップというパズルの最後の一片は、user.followers
メソッドを追加することです。これは上のuser.following
メソッドと対になります。図12.7を見ていて気付いた方もいると思いますが、フォロワーの配列を展開するために必要な情報は、relationships
テーブルに既にあります (リスト12.2コードをとおしてactive_relationships
テーブルを扱っています)。実は、follower_id
とfollowed_id
を入れ替えるだけで、フォロワーについてもユーザーのフォローのときとまったく同じ方法が使用できます。これはpassive_relationships
とactive_relationships
についても同じです。したがって、データモデルは図12.9のようになります。
図12.9を参考にしたデータモデルの実装をリスト12.12に示しますが、この実装はリスト12.8とまさに類似しています。
user.followers
を実装する app/models/user.rb
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
.
.
.
end
一点、リスト12.12で注意すべき箇所は、次のように参照先 (followers
) を指定するための:source
キーを省略してもよかったという点です。
has_many :followers, through: :passive_relationships
これは:followers
属性の場合、Railsが “followers” を単数形にして自動的に外部キーfollower_id
を探してくれるからです。リスト12.8と違って必要のない:source
キーをそのまま残しているのは、has_many :following
との類似性を強調させるためです。
次に、followers.include?
メソッドを使って先ほどのデータモデルをテストしていきます。テストコードはリスト12.13のとおりです。(リスト12.13では、following?
と対照的なfollowed_by?
メソッドを使ってもよかったのですが、サンプルアプリケーションで実際に使う場面がなかったので省略しました。)
followers
に対するテスト GREEN test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
.
.
.
test "should follow and unfollow a user" do
michael = users(:michael)
archer = users(:archer)
assert_not michael.following?(archer)
michael.follow(archer)
assert michael.following?(archer)
assert archer.followers.include?(michael)
michael.unfollow(archer)
assert_not michael.following?(archer)
end
end
リスト12.13ではリスト12.9に1行だけ追加していますが、実際には多くの処理が正しく動いていなければパスしません。つまり、リスト12.12の実装に対するテストは、実装の影響を受けやすいテストだといえます。
この時点で、全てのテストがGREENになるはずです。
$ bundle exec rake test
12.2 フォローしているユーザー用のWebインターフェイス
12.1ではやや複雑なデータモデリングの技術を説明しました。理解するのに時間がかかってしまっても大丈夫なので、安心してください。実際、使用されたさまざまな関連付けを理解するのに一番良いのは、Webインターフェイスで使用してみることです。
この章の最初に、フォローしているユーザーのページ表示の流れについて説明しました。この節では、モックアップで示したようにフォロー/フォロー解除の基本的なインターフェイスを実装します。また、フォローしているユーザーと、フォロワーにそれぞれ表示用のページを作成します。12.3では、ユーザーのステータスフィードを追加して、サンプルアプリケーションを完成させます。
12.2.1 フォローしているユーザーのサンプルデータ
1つ前の章のときと同じように、サンプルデータを自動作成するRakeタスクを使用して、データベースに実際のデータを登録するのがやはり便利です。先にサンプルデータを自動作成しておけば、Webページの見た目のデザインから先にとりかかることができ、バックエンドの機能の実装をこの節の後に回すことができます。
リスト12.14は、リレーションシップのサンプルデータを生成するためのコードです。ここでは、最初のユーザーにユーザー3からユーザー51までをフォローさせ、それから逆にユーザー4からユーザー41に最初のユーザーをフォローさせます。ソースを見るとわかるように、このような設定を自由に行うことができます。こうしてリレーションシップを作成しておけば、アプリケーションのインターフェイスを開発するには十分です。
# ユーザー
User.create!(name: "Example User",
email: "example@railstutorial.org",
password: "foobar",
password_confirmation: "foobar",
admin: true,
activated: true,
activated_at: Time.zone.now)
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}@railstutorial.org"
password = "password"
User.create!(name: name,
email: email,
password: password,
password_confirmation: password,
activated: true,
activated_at: Time.zone.now)
end
# マイクロポスト
users = User.order(:created_at).take(6)
50.times do
content = Faker::Lorem.sentence(5)
users.each { |user| user.microposts.create!(content: content) }
end
# リレーションシップ
users = User.all
user = users.first
following = users[2..50]
followers = users[3..40]
following.each { |followed| user.follow(followed) }
followers.each { |follower| follower.follow(user) }
リスト12.14を実行してデータベース上のサンプルデータを作り直すために、いつものコマンドを実行しましょう。
$ bundle exec rake db:migrate:reset
$ bundle exec rake db:seed
12.2.2 統計とフォロー用フォーム
これでサンプルユーザーに、フォローしているユーザーとフォロワーができました。ユーザープロファイルページとHomeページを更新してこれを反映しましょう。最初に、プロファイルページとHomeページに、フォローしているユーザーとフォロワーの統計情報を表示するためのパーシャルを作成します。次に、フォロー用とフォロー解除用のフォームを作成します。それから、フォローしているユーザーの一覧 ("following") とフォロワーの一覧 ("followers") を表示する専用のページを作成します。
12.1.1で指摘したように、Twitterの慣習にしたがってフォロー数の単位には“following”を使い、たとえば“50 following”といった具合に表示します。この単位は図12.1のモックアップの一部でも既に使われていました。該当箇所を拡大して図12.10に再掲します。
図12.10の統計情報には、現在のユーザーがフォローしている人数と、現在のフォロワーの人数が表示されています。それぞれの表示はリンクになっており、専用の表示ページに移動できます。第5章では、これらのリンクはダミーテキスト’#’
を使用して無効にしていました。しかしルーティングについての知識もだいぶ増えてきたので、今回は実装することにしましょう。実際のページ作成は12.2.3まで行いませんが、ルーティングは今実装します (リスト12.15)。このコードでは、resources
ブロックの内側で:member
メソッドを使用しています。これは初登場ですので、どんな動作をするのか推測してみてください。
following
アクションとfollowers
アクションを追加する config/routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users do
member do
get :following, :followers
end
end
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
end
この場合のURLは/users/1/followingや/users/1/followersのようになるのではないかと推測した方もいると思います。そして、リスト12.15のコードはまさにそれを行なっているのです。また、どちらもデータを表示するページなので、適切なHTTPメソッドはGETリクエストになります。したがって、get
メソッドを使って適切なレスポンスを返すようにします。ちなみに、member
メソッドを使うとユーザーidが含まれているURLを扱うようになりますが、idを指定せずにすべてのメンバーを表示するには、以下のようにcollection
メソッドを使用します。
resources :users do
collection do
get :tigers
end
end
このコードは/users/tigersというURLに応答します (アプリケーションにあるすべてのtigerのリストを表示します)7。
リスト12.15によって生成されるルーティングテーブルを表12.2に示します。ここにある、フォローしているユーザー用とフォロワー用の名前付きルートをこの後使用します。
HTTPリクエスト | URL | アクション | 名前付きルート |
GET | /users/1/following | following |
following_user_path(1) |
GET | /users/1/followers | followers |
followers_user_path(1) |
ルーティングを定義したので、統計情報のパーシャルを実装する準備が整いました。このパーシャルでは、divタグの中に2つのリンクを含めるようにします (リスト12.16)。
<% @user ||= current_user %>
<div class="stats">
<a href="<%= following_user_path(@user) %>">
<strong id="following" class="stat">
<%= @user.following.count %>
</strong>
following
</a>
<a href="<%= followers_user_path(@user) %>">
<strong id="followers" class="stat">
<%= @user.followers.count %>
</strong>
followers
</a>
</div>
このパーシャルはユーザー表示ページとHomeページの両方に表示されるので、リスト12.16の最初の行では、以下のコードを使用して適切な方を選択しています。
<% @user ||= current_user %>
コラム8.1でも説明したとおり、@user
が nil
でない場合 (つまりプロファイルページの場合) は何もせず、nilの場合 (つまりHomeページの場合) には@user
にカレントユーザーを設定します。フォローしているユーザーの人数と、フォロワーの人数は、以下の関連付けを使用して計算されます。
@user.following.count
さらに、以下のコードは
@user.followers.count
リスト11.23のマイクロポストのコードと比較してみましょう。あのときは次のように書きました。
@user.microposts.count
このコードを使用してマイクロポストの合計数を表示します。(以前同様、高速化のためにRailsはデータベースの中で合計を計算するようにしています。)
一部の要素で、以下のようにCSS idを指定していることにもぜひ注目してください。
<strong id="following" class="stat">
...
</strong>
こうしておくと、12.2.5でAjaxを実装するときに便利です。そこでは、一意のidを指定してページ要素にアクセスしています。
統計情報パーシャルができあがりました。Homeページにこの統計情報を表示するのは、リスト12.17のように簡単にできます。
<% if logged_in? %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="stats">
<%= render 'shared/stats' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
<div class="col-md-8">
<h3>Micropost Feed</h3>
<%= render 'shared/feed' %>
</div>
</div>
<% else %>
.
.
.
<% end %>
統計情報にスタイルを与えるために、リスト12.18のようにSCSSを追加しましょう (なお、このSCSSにはこの章で使用するスタイルがすべて含まれています)。変更の結果、Homeページは図12.11のようになります。
.
.
.
/* sidebar */
.
.
.
.gravatar {
float: left;
margin-right: 10px;
}
.gravatar_edit {
margin-top: 15px;
}
.stats {
overflow: auto;
margin-top: 0;
padding: 0;
a {
float: left;
padding: 0 10px;
border-left: 1px solid $gray-lighter;
color: gray;
&:first-child {
padding-left: 0;
border: 0;
}
&:hover {
text-decoration: none;
color: blue;
}
}
strong {
display: block;
}
}
.user_avatars {
overflow: auto;
margin-top: 10px;
.gravatar {
margin: 1px 1px;
}
a {
padding: 0;
}
}
.users.follow {
padding: 0;
}
/* forms */
.
.
.
この後すぐ、プロファイルにも統計情報パーシャルを表示しますが、今のうちにリスト11.23のようにフォロー/フォロー解除ボタン用のパーシャルも作成しましょう。
<% unless current_user?(@user) %>
<div id="follow_form">
<% if current_user.following?(@user) %>
<%= render 'unfollow' %>
<% else %>
<%= render 'follow' %>
<% end %>
</div>
<% end %>
このコードは、follow
とunfollow
のパーシャルに作業を振っているだけです。パーシャルでは、Relationshipsリソース用の新しいルーティングが必要です。これを、リスト11.29のMicropostsリソースの例に従って作成しましょう (リスト12.20)。
Rails.application.routes.draw do
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users do
member do
get :following, :followers
end
end
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
end
follow/unfollowパーシャル自体は、リスト12.21とリスト12.22に示します。
<%= form_for(current_user.active_relationships.build) do |f| %>
<div><%= hidden_field_tag :followed_id, @user.id %></div>
<%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
html: { method: :delete }) do |f| %>
<%= f.submit "Unfollow", class: "btn" %>
<% end %>
これらの2つのフォームでは、いずれもform_for
を使用してRelationshipモデルオブジェクトを操作しています。これらの2つのフォームの主な違いは、リスト12.21は新しいリレーションシップを作成するのに対し、リスト12.22は既存のリレーションシップを見つけ出すという点です。すなわち、前者はPOSTリクエストを Relationshipsコントローラに送信してリレーションシップをcreate
(作成) し、後者はDELETEリクエストを送信してリレーションシップをdestroy
(削除) するということです(これらのアクションは12.2.4で実装します)。最終的に、このfollow/unfollowフォームにはボタンしかないことを理解していただけたと思います。しかし、それでもこのフォームはfollowed_id
をコントローラに送信する必要があります。これを行うために、リスト12.21のhidden_field_tag
メソッドを使用します。このメソッドは 以下のフォームのHTMLを生成します。
<input id="followed_id" name="followed_id" type="hidden" value="3" />
10.2.4のリスト10.50で見たように、隠しフィールドのinput
タグを使うことで、ブラウザ上に表示させずに適切な情報を含めることができます。
これで、12.23のようにフォロー用のフォームをユーザープロファイルページにインクルードしてパーシャルを出力できるようになりました。プロファイルには、図12.12および図12.13のようにそれぞれ [Follow]、[Unfollow] ボタンが表示されます。
<% provide(:title, @user.name) %>
<div class="row">
<aside class="col-md-4">
<section>
<h1>
<%= gravatar_for @user %>
<%= @user.name %>
</h1>
</section>
<section class="stats">
<%= render 'shared/stats' %>
</section>
</aside>
<div class="col-md-8">
<%= render 'follow_form' if logged_in? %>
<% if @user.microposts.any? %>
<h3>Microposts (<%= @user.microposts.count %>)</h3>
<ol class="microposts">
<%= render @microposts %>
</ol>
<%= will_paginate @microposts %>
<% end %>
</div>
</div>
これらのボタンはもうすぐ動作するようになります。実はこのボタンの実装には2とおりの方法があります。1つは標準的な方法 (12.2.4)、もう1つはAjaxを使用する方法 (12.2.5) です。でもその前に、フォローしているユーザーとフォロワーを表示するページをそれぞれ作成してHTMLインターフェイスを完成させてしまいましょう。
12.2.3「フォローしているユーザー」ページと「フォロワー」ページ
フォローしているユーザーを表示するページと、フォロワーを表示するページは、いずれもユーザープロファイルページとユーザーインデックスページ (9.3.1) を合わせたような作りになるという点で似ています。どちらにもフォローの統計情報などのユーザー情報を表示するサイドバーと、ユーザーのリストがあります。さらに、サイドバーには小さめのユーザープロフィール画像のリンクを格子状に並べて表示する予定です。この要求に合うモックアップを図12.14 (フォローしているユーザー用) および 図12.15 (フォロワー用) に示します。
ここでの最初の作業は、フォローしているユーザーのリンクとフォロワーのリンクを動くようにすることです。Twitterにならい、どちらのページでもユーザーのログインを要求します。前回のアクセス制御と同様に、まずはテストから書いていきます。今回使うテストはリスト12.24のとおりです。
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
@user = users(:michael)
@other_user = users(:archer)
end
.
.
.
test "should redirect following when not logged in" do
get :following, id: @user
assert_redirected_to login_url
end
test "should redirect followers when not logged in" do
get :followers, id: @user
assert_redirected_to login_url
end
end
この実装には1つだけトリッキーな部分があります。それはUsersコントローラに2つの新しいアクションを追加する必要があるということです。これはリスト12.15で定義した2つのルーティングにもとづいており、これらはそれぞれfollowing
およびfollowers
と呼ぶ必要があります。それぞれのアクションでは、タイトルを設定し、ユーザーを検索し、@user.following
または@user.followers
からデータを取り出し、ページネーションを行なって、ページを出力する必要があります。変更した結果はリスト12.25のようになります。
following
アクションとfollowers
アクション 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
を呼び出すといった具合です。一方で、リスト12.25のいずれのアクションも、render
を明示的に呼び出し、show_follow
という同じビューを出力しています。したがって、作成が必要なビューはこれ1つです。renderで呼び出しているビューが同じである理由は、このERbはどちらの場合でもほぼ同じであり、リスト12.26で両方の場合をカバーできるためです。
show_follow
ビュー 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>
リスト12.25にあるアクションは、2通りの方法でリスト12.26のビューを呼び出します。“following”をとおって描画したビューを図12.16に、“followers”をとおって描画したビューを図12.17に示します。このとき、上のコードではカレントユーザーを一切使っていない点に注目してください。したがって、他のユーザーのフォロワー一覧ページもうまく動きます (図12.18)。
フォロー一覧もフォロワー一覧も動くようになったので、この振る舞いを検証するための2つの統合テストを書いていきましょう。これらの統合テストを基本的なテストに留め、網羅的なテストではありません。5.3.4でも指摘したように、HTML構造を網羅的にチェックするテストは壊れやすく、生産性を逆に落としかねないからです。したがって今回は、正しい数が表示されているかどうかと、正しいURLが表示されているかどうかの2つのテストを書きます。
いつものように、統合テストを生成するところから始めます。
$ rails generate integration_test following
invoke test_unit
create test/integration/following_test.rb
次に、テストデータをいくつか揃えます。リレーションシップ用のfixtureにデータを追加しましょう。11.2.3では、次のように書くことで
orange:
content: "I just ate an orange!"
created_at: <%= 10.minutes.ago %>
user: michael
ユーザーとマイクロポストを関連付けできたことを思い出してください。また、上のコードではユーザー名を書いていましたが、
user: michael
次のようにユーザーidでも関連付けできます。
user_id: 1
この例を参考にしてリレーションシップ用のfixtureにテストデータを追加すると、リスト12.27のようになります。
one:
follower: michael
followed: lana
two:
follower: michael
followed: mallory
three:
follower: lana
followed: michael
four:
follower: archer
followed: michael
リスト12.27のfixtureでは、前半の2つでMichaelがLanaとMalloryをフォローし、後半の2つでLanaとArcherがMichaelをフォローしています。あとは、正しい数かどうかを確認するために、assert_match
メソッド (リスト11.27) を使ってプロフィール画面のマイクロポスト数をテストします。さらに、正しいURLかどうかをテストするコードも加えると、リスト12.28のようになります。
require 'test_helper'
class FollowingTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
log_in_as(@user)
end
test "following page" do
get following_user_path(@user)
assert_not @user.following.empty?
assert_match @user.following.count.to_s, response.body
@user.following.each do |user|
assert_select "a[href=?]", user_path(user)
end
end
test "followers page" do
get followers_user_path(@user)
assert_not @user.followers.empty?
assert_match @user.followers.count.to_s, response.body
@user.followers.each do |user|
assert_select "a[href=?]", user_path(user)
end
end
end
なお、リスト12.28では、次のコードを加えていますが
assert_not @user.following.empty?
このコードは次のコードを確かめるためのテストなので、
@user.following.each do |user|
assert_select "a[href=?]", user_path(user)
end
無意味なテストではないことに注意してください (followers
についても同様です)。
これで、テストがGREENになるはずです。
$ bundle exec rake test
12.2.4 [フォローする] ボタン (標準的な方法)
ビューが整ってきました。いよいよ [フォローする] [フォロー解除する] ボタンを動作させましょう。フォローとフォロー解除はそれぞれリレーションシップの作成と削除に対応しているため、まずはRelationshipsコントローラが必要です。いつものようにコントローラを生成しましょう。
$ rails generate controller Relationships
リスト12.31でも説明しますが、Relationshipsコントローラのアクションでアクセス制御することはそこまで難しくありません。しかし、前回のアクセス制御のときと同様に最初にテストを書き、それをパスするように実装することでセキュリティモデルを確立させていきましょう。今回はまず、コントローラのアクションにアクセスするとき、ログイン済みのユーザーであるかどうかをチェックします。 もしログインしていなければ、ログインページにリダイレクトさせ、Relationshipのカウントが変わっていないことを確認します (リスト12.30)。
require 'test_helper'
class RelationshipsControllerTest < ActionController::TestCase
test "create should require logged-in user" do
assert_no_difference 'Relationship.count' do
post :create
end
assert_redirected_to login_url
end
test "destroy should require logged-in user" do
assert_no_difference 'Relationship.count' do
delete :destroy, id: relationships(:one)
end
assert_redirected_to login_url
end
end
次に、リスト12.30のテストをパスさせるために、logged_in_user
フィルターをRelationshipsコントローラのアクションに対して追加します (リスト12.31)。
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
end
def destroy
end
end
フォロー/フォロー解除ボタンを動かすためには、フォーム (リスト12.21/リスト12.22) から送信されたパラメータを使って、followed_id
に対応するユーザーを見つけてくる必要があります 。その後、見つけてきたユーザーに対して適切にfollow
/unfollow
メソッド (リスト12.10) を使います。このすべてを実装した結果を、リスト12.32に示します。
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
user = User.find(params[:followed_id])
current_user.follow(user)
redirect_to user
end
def destroy
user = Relationship.find(params[:id]).followed
current_user.unfollow(user)
redirect_to user
end
end
リスト12.32を見てみれば、先ほどのセキュリティ問題が実はそれほど重大なものではないことを理解いただけると思います。もしログインしていないユーザーが (curl
などのコマンドラインツールなどを使用して) これらのアクションに直接アクセスするようなことがあれば、current_user
はnil
になり、どちらのメソッドでも2行目で例外が発生します。エラーにはなりますが、アプリケーションやデータに影響は生じません。このままでも支障はありませんが、やはりこのような例外には頼らない方がよいので、上ではひと手間かけてセキュリティのためのレイヤーを追加しました。
これで、フォロー/フォロー解除の機能が完成しました。どのユーザーも、他のユーザーをフォローしたり、フォロー解除したりできます。ブラウザ上でボタンをクリックして、確かめてみてください。(振る舞いを検証する統合テストは12.2.6で実装します。) 2番目のユーザーをフォローする前の状態を図12.19に、フォローした結果を図12.20にそれぞれ示します。
12.2.5 [フォローする] ボタン (Ajax)
フォロー関連の機能の実装はこのとおり完了しましたが、ステータスフィードに取りかかる前にもう一つだけ機能を洗練させてみたいと思います。12.2.4では、Relationshipsコントローラのcreate
アクションと destroy
アクションを単に元のプロファイルにリダイレクトしていました。つまり、ユーザーはプロファイルページを最初に表示し、それからユーザーをフォローし、その後すぐ元のページにリダイレクトされるという流れになります。ユーザーをフォローした後、本当にそのページから離れて元のページに戻らないといけないのでしょうか。この点を考えなおしてみましょう。
これはAjaxを使用することで解決できます。Ajaxを使用すれば、Webページからサーバーに「非同期」で、ページを移動することなくリクエストを送信することができます8。WebフォームにAjaxを採用するのは今や当たり前になりつつあるので、RailsでもAjaxを簡単に実装できるようになっています。フォロー用とフォロー解除用のフォームパーシャルをこれに沿って更新するのは簡単です。以下のコードがあるとします。
form_for
上を以下のように置き換えます。
form_for ..., remote: true
たったこれだけで、Railsは自動的にAjaxを使用します。更新の結果をリスト12.33とリスト12.34に示します。
<%= form_for(current_user.active_relationships.build, remote: true) do |f| %>
<div><%= hidden_field_tag :followed_id, @user.id %></div>
<%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id),
html: { method: :delete },
remote: true) do |f| %>
<%= f.submit "Unfollow", class: "btn" %>
<% end %>
ERbによって実際に生成されるHTMLはそれほど重要ではありませんが、興味がある方のために、次にその核心部分をお見せします。
<form action="/relationships/117" class="edit_relationship" data-remote="true"
id="edit_relationship_117" method="post">
.
.
.
</form>
ここでは、formタグの内部でdata-remote="true"
変数を設定しています。これは、JavaScriptによるフォーム操作を許可することをRailsに知らせるためのものです。以前のRailsでは完全なJavaScriptコードを挿入していましたが、Rails 3からこのようにHTMLプロパティを使用して簡単にJavaScriptを使用できます。これは、JavaScriptを前面に出すべからずという哲学に従っています。
フォームの更新が終わったので、今度はこれに対応するRelationshipsコントローラを改造して、Ajaxリクエストに応答できるようにしましょう。こういったリクエストの種類によって応答を場合分けするときは、respond_to
というメソッドを使います。一般的な使い方は、次のような具合です。
respond_to do |format|
format.html { redirect_to user }
format.js
end
この文法は少々変わっていて混乱を招く可能性がありますが、上の (ブロック内の) コードのうち、いずれかの1行が実行されるという点が重要です (このためrespond_to
メソッドは、上から順に実行する逐次処理というより、if文を使った分岐処理に近いイメージです)。RelationshipsコントローラでAjaxに対応させるために、respond_to
メソッドをcreate
アクションとdestroy
アクション (リスト12.32) にそれぞれ追加してみましょう。変更の結果をリスト12.35に示します。このとき、ユーザーのローカル変数 (user
) をインスタンス変数 (@user
) に変更した点に注目してください。これは、リスト12.32のときはインスタンス変数は必要なかったのですが、リスト12.33やリスト12.34を実装したことにより、インスタンス変数が必要になったためです。
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
end
リスト12.35でAjaxリクエストに対応したので、今度はブラウザ側でJavaScriptが無効になっていた場合 (Ajaxリクエストが送れない場合) でもうまく動くようにします (リスト12.36)。
require File.expand_path('../boot', __FILE__)
.
.
.
module SampleApp
class Application < Rails::Application
.
.
.
# 認証トークンをremoteフォームに埋め込む
config.action_view.embed_authenticity_token_in_remote_forms = true
end
end
一方で、JavaScriptが有効になっていても、まだ十分に対応できていない部分があります。というのも、Ajaxリクエストを受信した場合は、Railsが自動的にアクションと同じ名前を持つJavaScript用の埋め込みRuby (.js.erb
) ファイル (create.js.erb
やdestroy.js.erb
など) を呼び出すからです。ご想像のとおり、これらのファイルではJavaScriptと埋め込みRuby (ERb) をミックスして現在のページに対するアクションを実行することができます。ユーザーをフォローしたときやフォロー解除したときにプロフィールページを更新するために、私たちがこれから作成および編集しなければならないのは、まさにこれらのファイルです。
JS-ERbファイルの内部では、Railsが自動的にjQuery JavaScriptヘルパーを提供します。これにより、DOM (Document Object Model) を使用してページを操作できます。11.4.2で見たように、jQueryライブラリにはDOM操作用の膨大なメソッドが提供されていますが、ここで使用するのはわずか2つです。それにはまず、ドル記号 ($) とCSS idを使用してDOM要素にアクセスする文法について知る必要があります。たとえば、follow_form
要素を操作するには、以下の文法を使用します。
$("#follow_form")
リスト12.19では、これはフォームを囲むdiv
タグであり、フォームそのものではなかったことを思い出してください。jQueryの文法はCSSの記法から影響を受けており、#
シンボルを使用してCSSのidを指定します。ご想像のとおり、jQueryはCSSと同様、ドット.
を使用してCSSクラスを操作できます。
次に必要なメソッドはhtml
です。これは、引数の中で指定された要素の内側にあるHTMLを更新します。たとえば、フォロー用フォーム全体を"foobar"
という文字列で置き換えるには、以下を使用します。
$("#follow_form").html("foobar")
純粋なJavaScriptと異なり、JS-ERbファイルでは組み込みRuby (ERb) を使用できます。create.js.erb
ファイルでは、フォロー用のフォームをunfollow
パーシャルで更新し、フォロワーのカウントを更新するのにERbを使用しています (もちろんこれは、フォローに成功した場合の動作です)。変更の結果をリスト12.37に示します。このコードではescape_javascript
メソッドを使用していることに注目してください。この関数は、JavaScriptファイル内にHTMLを挿入するときに実行結果をエスケープする (画面に表示しない) ために必要です。
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
$("#followers").html('<%= @user.followers.count %>');
各行の末尾にセミコロン ; があることに注目してください。 これはプログラミング言語によくある文法で、古くは1950年代中ごろに開発されたALGOLまで遡ります。
destroy.js.erb
ファイルの方も同様です (リスト12.38)。
$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
$("#followers").html('<%= @user.followers.count %>');
これらのコードにより、ユーザープロファイルを表示して、ページを更新せずにフォローまたはフォロー解除ができるようになったはずです。
12.2.6 フォローをテストする
フォローボタンが動くようになったので、バグを検知するためのシンプルなテストを書いていきましょう。ユーザーのフォローに対するテストでは、 /relationshipsに対してPOSTリクエストを送り、フォローされたユーザーが1人増えたことをチェックします。具体的なコードは次のとおりです。
assert_difference '@user.following.count', 1 do
post relationships_path, followed_id: @other.id
end
これは標準的なフォローに対するテストではありますが、Ajax版もやり方は大体同じです。Ajaxのテストでは、post
の代わりにxhr :post
を使うだけです。
assert_difference '@user.following.count', 1 do
xhr :post, relationships_path, followed_id: @other.id
end
ここで使っているxhr
(XmlHttpRequest) というメソッドは、Ajaxでリクエストを発行するします。したがって、リスト12.35のrespond_to
では、JavaScriptに対応した行が実行されるようになります。
また、ユーザーをアンフォローするときも構造はほとんど同じで、post
メソッドをdelete
メソッドに置き換えてテストします。つまり、そのユーザーのidとリレーションシップのidを使ってDELETEリクエストを送信し、フォローしている数が1つ減ることを確認します。
assert_difference '@user.following.count', -1 do
delete relationship_path(relationship),
relationship: relationship.id
end
Ajaxの場合は次のとおりです。
assert_difference '@user.following.count', -1 do
xhr :delete, relationship_path(relationship),
relationship: relationship.id
end
これらのテストをまとめた結果を、リスト12.39に示します。
require 'test_helper'
class FollowingTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
@other = users(:archer)
log_in_as(@user)
end
.
.
.
test "should follow a user the standard way" do
assert_difference '@user.following.count', 1 do
post relationships_path, followed_id: @other.id
end
end
test "should follow a user with Ajax" do
assert_difference '@user.following.count', 1 do
xhr :post, relationships_path, followed_id: @other.id
end
end
test "should unfollow a user the standard way" do
@user.follow(@other)
relationship = @user.active_relationships.find_by(followed_id: @other.id)
assert_difference '@user.following.count', -1 do
delete relationship_path(relationship)
end
end
test "should unfollow a user with Ajax" do
@user.follow(@other)
relationship = @user.active_relationships.find_by(followed_id: @other.id)
assert_difference '@user.following.count', -1 do
xhr :delete, relationship_path(relationship)
end
end
end
この時点では、テストはGREENになるはずです。
$ bundle exec rake test
12.3 ステータスフィード
ついに、サンプルアプリケーションの山頂が目の前に現れました。最後の難関、ステータスフィードの実装に取りかかりましょう。この節で扱われている内容は、本書の中でも最も高度なものです。完全なステータスフィードは、11.3.3で扱ったプロトフィードをベースにします。現在のユーザーにフォローされているユーザーのマイクロポストの配列を作成し、現在のユーザー自身のマイクロポストと合わせて表示します。このセクションを通して、複雑さを増したフィードの実装に進んでいきます。これ実現するためには、RailsとRubyの高度な機能の他に、SQLプログラミングの技術も必要です。
手強い課題に挑むのですから、ここで実装すべき内容を慎重に見直すことが重要です。図12.5でお見せしたステータスフィードの最終形を図12.21に再度掲載します。
12.3.1 動機と計画
ステータスフィードの基本的なアイディアはシンプルです。図12.22に、サンプルのmicroposts
データベーステーブルと、それをフィードした結果を示します。図の矢印で示されているように、この目的は、現在のユーザーによってフォローされているユーザーに対応するユーザーidを持つマイクロポストを取り出し、同時に現在のユーザー自身のマイクロポストも一緒に取り出すことです。
どのようにフィードを実装するのかはまだ明確ではありませんが、テストについてはやや明確そうなので、(コラム3.3のガイドラインに従って) まずはテストから書いていきます。このテストで重要なことは、フィードに必要な3つの条件を満たすことです。1) フォローしているユーザーのマイクロポストがフィードに含まれていること。2) 自分自身のマイクロポストもフィードに含まれていること。3) フォローしていないユーザーのマイクロポストがフィードに含まれていないこと。そしてリスト9.43とリスト11.51のfixtureファイルから、MichaelのフィードではLanaと自分自身の投稿が見えていて、Archerの投稿は見えないことがわかります。先ほどの3つの条件をアサーションに変換して、Userモデル (リスト11.44) feed
メソッドがあることに注意しながら、更新したUserモデルに対するテストを書いてみましょう。結果をリスト12.41に示します。
require 'test_helper'
class UserTest < ActiveSupport::TestCase
.
.
.
test "feed should have the right posts" do
michael = users(:michael)
archer = users(:archer)
lana = users(:lana)
# フォローしているユーザーの投稿を確認
lana.microposts.each do |post_following|
assert michael.feed.include?(post_following)
end
# 自分自身の投稿を確認
michael.microposts.each do |post_self|
assert michael.feed.include?(post_self)
end
# フォローしていないユーザーの投稿を確認
archer.microposts.each do |post_unfollowed|
assert_not michael.feed.include?(post_unfollowed)
end
end
end
もちろん、現在のフィードはただのプロトタイプなので、このテストはREDになるはずです。
$ bundle exec rake test
12.3.2 フィードを初めて実装する
ステータスフィードで要求される設計はリスト12.41のテストで明確になったので (このテストにパスすれば良いので)、早速フィードの実装に着手してみましょう。最終的なフィードの実装はやや込み入っているため、細かい部品を1つずつ確かめながら導入していきます。最初に、このフィードで必要なクエリについて考えましょう。ここで必要なのは、microposts
テーブルから、あるユーザー (つまり自分自身) がフォローしているユーザーに対応するidを持つマイクロポストをすべて選択 (select) することです。このクエリを模式的に書くと以下のようになります。
SELECT * FROM microposts
WHERE user_id IN (<list of ids>) OR user_id = <user id>
上のコードを書く際に、SQLがIN
というキーワードをサポートしていることを前提にしています (大丈夫、実際にサポートされています)。このキーワードを使用することで、idの集合の内包 (set inclusion) に対してテストを行えます。
11.3.3のプロトフィードでは、上のような選択を行うためにActive Recordでリスト11.44のようにwhere
メソッドを使用していたことを思い出してください。このときは、選択する対象はシンプルでした。現在のユーザーに対応するユーザーidを持つマイクロポストをすべて選択すればよかったのでした。
Micropost.where("user_id = ?", id)
ここで行いたい選択は、上よりももう少し複雑で、たとえば以下のような感じになります。
Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
これらの条件から、フォローされているユーザーに対応するidの配列が必要であることがわかってきました。これを行う方法の1つは、Rubyのmap
メソッドを使用することです。このメソッドはすべての "列挙可能 (enumerable)" オブジェクト (配列やハッシュなど、要素の集合で構成されるあらゆるオブジェクト9) で使用できます。なお、このメソッドは4.3.2でも出てきました。他の例題として、map
メソッドを使って配列を文字列に変換すると、次のようになります。
$ rails console
>> [1, 2, 3, 4].map { |i| i.to_s }
=> ["1", "2", "3", "4"]
上に示したような状況では、各要素に対して同じメソッド が実行されます。これは非常によく使われる方法であり、次のようにアンパサンド &
と、メソッドに対応するシンボルを使用した短縮表記 (4.3.2) も可能です。この短縮表記なら変数iを使用せずに済みます。
>> [1, 2, 3, 4].map(&:to_s)
=> ["1", "2", "3", "4"]
join
メソッド (4.3.1) を使用すれば、idを集めた文字列を以下のようにカンマ区切りでつなげることもできます。
>> [1, 2, 3, 4].map(&:to_s).join(', ')
=> "1, 2, 3, 4"
上のメソッドを使用すれば、user.following
にある各要素のid
を呼び出し、フォローしているユーザーのidの配列を構成することができます。たとえば、データベースの最初のユーザーの場合は、以下の配列になります。
>> User.first.following.map(&:id)
=> [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
=> [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
メソッドは、実はActive Recordによってhas_many :following
関連付けから自動生成されたものです (リスト12.8)。これにより、user.following
コレクションに対応するidを得るための_ids
を、関連付けの名前に追加するだけで済みます。
フォローしているユーザーidの文字列は以下のようになります。
>> User.first.following_ids.join(', ')
=> "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)
が無事に動きました! 変更の結果をリスト12.43に示します。
class User < ActiveRecord::Base
.
.
.
# パスワードリセットが期限切れなら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)
active_relationships.create(followed_id: other_user.id)
end
.
.
.
end
これでテストがGREENになるはずです。
$ bundle exec rake test
いくつかのアプリケーションにおいては、この初期実装だけで目的が達成され、十分に思えるかもしれません。しかしリスト12.43にはまだ足りないものがあります。それが何なのか、次の節に進む前に考えてみてください(ヒント:フォローしているユーザーが5000人もいたらどうなるでしょうか)。
12.3.3 サブセレクト
前節のヒントでおわかりのように、12.3.2のフィードの実装は、投稿されたマイクロポストの数が膨大になったときにうまくスケールアップできません。フォローしているユーザーが5000人程度になるとこういうことが起きる可能性があります。この節では、フォローしているユーザー数に応じてスケーリングできるように、ステータスフィードを再度実装します。
12.3.2で示したコードの問題は、following_ids
でフォローしているすべてのユーザーをメモリーから一気に取り出し、フォローしているユーザーの完全な配列を作り出したことです。リスト12.43の条件では、集合に内包されているかどうかだけしかチェックされていないため、この部分をもっと効率的なコードにできるはずです。そして、SQLは本来このような集合の操作に最適化されています。これを解決する方法は、フォローしているユーザーのidの検索をデータベースに保存するときにサブセレクト (subselect) を使用することです。
リスト12.45でコードを若干修正し、フィードをリファクタリングすることから始めましょう。
where
メソッド内の変数に、キーと値のペアを使う GREEN app/models/user.rb
class User < ActiveRecord::Base
.
.
.
# ユーザーのステータスフィードを返す
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についてすべてを選択することは、内部的には以下のような感じになります。
SELECT * FROM microposts
WHERE user_id IN (SELECT followed_id FROM relationships
WHERE follower_id = 1)
OR user_id = 1
このサブセレクトは、集合のロジックを (Railsではなく) データベースに保存するので、より効率が高まります。
これで基礎を固めることができましたので、リスト12.46のようにもっと効率なフィードを実装する準備ができました。ここに記述されているのは生のSQLなので、following_ids
という文字列はエスケープされているのではなく、式展開されることに注意してください
class User < ActiveRecord::Base
.
.
.
# ユーザーのステータスフィードを返す
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が複雑に絡み合っていて厄介ですが、ちゃんと動作します。
$ bundle exec rake test
もちろん、サブセレクトを使用すればいくらでもスケールアップできるなどということはありません。大規模なWebサイトでは、バックグラウンドジョブを使用して、フィードを非同期で生成するなどの対策が必要でしょう。Webサイトのスケーリングのようなデリケートな問題は本書の範疇を超えます。
リスト12.46をもって、ステータスフィードの実装は完了です。11.3.3でHomeページには既にフィードを追加していたことを思い出してください。 思い出すキッカケとして、home
アクションはリスト12.48に再掲します。第11章ではただのプロトタイプでしたが (図11.14)、リスト12.46の実装によって、Homeページで完全なフィードが表示できていることがわかります (図12.23)。
home
アクション内で、フィードにもページネーションを適用する app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end
end
.
.
.
end
この時点で、masterブランチに変更を取り込む準備ができました。
$ bundle exec rake 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 rake db:migrate
$ heroku run rake db:seed
本番環境で動作するステータスフィードは以下のようになります (図12.24)。
12.4 最後に
ステータスフィードが追加され、Ruby on Railsチュートリアルのサンプルアプリケーションがとうとう完成しました。このサンプルアプリケーションには、Railsの主要な機能 (モデル、ビュー、コントローラ、テンプレート、パーシャル、フィルタ、検証、コールバック、has_many
/belongs_to
/has_many through
関連付け、セキュリティ、テスティング、デプロイ) が多数含まれています。
これだけでもかなりの量ですが、 Web開発ついて学ぶべきことはまだまだたくさんあります。今後の学習の手始めとするために、この節では、より踏み込んだ学習をするための方法を紹介します。
12.4.2 読み物ガイド
読むに値するRails関連の書籍やドキュメントは書店やWebでいくらでも見つけられます。正直、あまりの多さに閉口するほどです。幸い、それらのほとんどが現在でも入手/アクセス可能です。より高度な技術を身に付けるためのお勧めリソースをいくつかリストアップします。
(訳注: 日本語の読み物ガイドとしては、@zyunnosukeさんの記事「Ruby on Railsを仕事にしていくための第一歩」がよくまとまっています。「Railsチュートリアルを読み終わった後はどうすれば良い?」とお悩みの方は、是非ご参考にしてみてください。)
- Railsスクリーンキャスト: 本書に合わせて、完全版のスクリーンキャスト (現在は英語版のみ) を用意してあります。このスクリーンキャストでは、本書の話題をすべてカバーしているだけでなく、さまざまなコツや秘訣も満載されており、スクリーンショットだけでは捉えにくい実際の動作を動画で視聴することもできます。スクリーンキャスト (英語) は Ruby on RailsチュートリアルWebサイト(英語版)で購入できます。
- RailsCasts: 最初にRailsCastsエピソードアーカイブを開いて、目についたトピックを適当に開くところから始めてみるとよいでしょう。
- Tealeaf Academy: 開発者自身による新人向けトレーニング講座が最近増えてきました。身の回りのそういった人がいればよいのですが、そうでない場合はオンラインでどこからでも受講できるTealeaf Academyがあります。もし体系化されたカリキュラムやインストラクターによるフィードバックが欲しければ、Tealeafは良い選択肢となり得るでしょう。
- Turing School of Software & Design: コロラド州デンバーで27週間 (約半年)、フルタイムでRuby/Rails/JavaScriptを学ぶプログラムです (訳注: プログラミング留学ってイメージですね)。ほとんどの生徒はプログラミング経験が乏しい時点からスタートしていますが、強い意志と高いモチベーションをもっています (上達にはこれらが必要です)。Turingスクールでは、生徒が卒業後に職を見つけることを保証していて、見つからなければ授業料を返還しています。Railsチュートリアルの読者は$500の割引クーポンが使えます。興味ある方はRAILSTUTORIAL500というクーポンコードを使ってください。
- Bloc: 体系化されたカリキュラムを使った、オンライン上のブートキャンプで、参加者に応じて適切なメンターを配置したり、プロジェクトを通した学習に特化している点が特長です。RAILSTUTORIAL500というクーポンコードを使うと、入会費を500ドル割引できます。
- Thinkful: プロのエンジニアとペアを組んで、プロジェクト実践型のカリキュラムで進んでいくオンライン講座です。対応している科目はRuby on Rails、フロントエンド開発、Webデザイン、データサイエンスです。
- Pragmatic Studio: Mike ClarkとNicole Clarkが教鞭を執っているオンラインのRailsクラスです。
- RailsApps: 教育目的の、Railsアプリケーションのサンプル集です。
- Code School: 非常に多種多様なプログラミングを対話的に学習できるコース
- Bala Paranj’s Test Driven Development in Ruby: Rubyを使ってテスト駆動開発を学ぶ、上級者向けのオンライン講座です。
- RubyやRailsのお勧め書籍: 「Beginning Ruby」(Peter Cooper 著)、「The Well-Grounded Rubyist」(David A. Black著)、「Eloquent Ruby」(Russ Olsen著)、Rubyをさらに深く学ぶのであれば 「The Ruby Way」(Hal Fulton著) がお勧めです。Railsをさらに深く学ぶのであれば「Agile Web Development with Rails」(Sam Ruby / Dave Thomas / David Heinemeier Hansson著)、「The Rails 4 Way」(Obie Fernandez / Kevin Faustino著)、「Rails 4 in Action」 (Ryan Bigg / Yehuda Katz著)がお勧めです。
- Railsガイド: トピック毎に分類された最新のRailsリファレンスです (訳注: RailsGuidesの日本語版を「Railsガイド」と呼んでいます)。
12.4.2 本章のまとめ
-
has_many :through
を使うと、複雑なデータ関係をモデリングできる -
has_many
メソッドには、クラス名や外部キーなど、いくつものオプションを渡すことができる - 適切なクラス名と外部キーと一緒に
has_many
/has_many :through
を使うことで、能動的関係 (フォローする) や受動的関係 (フォローされる) がモデリングできた - ルーティングは、ネストさせて使うことができる
-
where
メソッドを使うと、柔軟で強力なデータベースへの問い合わせが作成できる - Railsは (必要に応じて) 低級なSQLクエリを呼び出すことができる
- 本書で学んだすべてを駆使することで、フォローしているユーザーのマイクロポスト一覧をステータスフィードに表示させることができた
12.5 演習
注: 『演習の解答マニュアル (英語)』にはRuby on Railsチュートリアルのすべての演習の解答が掲載されており、www.railstutorial.orgで原著を購入いただいた方には無料で配布しています (訳注: 解答は英語です)。
なお、演習とチュートリアル本編の食い違いを避ける方法については、演習用のトピックブランチに追加したメモ (3.6) を参考にしてください。
- HomeページとProfileページにある統計情報のテストを書いてみてください。ヒント: リスト11.27のテストに追加してください。(Homeページの統計情報は別のテストにしてみませんか。)
- Homeページに表示されている1ページ目のフィードをテストしてください。リスト12.49はそのテンプレートです。
CGI.escapeHTML
でHTMLのエスケープ処理を使っている点に注目して、なぜこれが必要なのか考えてみてください。(試しにエスケープ処理を外して、HTMLのソースコードを注意深く調べてください。マイクロポストの内容がおかしいはずです。)
require 'test_helper'
class FollowingTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
log_in_as(@user)
end
.
.
.
test "feed on Home page" do
get root_path
@user.feed.paginate(page: 1).each do |micropost|
assert_match CGI.escapeHTML(FILL_IN), FILL_IN
end
end
end
- モックアップツアーの写真は、それぞれ http://www.flickr.com/photos/john_lustig/2518452221/ と https://www.flickr.com/photos/renemensen/9187111340 から引用しました。 ↑
- 見えやすくするため、図12.6から
following
テーブルのid
カラムを省略しました。↑ - 読者のPaul Fioravantiがこの用語を提案してくれました。ありがとうございます。↑
- 技術的には、Railsは
has_many
に与えられた引数を、classify
メソッドを使ってクラス名に変換しています。このメソッドは、たとえば"foo_bars"
であれば"FooBar"
に変換します。↑ - 技術的には、Railsは
underscore
メソッドを使用してクラス名をidに変換しています。たとえば、"FooBar".underscore
を実行すると"foo_bar"
に変換されます。従って、FooBar
オブジェクトの外部キーはfoo_bar_id
になるでしょう↑ - 特定の分野でモデリングの経験を多く積めば、このようなユーティリティメソッドが必要になることを事前に思い付けるようになるでしょう。たとえ思い付けないことがあったとしても、明確なテストを書こうとするときに、いつの間にかこういうメソッドを自分が作成していることに気付くことでしょう。だからというわけではありませんが、今はこのようなメソッドが必要であるということに気付けなくても問題ありません。ソフトウェアの開発は、繰りかえしに次ぐ繰り返しです。読みづらくなるまでコードを書き足し、そのコードをリファクタリングする、その繰り返しです。そして、より簡潔なコードを書くために、本書が少しでもお役に立てばと思います。↑
- Railsにはさまざまなルーティングオプションがありますが、詳細についてはRailsガイドの記事「Railsルーティング」を参照してください。↑
- Asynchronous (非同期の) JavaScript And XMLの それぞれの頭文字をとっています。Ajaxはしばしば “AJAX” と大文字で書かれますが、Ajaxの起源となる記事では一貫して “Ajax” となっています。↑
- 列挙可能 (enumerable) オブジェクトであることの主な条件は、
each
メソッドを実装していることです。このメソッドはコレクションを列挙します。↑
Railsチュートリアルは YassLab 社によって運営されています。
コンテンツを継続的に提供するため、書籍・動画・質問対応サービスなどもご検討していただけると嬉しいです。
研修支援や教材連携にも対応しています。note マガジンや YouTube チャンネルも始めたので、よければぜひ遊びに来てください!