@ned4ded
Верстка, Фронтенд

Где хранить статические переменные и константы для view в RoR?

Всем добрый день. В основном я занимаюсь фронтендом (сейчас больше 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


Работает так, как мне нужно, в этом проблемы не возникло. Но, собственно, вопросы у меня в следующем:
  1. К какому типу сущности он относится? С одной стороны, он обладает логикой презентера (в терминологии руби), т.е. преобразует данные нужным мне для вьюхи образом, с другой - не имеет модели для репрезентации, имеет только статический набор данных, определенных в самом себе (т.е. массив ссылок). Изначально я сделал его как NavbarPresenter, но сейчас засомневался
  2. Где мне его инициализировать? Сейчас я его объявляю как переменную внутри application_controller @navbar = NavbarPresenter.new(view_context), но он так доступен по всем вьюхам, а мне бы хотелось сделать его доступным только внутри навбара (навбар у меня partial)
  3. Куда мне его разместить? В presenters? Как мне кажется, внутри его исполняется и логика модели MenuLinks и логика презентера для такой модели. Разбить его на разные сущности? Допустимо ли добавить модель MenuLinks, которая просто определяет набор ссылок для навбар или это уже зашквар?
  4. Разумно ли таким же способом сделать классы для управления логикой разных вьюх? Я понимаю, что можно сделать нормальные и логичные презентеры для моделей, например, для Event, User и тп, использовать их во вьюхах, но разумно ли делать это для "статических" сущностей, изменение которых не предполагается (по крайней мере частое) и которые не имеют модели как таковой? Ну то есть у меня сделан сложный слайдер, в нем есть условная логика, есть несколько констант (индекс первого слайда, макс количество слайдов, id слайдера и т.п). Если бы я писал такое на vue, то никогда бы не использовал во вьюхе "захардкоденные" значения, а вынес бы их в переменные, в стор, куда угодно, кроме вьюхи


Если вы считаете, что я как-то в корне неверно смотрю на рор и нужно оставить всю эту логику во вьюхе - дайте знать, мне будет полезно узнать мнение со стороны.
  • Вопрос задан
  • 331 просмотр
Решения вопроса 2
nbekseitov
@nbekseitov
Ruby developer
Методу links не место в контроллере. Переместите метод в отдельный хелпер или используйте гем cells
Ответ написан
c3gdlk
@c3gdlk
Ментор в http://rubyboost.ru/
Непонятно зачем это все изобретать. Чем плохи просто много ссылок в представлении, какой у этого кода недостаток? Чем возможность вывести ссылки в цикле оправдывает введение новой абстракции. Тем более, что у Вас все равно под каждую ссылку отдельный метод, просто не в представлении, а в презентере. Просто представьте, как усложнится код, если к примеру придется добавить проверку на доступность ссылки пользователю в зависимости от его прав.

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

Можно еще просто сделать отдельными хелперами, что наверно будет самым стандартным вариантом. Но имхо если у Вас навигация в отдельном паршиале, то он является достаточно законченной единицей и можно все поместить туда. Не бывает идеального кода, всегда компромиссы
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы