Упорядочиваем архивные записи в Rails

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

Подобный стиль больше всего подходит ведущим блогов, и именно он и будет рассмотрен нами сегодня.

С чего начать?

Смею порекомендовать обратить внимание на изображение, иллюстрирующее то, как Rails обрабатывает запросы, поступающие от браузера пользователя. Этот момент подробно расписан на railstutorial.org, но давайте немного коснёмся его здесь.

Браузер посылает запрос, который принимает веб-сервер. Веб-сервер передает запрос контроллеру Rails. Именно контроллер отвечает за дальнейшие действия. В некоторых случаях (статические страницы), контроллер немедленно отобразит страницу, представляющую собой шаблон с расширением html.erb. Шаблон преобразуется в чистый HTML и отображается в браузере.

Если же речь идёт о динамических сайтах, контроллер взаимодействует моделью, а модель в свою очередь отвечает за связь с базой данных. После обработки запрошенных данных, контроллер направляется к соответствующим шаблонам из app/views/ и отображает страницу в виде HTML как и в первом случае.

Адрес, где находится страница, определяется при помощи файла config/routes.rb.

rails: request and responce

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

Предполагается, что модель и контроллёр для статей (articles) уже существуют. Мы просто добавим дополнительный метод имеющемуся контроллёру и создадим новый шаблон представления.

Контроллер

Создадим новый метод archive, оперирующий переменной archives_by_year. Эта переменная будет включать в себя все существующие публикации, упорядоченные по дате создания с установленным лимитом на вывод количества постов на одной странице (вряд ли нам действительно понадобится последнее действие, но лучше подстраховаться).

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

Примечание

Для получения подробной информации обратитесь к документации по API RoR. Все «временны́е» методы можно отыскать в описании модуля DateAndTime::Calculations (для RoR 4).

# app/controllers/articles-controller.rb

class ArticlesController < ApplicationController
  def archive
    @articles_by_year = Article.limit(300).find(
      :all,
      :order => "created_at DESC"
      ).group_by { |article| article.created_at.beginning_of_year }
  end
end

Модель

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

# app/models/article.rb

class Article < ActiveRecord::Base
  # сортировка публикаций
  default_scope order: 'articles.created_at DESC'
  # при необходимости здесь же указываем валидацию
  # для элементов статьи
  validates :slug, presence: true, length: {maximum:30}

Представление

Собственно, оно будет отвечать за то как выводить архив. Здесь мы берём созданную нами переменную articles_by_year и проводим итерацию по всем содержащимся в ней элементам. Тут же форматируем вывод дат при помощи метода strftime и даже подсчитываем количество опубликованных постов при необходимости (посредством методов count или length).

Затем для каждой из статей указываем ссылку, при нажатии на которую можно перейти к полной версии, и время создания исключительно для удобства восприятия. Для времени создания также используем форматирование, чтобы отобразить только день и месяц ("%d %B").

<%# app/views/articles/archive.html.erb %>

<% @articles_by_year.each do |year, articles| %>
  <h2><%= "#{year.strftime('%Y')} (#{articles.count})" %></h2>

  <dl>
    <% for article in articles %>
      <dd><%= link_to article.title, article %>
      <span><%= article.created_at.strftime("%d %B") %></span>
      </dd>
    <% end %>
  </dl>
<% end %>

Результат должен быть похожим на это:

archive

Но, постойте, кажется, мы забыли кое-что.

Роутинг

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

# config/routes.rb

Myblog::Application.routes.draw do
  resources :articles
  root 'articles#index' # главная страница
  get 'archive/' => 'articles#archive' # архив
  # предположим, у нас имеются статические страницы
  get 'about/' => 'static_pages#about'
  get 'authors/' => 'static_pages#authors'
end

Выше представлен простейший вариант определения маршрутов. Чтобы изменения вступили в силу, следует дать команду:

$ rake routes
Prefix Verb   URI Pattern         Controller#Action
root GET      /                   articles#index
about GET     /about(.:format)    static_pages#about
authors GET   /authors(.:format)  static_pages#authors
archive GET   /archive(.:format)  articles#archive

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