В этом руководстве, состоящем из нескольких частей, мы создадим приложение Ruby on Rails, которое реализует внешний вид и функции популярного гиганта социальных сетей Instagram. Мы будем использовать Rails 6, Webpacker 4, Turbolinks, Stimulus Js и Tailwindcss.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: хотя цель этого руководства - поделиться знаниями и показать, как реализовать определенные функции для обучения, из-за его длины и текстового формата оно не предназначено для начинающих разработчиков Rails и не является введением. в рельсы. Если вы все еще не знакомы со структурой приложения Rails, я рекомендую сначала начать с некоторых базовых руководств по Rails.

Я установил приложение Rails 6 (версия rc1) с Webpacker 4, Turbolinks и предстоящим выпуском 1.0 Tailwindcss. Если вы хотите продолжить, вы можете клонировать приложение из следующей ветки:

git clone -b tutorial-start [email protected]:rodloboz/railstagram.git

Чтобы запустить шаблон, на вашем компьютере должен быть установлен ruby 2.5.3, а затем запускаться из терминала в папке клонированного проекта:

bundle install
yarn install
rails db:create db:migrate db:seed
rails s

Цель состоит в том, чтобы больше сосредоточиться на способе реализации функций Rails (backend и frontend) и меньше на частях HTML и CSS, поэтому мы не будем слишком углубляться в эти темы. Таким образом, к указанной выше ветке уже применен некоторый стиль с Tailwindcss и Fontawesome, воссоздающий внешний вид Instagram.

Но прежде чем мы начнем, краткое введение в Tailwindcss:

В отличие от Bootstrap, Foundation, Bulma, Tailwindcss не является комплектом пользовательского интерфейса (нет темы или встроенных компонентов пользовательского интерфейса). Согласно документации:

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

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

// Using Tailwind's utility classes to 
// create variations of buttons
.btn-blue {
  @apply bg-blue-500 text-white;
}
.btn-white {
  @apply text-blue-500
}
.btn-white:focus {
  @apply text-blue-400
}

Tailwind написан на PostCSS и настроен на JavaScript. Поэтому я поместил компоненты в app / javascript / stylesheets для компиляции с помощью Webpack. Вы можете найти основной файл конфигурации Tailwind в app / javascript / stylesheets / config / tailwind.config.js

Модель пользователя

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

Сгенерируем миграцию в терминале:

rails g migration AddAttributesToUsers full_name username:string:uniq about:text

Это генерирует следующую миграцию:

Instagram использует имя пользователя для создания URL-адресов профилей пользователей. Таким образом, имена пользователей должны быть уникальными, поэтому мы добавляем это ограничение в базу данных при миграции. Он также добавляет столбец about, который мы реализуем позже.

Не забудьте rails db:migrate применить перенос.

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

validates :username,presence: true,
                    format: { with: /\A(?=.*[a-z])[a-z\d]+\Z/i },
                    uniqueness: { case_sensitive: false }

Instagram не требует от пользователей подтверждения пароля и позволяет им входить в систему со своим адресом электронной почты или именем пользователя. Перейдите к просмотрам, созданным устройством, и удалите поле подтверждения пароля из registrations#new и sessions#new.

Нам нужно разрешить пользователям указывать дополнительные параметры в форме регистрации. Добавьте это в ApplicationController.

Это позволяет добавлять дополнительные атрибуты к сильным параметрам Devise.

Нам нужно будет изменить нашу навигационную панель, чтобы отображать аватар пользователя и раскрывающийся список, чтобы вошедшие в систему пользователи могли выйти. Вы можете увидеть обновленный код здесь: https://github.com/rodloboz/railstagram/blob/master/app/views/shared/_navbar.html.erb

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

// app/javascript/stylesheets/config/tailwind.config.js
variants: {
    borderColors: ['responsive', 'hover', 'focus', 'group-hover'],
    visibility: ['responsive', 'group-hover'],
  }

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

Мы также воспользуемся помощником просмотра, чтобы определить правильный URL аватара пользователя. Пока мы не реализуем загрузку изображений, есть только два варианта: мы проверяем, связан ли адрес электронной почты пользователя с учетной записью Gravatar. Если нет, мы отображаем изображение аватара пользователя по умолчанию:

Разрешение пользователям входить в систему с именем пользователя

По умолчанию Devise позволяет пользователям войти в систему и сбросить пароль, указав свой адрес электронной почты. Instagram позволяет пользователям указывать имя пользователя или адрес электронной почты. Для этого нам нужно переопределить настройки Devise по умолчанию.

Добавьте логин в качестве средства доступа к атрибуту в модель пользователя: attr_accessor :login

Измените config/initializers/devise.rb, чтобы:

config.authentication_keys = [:login]

И переопределите find_for_database_authentication метод Devise в модели пользователя:

Вы можете следовать этой вики, чтобы сделать то же самое для восстановления пароля: https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign_in-using -их-имя-пользователя или адрес электронной почты

