# coding: utf-8

12.1.2 Ассоциации пользователь/взаимоотношение

Прежде чем приступить к реализации читателей и читаемых, нам вначале необходимо установить ассоциацию между пользователями и взаимоотношниями. Пользователь has_many (имеет_много) взаимоотношений, и, так как взаимоотношения включают двух пользователей — взаимоотношение belongs_to (принадлежит_к) читающим и читаемым пользователям.

Как и с микросообщениями в Разделе 11.1.2, мы будем создавать новые взаимоотношения используя ассоциацию, с помощью такого кода

user.relationships.create(:followed_id => ...)

Мы начнем с теста, показанного в Листинге 12.3, который устанавливает переменную экземпляра @relationships (используется ниже) и проверяет, что она может быть сохранена используя save!. Как и create!, метод save! вызывает исключение в случае неудачного сохранения; сравните это с использованием create! в Листинге 11.4.

Листинг 12.3. Тестирование создания Relationship с save!.

spec/models/relationship_spec.rb
require 'spec_helper'

describe Relationship do

  before(:each) do
    @follower = Factory(:user)
    @followed = Factory(:user, :email => Factory.next(:email))
    @relationship = @follower.relationships.build(:followed_id => @followed.id)
  end

  it "should create a new instance given valid attributes" do
    @relationship.save!
  end
end

Мы также должны протестировать модель User на relationships атрибут, как показано в Листинге 12.4.

Листинг 12.4. Тестирование на user.relationships атрибут.

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
  end
end

На этом этапе вы могли бы ожидать код приложения, как в Разделе 11.1.2, и он аналогичен, но есть одно существенное отличие: в случае с моделью Micropost мы могли бы сказать

class Micropost < ActiveRecord::Base

  belongs_to :user
  .
  .
  .
end

и

class User < ActiveRecord::Base
  has_many :microposts
  .
  .
  .
end

так как в microposts есть атрибут user_id для идентификации пользователя (Раздел 11.1.1). Id используемый таким способом для связи двух таблиц базы данных, известен как внешний ключ, и когда внешним ключом для объекта модели User является user_id, Rails может вывести ассоциацию автоматически: по умолчанию, Rails ожидает внешний ключ в форме <class>_id, где <class> является строчной версией имени класса.5 В данном случае, несмотря на то, что мы по прежнему имеем дело с пользователями, они теперь отождествляются с внешним ключом follower_id, поэтому мы должны сообщить об этом Rails, как показано в Листинге 12.5.6

Листинг 12.5. Реализация has_many ассоциации пользователь/взаимоотношение.

app/models/user.rb
class User < ActiveRecord::Base
  .
  .
  .
  has_many :microposts, :dependent => :destroy

  has_many :relationships, :foreign_key => "follower_id",
                           :dependent => :destroy
  .
  .
  .
end

(Поскольку уничтожение пользователя должно также уничтожить его взаимоотношения мы пошли еще дальше и добавили :dependent => :destroy к ассоциации; написание теста на это останется в качестве упражнения (Раздел 12.5).) На данный момент, тест ассоциации из Листинга 12.3 и Листинга 12.4 должен пройти.

Как и у модели Micropost, у Relationship модели есть belongs_to взаимоотношения с пользователями; в данном случае, объект взаимоотношение принадлежит к обоим follower и followed пользователям, что мы и тестируем в Листинге 12.6.

Листинг 12.6. Тестирование belongs_to ассоциации пользователь/взаимоотношения.

spec/models/relationship_spec.rb
describe Relationship do
  .
  .
  .
  describe "follow methods" do

    before(:each) do
      @relationship.save
    end

    it "should have a follower attribute" do
      @relationship.should respond_to(:follower)
    end

    it "should have the right follower" do
      @relationship.follower.should == @follower
    end

    it "should have a followed attribute" do
      @relationship.should respond_to(:followed)
    end

    it "should have the right followed user" do
      @relationship.followed.should == @followed
    end
  end
end

Чтобы написать код приложения, мы определяем belongs_to взаимоотношения как обычно. Rails выводит названия внешних ключей из соответствующих символов (т.e., follower_id из :follower, и followed_id из :followed), но, так как нет ни Followed ни Follower моделей, мы должны снабдить их именем класса User. Результат показан в Листинге 12.7.

Листинг 12.7. Добавление belongs_to ассоциаций к модели Relationship.

app/models/relationship.rb
class Relationship < ActiveRecord::Base

  attr_accessible :followed_id

  belongs_to :follower, :class_name => "User"

  belongs_to :followed, :class_name => "User"
end

Ассоциация followed на самом деле не потребуется до Раздела 12.1.5, но параллельность структуры читатели/читаемые лучше видна при одновременной реализации.

# coding: utf-8