lib/sequel/plugins/validation_helpers.rb in sequel-3.46.0 vs lib/sequel/plugins/validation_helpers.rb in sequel-3.47.0
- old
+ new
@@ -4,39 +4,40 @@
# class-level validations. The names and APIs are different, though. Example:
#
# Sequel::Model.plugin :validation_helpers
# class Album < Sequel::Model
# def validate
+ # super
# validates_min_length 1, :num_tracks
# end
# end
#
- # The validates_unique validation has a unique API, but the other validations have
- # the API explained here:
+ # The validates_unique and validates_schema_types methods have a unique API, but the other
+ # validations have the API explained here:
#
# Arguments:
- # * atts - Single attribute symbol or an array of attribute symbols specifying the
- # attribute(s) to validate.
+ # atts :: Single attribute symbol or an array of attribute symbols specifying the
+ # attribute(s) to validate.
# Options:
- # * :allow_blank - Whether to skip the validation if the value is blank. You should
- # make sure all objects respond to blank if you use this option, which you can do by:
+ # :allow_blank :: Whether to skip the validation if the value is blank. You should
+ # make sure all objects respond to blank if you use this option, which you can do by:
# Sequel.extension :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.
- # * :message - The message to use. Can be a string which is used directly, or a
- # proc which is called. If the validation method takes a argument before the array of attributes,
- # that argument is passed as an argument to the proc. The exception is the
- # validates_not_string method, which doesn't take an argument, but passes
- # the schema type symbol as the argument to the proc.
+ # :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.
+ # :message :: The message to use. Can be a string which is used directly, or a
+ # proc which is called. If the validation method takes a argument before the array of attributes,
+ # that argument is passed as an argument to the proc. The exception is the
+ # validates_not_string method, which doesn't take an argument, but passes
+ # the schema type symbol as the argument to the proc.
#
# The default validation options for all models can be modified by
# changing the values of the Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS hash. You
# change change the default options on a per model basis
# by overriding a private instance method default_validation_helpers_options.
@@ -81,17 +82,18 @@
:includes=>{:message=>lambda{|set| "is not in range or set: #{set.inspect}"}},
:integer=>{:message=>lambda{"is not a number"}},
:length_range=>{:message=>lambda{|range| "is too short or too long"}},
:max_length=>{:message=>lambda{|max| "is longer than #{max} characters"}, :nil_message=>lambda{"is not present"}},
:min_length=>{:message=>lambda{|min| "is shorter than #{min} characters"}},
+ :not_null=>{:message=>lambda{"is not present"}},
:not_string=>{:message=>lambda{|type| type ? "is not a valid #{type}" : "is a string"}},
:numeric=>{:message=>lambda{"is not a number"}},
- :type=>{:message=>lambda{|klass| "is not a #{klass}"}},
+ :type=>{:message=>lambda{|klass| klass.is_a?(Array) ? "is not a valid #{klass.join(" or ").downcase}" : "is not a valid #{klass.to_s.downcase}"}},
:presence=>{:message=>lambda{"is not present"}},
:unique=>{:message=>lambda{'is already taken'}}
}
-
+
module InstanceMethods
# Check that the attribute values are the given exact length.
def validates_exact_length(exact, atts, opts={})
validatable_attributes_for_type(:exact_length, atts, opts){|a,v,m| validation_error_message(m, exact) unless v && v.length == exact}
end
@@ -134,10 +136,15 @@
# Check that the attribute values are not shorter than the given min length.
def validates_min_length(min, atts, opts={})
validatable_attributes_for_type(:min_length, atts, opts){|a,v,m| validation_error_message(m, min) unless v && v.length >= min}
end
+ # Check attribute value(s) are not NULL/nil.
+ def validates_not_null(atts, opts={})
+ validatable_attributes_for_type(:not_null, atts, opts){|a,v,m| validation_error_message(m) if v.nil?}
+ end
+
# Check that the attribute value(s) 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
@@ -156,16 +163,32 @@
validation_error_message(m)
end
end
end
- # Check if value is an instance of a class
+ # Validates for all of the model columns (or just the given columns)
+ # that the column value is an instance of the expected class based on
+ # the column's schema type.
+ def validates_schema_types(atts=keys)
+ Array(atts).each do |k|
+ next unless type = schema_type_class(k)
+ validates_type(type, k)
+ end
+ end
+
+ # Check if value is an instance of a class. If +klass+ is an array,
+ # the value must be an instance of one of the classes in the array.
def validates_type(klass, atts, opts={})
klass = klass.to_s.constantize if klass.is_a?(String) || klass.is_a?(Symbol)
- validatable_attributes_for_type(:type, atts, opts){|a,v,m| validation_error_message(m, klass) if !v.nil? && !v.is_a?(klass)}
+ validatable_attributes_for_type(:type, atts, opts) do |a,v,m|
+ next if v.nil?
+ if klass.is_a?(Array) ? !klass.any?{|kls| v.is_a?(kls)} : !v.is_a?(klass)
+ validation_error_message(m, klass)
+ end
+ end
end
-
+
# Check attribute value(s) is not considered blank by the database, but allow false values.
def validates_presence(atts, opts={})
validatable_attributes_for_type(:presence, atts, opts){|a,v,m| validation_error_message(m) if model.db.send(:blank_object?, v) && v != false}
end
@@ -218,9 +241,10 @@
end
message = validation_error_message(opts[:message])
where = opts[:where]
atts.each do |a|
arr = Array(a)
+ next if arr.any?{|x| errors.on(x)}
next if opts[:only_if_modified] && !new? && !arr.any?{|x| changed_columns.include?(x)}
ds = if where
where.call(model.dataset, self, arr)
else
vals = arr.map{|x| send(x)}