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
|
# frozen-string-literal: true
module Sequel
module Plugins
# The validate_associated plugin allows you to validate associated
# objects. It also offers the ability to delay the validation of
# associated objects until the current object is validated.
# If the associated object is invalid, validation error messages
# from the associated object will be added to the current object's
# validation errors.
#
# Usage:
#
# # Make all model subclass support validating associated objects
# Sequel::Model.plugin :validate_associated
#
# # Make the Album class support validating associated objects
# Album.plugin :validate_associated
#
# class Album
# many_to_one :artist
# many_to_many :tags
#
# # Always validate associated artist when saving the album
# def validate
# super
# if artist
# validate_associated_object(model.association_reflection(:artist), artist)
# end
# end
#
# # When saving after calling this method, validate the given tag as well.
# def check_tag!(tag)
# delay_validate_associated_object(model.association_reflection(:tags), tag)
# end
# end
module ValidateAssociated
# Depend on the instance_hooks plugin.
def self.apply(mod)
mod.plugin :instance_hooks
end
module InstanceMethods
private
# Delay validating the associated object until validating the current object.
def delay_validate_associated_object(reflection, obj)
after_validation_hook{validate_associated_object(reflection, obj)}
end
# Validate the given associated object, adding any validation error messages from the
# given object to the parent object.
def validate_associated_object(reflection, obj)
return if reflection[:validate] == false
association = reflection[:name]
if (reflection[:type] == :one_to_many || reflection[:type] == :one_to_one) && (key = reflection[:key]).is_a?(Symbol) && !(pk_val = obj.values[key])
p_key = pk unless pk.is_a?(Array)
if p_key
obj.values[key] = p_key
else
ignore_key_errors = true
end
end
unless obj.valid?
if ignore_key_errors
# Ignore errors on the key column in the associated object. This column
# will be set when saving to a presumably valid value using a column
# in the current object (which may not be available until after the current
# object is saved).
obj.errors.delete(key)
obj.errors.delete_if{|k,| Array === k && k.include?(key)}
end
obj.errors.full_messages.each do |m|
errors.add(association, m)
end
end
nil
end
end
end
end
end
|