class Schemacop::V3::HashNode
Constants
- ATTRIBUTES
Attributes
properties[R]
Public Class Methods
allowed_options()
click to toggle source
Calls superclass method
# File lib/schemacop/v3/hash_node.rb, line 16 def self.allowed_options super + ATTRIBUTES - %i[dependencies] + %i[additional_properties] end
dsl_methods()
click to toggle source
Calls superclass method
# File lib/schemacop/v3/hash_node.rb, line 20 def self.dsl_methods super + NodeRegistry.dsl_methods(true) + %i[dsl_dep dsl_add] end
Public Instance Methods
_validate(data, result: Result.new)
click to toggle source
Calls superclass method
# File lib/schemacop/v3/hash_node.rb, line 84 def _validate(data, result: Result.new) super_data = super return if super_data.nil? original_data_hash = super_data.dup data_hash = super_data.with_indifferent_access if original_data_hash.size != data_hash.size ambiguous_properties = original_data_hash.keys - data_hash.keys result.error "Has #{ambiguous_properties.size} ambiguous properties: #{ambiguous_properties}." end # Validate min_properties # if options[:min_properties] && data_hash.size < options[:min_properties] result.error "Has #{data_hash.size} properties but needs at least #{options[:min_properties]}." end # Validate max_properties # if options[:max_properties] && data_hash.size > options[:max_properties] result.error "Has #{data_hash.size} properties but needs at most #{options[:max_properties]}." end # Validate specified properties # @properties.each_value do |node| result.in_path(node.name) do next if node.name.is_a?(Regexp) node._validate(data_hash[node.name], result: result) end end # Validate additional properties # specified_properties = @properties.keys.to_set additional_properties = data_hash.reject { |k, _v| specified_properties.include?(k.to_s) } property_patterns = {} @properties.each_value do |property| if property.name.is_a?(Regexp) property_patterns[property.name] = property end end property_names = options[:property_names] property_names = Regexp.compile(property_names) if property_names additional_properties.each do |name, additional_property| if property_names && !property_names.match?(name) result.error "Property name #{name.inspect} does not match #{options[:property_names].inspect}." end if options[:additional_properties].is_a?(TrueClass) next elsif options[:additional_properties].is_a?(FalseClass) || options[:additional_properties].blank? match = property_patterns.keys.find { |p| p.match?(name.to_s) } if match result.in_path(name) do property_patterns[match]._validate(additional_property, result: result) end else result.error "Obsolete property #{name.to_s.inspect}." end elsif options[:additional_properties].is_a?(Node) result.in_path(name) do options[:additional_properties]._validate(additional_property, result: result) end end end # Validate dependencies # options[:dependencies]&.each do |source, targets| targets.each do |target| if data_hash[source].present? && data_hash[target].blank? result.error "Missing property #{target.to_s.inspect} because #{source.to_s.inspect} is given." end end end end
add_child(node)
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 24 def add_child(node) unless node.name fail Exceptions::InvalidSchemaError, 'Child nodes must have a name.' end @properties[node.name] = node end
allowed_types()
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 80 def allowed_types { Hash => :object } end
as_json()
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 45 def as_json properties = {} pattern_properties = {} @properties.each do |name, property| if name.is_a?(Regexp) pattern_properties[name] = property else properties[name] = property end end json = {} json[:properties] = Hash[properties.values.map { |p| [p.name, p.as_json] }] if properties.any? json[:patternProperties] = Hash[pattern_properties.values.map { |p| [V3.sanitize_exp(p.name), p.as_json] }] if pattern_properties.any? # In schemacop, by default, additional properties are not allowed, # the users explicitly need to enable additional properties if options[:additional_properties].is_a?(TrueClass) json[:additionalProperties] = true elsif options[:additional_properties].is_a?(Node) json[:additionalProperties] = options[:additional_properties].as_json else json[:additionalProperties] = false end required_properties = @properties.values.select(&:required?).map(&:name) if required_properties.any? json[:required] = required_properties end return process_json(ATTRIBUTES, json) end
cast(data)
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 168 def cast(data) result = {}.with_indifferent_access data ||= default return nil if data.nil? data_hash = data.dup.with_indifferent_access property_patterns = {} as_names = [] @properties.each_value do |prop| if prop.name.is_a?(Regexp) property_patterns[prop.name] = prop next end as_names << prop.as&.to_s if prop.as.present? prop_name = prop.as&.to_s || prop.name casted_data = prop.cast(data_hash[prop.name]) if casted_data.present? || data_hash.include?(prop.name) result[prop_name] = casted_data end if result[prop_name].nil? && !data_hash.include?(prop.name) && !as_names.include?(prop.name) result.delete(prop_name) end end # Handle regex properties specified_properties = @properties.keys.to_set additional_properties = data_hash.reject { |k, _v| specified_properties.include?(k.to_s.to_sym) } if additional_properties.any? && property_patterns.any? additional_properties.each do |name, additional_property| match_key = property_patterns.keys.find { |p| p.match?(name.to_s) } match = property_patterns[match_key] result[name] = match.cast(additional_property) end end # Handle additional properties if options[:additional_properties].is_a?(TrueClass) result = data_hash.merge(result) elsif options[:additional_properties].is_a?(Node) specified_properties = @properties.keys.to_set additional_properties = data_hash.reject { |k, _v| specified_properties.include?(k.to_s.to_sym) } if additional_properties.any? additional_properties_result = {} additional_properties.each do |key, value| additional_properties_result[key] = options[:additional_properties].cast(value) end result = additional_properties_result.merge(result) end end return result end
children()
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 164 def children @properties.values end
dsl_add(type, **options, &block)
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 32 def dsl_add(type, **options, &block) if @options[:additional_properties].is_a?(Node) fail Exceptions::InvalidSchemaError, 'You can only use "add" once to specify additional properties.' end @options[:additional_properties] = create(type, **options, &block) end
dsl_dep(source, *targets, **_kwargs)
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 40 def dsl_dep(source, *targets, **_kwargs) @options[:dependencies] ||= {} @options[:dependencies][source] = targets end
Protected Instance Methods
init()
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 231 def init @properties = {} @options[:type] = :object unless @options[:additional_properties].nil? || @options[:additional_properties].is_a?(TrueClass) || @options[:additional_properties].is_a?(FalseClass) fail Schemacop::Exceptions::InvalidSchemaError, 'Option "additional_properties" must be a boolean value' end # Default the additional_properties option to false if it's not given if @options[:additional_properties].nil? @options[:additional_properties] = false end end
validate_self()
click to toggle source
# File lib/schemacop/v3/hash_node.rb, line 244 def validate_self unless options[:min_properties].nil? || options[:min_properties].is_a?(Integer) fail 'Option "min_properties" must be an "integer"' end unless options[:max_properties].nil? || options[:max_properties].is_a?(Integer) fail 'Option "max_properties" must be an "integer"' end if @properties.values.any? { |p| p.name.is_a?(Regexp) && p.required? } fail 'Pattern properties can\'t be required.' end end