Статьи

Введение в ячейки: лучший уровень просмотра для Rails

«Нет! Я сделал это снова! Не убивай меня! Пожалуйста! Не надо! » Обычный день в случайном магазине Rails. Один разработчик, Скотт, просто хотел запустить простую миграцию базы данных. Он не только обновил схему базы данных, но и отправил полмиллиона писем с приветствиями существующим пользователям приложения.

Скотт забыл о after_save Скотт опускает голову от стыда и хватает стакан воды, чтобы подавить свой гнев.

Скотт, это не только твоя вина.

Все больше и больше разработчиков Ruby on Rails пытаются реализовать сложные веб-приложения, следуя ванильному Rails Way ™. Они ищут альтернативные модели, методы или подходы для контроля растущей сложности.

Одним из вариантов альтернативных шаблонов является проект Trailblazer , представляющий собой набор слоев, расположенных поверх существующих веб-фреймворков, таких как Rails, Grape или Hanami.

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

Говоря о проблемах — в Rails есть много приложений, которые его используют.

И самым большим из них является отсутствие уровней абстракции, что проявляется в постоянно возникающем вопросе «Куда я помещаю этот код?» — задаются себе разработчики Rails, такие как Скотт, по всему миру, с отчаянным выражением лица и холодным потом на лбу. ,

Давайте сосредоточимся на том, как улучшить эту структуру. Почему бы не начать со слоя представления?

Rails ‘View Layer: презентация динозавра

Rails утверждает, что он очень прост, что позволяет кратко объяснить слой представления.

Действие контроллера объединяет данные для представления. Данные часто присваиваются переменным экземпляра, а затем они помещаются в так называемый «шаблон».

Шаблоны — это файлы, написанные на таких языках, как Haml или ERB. Они содержат заполнители, которые заменяются данными контроллера, превращая все это в статический HTML.

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

Rails также позволяет инкапсулировать код Ruby в методы, называемые «помощниками», которые можно использовать в представлении, чтобы уменьшить логику в представлении.

Это потому, что вы не должны иметь логику в своих взглядах — и это подчеркивается везде в Rails. «Не имейте сложный код в ваших взглядах!» Будет тенором. Самое смешное, что в большинстве представлений Rails вы найдете массивные куски кода, что делает невероятно трудным понять, что на самом деле делает представление.

Теперь, почему это?

Rails Views == PHP 4

Уровень представления в Rails сильно вдохновлен PHP. По словам харизматического изобретателя, это сделано специально и приносит все хорошее из PHP, оставляя при этом все плохое.

Это очень верно. Правда при сравнении представлений Rails с приложением PHP 4, созданным 10 лет назад.

И это именно то, что представляют собой Rails: PHP-скрипты с нулевой инкапсуляцией, доступ к глобальному состоянию и глобальные функции, называемые помощниками. Те из вас, кто работал в устаревших приложениях PHP 4, увидят сходство обеих концепций.

Нет ничего плохого в том, чтобы все было просто. Это концепция, которую новые разработчики сразу понимают и дают им возможность реализовывать динамические веб-страницы. Тем не менее, когда абстракция начинает протекать, и разработчики изо всех сил пытаются поддерживать чистый, предсказуемый код представления, не является ли это знаком для введения понятий более высокого уровня?

Посмотреть модели на помощь!

Каждый веб-фреймворк, кроме Rails, например, Django, Symfony или Phoenix, поставляется с определенной формой моделей представления : абстракция, где отдельный объект представляет фрагмент вашей страницы — вместо того, чтобы полагаться на глобально действующий «скрипт PHP» ,

Почему у нас этого нет в Rails? Потому что нам это не нужно!

Но разве я не сказал обратное?

Да. Тем не менее, ядро ​​Rails по-прежнему заставляет людей, подобных Скотту, думать, что его уровень представления абсолютно достаточен. Вместо интеграции более высокой абстракции, введение глобального метода ApplicationController::render

Вскоре у нас появятся сотни различных реализаций объектов «оформитель» или «презентатор», плавающих в нашем прекрасном стеке MVC, или, что еще проще, рендеринг представлений прямо из моделей! Ужасающая мысль и полное поражение цели конвенциональной структуры.

