12.1.4 Читаемые пользователи
Теперь мы переходим к сердцу ассоциаций Relationship: following
и followers
. Мы начнем с following
, как показано в Листинге 12.10.
user.following
. spec/models/user_spec.rb
describe User do
.
.
.
describe "relationships" do
before(:each) do
@user = User.create!(@attr)
@followed = Factory(:user)
end
it "should have a relationships method" do
@user.should respond_to(:relationships)
end
it "should have a following method" do
@user.should respond_to(:following)
end
end
end
Реализация впервые использует has_many :through
: пользователь имеет много читаемых (пользователей) через взаимоотношения, как показано на Рис. 12.7. По умолчанию, в ассоциации has_many :through
Rails ищет внешний ключ, соответствующий ассоциации в единственном числе; другими словами, код
has_many :followeds, :through => :relationships
будет составлять массив, используя followed_id
в таблице relationships
. Но, как отмечалось в Разделе 12.1.1, user.followeds
это довольно неуклюже; гораздо более естественным будет использование “following” в качестве множественноо числа для “followed”, и написание user.following
для массива читаемых пользователей. Естественно, Rails позволяет переопределить умолчание, в данном случае с помощью :source
параметра (Листинг 12.11), что явно говорит Rails, что источником массива following
является множество followed
id.
following
с has_many :through
. app/models/user.rb
class User < ActiveRecord::Base
.
.
.
has_many :microposts, :dependent => :destroy
has_many :relationships, :foreign_key => "follower_id",
:dependent => :destroy
has_many :following, :through => :relationships, :source => :followed
.
.
.
end
Чтобы создать взаимоотношение с читаемыми (пользователями), мы введем служебный метод follow!
и мы сможем написать user.follow!(other_user)
.7 Мы также добавим связанный булев метод following?
для того чтобы протестировать, читает ли пользователь сообщения других пользователей.8 Тесты в Листинге 12.12 показывают как мы планируем использовать эти методы на практике.
spec/models/user_spec.rb
describe User do
.
.
.
describe "relationships" do
.
.
.
it "should have a following? method" do
@user.should respond_to(:following?)
end
it "should have a follow! method" do
@user.should respond_to(:follow!)
end
it "should follow another user" do
@user.follow!(@followed)
@user.should be_following(@followed)
end
it "should include the followed user in the following array" do
@user.follow!(@followed)
@user.following.should include(@followed)
end
end
end
Обратите внимание, что мы заменили метод include?
виденный в Листинге 11.31 на should include
, преобразовав
@user.following.include?(@followed).should be_true
в более ясный и краткий
@user.following.should include(@followed)
Этот пример показывает, насколько гибкой является булевая конвенция RSpec; несмотря на то, что include
уже является ключевым словом Ruby (используется для включения модуля, как мы видели, например, в Листинге 9.11), в этом контексте RSpec правильно угадывает, что мы хотим протестировать включение массива.
В коде приложения, метод following?
принимает пользователя, называемого followed
, и проверяет, существует ли он в базе данных; метод follow!
вызывает create!
через relationships
ассоциацию для создания взаимоотношения с читаемым. Результаты представлены в Листинге 12.13.9
following?
и follow!
. app/models/user.rb
class User < ActiveRecord::Base
.
.
.
def self.authenticate_with_salt(id, stored_salt)
.
.
.
end
def following?(followed)
relationships.find_by_followed_id(followed)
end
def follow!(followed)
relationships.create!(:followed_id => followed.id)
end
.
.
.
end
Отметим, что в Листинге 12.13 мы опустили self пользователя, написав просто
relationships.create!(...)
вместо эквивалентного кода
self.relationships.create!(...)
Явное включение или невключение self
в данном случае дело вкуса.
Конечно, пользователи должны иметь возможность прекратить слежение за сообщениями других пользователей, что приводит нас к немного предсказуемому методу unfollow!
, как показано в Листинге 12.14.10
spec/models/user_spec.rb
describe User do
.
.
.
describe "relationships" do
.
.
.
it "should have an unfollow! method" do
@followed.should respond_to(:unfollow!)
end
it "should unfollow a user" do
@user.follow!(@followed)
@user.unfollow!(@followed)
@user.should_not be_following(@followed)
end
end
end
Код для unfollow!
прост: нужно просто найти взаимоотношение по followed id и уничтожить его (Листинг 12.15).11
app/models/user.rb
class User < ActiveRecord::Base
.
.
.
def following?(followed)
relationships.find_by_followed_id(followed)
end
def follow!(followed)
relationships.create!(:followed_id => followed.id)
end
def unfollow!(followed)
relationships.find_by_followed_id(followed).destroy
end
.
.
.
end