module Volt::Validations
Include in any class to get validation logic
Public Class Methods
# 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
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
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
# 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
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
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
# File lib/volt/models/validations/validations.rb, line 112 def marked_errors errors(true) end
# File lib/volt/models/validations/validations.rb, line 88 def marked_fields @marked_fields ||= ReactiveHash.new end
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
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
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
# 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
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
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
# 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