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

[View source]

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

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

#target_classObject

[View source]

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

def target_class
  options[:to]
end

#target_instance(record) ⇒ Object

[View source]

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

[View source]

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…”)

[View source]

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.

[View source]

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

[View source]

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(:ganization),
      current_participatory_space: record.try(:current_participatory_space) || record.try(:participatory_space),
      current_component: record.try(:current_component) || record.try(:component)
    )
  end
end