Rails fixtures и машина времени
Fixtures - это способ в удобном текстовом формате (CSV или YAML) собирать тестовые записи для тестирования Rails-приложения. У такого способа тестирования есть недостаток по скорости (поскольку в базу при каждом прогоне тестов данные загружаются заново), но есть и ряд преимуществ - можно тестировать реальные SQL-выборки.
Стандартный файл с fixtures выглядит так:
first_comment:
id: 1
from: Joe User
created_on: 2001-10-02
second_comment:...
и так далее. Тем не менее, иногда полезно воспользоваться ERB - Embedded Ruby - для генерации данных, зависимых от среды, в которой проводятся тесты. Благодаря тому, что при каждом прогоне тестов шаблон рендерится заново, можно пользоваться полезными свойствами языка чтобы сделать fixture гораздо более понятной при чтении.
Например, предположим, что в базе есть пароли, закодированные как MD5:
main_site:
id: 1
name: Blogue
admin_password: 5ebe2294ecd0e0f08eab7690d2a6ee69
admin_email: authors@blog.ru
approves_comments: 0
При использовании ERB это легко можно заменить на пароль в “естественном виде”:
main_site:
admin_password: <%= MD5.new('secret') %> <!--more-->
Но это скорее удобство. А вот где это совершенно незаменимо - так это в работе с датами и временем. К примеру - нам нужно показывать только сообщения, появившиеся на сайте за последнюю неделю. А в файле с fixtures мы укажем их больше - например пяток, из которых два сообщения размещены раньше. Поскольку у базы и системы в целом время работает когерентно, тестировать такие данные крайне неудобно. В решении этой проблемы больше всего помогает ERB.
Имейте в виду, что тобы база восприняла текст как корректную дату или время надо применять очень четкое их форматирование.
Например, сообщение, которое только будет размещено в базе через 10 дней:
published_on: <%= (10.days.from_now).to_date %>
Или сообщение, размещенное год назад (методы year/seconds возвращают целое число, обозначающее количество секунд, конструкции ago и from_now вычисляют исходя из них абсолютное время в обьекте Time):
published_on: <%= (1.year.ago).to_date %>
Вызов to_date необходимо делать, чтобы данные были сконвертированы в формат даты а не времени (иначе MySQL не воспримет их как корректную дату и проставит ее вам в 0000-00-00).
Cо значениями времени (поле datetime) схема немного другая - для него самым обещупотребительным (и в частности используемым в ATOM и RSS) является формат ISO 8601 с целым рядом достоинств - фиксированная длина, учет временной зоны и так далее. Заодно этот формат будет адекватно воспринят базой данных. Поэтому для выражения в fixtures времени применяется метод iso8601, добавляемый Рельсами к обьектам Time. Например, вот как мы бы выразили в fixtures объект “заказа аренды некоего устройства на 10 дней, начинающейся через месяц”.
booking:
id: 2
client_id: 2
device_id: 1
confirmed: 1
starts: <%= (1.month.from_now).iso8601 %>
ends: <%= (40.days.from_now).iso8601 %>
Альтернативно можно вызывать to_s(:db)
В итоге такой обьект всегда при занесении в базу будет иметь значения, соответствующие системному времени и начинающиеся через месяц и 40 дней соответственно.
Машина времени.