Class: Axn::Validators::ValidateValidator

Inherits:
ActiveModel::EachValidator
  • Object
show all
Defined in:
lib/axn/core/validation/validators/validate_validator.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.apply_syntactic_sugar(value, _fields) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/axn/core/validation/validators/validate_validator.rb', line 10

def self.apply_syntactic_sugar(value, _fields)
  if value.is_a?(Hash)
    # `validate:` is the CUSTOM-callable validator; a Hash form must carry the callable under
    # `:with` (`validate: { with: <callable>, message: "…" }`). A Hash without `:with` is a
    # misuse — most often ActiveModel validator keys mistakenly nested under `validate:`
    # (`validate: { inclusion: { in: [...] } }`), which enforces nothing and would otherwise
    # raise a bare `must supply :with` at CALL time. Fail loudly here (declaration time) with the
    # fix, since this runs during `expects`/`exposes`.
    unless value.key?(:with)
      raise ArgumentError,
            "`validate:` expects a callable — `validate: ->(value) { ... }` or " \
            "`validate: { with: <callable>, message: \"...\" }` — but got a Hash with no `:with` key " \
            "(keys: #{value.keys.inspect}). If you meant a standard validation such as an " \
            "allowed-value set, declare it directly (e.g. `inclusion: { in: [...] }`), not under `validate:`."
    end

    return value
  end

  { with: value }
end

Instance Method Details

#check_validity!Object

Runtime backstop for a :with-less options Hash that bypassed the declaration guard above (e.g. validations assembled directly). Mirrors the guard's guidance.

Raises:

  • (ArgumentError)


34
35
36
37
38
39
40
41
# File 'lib/axn/core/validation/validators/validate_validator.rb', line 34

def check_validity!
  return unless options[:with].nil?

  raise ArgumentError,
        "`validate:` requires a callable under `:with` (`validate: { with: <callable> }`) or the bare form " \
        "`validate: ->(value) { ... }`. For a standard validation such as an allowed-value set, use the " \
        "validator directly (e.g. `inclusion: { in: [...] }`), not `validate:`."
end

#validate_each(record, attribute, value) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/axn/core/validation/validators/validate_validator.rb', line 43

def validate_each(record, attribute, value)
  msg = begin
    options[:with].call(value)
  # Catches what axn absorbs, not just StandardError: the fallback message names the real error
  # ("failed validation: stack level too deep"), so failing the field stays both accurate and
  # uniform — a validator that blows the stack takes the same path as one raising ArgumentError.
  rescue StandardError, *Axn::Extensions::SWALLOWABLE_BEYOND_STANDARD_ERROR => e
    # Log the raised error best-effort, then surface it as this field's validation message — a
    # crashing custom validator fails the field rather than silently passing.
    Axn::Extensions.best_effort("applying custom validation on field '#{attribute}'") { raise e }

    "failed validation: #{Axn::Internal::Rendering.exception_message(e)}"
  end

  record.errors.add(attribute, msg) if msg.present?
end