И на далеком расстоянии, на горизонте, где собирается шторм, и черные облака начинают скрывать небо, вы можете услышать последний рев умирающего ActionView T-Rex.

Как только дождь заканчивается, луч солнца падает на влажную землю. Слой альтернативного вида для Rails называется Cells . Он созрел за многие годы, впервые появившись в Rails 1.2.3.

Драгоценный камень Клетки собирается взломать миллион загрузок для многих счастливых пользователей. Он реализует полезную вариацию шаблона модели представления, которая позволяет сопоставлять фрагменты HTML с объектами.

Анатомия Клетки

Так что же такое клетка? Ячейка — это объект, который отображает фрагмент представления. Больше ничего

Люди, плохо знакомые с Cells, часто изо всех сил пытаются понять, что такое работа клетки. Предполагается, что он отображает всю страницу, или только некоторые компоненты в представлении, или в чем дело с Cells?

Ответ так же прост, как и концепция драгоценного камня: клетки могут воспроизводить все, что вы хотите. Их можно использовать для включения боковой панели, формы входа или всей страницы. Или все вышеперечисленное, вложив свои клетки!

Многие команды разработчиков используют ячейки для визуализации страниц, и на этих страницах больше ячеек реализуют меньшие компоненты. Работая с многочисленными командами, я часто слышал, как люди говорили: «Ах, это немного похоже на компонент React в Ruby!». Хотя Cells не приносит вам интерактивности, это хорошее сравнение.

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

Чтобы дать вам примитивный пример, вот как вы можете представить коллекцию комментариев в виде контроллера Rails, используя ячейку:

приложение / просмотров / комментариев / index.html.haml

 %h1 All comments

%ul
- @comments.each do |c|
  %li
    = cell(:comment, c)

По иронии судьбы, ячейка может быть отображена с помощью помощника, помощника cell Зацикливая коллекцию комментариев и вызывая ячейку для каждой модели, мы компилируем список комментариев в формате HTML.

Итак, вызов ячейки из представления сводится к следующему коду:

 comment = Comment.new(body: "Fantastic!")

cell(:comment, comment) #=> "<div>... HTML"

Внутренне, этот вызов помощника буквально делает одну вещь.

 CommentCell.new(comment).call

Интересный. Он CommentCellcommentcallapp/cells Ячейка вернет фрагмент HTML, и все готово.

Клетки являются объектами

Чтобы полностью понять модели представлений, нам нужно попробовать класс, реализующий ячейку. И в этом классе вы, дорогой Скотт, будете тратить половину своего времени на написание ячеек.

В соответствии с соглашением, ячейки организованы в новый каталог class CommentCell < Cell::ViewModel
def show
"Hello! I feel #{model.body}"
end
end

приложение / клетки / comment_cell.rb

 cell

При вызове ячейки через помощника callshowshow

В соответствии с соглашением, метод model В приведенном выше фрагменте класса все, что делает этот метод, это возвращает строку. В строке используется метод загадочной cell Это просто комментарий, который вы передали в вызов comment = Comment.new(body: "Fantastic!")

cell(:comment, comment)
#=> "Hello! I feel Fantastic!"

Давайте соединим эти два конца вместе. Вот, опять же, самый верхний вызов вместе с результатом:

 cell

Имеет ли это смысл? Помощник CommentCellcommentshowmodel В ячейке все, что вы передали, доступно через метод show Возвращаемое значение class CommentCell < Cell::ViewModel
def show
render # renders app/cells/comment/show.haml
end
end

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

Взгляды

Вместо того, чтобы возвращать простую строку, вы также можете использовать шаблон представления с заполнителями, именно так, как это делает Rails:

приложение / клетки / comment_cell.rb

 render

Используя render Обратите внимание, что это совершенно отдельная реализация, не разделяющая никакой логики с ActionView. Стек рендеринга ячеек на самом деле составляет около 50 строк кода. И это, по сравнению с 7 000 строк ActionView, немного объясняет, почему ячейки в 10 раз быстрее, чем обычный T-Rex.

