Class: PassthruValidator

Inherits:
ActiveModel::EachValidator
  • Object
show all
Defined in:
decidim-core/app/validators/passthru_validator.rb

Overview

This validator passes through the file upload validations on a record attribute to an associated record. Useful e.g. with the forms that are not aware of the actual record's validators but they still need to apply the same validators as in the model they represent. Needs to be configured with the target class and optionally the target attribute where the validators are fetched from. By default the attribute name matches the attribute the validator is set for. Works only for each validators.

Example:

class ParticipantForm < Decidim::Form
  # Passes both the validator class only
  validates :avatar_image, passthru: { to: Person }

  # Passes both the validator class and attribute
  validates :image, passthru: { to: Person, attribute: :avatar_image }
end

Instance Method Summary collapse

Instance Method Details

#target_attribute(default = nil) ⇒ Object


76
77
78
# File 'decidim-core/app/validators/passthru_validator.rb', line 76

def target_attribute(default = nil)
  options[:attribute] || default
end

#target_classObject


72
73
74
# File 'decidim-core/app/validators/passthru_validator.rb', line 72

def target_class
  options[:to]
end

#target_instance(record) ⇒ Object


80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'decidim-core/app/validators/passthru_validator.rb', line 80

def target_instance(record)
  instance_attributes = if options[:with].respond_to?(:call)
                          options[:with].call(record)
                        else
                          options[:with] || {}
                        end

  instance_attributes.each do |key, val|
    instance_attributes[key] = val.call(record) if val.respond_to?(:call)
  end

  target_class.new(instance_attributes)
end

#target_validators(attribute) ⇒ Object


68
69
70
# File 'decidim-core/app/validators/passthru_validator.rb', line 68

def target_validators(attribute)
  target_class.validators_on(target_attribute(attribute))
end

#validate_each(record, attribute, value) ⇒ Object

record - Form object (e.g. Decidim::UploadValidationForm) attribute - The attribute to validate (e.g. :avatar) value - Blob's signed id (e.g. “eyJfcmFpbHMi…”)


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
# File 'decidim-core/app/validators/passthru_validator.rb', line 24

def validate_each(record, attribute, value)
  return unless target_class

  dummy_attr = target_attribute(attribute)

  # Create a dummy record for which the validations are actually run on
  dummy = validation_record(record)
  if dummy.respond_to?(dummy_attr) && !(value.class <= ActiveStorage::Attached)
    dummy.public_send("#{dummy_attr}=", value)
    value = dummy.public_send(dummy_attr)
  elsif dummy.respond_to? :file
    dummy.public_send("file=", value)
    value = dummy.public_send(:file)
  end

  target_validators(attribute).each do |validator|
    next unless validator.is_a?(ActiveModel::EachValidator)
    next unless check_validator_conditions(dummy, validator)

    dummy.errors.clear

    validator.validate_each(dummy, dummy_attr, value)
    dummy.errors[dummy_attr].each do |err|
      record.errors.add(attribute, err)
    end
  end
end

#validation_record(record) ⇒ Object

Creates a dummy validation record that passes the correct file upload validation context from the original record for the validators.


54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'decidim-core/app/validators/passthru_validator.rb', line 54

def validation_record(record)
  dummy = target_instance(record)
  if dummy.is_a?(Decidim::Attachment)
    if record.respond_to?(:attached_to)
      dummy.attached_to = record.attached_to
    elsif record.respond_to?(:organization)
      dummy.attached_to = record.organization
    end
  elsif dummy.respond_to?(:organization=) && record.respond_to?(:organization)
    dummy.organization = record.organization
  end
  dummy
end