/usr/lib/ruby/vendor_ruby/sequel/plugins/validation_class_methods.rb is in ruby-sequel 3.33.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | module Sequel
extension :blank
module Plugins
# Sequel's built-in validation_class_methods plugin adds backwards compatibility
# for the legacy class-level validation methods (e.g. validates_presence_of :column).
#
# It is recommended to use the validation_helpers plugin instead of this one,
# as it is less complex and more flexible. However, this plugin provides reflection
# support, since it is class-level, while the instance-level validation_helpers
# plugin does not.
#
# Usage:
#
# # Add the validation class methods to all model subclasses (called before loading subclasses)
# Sequel::Model.plugin :validation_class_methods
#
# # Add the validation class methods to the Album class
# Album.plugin :validation_class_methods
module ValidationClassMethods
# Setup the validations hash for the given model.
def self.apply(model)
model.class_eval do
@validation_mutex = Mutex.new
@validations = {}
@validation_reflections = {}
end
end
module ClassMethods
# A hash of validations for this model class. Keys are column symbols,
# values are arrays of validation procs.
attr_reader :validations
# A hash of validation reflections for this model class. Keys are column
# symbols, values are an array of two element arrays, with the first element
# being the validation type symbol and the second being a hash of validation
# options.
attr_reader :validation_reflections
# The Generator class is used to generate validation definitions using
# the validates {} idiom.
class Generator
# Initializes a new generator.
def initialize(receiver ,&block)
@receiver = receiver
instance_eval(&block)
end
# Delegates method calls to the receiver by calling receiver.validates_xxx.
def method_missing(m, *args, &block)
@receiver.send(:"validates_#{m}", *args, &block)
end
end
# Returns true if validations are defined.
def has_validations?
!validations.empty?
end
# Setup the validations and validation_reflections hash in the subclass.
def inherited(subclass)
super
vr = @validation_reflections
subclass.class_eval do
@validation_mutex = Mutex.new
@validations = {}
h = {}
vr.each{|k,v| h[k] = v.dup}
@validation_reflections = h
end
end
# Instructs the model to skip validations defined in superclasses
def skip_superclass_validations
@skip_superclass_validations = true
end
# Instructs the model to skip validations defined in superclasses
def skip_superclass_validations?
defined?(@skip_superclass_validations) && @skip_superclass_validations
end
# Defines validations by converting a longhand block into a series of
# shorthand definitions. For example:
#
# class MyClass < Sequel::Model
# validates do
# length_of :name, :minimum => 6
# length_of :password, :minimum => 8
# end
# end
#
# is equivalent to:
# class MyClass < Sequel::Model
# validates_length_of :name, :minimum => 6
# validates_length_of :password, :minimum => 8
# end
def validates(&block)
Generator.new(self, &block)
end
# Validates the given instance.
def validate(o)
superclass.validate(o) if superclass.respond_to?(:validate) && !skip_superclass_validations?
validations.each do |att, procs|
v = case att
when Array
att.collect{|a| o.send(a)}
else
o.send(att)
end
procs.each {|tag, p| p.call(o, att, v)}
end
end
# Validates acceptance of an attribute. Just checks that the value
# is equal to the :accept option. This method is unique in that
# :allow_nil is assumed to be true instead of false.
#
# Possible Options:
# * :accept - The value required for the object to be valid (default: '1')
# * :message - The message to use (default: 'is not accepted')
def validates_acceptance_of(*atts)
opts = {
:message => 'is not accepted',
:allow_nil => true,
:accept => '1',
:tag => :acceptance,
}.merge!(extract_options!(atts))
reflect_validation(:acceptance, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
o.errors.add(a, opts[:message]) unless v == opts[:accept]
end
end
# Validates confirmation of an attribute. Checks that the object has
# a _confirmation value matching the current value. For example:
#
# validates_confirmation_of :blah
#
# Just makes sure that object.blah = object.blah_confirmation. Often used for passwords
# or email addresses on web forms.
#
# Possible Options:
# * :message - The message to use (default: 'is not confirmed')
def validates_confirmation_of(*atts)
opts = {
:message => 'is not confirmed',
:tag => :confirmation,
}.merge!(extract_options!(atts))
reflect_validation(:confirmation, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
o.errors.add(a, opts[:message]) unless v == o.send(:"#{a}_confirmation")
end
end
# Adds a validation for each of the given attributes using the supplied
# block. The block must accept three arguments: instance, attribute and
# value, e.g.:
#
# validates_each :name, :password do |object, attribute, value|
# object.errors.add(attribute, 'is not nice') unless value.nice?
# end
#
# Possible Options:
# * :allow_blank - Whether to skip the validation if the value is blank.
# * :allow_missing - Whether to skip the validation if the attribute isn't a key in the
# values hash. This is different from allow_nil, because Sequel only sends the attributes
# in the values when doing an insert or update. If the attribute is not present, Sequel
# doesn't specify it, so the database will use the table's default value. This is different
# from having an attribute in values with a value of nil, which Sequel will send as NULL.
# If your database table has a non NULL default, this may be a good option to use. You
# don't want to use allow_nil, because if the attribute is in values but has a value nil,
# Sequel will attempt to insert a NULL value into the database, instead of using the
# database's default.
# * :allow_nil - Whether to skip the validation if the value is nil.
# * :if - A symbol (indicating an instance_method) or proc (which is instance_evaled)
# skipping this validation if it returns nil or false.
# * :tag - The tag to use for this validation.
def validates_each(*atts, &block)
opts = extract_options!(atts)
blk = if (i = opts[:if]) || (am = opts[:allow_missing]) || (an = opts[:allow_nil]) || (ab = opts[:allow_blank])
proc do |o,a,v|
next if i && !validation_if_proc(o, i)
next if an && Array(v).all?{|x| x.nil?}
next if ab && Array(v).all?{|x| x.blank?}
next if am && Array(a).all?{|x| !o.values.has_key?(x)}
block.call(o,a,v)
end
else
block
end
tag = opts[:tag]
atts.each do |a|
a_vals = @validation_mutex.synchronize{validations[a] ||= []}
if tag && (old = a_vals.find{|x| x[0] == tag})
old[1] = blk
else
a_vals << [tag, blk]
end
end
end
# Validates the format of an attribute, checking the string representation of the
# value against the regular expression provided by the :with option.
#
# Possible Options:
# * :message - The message to use (default: 'is invalid')
# * :with - The regular expression to validate the value with (required).
def validates_format_of(*atts)
opts = {
:message => 'is invalid',
:tag => :format,
}.merge!(extract_options!(atts))
unless opts[:with].is_a?(Regexp)
raise ArgumentError, "A regular expression must be supplied as the :with option of the options hash"
end
reflect_validation(:format, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
o.errors.add(a, opts[:message]) unless v.to_s =~ opts[:with]
end
end
# Validates the length of an attribute.
#
# Possible Options:
# * :is - The exact size required for the value to be valid (no default)
# * :maximum - The maximum size allowed for the value (no default)
# * :message - The message to use (no default, overrides :nil_message, :too_long, :too_short, and :wrong_length
# options if present)
# * :minimum - The minimum size allowed for the value (no default)
# * :nil_message - The message to use use if :maximum option is used and the value is nil (default: 'is not present')
# * :too_long - The message to use use if it the value is too long (default: 'is too long')
# * :too_short - The message to use use if it the value is too short (default: 'is too short')
# * :within - The array/range that must include the size of the value for it to be valid (no default)
# * :wrong_length - The message to use use if it the value is not valid (default: 'is the wrong length')
def validates_length_of(*atts)
opts = {
:nil_message => 'is not present',
:too_long => 'is too long',
:too_short => 'is too short',
:wrong_length => 'is the wrong length'
}.merge!(extract_options!(atts))
opts[:tag] ||= ([:length] + [:maximum, :minimum, :is, :within].reject{|x| !opts.include?(x)}).join('-').to_sym
reflect_validation(:length, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
if m = opts[:maximum]
o.errors.add(a, opts[:message] || (v ? opts[:too_long] : opts[:nil_message])) unless v && v.size <= m
end
if m = opts[:minimum]
o.errors.add(a, opts[:message] || opts[:too_short]) unless v && v.size >= m
end
if i = opts[:is]
o.errors.add(a, opts[:message] || opts[:wrong_length]) unless v && v.size == i
end
if w = opts[:within]
o.errors.add(a, opts[:message] || opts[:wrong_length]) unless v && w.send(w.respond_to?(:cover?) ? :cover? : :include?, v.size)
end
end
end
# Validates whether an attribute is not a string. This is generally useful
# in conjunction with raise_on_typecast_failure = false, where you are
# passing in string values for non-string attributes (such as numbers and dates).
# If typecasting fails (invalid number or date), the value of the attribute will
# be a string in an invalid format, and if typecasting succeeds, the value will
# not be a string.
#
# Possible Options:
# * :message - The message to use (default: 'is a string' or 'is not a valid (integer|datetime|etc.)' if the type is known)
def validates_not_string(*atts)
opts = {
:tag => :not_string,
}.merge!(extract_options!(atts))
reflect_validation(:not_string, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
if v.is_a?(String)
unless message = opts[:message]
message = if sch = o.db_schema[a] and typ = sch[:type]
"is not a valid #{typ}"
else
"is a string"
end
end
o.errors.add(a, message)
end
end
end
# Validates whether an attribute is a number.
#
# Possible Options:
# * :message - The message to use (default: 'is not a number')
# * :only_integer - Whether only integers are valid values (default: false)
def validates_numericality_of(*atts)
opts = {
:message => 'is not a number',
:tag => :numericality,
}.merge!(extract_options!(atts))
reflect_validation(:numericality, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
begin
if opts[:only_integer]
Kernel.Integer(v.to_s)
else
Kernel.Float(v.to_s)
end
rescue
o.errors.add(a, opts[:message])
end
end
end
# Validates the presence of an attribute. Requires the value not be blank,
# with false considered present instead of absent.
#
# Possible Options:
# * :message - The message to use (default: 'is not present')
def validates_presence_of(*atts)
opts = {
:message => 'is not present',
:tag => :presence,
}.merge!(extract_options!(atts))
reflect_validation(:presence, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
o.errors.add(a, opts[:message]) if v.blank? && v != false
end
end
# Validates that an attribute is within a specified range or set of values.
#
# Possible Options:
# * :in - An array or range of values to check for validity (required)
# * :message - The message to use (default: 'is not in range or set: <specified range>')
def validates_inclusion_of(*atts)
opts = extract_options!(atts)
n = opts[:in]
unless n && (n.respond_to?(:cover?) || n.respond_to?(:include?))
raise ArgumentError, "The :in parameter is required, and must respond to cover? or include?"
end
opts[:message] ||= "is not in range or set: #{n.inspect}"
reflect_validation(:inclusion, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
o.errors.add(a, opts[:message]) unless n.send(n.respond_to?(:cover?) ? :cover? : :include?, v)
end
end
# Validates only if the fields in the model (specified by atts) are
# unique in the database. Pass an array of fields instead of multiple
# fields to specify that the combination of fields must be unique,
# instead of that each field should have a unique value.
#
# This means that the code:
# validates_uniqueness_of([:column1, :column2])
# validates the grouping of column1 and column2 while
# validates_uniqueness_of(:column1, :column2)
# validates them separately.
#
# You should also add a unique index in the
# database, as this suffers from a fairly obvious race condition.
#
# Possible Options:
# * :message - The message to use (default: 'is already taken')
def validates_uniqueness_of(*atts)
opts = {
:message => 'is already taken',
:tag => :uniqueness,
}.merge!(extract_options!(atts))
reflect_validation(:uniqueness, opts, atts)
atts << opts
validates_each(*atts) do |o, a, v|
error_field = a
a = Array(a)
v = Array(v)
ds = o.class.filter(a.zip(v))
num_dups = ds.count
allow = if num_dups == 0
# No unique value in the database
true
elsif num_dups > 1
# Multiple "unique" values in the database!!
# Someone didn't add a unique index
false
elsif o.new?
# New record, but unique value already exists in the database
false
elsif ds.first === o
# Unique value exists in database, but for the same record, so the update won't cause a duplicate record
true
else
false
end
o.errors.add(error_field, opts[:message]) unless allow
end
end
private
# Removes and returns the last member of the array if it is a hash. Otherwise,
# an empty hash is returned This method is useful when writing methods that
# take an options hash as the last parameter.
def extract_options!(array)
array.last.is_a?(Hash) ? array.pop : {}
end
# Add the validation reflection to the class's validations.
def reflect_validation(type, opts, atts)
atts.each do |att|
(validation_reflections[att] ||= []) << [type, opts]
end
end
# Handle the :if option for validations
def validation_if_proc(o, i)
case i
when Symbol then o.send(i)
when Proc then o.instance_eval(&i)
when nil then true
else raise(::Sequel::Error, "invalid value for :if validation option")
end
end
end
module InstanceMethods
# Validates the object.
def validate
model.validate(self)
end
end
end
end
end
|