Возможно, вы уже догадались. При вызове app/cells/comment/show.hamlshow.haml

Учитывая, что мы использовали Haml в качестве шаблона, вот как может выглядеть представление # app/cells/comment/show.haml

%h1 Comment
= model.body
.author
= link_to model.author.name, model.author

 app/cell/comment

Это простой шаблон Haml, как вы писали их много раз раньше. Однако, хотя представления Rails находятся в глобальном каталоге, это представление ячейки находится в своем личном каталоге .html Кроме того, при просмотре ячеек удаляется лишняя часть имени cells-haml Класс ячеек всегда отображает только один формат, что делает ненужным его кодирование в имени файла — удобство, которое ценят многие пользователи Cells.

С другой стороны, Cells поддерживает Haml, Slim и ERB. Не забудьте быстро прочитать инструкции по установке и никогда не забывайте включать гем link_to

Вернуться к просмотру файла. Как вы, несомненно, заметили, вы можете использовать помощники Rails, такие как model Также у вас есть доступ к модели клетки. Однако в этом представлении нет переменных экземпляра. Скотт! Ты скучаешь по ним? Вам не нужно! Ячейка предоставляет методы экземпляра для доступа к данным презентации. Давайте узнаем об этом немного больше.

Безлоги

Настоящая сила Клеток заключается в том, как работают «помощники». Приведенный выше вид может быть упрощен следующим образом:

приложение / клетки / комментировать / show.haml

 %h1 Comment
= body
.author
  = author_link

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

Проходят минуты, и Скотт все еще смотрит на шаблон представления. Нет логики. Никаких неуклюжих помощников, вызывающих вздутие живота. Просто простые шаблоны. Мысль о ActionView T-Rex и его ужасающей внешности возвращает Скотта обратно в реальность.

Помощники — это методы экземпляра

Что происходит в представлении, когда мы вызываем author_link В Rails это вызвало бы — надеюсь существующий — глобальный помощник, где-нибудь. Где именно, никто действительно не понимает. Давайте пока забудем о Rails, помощниках и вонючих T-Rex.

При вызове author_link Посмотрите на реализацию, и все это будет иметь смысл.

приложение / клетки / comment_cell.rb

 class CommentCell < Cell::ViewModel
  def show
    render # renders app/cells/comment/show.haml
  end

private
  def author_link
    link_to(model.author.name, model.author)
  end
end

Правильно, «помощник» в Cells — это метод экземпляра, привязанный к одному конкретному классу, а не глобальный, топающий монстр. В этом методе вы можете использовать любой Ruby, который вам нужен, даже помощники Rails.

Но на этот раз у вас не будет конфликтов имен, у вас не будет доступа к глобальным переменным, которые вы, возможно, даже не захотите видеть, и вам не придется искусственно инкапсулировать вашего «помощника»! Ruby и его объектная модель делают это для вас. Что еще круче, вы можете свободно использовать функции ООП, такие как наследование, модули для совместного использования общих помощников или декораторы, как видно из драгоценного камня Draper внутри ячеек.

Говоря об украшениях: Cells также предоставляет быстрый способ генерировать читатели для объекта model Это удобный способ сократить код.

приложение / клетки / comment_cell.rb

 class CommentCell < Cell::ViewModel
  property :body
  property :author
  # ..

private
  def author_link
    link_to(author.name, author)
  end

Использование метода класса propertymodelbodyauthor И так как это один и тот же контекст, вы можете использовать эти ярлыки как в экземпляре ячейки, так и в представлении.

Заворачивать

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

Прошли времена случайного переопределения вспомогательных функций или копания во вложенных частичных вызовах. Ячейки реализуют то же поведение с очень функциональной семантикой. Состояние и зависимости должны быть переданы в модель представления извне.

В том же смысле он использует объектно-ориентированные методы, где они имеют смысл. Вызов методов или «помощников» в представлении будет делегирован экземпляру ячейки, где вы должны предоставить метод экземпляра для реализации этого самого помощника.

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

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

Не ждите до тех пор, сделайте это так, как Скотт, иди и используйте Cells прямо сейчас и почувствуйте силу инкапсуляции в вашем слое вида!