[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
И все... Теперь, при добавлении подписчика происходит апдейт списка с подписчиками, и он автоматом попадет на первое место в выборке. Читайте доку внимательнее.