This is an article about validation of Json columns using json-schema in Rails.
It's been a long time since the Json column was added to MySQL.
** Rails doesn't seem to be the default, but I want to validate Json properly! ** ** But when I try to write it honestly with a custom method ...
some_model.rb
class SomeModel < ApplicationRecord
validate :verify_timetable
private
def verify_timetable
errors.add(:timetable, 'Not an array') && return unless timetable.is_a?(Array)
timetable.each do |el|
errors.add(:timetable, 'Element is not Hash') && next unless el.is_a?(Hash)
errors.add(:timetable, 'Hash key is invalid') && next unless el.keys.sort == %w[end_at start_at]
%w[start_at end_at].each do |key|
DateTime.parse(el[key])
rescue ArgumentError
errors.add(:timetable, "#{key}The value of is invalid")
end
end
end
end
――It will be a considerable amount ... ――It is difficult to manage fine tuning such as "Do you allow excess or deficiency of keys?"
I think there are many such customers. If you don't think about it, do you do validation on the application side?
This time, I will use json-schema to introduce the coolest Json validation method I have ever thought of.
some_model.rb
class SomeModel < ApplicationRecord
TIMETABLE_SCHEMA = {
type: 'array',
items: {
type: 'object',
properties: {
start_at: { type: 'string', format: :datetime },
end_at: { type: 'string', format: :datetime },
},
},
}.freeze
validates :timetable, json: { schema: TIMETABLE_SCHEMA }
end
It's pretty intuitive, isn't it? You can place the schema anywhere you like. I want to summarize validation in validation, so I define it just before validates.
Gemfile
gem 'json-schema'
It is a setting to be able to call with validates: hoge, json: {schema: fuga}
.
I put it in ʻapp / validators`.
app/validators/json_validator.rb
class JsonValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
JSON::Validator.validate!(options[:schema], value, strict: true)
rescue JSON::Schema::ValidationError => e
record.errors[attribute] << (options[:message] || e.message)
end
end
At this point, validation according to normal Json Schema (pattern matching by regular expression, definition of minLength, maxLength, etc.) is possible.
While googled the Json Schema specifications, read the Gem Readme and try various things.
(For example, strict: true
is a setting that ignores the required content and does not allow excess or deficiency of keys)
It's easy because you can write it with a Ruby hash.
There is no date type in the Json type definition. But for the time being, there are cases where you want to put date and time information into Json. Here, referring to this issue, the validator for DateTime is added to JSON :: Validator.
config/initializers/json_validator.rb
datetime = -> value {
begin
DateTime.parse(value)
rescue ArgumentError
raise JSON::Schema::CustomFormatError.new('not datetime format')
end
}
JSON::Validator.register_format_validator(:datetime, datetime)
If you have something like "I want this custom format!", You can add it here.
How was it? This seems to free you from personal validation descriptions. Even engineers who have neglected Json's validation will take this opportunity to enhance the validation on the application side and lead a healthy Rails life!
Recommended Posts