module Sequel::Plugins::ValidationClassMethods::ClassMethods
def extract_options!(array)
an empty hash is returned This method is useful when writing methods that
Removes and returns the last member of the array if it is a hash. Otherwise,
def extract_options!(array) array.last.is_a?(Hash) ? array.pop : OPTS end
def freeze
def freeze @validations.freeze.each_value(&:freeze) @validation_reflections.freeze.each_value do |vs| vs.freeze.each do |v| v.freeze v.last.freeze end end super end
def has_validations?
def has_validations? !validations.empty? end
def reflect_validation(type, opts, atts)
def reflect_validation(type, opts, atts) atts.each do |att| (validation_reflections[att] ||= []) << [type, opts] end end
def skip_superclass_validations
def skip_superclass_validations superclass.validations.each do |att, procs| if @validations[att] @validations[att] -= procs end end @skip_superclass_validations = true end
def skip_superclass_validations?
def skip_superclass_validations? @skip_superclass_validations end
def validate(o)
def validate(o) validations.each do |att, procs| v = case att when Array att.map{|a| o.get_column_value(a)} else o.get_column_value(att) end procs.each {|tag, p| p.call(o, att, v)} end end
def validates(&block)
validates_length_of :password, minimum: 8
validates_length_of :name, minimum: 6
class MyClass < Sequel::Model
is equivalent to:
end
end
length_of :password, minimum: 8
length_of :name, minimum: 6
validates do
class MyClass < Sequel::Model
shorthand definitions. For example:
Defines validations by converting a longhand block into a series of
def validates(&block) Generator.new(self, &block) end
def validates_acceptance_of(*atts)
:accept :: The value required for the object to be valid (default: '1')
Possible Options:
:allow_nil is assumed to be true instead of false.
is equal to the :accept option. This method is unique in that
Validates acceptance of an attribute. Just checks that the value
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
def validates_confirmation_of(*atts)
Possible Options:
or email addresses on web forms.
Just makes sure that object.blah = object.blah_confirmation. Often used for passwords
validates_confirmation_of :blah
a _confirmation value matching the current value. For example:
Validates confirmation of an attribute. Checks that the object has
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.get_column_value(:"#{a}_confirmation") end end
def validates_each(*atts, &block)
skipping this validation if it returns nil or false.
:if :: A symbol (indicating an instance_method) or proc (which is used to define an instance method)
:allow_nil :: Whether to skip the validation if the value is nil.
database's default.
Sequel will attempt to insert a NULL value into the database, instead of using the
don't want to use allow_nil, because if the attribute is in values but has a value nil,
If your database table has a non NULL default, this may be a good option to use. You
from having an attribute in values with a value of nil, which Sequel will send as NULL.
doesn't specify it, so the database will use the table's default value. This is different
in the values when doing an insert or update. If the attribute is not present, Sequel
values hash. This is different from allow_nil, because Sequel only sends the attributes
:allow_missing :: Whether to skip the validation if the attribute isn't a key in the
:allow_blank :: Whether to skip the validation if the value is blank.
Possible Options:
end
object.errors.add(attribute, 'is not nice') unless value.nice?
validates_each :name, :password do |object, attribute, value|
value, e.g.:
block. The block must accept three arguments: instance, attribute and
Adds a validation for each of the given attributes using the supplied
def validates_each(*atts, &block) opts = extract_options!(atts) blank_meth = db.method(:blank_object?).to_proc i = opts[:if] am = opts[:allow_missing] an = opts[:allow_nil] ab = opts[:allow_blank] blk = if i || am || an || ab if i.is_a?(Proc) i = Plugins.def_sequel_method(self, "validation_class_methods_if", 0, &i) end proc do |o,a,v| next if i && !validation_if_proc(o, i) next if an && Array(v).all?(&:nil?) next if ab && Array(v).all?(&blank_meth) next if am && Array(a).all?{|x| !o.values.has_key?(x)} yield(o, a, v) end else block end tag = opts[:tag] atts.each do |a| a_vals = Sequel.synchronize{validations[a] ||= []} if tag && (old = a_vals.find{|x| x[0] == tag}) old[1] = blk else a_vals << [tag, blk] end end end
def validates_format_of(*atts)
:message :: The message to use (default: 'is invalid')
Possible Options:
value against the regular expression provided by the :with option.
Validates the format of an attribute, checking the string representation of the
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
def validates_inclusion_of(*atts)
:in :: An array or range of values to check for validity (required)
Possible Options:
Validates that an attribute is within a specified range or set of values.
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.public_send(n.respond_to?(:cover?) ? :cover? : :include?, v) end end
def validates_length_of(*atts)
:within :: The array/range that must include the size of the value for it to be valid (no default)
:too_short :: The message to use use if it the value is too short (default: 'is too short')
:too_long :: The message to use use if it the value is too long (default: 'is too long')
:nil_message :: The message to use use if :maximum option is used and the value is nil (default: 'is not present')
:minimum :: The minimum size allowed for the value (no default)
options if present)
:message :: The message to use (no default, overrides :nil_message, :too_long, :too_short, and :wrong_length
:maximum :: The maximum size allowed for the value (no default)
:is :: The exact size required for the value to be valid (no default)
Possible Options:
Validates the length of an attribute.
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.public_send(w.respond_to?(:cover?) ? :cover? : :include?, v.size) end end end
def validates_numericality_of(*atts)
:message :: The message to use (default: 'is not a number')
Possible Options:
Validates whether an attribute is a number.
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
def validates_presence_of(*atts)
Possible Options:
with false considered present instead of absent.
Validates the presence of an attribute. Requires the value not be blank,
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 db.send(:blank_object?, v) && v != false end end
def validates_schema_type(*atts)
Possible Options:
time instead of at setter time.
raise_on_typecast_failure = false, to handle typecasting errors at validation
database type. This is generally useful in conjunction with
Validates whether an attribute has the correct ruby type for the associated
def validates_schema_type(*atts) opts = { :tag => :schema_type, }.merge!(extract_options!(atts)) reflect_validation(:schema_type, opts, atts) atts << opts validates_each(*atts) do |o, a, v| next if v.nil? || (klass = o.send(:schema_type_class, a)).nil? if klass.is_a?(Array) ? !klass.any?{|kls| v.is_a?(kls)} : !v.is_a?(klass) message = opts[:message] || "is not a valid #{Array(klass).join(" or ").downcase}" o.errors.add(a, message) end end end
def validates_uniqueness_of(*atts)
Possible Options:
database, as this suffers from a fairly obvious race condition.
You should also add a unique index in the
validates them separately.
validates_uniqueness_of(:column1, :column2)
validates the grouping of column1 and column2 while
validates_uniqueness_of([:column1, :column2])
This means that the code:
instead of that each field should have a unique value.
fields to specify that the combination of fields must be unique,
unique in the database. Pass an array of fields instead of multiple
Validates only if the fields in the model (specified by atts) are
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) next if v.empty? || !v.all? ds = o.class.where(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
def validation_if_proc(o, i)
def validation_if_proc(o, i) case i when Symbol o.get_column_value(i) else raise(::Sequel::Error, "invalid value for :if validation option") end end