Ruby on Rails Tutorial (24)
先週末に終われるか、とか言ってたのですがとんでもなかったですね。でもようやくラストの節になります。頑張ろう。
11.3 The status feed
もうすぐてっぺん。つうか自分とフォローしてる人の post を表示ってのは色々な意味で確かにアレですね。
11.3.1 Motivation and strategy
確かにこんなのがあれば話は早いですね。
Micropost.from_users_followed_by(user)
これを実装するのかな。でも今のところは分かんないよね、とのこと。とりあえず試験を書きましょう、ということで spec/models/user_spec.rb に以下を追加する模様。
まず、micropost associations な先頭部分を以下にして
describe "micropost associations" do before { @user.save } let!(:older_micropost) do FactoryGirl.create(:micropost, user: @user, created_at: 1.day.ago) end let!(:newer_micropost) do FactoryGirl.create(:micropost, user: @user, created_at: 1.hour.ago) end
末端を以下に、とのこと。
describe "status" do let(:unfollowed_post) do FactoryGirl.create(:micropost, user: FactoryGirl.create(:user)) end let(:followed_user) { FactoryGirl.create(:user) } before do @user.follow!(followed_user) 3.times { followed_user.microposts.create!(content: "Lorem ipsum") } end its(:feed) { should include(newer_micropost) } its(:feed) { should include(older_micropost) } its(:feed) { should_not include(unfollowed_post) } its(:feed) do followed_user.microposts.each do |micropost| should include(micropost) end end end
どーゆーことかというと、@user.save しておいて let! で older_micropost と newer_micropost なオブジェクトを作っていますね。その上で status な試験として
- 準備として以下
- unfollowed_post として micropost なオブジェクトを作る
- post したユーザは @user から follow されていない
- followed_user としてユーザを作る
- followed_user は @user に follow されている状態に
- followed_user が 3 件ポスト
- unfollowed_post として micropost なオブジェクトを作る
- @user.feed が newer_micropost を含んでいること
- @user.feed が older_micropost を含んでいること
- @user.feed が unfollowed_post を含んでいないこと
- @user.feed が followed_user.microposts を全て含んでいること
ということを確認しているのか。
で、最後に app/models/user.rb の feed な属性 (メソド) を追加。
def feed Micropost.from_users_followed_by(selr) end
試験は全部追加って訳ではなかったんですね。あと feed なメソドも既存でした。
def feed # This is preliminary. See "Following users" for the full implementation. Micropost.where("user_id = ?", id) end
11.3.2 A first feed implementation
Micropost.from_users_followed_by 実装とのこと。とりあえずどんな query が必要とされているのか、ということを考えましょう、と。
SELECT * FROM microposts WHERE user_id IN (<list of ids>) OR user_id = <user id>
確かに。そして上に引用した通り最初は確かにこうなってます。
Micropost.where("user_id = ?", id)
これをこう、とい記述がありますね。
where("user_id in (?) OR user_id = ?", following_ids, user)
SQL そのまんまです。次に何をするのか、と思ったら following_ids を作る作戦に出たのかどうか。以下は
>> User.first.followed_users.map(&:id)
User の最初のレコード (?) の followed_users の id を配列にしたもの、が戻っていますね。ちなみにこれは AR で以下のように書けるようなナニがある模様。
>> User.first.followed_user_ids
上は配列が戻るので文字列にしたけりゃこうしろ、と。
>> User.first.followed_user_ids.join(',')
つーことで Micropost.from_users_followed_by(user) は以下で書けるよね、とのこと。
def self.from_users_followed_by(user) followed_user_ids = users_followed_user_ids where("user_id IN (?) OR user_id = ?", followed_user_ids, user) end
where という記述で midroposts なソレを選って戻せるのか。知らなんだ。そしてこれを盛り込むことで試験 green になる模様。ヤッてみます。
あら、試験 red ですね。何が悪いのやら。盛り込んだファイルは三つくらいしかないんですが 12 も failure が云々て。
この時点で
横着して git add . していないおかげで Untracked files がアレなことが分かりました。これはもうどうにもなりませんな。修復不可能orz
つーか fail なソレの意味が分からない箇所が多数あります。これは困りました。
結果をよくよく見るに Micropost.from_users_followed_by の記述誤りでした。
followed_user_ids = users_followed_user_ids
正しくは以下。
followed_user_ids = users.followed_user_ids
しかしなんでこんなナチュラルをやってしまったかorz
ちゃんと git status 見れよ、って話ですねorz
しかも最後の直前に自分で見つけるとかアレ杉orz
11.3.3 Subselects
構わず続行。sub query ではなくて subselects って言うのかな。確かに件数多い場合非常にやっかいなのは簡単に分かります。subselect を使って云々、とのこと。
とりあえず Micropost.from_users_followed_by のリファクタリングから開始。
# Returns microposts from the users being followed by the given user. def self.from_users_followed_by(user) followed_user_ids = user.followed_user_ids where("user_id IN (:followed_user_ids) OR user_id = :user_id", followed_user_ids: followed_user_ids, user_id: user) end
ちなみに
where("user_id IN (?) OR user_id = ?", followed_user_ids, user)
と
where("user_id IN (:followed_user_ids) OR user_id = :user_id", followed_user_ids: followed_user_ids, user_id: user)
は同じ意味とのこと。
つうか結局のところ、AR てきにメモリを消費しそうなソレについては subselect という名目で直接 SQL 書け、というのが普通なの?
def self.from_users_followed_by(user) followed_user_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id" where("user_id IN (#{followed_user_ids}) OR user_id = :user_id", user_id: user.id) end
一応盛り込んでおきます。試験 green も確認。
11.3.4 The new status feed
最後のこれは何なのか。paginate は全部読むんじゃなくて一定の件数 (30?) 分を云々なので、ということなのか。このあたりって中身を確認したいなと思っています。
11.4 Conclusion
とりあえず以下で。
$ git add . $ git commit -m 'Add user following' $ git checkout master $ git merge following-users --no-ff
Heroku は別途。新規作成します。しかしこれは酷い。バージョン管理した意味が全然無いです。とは言えすでにやりなおすリキは無いかも。
とりあえず、明日以降で掘削に着手します。