Всем добрый день. В основном я занимаюсь фронтендом (сейчас больше vue, но знаком и с angular), с рельсами работаю по обстоятельствам (бекенд многих проектов, над которыми я работаю, написан именно на рельсах). У меня возникла небольшая загвоздка с пониманием некоторых базовых концепций, на которых построен этот фреймворк. И вопрос больше касается именно репрезентации данные, т.е. больше связан с вьюхами и сопутствующей логикой.
Предположим, что в приложении есть меню, в котором представлен набор ссылок: события, архив, блог, аккаунт, язык, entry_btn. Несколько кнопок имеют условные зависимости, например: кнопка выбора языка зависит от текущего выбранного языка, контент кнопки entry_btn и присутствие кнопки account зависит от того, залогинен пользователь или нет.
самое простое, очевидное и хорошо работающее решение:
/ slim-lang
ul.navbar__menu
li = link_to 'Events', root_path, class: 'navbar__item'
/ ... other links
li
- if I18n.locale == :ru
link_to 'Eng', locale_path(:en), 'navbar__item'
- else
link_to 'Ru', locale_path(:ru), 'navbar__item'
- if signed_in?
li = link_to 'Аккаунт', edit_user_registration_path, class: 'navbar__item'
li = link_to 'Выход', destroy_user_session_path, class: 'navbar__item', method: :delete
- else
li = link_to 'Вход', new_user_session_path, class: 'navbar__item'
Это выливается в написание сниппета для каждого пункта меню и, как следствие, огромный объем кода, но хочется сделать что-то более компактное, чтобы вьюха по размеру занимала строчек 20-30, а не 120. Мое решение - повысить уровень абстракции путем вывода конкретных ссылок в массив и вывод ссылок через итерацию по массиву. Грубо говоря что-то такое:
/ slim-lang
- links = [{ label: 'Events', path: root_path }, ... ]
- links.each do |link|
li = link_to link[:label], link[:path], class: 'navbar__item'
Но при таком подходе все равно остаются проблемы с условными ссылками. В общем, посидев несколько часов со спекой рора, я пришел к тому, что создал класс Navbar, внутри которого сделал нужный навбару набор методов для вывода ссылок, сократил весь код до простой конструкции:
/ для верстки мне нужно было разбить список на 2 дива, так что передаваемые символы в метод нужны для поиска объектов
- @navbar.links(:root, :archive, :blog, :locale, :entry_group).each do |link|
li = link_to link[:label], link[:path], class: 'navbar__item', **link[:options]
Внутри навбара:
class NavbarPresenter < BasePresenter
include Rails.application.routes.url_helpers
def links *args
methods = args.select { |method| self.respond_to? method, :include_private and available_links.include? method }
links = methods.map { |method| self.send method }
links.flatten(1)
end
private
def format_link label, path, **args
{ label: label, path: path, options: args }
end
def available_links
[:root, :archive, :blog, :locale, :entry_group]
end
def blog
format_link I18n.t('header.items.blog'), posts_path
end
# ...
# other links with different logic
end
Работает так, как мне нужно, в этом проблемы не возникло. Но, собственно,
вопросы у меня в следующем:
- К какому типу сущности он относится? С одной стороны, он обладает логикой презентера (в терминологии руби), т.е. преобразует данные нужным мне для вьюхи образом, с другой - не имеет модели для репрезентации, имеет только статический набор данных, определенных в самом себе (т.е. массив ссылок). Изначально я сделал его как NavbarPresenter, но сейчас засомневался
- Где мне его инициализировать? Сейчас я его объявляю как переменную внутри application_controller
@navbar = NavbarPresenter.new(view_context)
, но он так доступен по всем вьюхам, а мне бы хотелось сделать его доступным только внутри навбара (навбар у меня partial)
- Куда мне его разместить? В presenters? Как мне кажется, внутри его исполняется и логика модели MenuLinks и логика презентера для такой модели. Разбить его на разные сущности? Допустимо ли добавить модель MenuLinks, которая просто определяет набор ссылок для навбар или это уже зашквар?
- Разумно ли таким же способом сделать классы для управления логикой разных вьюх? Я понимаю, что можно сделать нормальные и логичные презентеры для моделей, например, для Event, User и тп, использовать их во вьюхах, но разумно ли делать это для "статических" сущностей, изменение которых не предполагается (по крайней мере частое) и которые не имеют модели как таковой? Ну то есть у меня сделан сложный слайдер, в нем есть условная логика, есть несколько констант (индекс первого слайда, макс количество слайдов, id слайдера и т.п). Если бы я писал такое на vue, то никогда бы не использовал во вьюхе "захардкоденные" значения, а вынес бы их в переменные, в стор, куда угодно, кроме вьюхи
Если вы считаете, что я как-то в корне неверно смотрю на рор и нужно оставить всю эту логику во вьюхе - дайте знать, мне будет полезно узнать мнение со стороны.