[Ruby] Читайте документацию или как красиво работать с many-to-many
Поделюсь решением проблемы, возникшей при выполнении очередной задачи на работе.
Ситуация:
Есть список рассылок List, у него может быть много подписчиков Subscribers (связь многие ко многим), реализовано через третью таблицу SubscriberLists.
class List < ActiveRecord::Base
has_many :subscriber_lists, :dependent => :destroy
has_many :subscribers, :through => :subscriber_lists
end
Требуется:
Выводить списки рассылки в порядке обновления, т. е. те, кто был недавно обновлен должны быть выше. Под обновлением в нашем случае понимается как изменение атрибутов List, так и добавление или удаление подписчиков в список рассылки.
Решение:
Для вывода списка в апи делаем:
lists = current_client.lists.by_updated_at
Кого смутило by_updated_at — сюда. Великолепный гем UsefullScopes от Kaize.
Подпишем желающих(в классе List):
def add_subscriber(subscriber)
self.subscribers << subscriber
end
Вот оно счастье, но тесты мне ответили красным светом. Добавление подписчика вносило изменения лишь во вспомогательную таблицу.
Тут возникла проблема: как выстроить списки в нужном порядке, если без лишних телодвижений обновления не будет.
Держа в голове, что я уже почти 3 месяца как пишу на руби, решил слазить в гугл, а не костылять сразу. Но запросы вида «как мне обновить связанную запись» выдавали исключительно информацию про accepts_nested_attributes_for, что к моей проблеме отношения не имело. Попытав удачу в течение минут 15, решил подойти к проблеме с другой стороны — можно повесить действие на изменение объекта в SubscriberLists:
def after_commit(subscriber_list)
subscriber_list.list.update_attributes(:updated_at => Time.zone.now)
end
У List даем доступ на запись в updated_at
attr_accessible :updated_at
Выглядит монструозно, но работает... Но это же Ruby, но это же Rails, тут должно быть красивое решение! И оно нашлось:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html (ищем belongs_to и далее :touch)
Решением большинства проблем является фраза: RTFM, и тут прямо в точку!
Пишем в SubscriberLists:
belongs_to :list, :touch => true
И все... Теперь, при добавлении подписчика происходит апдейт списка с подписчиками, и он автоматом попадет на первое место в выборке. Читайте доку внимательнее.