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
|
# frozen-string-literal: true
module Sequel
module Plugins
# The validates_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])
# There could be a presence validation on the foreign key in the associated model,
# which will fail if we validate before saving the current object. If there is
# no value for the foreign key, set it to the current primary key value, or a dummy
# value of 0 if we haven't saved the current object.
p_key = pk unless pk.is_a?(Array)
obj.values[key] = p_key || 0
key = nil if p_key
end
obj.errors.full_messages.each{|m| errors.add(association, m)} unless obj.valid?
if key && !pk_val
# If we used a dummy value of 0, remove it so it doesn't accidently remain.
obj.values.delete(key)
end
end
end
end
end
end
|