module Volt::Validations

Include in any class to get validation logic

Public Class Methods

included(base) click to toggle source
# File lib/volt/models/validations/validations.rb, line 77
def self.included(base)
  base.send :extend, ClassMethods
  base.class_attribute(:custom_validations, :validations_to_run)
end

Public Instance Methods

clear_server_errors(key) click to toggle source

When a field is changed, we want to clear any errors from the server

# File lib/volt/models/validations/validations.rb, line 124
def clear_server_errors(key)
  @server_errors.delete(key)
end
error_in_changed_attributes?() click to toggle source

Returns true if any of the changed fields now has an error @return [Boolean] true if one of the changed fields has an error.

# File lib/volt/models/validations/validations.rb, line 161
def error_in_changed_attributes?
  errs = errors

  changed_attributes.each_pair do |key, _|
    # If any of the fields with errors are also the ones that were
    return true if errs[key]
  end

  false
end
errors(marked_only = false) click to toggle source
# File lib/volt/models/validations/validations.rb, line 128
def errors(marked_only = false)
  @errors ||= Errors.new

  if marked_only
    # Only return the fields that have been marked
    @errors.to_h.select { |key, _| marked_fields[key] }
  else
    @errors
  end
end
mark_all_fields!() click to toggle source

Marks all fields, useful for when a model saves.

# File lib/volt/models/validations/validations.rb, line 93
def mark_all_fields!
  # TODO: We can use a Set here, but set was having issues.  Check in a
  # later version of opal.
  fields_to_mark = []

  # Look at each validation
  validations = self.class.validations_to_run
  if validations
    fields_to_mark += validations.keys
  end

  # Also include any current fields
  fields_to_mark += attributes.keys

  fields_to_mark.each do |key|
    mark_field!(key.to_sym)
  end
end
mark_field!(field_name) click to toggle source

Once a field is ready, we can use include_in_errors! to start showing its errors.

# File lib/volt/models/validations/validations.rb, line 84
def mark_field!(field_name)
  marked_fields[field_name] = true
end
marked_errors() click to toggle source
# File lib/volt/models/validations/validations.rb, line 112
def marked_errors
  errors(true)
end
marked_fields() click to toggle source
# File lib/volt/models/validations/validations.rb, line 88
def marked_fields
  @marked_fields ||= ReactiveHash.new
end
server_errors() click to toggle source

server errors are errors that come back from the server when we save! Any changes to the associated fields will clear the error until another save!

# File lib/volt/models/validations/validations.rb, line 119
def server_errors
  @server_errors ||= ReactiveHash.new
end
validate(field_name = nil, options = nil, &block) click to toggle source

Called on the model inside of a validations block. Allows the user to control if validations should be run.

# File lib/volt/models/validations/validations.rb, line 64
def validate(field_name = nil, options = nil, &block)
  if block
    # Setup a custom validation inside of the current validations block.
    if field_name || options
      fail 'validate should be passed a field name and options or a block, not both.'
    end
    @instance_custom_validations << block
  else
    @instance_validations[field_name] ||= {}
    @instance_validations[field_name].merge!(options)
  end
end
validate!() click to toggle source

TODO: Errors is being called for any validation change. We should have errors return a hash like object that only calls the validation for each one.

# File lib/volt/models/validations/validations.rb, line 141
def validate!
  errors.clear

  # Run the before_validate callbacks
  run_callbacks(:before_validate).then do
    # Run the actual validations
    run_validations
  end.then do
    # See if any server errors are in place and merge them in if they are
    errors.merge!(server_errors.to_h) if Volt.client?
  end.then do
    run_custom_validations
  end.then do
    # Return the errors object
    errors
  end
end

Private Instance Methods

run_custom_validations(custom_validations = nil) click to toggle source
# File lib/volt/models/validations/validations.rb, line 217
def run_custom_validations(custom_validations = nil)
  # Default to running the class level custom validations
  custom_validations ||= self.class.custom_validations

  promise = Promise.new.resolve(nil)

  if custom_validations
    custom_validations.each do |custom_validation|
      # Add to the promise chain
      promise = promise.then do
        # Run the validator in the context of the model
        instance_exec(&custom_validation)
      end.then do |errs|
        errors.merge!(errs)
      end
    end
  end

  promise
end
run_validation(field_name, options) click to toggle source

Runs an individual validation @returns [Promise]

# File lib/volt/models/validations/validations.rb, line 195
def run_validation(field_name, options)
  promise = Promise.new.resolve(nil)
  options.each_pair do |validation, args|
    # Call the specific validator, then merge the results back
    # into one large errors hash.
    klass = validation_class(validation, args)

    if klass
      # Chain on the promises
      promise = promise.then do
        klass.validate(self, field_name, args)
      end.then do |errs|
        errors.merge!(errs)
      end
    else
      fail "validation type #{validation} is not specified."
    end
  end

  promise
end
run_validations(validations = nil) click to toggle source

Runs through each of the normal validations. @param [Array] An array of validations to run @return [Promise] a promsie to run all validations

# File lib/volt/models/validations/validations.rb, line 177
def run_validations(validations = nil)
  # Default to running the class level validations
  validations ||= self.class.validations_to_run

  promise = Promise.new.resolve(nil)
  if validations

    # Run through each validation
    validations.each_pair do |field_name, options|
      promise = promise.then { run_validation(field_name, options) }
    end
  end

  promise
end
validation_class(validation, args) click to toggle source
# File lib/volt/models/validations/validations.rb, line 238
def validation_class(validation, args)
  Volt.const_get(:"#{validation.camelize}Validator")
rescue NameError => e
  Volt.logger.error "Unable to find #{validation} validator"
end