class MSFL::Validators::Semantic

Constants

BOOLEAN_OPERATORS
ENUMERATION_OPERATORS

Attributes

current_field[RW]
current_operator[RW]
dataset[RW]
errors[RW]

Public Class Methods

new(dataset = nil, opts = {}) click to toggle source

Used for creating new semantic validator instances

If the dataset argument is specified it will be used as the dataset for the validator. Otherwise the instance’s dataset will default to the first value in MSFL.configuration.datasets, unless it is empty, in which case it will revert to MSFL::Datasets::Base, which will deliberately break execution when validate is called on the validator instance, raising a NoMethodError.

@param dataset [MSFL::Dataset::Base] optionally override the dataset instance that should be used by the validator @param opts [Hash] optional; currently not used, included for future additions

# File lib/msfl/validators/semantic.rb, line 23
def initialize(dataset = nil, opts = {})
  @dataset = dataset unless dataset.nil?
  @dataset ||= MSFL.configuration.datasets.first.new unless MSFL.configuration.datasets.empty?
  @dataset ||= Datasets::Base.new
  @current_field = nil
  @current_operator = nil
end

Public Instance Methods

validate(hash, errors = [], opts = {}) click to toggle source

Returns true if the object is valid, false if the object is invalid An array of hashes of errors is available at errors

This method is not meant to be called recursively, the private method recursive_validate is used

for this purpose

@param hash [Hash] the object to be validated @param errors [Array] optionally provide an array that contains errors from previous validators in the

validation chain
# File lib/msfl/validators/semantic.rb, line 40
def validate(hash, errors = [], opts = {})
  errors << "Object to validate must be a Hash" unless hash.is_a?(Hash)
  recursive_validate hash, errors, opts
  @errors = errors
  result = true if @errors.empty?
  result ||= false
  result
end
validate_boolean_set(set, errors, opts) click to toggle source

Returns the result of merging errors with any newly encountered errors found in validating the set

@param set [MSFL::Types::Set] the set to validate @param errors [Array] an array of validation errors - empty indicates that no errors have been encountered @return [Array] errors merged with any validation errors encountered in validating the set

# File lib/msfl/validators/semantic.rb, line 120
def validate_boolean_set(set, errors, opts)
  # Every member needs to be a hash
  set.each do |value|
    errors << "Every member of a boolean set must be a Hash" unless value.is_a?(Hash)
    # recursively call validate on each member
    recursive_validate value, errors, opts
  end
  errors
end
validate_enumeration_set(set, errors, opts) click to toggle source

Validates a set of enumerationd scalar values

# File lib/msfl/validators/semantic.rb, line 131
def validate_enumeration_set(set, errors, opts)
  current_field = opts[:parent_field] if opts.has_key?(:parent_field)
  current_field ||= nil
  errors << "Validate enumeration set requires the parent_field option to be set" if current_field.nil?
  set.each do |value|
    # this isn't quite right, it feels dirty to use :each
    errors << "No members of an enumeration set may permit iteration across itself" if value.respond_to?(:each)
    dataset.validate_type_conforms value, current_field, errors
    dataset.validate_value_conforms value, current_field, errors
  end
  errors
end
validate_hash(hash, errors, opts) click to toggle source

Returns the result of merging errors with any newly encountered errors found in validating the hash

@param hash [Hash] the Hash to validate @param errors [Array] an array of validation errors - empty indicates that no errors have been encountered @param opts [Hash] the options hash @return [Array] errors merged with any validation errors encountered in validating the hash

# File lib/msfl/validators/semantic.rb, line 55
def validate_hash(hash, errors, opts)
  # set current field
  current_field = nil
  # validate the keys and values
  hash.each do |k, value|
    key = k.to_sym
    # validate the current hash key using broad hash key validation
    errors << "Hash key encountered that is broadly invalid." unless valid_hash_key?(key)

    # validate that the hash key is supported as an operator or dataset field

    # if they key is an operator validate the dataset supports the operator for the current field
    #
    # if the key is a field of the dataset then we need to validate that the _value_ conforms to the dataset
    #  specific validation rules for that field
    #
    # if they key is neither an operator nor a field we raise an ArgumentError
    #
    # Then make a recursive call to validate on the value so that it and its elements are validated
    #  later I might be able to optimize this by only making the recursive call for Sets and Hashes
    #
    if dataset.has_operator? key
      dataset.validate_operator_conforms key, current_field, errors
      opts[:parent_operator] = key
    elsif dataset.has_field? key
      current_field = key
      dataset.validate_type_conforms value, current_field, errors
      dataset.validate_value_conforms value, current_field, errors
      opts[:parent_field] = current_field
    else
      errors << "Encountered hash key that is neither an operator nor a property of the dataset"
    end
    recursive_validate value, errors, opts
  end
  errors
end
validate_set(set, errors, opts) click to toggle source

Acts as a helper method that forwards validation requests for sets to the right handler based on the

:parent_operator option's value

@param set [MSFL::Types::Set] the set to validate @param errors [Array] the existing array of validation errors @param opts [Hash] the options hash @return [Array] the errors array argument with any additional validation errors appended

# File lib/msfl/validators/semantic.rb, line 99
def validate_set(set, errors, opts)
  error_message =
      "Validate set requires the :parent_operator option be set and represented in either the BOOLEAN_OPERATORS
      or ENUMERATION_OPERATORS constant"
  errors << error_message unless opts.has_key?(:parent_operator)

  if BOOLEAN_OPERATORS.include?(opts[:parent_operator])
    validate_boolean_set set, errors, opts
  elsif ENUMERATION_OPERATORS.include?(opts[:parent_operator])
    validate_enumeration_set set, errors, opts
  else
    errors << error_message
  end
  errors
end

Private Instance Methods

recursive_validate(obj, errors, opts) click to toggle source
# File lib/msfl/validators/semantic.rb, line 146
def recursive_validate(obj, errors, opts)
  # store a copy of parent_operator and parent_field to restore after recursion
  parent_operator = opts[:parent_operator] if opts.has_key?(:parent_operator)
  parent_operator ||= nil
  parent_field = opts[:parent_field] if opts.has_key?(:parent_field)
  parent_field ||= nil
  if obj.is_a?(Hash)
    validate_hash obj, errors, opts
  elsif obj.is_a?(Types::Set)
    validate_set obj, errors, opts
  end
  opts.delete :parent_operator
  opts[:parent_operator] = parent_operator unless parent_operator.nil?
  opts.delete :parent_field
  opts[:parent_field] = parent_field unless parent_field.nil?
  errors
end