Class: MSFL::Validators::Semantic

Inherits:
Object
  • Object
show all
Includes:
Definitions::HashKey
Defined in:
lib/msfl/validators/semantic.rb

Constant Summary collapse

BOOLEAN_OPERATORS =
[:and, :or]
ENUMERATION_OPERATORS =
[:in]

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Definitions::HashKey

#all_logical_operators?, #all_operators?, #any_operators?, #binary_operators, #foreign_operators, #hash_key_operators, #logical_operators, #operator?, #partial_operators, #valid_hash_key?, #valid_hash_keys

Constructor Details

#initialize(dataset = nil, opts = {}) ⇒ Semantic

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.

Parameters:

  • dataset (MSFL::Dataset::Base) (defaults to: nil)

    optionally override the dataset instance that should be used by the validator

  • opts (Hash) (defaults to: {})

    optional; currently not used, included for future additions



23
24
25
26
27
28
29
# 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

Instance Attribute Details

#current_fieldObject

Returns the value of attribute current_field.



9
10
11
# File 'lib/msfl/validators/semantic.rb', line 9

def current_field
  @current_field
end

#current_operatorObject

Returns the value of attribute current_operator.



9
10
11
# File 'lib/msfl/validators/semantic.rb', line 9

def current_operator
  @current_operator
end

#datasetObject

Returns the value of attribute dataset.



9
10
11
# File 'lib/msfl/validators/semantic.rb', line 9

def dataset
  @dataset
end

#errorsObject

Returns the value of attribute errors.



9
10
11
# File 'lib/msfl/validators/semantic.rb', line 9

def errors
  @errors
end

Instance Method Details

#validate(hash, errors = [], opts = {}) ⇒ Object

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

Parameters:

  • hash (Hash)

    the object to be validated

  • errors (Array) (defaults to: [])

    optionally provide an array that contains errors from previous validators in the validation chain



40
41
42
43
44
45
46
47
# 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) ⇒ Array

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

Parameters:

  • set (MSFL::Types::Set)

    the set to validate

  • errors (Array)

    an array of validation errors - empty indicates that no errors have been encountered

Returns:

  • (Array)

    errors merged with any validation errors encountered in validating the set



120
121
122
123
124
125
126
127
128
# 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) ⇒ Object

Validates a set of enumerationd scalar values



131
132
133
134
135
136
137
138
139
140
141
142
# 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) ⇒ Array

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

Parameters:

  • hash (Hash)

    the Hash to validate

  • errors (Array)

    an array of validation errors - empty indicates that no errors have been encountered

  • opts (Hash)

    the options hash

Returns:

  • (Array)

    errors merged with any validation errors encountered in validating the hash



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 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) ⇒ Array

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

:parent_operator option's value

Parameters:

  • set (MSFL::Types::Set)

    the set to validate

  • errors (Array)

    the existing array of validation errors

  • opts (Hash)

    the options hash

Returns:

  • (Array)

    the errors array argument with any additional validation errors appended



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# 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