Module: Sequel::Plugins::ValidationClassMethods::ClassMethods

Defined in:
lib/sequel/plugins/validation_class_methods.rb

Defined Under Namespace

Classes: Generator

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#validation_reflectionsObject (readonly)

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.



39
40
41
# File 'lib/sequel/plugins/validation_class_methods.rb', line 39

def validation_reflections
  @validation_reflections
end

#validationsObject (readonly)

A hash of validations for this model class. Keys are column symbols, values are arrays of validation procs.



33
34
35
# File 'lib/sequel/plugins/validation_class_methods.rb', line 33

def validations
  @validations
end

Instance Method Details

#has_validations?Boolean

Returns true if validations are defined.

Returns:

  • (Boolean)


57
58
59
# File 'lib/sequel/plugins/validation_class_methods.rb', line 57

def has_validations?
  !validations.empty?
end

#inherited(subclass) ⇒ Object

Setup the validations and validation_reflections hash in the subclass.



62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sequel/plugins/validation_class_methods.rb', line 62

def inherited(subclass)
  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
  super
end

#skip_superclass_validationsObject

Instructs the model to skip validations defined in superclasses



75
76
77
# File 'lib/sequel/plugins/validation_class_methods.rb', line 75

def skip_superclass_validations
  @skip_superclass_validations = true
end

#skip_superclass_validations?Boolean

Instructs the model to skip validations defined in superclasses

Returns:

  • (Boolean)


80
81
82
# File 'lib/sequel/plugins/validation_class_methods.rb', line 80

def skip_superclass_validations?
  defined?(@skip_superclass_validations) && @skip_superclass_validations
end

#validate(o) ⇒ Object

Validates the given instance.



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/sequel/plugins/validation_class_methods.rb', line 104

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(&block) ⇒ Object

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


99
100
101
# File 'lib/sequel/plugins/validation_class_methods.rb', line 99

def validates(&block)
  Generator.new(self, &block)
end

#validates_acceptance_of(*atts) ⇒ Object

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’)



124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/sequel/plugins/validation_class_methods.rb', line 124

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(*atts) ⇒ Object

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’)



148
149
150
151
152
153
154
155
156
157
158
# File 'lib/sequel/plugins/validation_class_methods.rb', line 148

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

#validates_each(*atts, &block) ⇒ Object

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.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/sequel/plugins/validation_class_methods.rb', line 183

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_format_of(*atts) ⇒ Object

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).



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/sequel/plugins/validation_class_methods.rb', line 213

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_inclusion_of(*atts) ⇒ Object

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>’)



346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/sequel/plugins/validation_class_methods.rb', line 346

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_length_of(*atts) ⇒ Object

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’)



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
# File 'lib/sequel/plugins/validation_class_methods.rb', line 243

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_not_string(*atts) ⇒ Object

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)



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/sequel/plugins/validation_class_methods.rb', line 279

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_numericality_of(*atts) ⇒ Object

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)



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/sequel/plugins/validation_class_methods.rb', line 304

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_presence_of(*atts) ⇒ Object

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’)



329
330
331
332
333
334
335
336
337
338
339
# File 'lib/sequel/plugins/validation_class_methods.rb', line 329

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_uniqueness_of(*atts) ⇒ Object

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’)



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
# File 'lib/sequel/plugins/validation_class_methods.rb', line 376

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