Учебник по расширению «скрытых» моделей драгоценных камней

Задний план

Мы уже много лет используем удивительную жемчужину форума Thredded на Notebook.ai. Это полнофункциональный встроенный движок Rails, который мгновенно активирует форум в вашем приложении Rails, и мы видели огромное его использование в нашем маленьком мире писателей: было создано более 20 000 тем и более двух миллионов ответов, размещенных в этих темах.

В следующем выпуске Notebook.ai мы добавим общедоступный поток событий для пользователей. Пользователи могут подписываться на других пользователей и делиться персонажами, локациями, предметами и вселенными, которые они создали, со своими подписчиками.

Чтобы держать пользователей в курсе обсуждений Thredded, которые создают их друзья, мы также добавляем в ленту новые темы Thredded. Для этого мы расширяем нативную модель Thredded::Topic обратным вызовом after_create, который создает потоковые события.

Выполнение

Чтобы добавить обратный вызов after_create в модель, к которой у нас нет прямого доступа (например, в геме), нам нужны две вещи.

  1. Нам нужно расширение модели, которое мы поместим в lib/extensions/thredded/topic.rb, и
  2. Нам нужно расширить модель этим расширением, что мы и сделаем в 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, по двум причинам:

  1. Ссылка, на которую мы хотим отправить пользователей, находится внутри именованного движка (например, вместо main_app.characters_path(character) нам нужно использовать что-то вроде thredded.topics_path(topic) .
  2. Фактический путь 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, и маршруты для любого из них строятся без проблем.

Это позволяет нам добавлять новые темы в ленту активности, при нажатии на которую пользователи переходят непосредственно к рассматриваемой теме!