/usr/lib/ruby/vendor_ruby/sequel/plugins/constraint_validations.rb is in ruby-sequel 4.15.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 | module Sequel
module Plugins
# The constraint_validations plugin is designed to be used with databases
# that used the constraint_validations extension when creating their
# tables. The extension adds validation metadata for constraints created,
# and this plugin reads that metadata and automatically creates validations
# for all of the constraints. For example, if you used the extension
# and created your albums table like this:
#
# DB.create_table(:albums) do
# primary_key :id
# String :name
# validate do
# min_length 5, :name
# end
# end
#
# Then when you went to save an album that uses this plugin:
#
# Album.create(:name=>'abc')
# # raises Sequel::ValidationFailed: name is shorter than 5 characters
#
# Usage:
#
# # Make all model subclasses use constraint validations (called before loading subclasses)
# Sequel::Model.plugin :constraint_validations
#
# # Make the Album class use constraint validations
# Album.plugin :constraint_validations
module ConstraintValidations
# The default constraint validation metadata table name.
DEFAULT_CONSTRAINT_VALIDATIONS_TABLE = :sequel_constraint_validations
# Automatically load the validation_helpers plugin to run the actual validations.
def self.apply(model, opts=OPTS)
model.instance_eval do
plugin :validation_helpers
@constraint_validations_table = DEFAULT_CONSTRAINT_VALIDATIONS_TABLE
@constraint_validation_options = {}
end
end
# Parse the constraint validations metadata from the database. Options:
# :constraint_validations_table :: Override the name of the constraint validations
# metadata table. Should only be used if the table
# name was overridden when creating the constraint
# validations.
# :validation_options :: Override/augment the options stored in the database with the
# given options. Keys should be validation type symbols (e.g.
# :presence) and values should be hashes of options specific
# to that validation type.
def self.configure(model, opts=OPTS)
model.instance_eval do
if table = opts[:constraint_validations_table]
@constraint_validations_table = table
end
if vos = opts[:validation_options]
vos.each do |k, v|
if existing_options = @constraint_validation_options[k]
v = existing_options.merge(v)
end
@constraint_validation_options[k] = v
end
end
parse_constraint_validations
end
end
module DatabaseMethods
# A hash of validation method call metadata for all tables in the database.
# The hash is keyed by table name string and contains arrays of validation
# method call arrays.
attr_accessor :constraint_validations
end
module ClassMethods
# An array of validation method call arrays. Each array is an array that
# is splatted to send to perform a validation via validation_helpers.
attr_reader :constraint_validations
# A hash of reflections of constraint validations. Keys are type name
# symbols. Each value is an array of pairs, with the first element being
# the validation type symbol (e.g. :presence) and the second element being
# options for the validation. If the validation takes an argument, it appears
# as the :argument entry in the validation option hash.
attr_reader :constraint_validation_reflections
# The name of the table containing the constraint validations metadata.
attr_reader :constraint_validations_table
Plugins.inherited_instance_variables(self, :@constraint_validations_table=>nil, :@constraint_validation_options=>:hash_dup)
Plugins.after_set_dataset(self, :parse_constraint_validations)
private
# If the database has not already parsed constraint validation
# metadata, then run a query to get the metadata data and transform it
# into arrays of validation method calls.
#
# If this model has associated dataset, use the model's table name
# to get the validations for just this model.
def parse_constraint_validations
db.extend(DatabaseMethods)
unless hash = Sequel.synchronize{db.constraint_validations}
hash = {}
db.from(constraint_validations_table).each do |r|
(hash[r[:table]] ||= []) << r
end
Sequel.synchronize{db.constraint_validations = hash}
end
if @dataset
ds = @dataset.clone
ds.quote_identifiers = false
table_name = ds.literal(ds.first_source_table)
reflections = {}
@constraint_validations = (Sequel.synchronize{hash[table_name]} || []).map{|r| constraint_validation_array(r, reflections)}
@constraint_validation_reflections = reflections
end
end
# Given a specific database constraint validation metadata row hash, transform
# it in an validation method call array suitable for splatting to send.
def constraint_validation_array(r, reflections)
opts = {}
opts[:message] = r[:message] if r[:message]
opts[:allow_nil] = true if db.typecast_value(:boolean, r[:allow_nil])
type = r[:validation_type].to_sym
arg = r[:argument]
column = r[:column]
case type
when :like, :ilike
arg = constraint_validation_like_to_regexp(arg, type == :ilike)
type = :format
when :exact_length, :min_length, :max_length
arg = arg.to_i
when :length_range
arg = constraint_validation_int_range(arg)
when :format
arg = Regexp.new(arg)
when :iformat
arg = Regexp.new(arg, Regexp::IGNORECASE)
type = :format
when :includes_str_array
arg = arg.split(',')
type = :includes
when :includes_int_array
arg = arg.split(',').map{|x| x.to_i}
type = :includes
when :includes_int_range
arg = constraint_validation_int_range(arg)
type = :includes
end
column = if type == :unique
column.split(',').map{|c| c.to_sym}
else
column.to_sym
end
if type_opts = @constraint_validation_options[type]
opts = opts.merge(type_opts)
end
reflection_opts = opts
a = [:"validates_#{type}"]
if arg
a << arg
reflection_opts = reflection_opts.merge(:argument=>arg)
end
a << column
unless opts.empty?
a << opts
end
if column.is_a?(Array) && column.length == 1
column = column.first
end
(reflections[column] ||= []) << [type, reflection_opts]
a
end
# Return a range of integers assuming the argument is in
# 1..2 or 1...2 format.
def constraint_validation_int_range(arg)
arg =~ /(\d+)\.\.(\.)?(\d+)/
Range.new($1.to_i, $3.to_i, $2 == '.')
end
# Transform the LIKE pattern string argument into a
# Regexp argument suitable for use with validates_format.
def constraint_validation_like_to_regexp(arg, case_insensitive)
arg = Regexp.escape(arg).gsub(/%%|%|_/) do |s|
case s
when '%%'
'%'
when '%'
'.*'
when '_'
'.'
end
end
arg = "\\A#{arg}\\z"
if case_insensitive
Regexp.new(arg, Regexp::IGNORECASE)
else
Regexp.new(arg)
end
end
end
module InstanceMethods
# Run all of the constraint validations parsed from the database
# when validating the instance.
def validate
super
model.constraint_validations.each do |v|
send(*v)
end
end
end
end
end
end
|