Сходу оговорюсь: само собой разумеется, нам нужна всего только валидация на уровне модели, ActiveAdmin "из коробки" подхватит логику, блокируя при провале валидации запись данных в db и выведя в панели управления соответствующее уведомление. Мы только подредактируем последнее, сделав чуть более информативным (остановлюсь на этом далее). Работать будем на примере блога на рельсах, приняв, что модель Slideshow и интеграция с ActiveAdmin (см. цикл статей "Rails Blog") уже имеют место быть.
Что же, с места в карьер. Добавляем в Gemfile и инсталлируем:
# ActiveRecord::JSONValidator makes it easy to validate JSON attributes against a JSON schema.
gem 'activerecord_json_validator', '~> 2.0.0'
Мы собираемся проверять данные (вводимые администратором блога во вкладке ActiveAdmin) на соответствие актуальным стандартам JSON, добавив, кроме того, еще и собственные критерии валидации. Для чего создаем этот профайл:
config/schemas/slideshow.rb
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema#",
"properties": {
"resize_to_limit": { "type": "array" },
"monochrome": { "type": "boolean" },
"kuwahara": {"type": "string"},
"sepia-tone": {"type": "string"},
"quality": { "type": "integer" },
"polaroid": { "type": "integer" }
},
"required": ["resize_to_limit"]
}
Короткой строкой и в скобках. В "properties" находятся опции ImageMagick, которые мы намереваемся использовать в ActiveStorage::Variant для нарезки превью изображений. Забегая вперед: демо того, что в итоге получилось, можно взглянуть здесь:
Теперь добавляем в модель...
PROFILE_JSON_SCHEMA = Pathname.new(Rails.root.join('config', 'schemas', 'slideshow.json'))
validates :options, presence: true, json: {
schema: PROFILE_JSON_SCHEMA,
message: ->(errors) { errors }
}
...и все уже должно работать. Пробуйте: в случае несоответствия параметров указанным критериям вкладка ActiveAdmin отобразит message с описанием допущенных ошибок. Помните, обещано было сделать сообщение информативным? - done.
Впрочем, "пробовать" будем по-взрослому, написав тесты RSpec. Предлагаю как-то так:
Вы в любой момент можете клонировать репозиторий CRUD-Blog и самостоятельно поэкспериментировать с кодом.
require 'rails_helper'
RSpec.describe Slideshow, type: :model do
describe 'validates data column' do
subject { described_class.new(options: options) }
let(:valid_data) do
{
"resize_to_limit": [1, 2],
"kuwahara": 'string',
"polaroid": 1
}
end
describe 'valid data' do
let(:options) { valid_data }
it { is_expected.to be_valid }
end
describe 'value type is invalid (case 1)' do
let(:options) { valid_data.merge quality: 'string' }
it { is_expected.not_to be_valid }
end
describe 'value type is invalid (case 2)' do
let(:options) { valid_data.merge monochrome: 'array' }
it { is_expected.not_to be_valid }
end
describe 'value type is valid' do
let(:options) { valid_data.merge monochrome: true }
it { is_expected.to be_valid }
end
describe 'missing a optional element' do
let(:options) { valid_data.except :kuwahara }
it { is_expected.to be_valid }
end
describe 'missing a necessary element' do
let(:options) { valid_data.except :resize_to_limit }
it { is_expected.not_to be_valid }
end
end
end
Всего шесть тестов: для начала убеждаемся, что данные :valid_data адекватны, затем добавляем к ним один за другим пару элементов, где value принадлежат типу данных, противоречащих указанным в профайле, и еще один, где значение соответствует нужному; напоследок удаляем необязательный элемент и, наконец, обязательный. Все тесты должны пройти:
$ bundle exec rspec spec/models/slideshow_spec.rb
......
Finished in 0.33026 seconds (files took 10.42 seconds to load)
6 examples, 0 failures