Учебник по расширению «скрытых» моделей драгоценных камней
Задний план
Мы уже много лет используем удивительную жемчужину форума Thredded на Notebook.ai. Это полнофункциональный встроенный движок Rails, который мгновенно активирует форум в вашем приложении Rails, и мы видели огромное его использование в нашем маленьком мире писателей: было создано более 20 000 тем и более двух миллионов ответов, размещенных в этих темах.
В следующем выпуске Notebook.ai мы добавим общедоступный поток событий для пользователей. Пользователи могут подписываться на других пользователей и делиться персонажами, локациями, предметами и вселенными, которые они создали, со своими подписчиками.
Чтобы держать пользователей в курсе обсуждений Thredded, которые создают их друзья, мы также добавляем в ленту новые темы Thredded. Для этого мы расширяем нативную модель Thredded::Topic
обратным вызовом after_create
, который создает потоковые события.
Выполнение
Чтобы добавить обратный вызов after_create
в модель, к которой у нас нет прямого доступа (например, в геме), нам нужны две вещи.
- Нам нужно расширение модели, которое мы поместим в
lib/extensions/thredded/topic.rb
, и - Нам нужно расширить модель этим расширением, что мы и сделаем в
config/initializers/thredded.rb
.
Расширение
Мы используем способ Rails 6 для создания Concern
, с помощью которого мы можем расширить нашу модель. Это просто определяет триггер after_create
и определение функции. В приведенном ниже коде также добавлено несколько других методов, которые я использую в приложении (icon
и color
), но вы можете добавить все, что будет включать типичный Concern.
Как видите, у нас есть полный доступ ко всем атрибутам модели в этом after_create
, что упрощает создание события потока с нужной информацией!
После написания расширения нам также необходимо расширить с его помощью модель Thredded::Topic
.
Мы делаем это в конце инициализатора config/initializers/thredded.rb
, который мы уже используем, со следующим кодом:
Код довольно прост, но имеет несколько нюансов, которые Виктор Леонг очень хорошо объясняет в сообщении в блоге, из которого взят этот код.
И это все! Каждый раз, когда создается новый Thredded::Topic
, он запускает after_create
и создает ContentPageShare
, который наш поток перечисляет для отображения.
Интеграция прямых ссылок с потоком
Это все, и хорошо, но у нашего варианта использования есть еще одна проблема, которую нужно решить: полиморфное связывание с темами Thredded из потока.
Прямо сейчас мы используем полиморфное поле content
(в частности, поля content_type
и content_id
), чтобы использовать встроенный помощник Rails polymorphic_path(content)
для всех страниц нашей записной книжки; это позволяет нам иметь единую ссылку в представлении, которая может вести к персонажам, локациям, зданиям, существам и т. д., даже если все они являются отдельными моделями.
Это становится неуместным, когда модель Thredded::Topic
, по двум причинам:
- Ссылка, на которую мы хотим отправить пользователей, находится внутри именованного движка (например, вместо
main_app.characters_path(character)
нам нужно использовать что-то вродеthredded.topics_path(topic)
. - Фактический путь Thredded, который мы используем, требует больше информации для его построения, чем просто идентификатор темы. (В частности, маршрут
messageboard_topic
имеет следующий формат:GET /:messageboard_id/:id(/page-:page)(.:format)
Я не смог найти «волшебный» метод, который можно было бы определить в самой модели Topic, чтобы вручную установить маршрут к экземплярам этой модели (для использования polymorphic_path
). Посещение страницы дало мне NoMethodError для сгенерированного полиморфного маршрута.
Поэтому я сделал кое-что немного странное.
Это сработало, но я хотел бы услышать от читателей, есть ли здесь лучший подход.
Я заметил, что маршрут, который Rails использует по умолчанию для Thredded::Topic
, переданного в polymorphic_path
, не привязан к thredded
— он неожиданно ищет topic_path
в пространстве имен main_app
вместо движка, в котором определена модель. (Примечание: это пахнет ошибками коллизий; я я рад, что у меня еще не было моей собственной модели с именем Topic
!).
Поэтому я создал ThreddedProxyController, который принимает слаг темы, ищет в нем фактическую модель Thredded::Topic
, а затем строит полный маршрут Thredded, чтобы перенаправить пользователя.
Я добавил его в config/routes.rb
и обязательно назвал маршрутtopic
, чтобы маршрут по умолчанию, который Rails использовал до — main_app.topic_path
— направлялся к нему.
get '/topic/:slug', to: 'thredded_proxy#topic', as: :topic
Это определяет topic_path
в основном пространстве имен приложения, которое успешно маршрутизируется из маршрута по умолчанию, созданного Rails из исходного вызова polymorphic_path
выше: main_app.topic_path
.
Вот как выглядит прокси-контроллер:
И теперь все просто работает!
Полиморфный столбец content
на ContentPageShares
может продолжать функционировать независимо от того, указывает ли он на один из многих типов страниц в Notebook.ai или на Thredded::Topic
, и маршруты для любого из них строятся без проблем.
Это позволяет нам добавлять новые темы в ленту активности, при нажатии на которую пользователи переходят непосредственно к рассматриваемой теме!