На этом этапе вы должны протестировать свое приложение, чтобы убедиться, что оно работает, прежде чем двигаться дальше.

Рефакторинг модели User с учетом модели.

К настоящему времени ваша модель User начинает выглядеть толстой, с множеством методов разработки, а мы только начали. Давайте выделим весь этот код в модельную проблему. Создайте файл в app/models/concerns/authenticable.rb и переместите весь этот код из модели в этот файл:

Затем все, что вам нужно сделать, это добавить include Authenticable к модели User. Обязательно проверьте это снова.

Профиль пользователя

Instagram использует имена пользователей в корне своих URL-адресов для доступа к профилям пользователей. Для этого нам понадобится следующий маршрут:

resources :users, path: '/', param: :username, only: %i[show]

Это создает маршрут show в корне нашего приложения, который захватывает имя пользователя в параметрах и отправляет его в userscontroller # show. Затем нам нужно указать нашей модели пользователя, как сгенерировать параметры URL-адреса для себя:

# User 
def to_param   
  username 
end

Здесь я добавил еще несколько классов, чтобы стилизовать интерфейс и придать ему ощущение Instagram. Помните, что мы используем Tailwindcss, поэтому все наши пользовательские компоненты создаются в app / javascript / stylesheets / components. Вы можете ознакомиться с полным кодом в репозитории Github, доступном в конце этого руководства.

Следующие пользователи

Наконец, мы собираемся добавить возможность подписаться на других пользователей. Это может быть достигнуто с помощью таблицы соединения, которая соединяет двух пользователей: подписчика, на который ссылается столбец follower_id, и подписчика, или пользователя, за которым подписчик подписывается, на которого ссылается столбец following_id. Сгенерируем модель:

rails g model Follow

Измените миграцию соответствующим образом:

И извлеките код в соответствующую проблему:

Обновите маршруты:

Мы будем пространство имен FollowsController для пользователей и использовать действие create (с именем follow_path), которое вызывает метод follow. мы реализовали выше в проблеме и действии уничтожить (названном unfollow_path), которое вызывает действие отменить подписку, реализованное в той же проблеме. Оба действия находятся в одной и той же конечной точке /:username/follow, и мы используем HTTP-команды POST и DELETE, чтобы различать их. (более подробную статью о возможностях маршрутизации Rails вы можете прочитать здесь).

Во-первых, мы убеждаемся, что кнопка подписаться / отменить подписку в представлении работает правильно с помощью обычного HTML-запроса. Затем мы jaxify добавляем параметр remote: true к этим ссылкам. Это отправляет запрос AJAX на бэкэнд вместо перезагрузки всей страницы, а затем мы обновляем кнопку и соответствующую ссылку с помощью javascript. Для этой цели я извлек код кнопки в часть представления: app/views/users/_follow_btn.html.erb

Счетчик кеширования

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

Во-первых, нам нужно добавить два столбца в нашу модель User, которые будут отслеживать эти два счетчика при следующей миграции:

Затем мы добавляем параметр counter_cache в модель Follow. Это указывает Rails обновлять столбцы User с правильным счетчиком подписчиков и следующих каждый раз, когда создается или уничтожается запись Follow.

Затем обновите наше представление этими двумя новыми столбцами, а также отредактируйте create.js.erb, чтобы представление обновлялось во время запросов AJAX:

Поскольку экземпляр @user загружается до срабатывания counter_cache и до обновления столбцов счетчика каждый раз, когда пользователь подписывается на другого пользователя или отменяет его подписку, нам необходимо перезагрузить экземпляр в чтобы отображать правильное количество подписчиков и подписок. Мы делаем это, добавляя @user.reload внутри блока в действиях create и destroy FollowsController.

Наконец, мы добавим Стимул, чтобы обеспечить множественное число в количестве подписчиков. Мы могли бы использовать метод Rails pluralize, но Stimulus нам пригодится и позже.

Установите стимул в терминал:

bundle exec rails webpacker:install:stimulus

Стимул работает, добавляя в HTML-элементы представления атрибуты данных, которые ссылаются на контроллеры и цели и действия по событию. В нашем users#show представлении мы добавляем контроллер к элементу заголовка: <headerclass="flex mb-16" data-controller="follow-button">.

Затем нам нужно указать целевой элемент, в котором мы будем считывать фактическое количество подписчиков, и целевой элемент, в который мы вставим слово Follower в единственном или множественном числе. Это делается путем добавления атрибута data-target к соответствующим элементам:

Затем мы настраиваем нашу кнопку отслеживания / отмены подписки для прослушивания события ajax:success с атрибутом data-action и обновляем сообщение / слово подписчика с помощью стимула:

А контроллер стимула выглядит так:

Это все для части 1 этого руководства. Мы продолжим наш клон Instagram в Части 2. Вы можете проверить полный код этой части здесь 👉 https://github.com/rodloboz/railstagram/tree/tutorial-part1

ПРИМЕЧАНИЕ. Большое спасибо Juliette Chevalier за предложения по улучшению этой статьи и за устранение опечаток и ошибок.