12.3 动态流
最后更新于:2022-04-01 22:30:41
# 12.3 动态流
接下来我们要实现演示应用最难的功能:微博动态流。基本上本节的内容算是全书最高深的。完整的动态流以 [11.3.3 节](chapter11.html#a-proto-feed)的动态流原型为基础实现,动态流中除了当前用户自己的微博之外,还包含他关注的用户发布的微博。我们会采用循序渐进的方式实现动态了。在实现的过程中,会用到一些相当高级的 Rails、Ruby 和 SQL 技术。
因为我们要做的事情很多,在此之前最好先清楚我们要实现的是什么样的功能。[图 12.5](#fig-page-flow-home-page-feed-mockup) 显示了最终要实现的动态流,[图 12.21](#fig-home-page-feed-mockup) 是同一幅图。
## 12.3.1 目的和策略
我们对动态流的构思很简单。[图 12.22](#fig-user-feed) 中显示了一个示例的 `microposts` 表和要显示的动态。动态流就是要把当前用户关注的用户发布的微博(也包括当前用户自己的微博)从 `microposts` 表中取出来,如图中的箭头所示。
![page flow home page feed mockup bootstrap](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-11_573330818264f.png)图 12.21:某个用户登录后看到的首页,显示有动态流![user feed](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-05-11_573330819a3fb.png)图 12.22:ID 为 1 的用户关注了 ID 为 2,7,8,10 的用户后得到的动态流
虽然我们还不知道怎么实现动态流,但测试的方法很明确,所以我们先写测试。测试的关键是要覆盖三种情况:动态流中既要包含关注的用户发布的微博,还要有用户自己的微博,但是不能包含未关注用户的微博。根据[代码清单 9.43](chapter9.html#listing-users-fixtures-extra-users) 和[代码清单 11.51](chapter11.html#listing-add-micropost-different-owner) 中的固件,也就是说,Michael 要能看到 Lana 和自己的微博,但不能看到 Archer 的微博。把这个需求转换成测试,如[代码清单 12.41](#listing-full-feed-test) 所示。(用到了[代码清单 11.44](chapter11.html#listing-proto-status-feed) 中定义的 `feed` 方法。)
##### 代码清单 12.41:测试动态流 RED
test/models/user_test.rb
```
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
```
当然,现在的动态流只是个原型,测试无法通过:
##### 代码清单 12.42:**RED**
```
$ bundle exec rake test
```
## 12.3.2 初步实现动态流
有了检查动态流的测试后([代码清单 12.41](#listing-full-feed-test)),我们可以开始实现动态流了。因为要实现的功能有点复杂,因此我们会一点一点实现。首先,我们要知道该使用怎样的查询语句。我们要从 `microposts` 表中取出关注的用户发布的微博(也要取出用户自己的微博)。为此,我们可以使用类似下面的查询语句:
```
SELECT * FROM microposts
WHERE user_id IN (
';
- ) OR user_id =