Class: PassthruValidator

Inherits:
ActiveModel::EachValidator
  • Object
show all
Defined in:
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



87
88
89
# File 'app/validators/passthru_validator.rb', line 87

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

#target_classObject



83
84
85
# File 'app/validators/passthru_validator.rb', line 83

def target_class
  options[:to]
end

#target_instance(record) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'app/validators/passthru_validator.rb', line 91

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



79
80
81
# File 'app/validators/passthru_validator.rb', line 79

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 '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
67
# File '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
  validation_record_context(dummy, record)
  dummy
end

#validation_record_context(dummy, record) ⇒ Object



69
70
71
72
73
74
75
76
77
# File 'app/validators/passthru_validator.rb', line 69

def validation_record_context(dummy, record)
  if dummy.is_a?(Decidim::Form)
    dummy.with_context(
      current_organization: record.try(:current_organization) || record.try(:organization),
      current_participatory_space: record.try(:current_participatory_space) || record.try(:participatory_space),
      current_component: record.try(:current_component) || record.try(:component)
    )
  end
end