File: validate_associated.rb

package info (click to toggle)
ruby-sequel 5.63.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 10,408 kB
  • sloc: ruby: 113,747; makefile: 3
file content (85 lines) | stat: -rw-r--r-- 2,992 bytes parent folder | download | duplicates (2)
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