12.3 the status feed

4881 단어
자세히 보기
1. we need to get all the micropost of the users followed by current user.
 
Micropost.from_users_followed_by(user)

 so we can write a test this method.
 
describe Micropost do
  .
  .
  .
  describe "from_users_followed_by" do

    before(:each) do
      @other_user = Factory(:user, :email => Factory.next(:email))
      @third_user = Factory(:user, :email => Factory.next(:email))

      @user_post  = @user.microposts.create!(:content => "foo")
      @other_post = @other_user.microposts.create!(:content => "bar")
      @third_post = @third_user.microposts.create!(:content => "baz")

      @user.follow!(@other_user)
    end

    it "should have a from_users_followed_by class method" do
      Micropost.should respond_to(:from_users_followed_by)
    end

    it "should include the followed user's microposts" do
      Micropost.from_users_followed_by(@user).should include(@other_post)
    end

    it "should include the user's own microposts" do
      Micropost.from_users_followed_by(@user).should include(@user_post)
    end

    it "should not include an unfollowed user's microposts" do
      Micropost.from_users_followed_by(@user).should_not include(@third_post)
    end
  end
end

 
2. since the feed it self lives in the user model, so we still need test for the user model:
 
describe User do
  .
  .
  .
  describe "micropost associations" do
    .
    .
    .
    describe "status feed" do

      it "should have a feed" do
        @user.should respond_to(:feed)
      end

      it "should include the user's microposts" do
        @user.feed.should include(@mp1)
        @user.feed.should include(@mp2)
      end

      it "should not include a different user's microposts" do
        mp3 = Factory(:micropost,
                      :user => Factory(:user, :email => Factory.next(:email)))
        @user.feed.should_not include(mp3)
      end

      it "should include the microposts of followed users" do
        followed = Factory(:user, :email => Factory.next(:email))
        mp3 = Factory(:micropost, :user => followed)
        @user.follow!(followed)
        @user.feed.should include(mp3)
      end
    end
    .
    .
    .
  end
end

 
3. the implemention of feed method is easy, we just defer to from_users_followed_by method.
 
  def feed
    Micropost.from_users_followed_by(self)
  end

 
4. next we will implement this method:
the sql is like this:
 
SELECT * FROM microposts
WHERE user_id IN () OR user_id = 

before, we have seen:
Microposts.where("user_id = ?", id)

  here, it should be:
 
where("user_id in (#{following_ids}) or user_id = ?", user)

 note
a. we omitte Micropost, since this method is inside the class.
b. we used the rails convention if using user instead of user.id.
 
so we need to map the following user ids into an array.
map method will rescue us here:
 
[1,2,3,4].map{|i| i.to_s}

 since this structure is so common, ruby makes a shortcut for it:
 
[1,2,3,4].map(&:to_s)

so we can use:
User.first.following.map(&:id)

 but rails make it more convenient, 
 
User.first.following_ids

 so, in the end, our method will look like this:
 
def self.from_users_followed_by(user)
  following_ids = user.following_ids
  where("user_id in (#{following_id}) or user_id = ?", user)
end
 
5. now it works!
but we can refactor it:
where method can take a hash as argument.
 
where(:user_id => user.following.push(user))

 this is much more neater.
 
 
it works, but when a user is following 5000 users, it will fetch all the 5000 users, then get all the microposts of the 5000 users, this is not good, we will refactor it in next chapter.

좋은 웹페이지 즐겨찾기