Class: Respect::HashSchema

Inherits:
Schema
  • Object
show all
Includes:
Enumerable
Defined in:
lib/respect/hash_schema.rb

Overview

A schema to specify the structure of a hash.

This schema defines the structure of a hash by listing the expected property name and they associated schema.

Property can be define by using a symbol, a string or a regular expression. In the later case, the associated schema will be used to validate the value of all the properties matching the regular expression. You can get the list of all pattern properties using the #pattern_properties method.

You can specify optional property by either setting the "required" option to false or y setting a non nil default value. You can get the list of all optional properties using the #optional_properties method.

Access to the object's value being validated is done using either string key or symbol key. In other word { i: "42" } and { "i" => "42" } are the same object for the #validate method. The object passed is left untouched. The sanitized object is a hash with indifferent access. Note that when an object is sanitized in-place, its original keys are kept (see sanitize_object!). Only validated keys are included in the sanitized object.

You can pass several options when creating an HashSchema:

strict

if set to true the hash must not have any extra properties to be validated. (false by default)

Instance Attribute Summary (collapse)

Attributes inherited from Schema

#last_error, #options, #sanitized_object

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods inherited from Schema

#allow_nil?, def_class, def_class_name, #default, define, #documentation, #documented?, #has_default?, #inspect, #non_default_options, #optional?, #required?, #sanitize!, #sanitize_object!, statement_name, #to_h, #to_json, #to_s, #validate!, #validate?

Methods included from DocHelper

#description, #title

Constructor Details

- (HashSchema) initialize(options = {})

A new instance of HashSchema



44
45
46
47
# File 'lib/respect/hash_schema.rb', line 44

def initialize(options = {})
  super(self.class.default_options.merge(options))
  @properties = {}
end

Instance Attribute Details

- (Object) properties (readonly)

Returns the set of properties of this schema index by their name.



74
75
76
# File 'lib/respect/hash_schema.rb', line 74

def properties
  @properties
end

Class Method Details

+ (Object) default_options

Overwritten method. See Schema::default_options



35
36
37
38
39
# File 'lib/respect/hash_schema.rb', line 35

def default_options
  super().merge({
      strict: false,
    }).freeze
end

Instance Method Details

- (Object) ==(other)



209
210
211
# File 'lib/respect/hash_schema.rb', line 209

def ==(other)
  super && @properties == other.properties
end

- (Object) [](name)

Get the schema for the given property name.



55
56
57
# File 'lib/respect/hash_schema.rb', line 55

def [](name)
  @properties[name]
end

- (Object) []=(name, schema)

Set the given schema for the given property name. A name can be a Symbol, a String or a Regexp.



61
62
63
64
65
66
67
68
69
70
71
# File 'lib/respect/hash_schema.rb', line 61

def []=(name, schema)
  case name
  when Symbol, String, Regexp
    if @properties.has_key?(name)
      raise InvalidSchemaError, "property '#{name}' already defined"
    end
    @properties[name] = schema
  else
    raise InvalidSchemaError, "unsupported property name type #{name}:#{name.class}"
  end
end

- (Object) documented_properties

Return all the properties with a non-false documentation.



205
206
207
# File 'lib/respect/hash_schema.rb', line 205

def documented_properties
  @properties.select{|name, schema| schema.documented? }
end

- (Object) each(&block)

FIXME(Nicolas Despres): Add a test for me.



214
215
216
# File 'lib/respect/hash_schema.rb', line 214

def each(&block)
  @properties.each(&block)
end

- (Object) eval(&block)

Evaluate the given block as a hash schema definition (i.e. in the context of Respect::HashDef) and merge the result with this hash schema. This is a way to "re-open" this hash schema definition to add some more.



200
201
202
# File 'lib/respect/hash_schema.rb', line 200

def eval(&block)
  self.merge!(HashSchema.define(&block))
end

- (Boolean) has_property?(property_name)

Return whether property_name is defined in this hash schema.

Returns:

  • (Boolean)


193
194
195
# File 'lib/respect/hash_schema.rb', line 193

def has_property?(property_name)
  @properties.has_key?(property_name)
end

- (Object) initialize_copy(other)



49
50
51
52
# File 'lib/respect/hash_schema.rb', line 49

def initialize_copy(other)
  super
  @properties = other.properties.dup
end

- (Object) merge(hash_schema)

Merge the given hash_schema with this object schema. It works like Hash.merge.



188
189
190
# File 'lib/respect/hash_schema.rb', line 188

def merge(hash_schema)
  self.dup.merge!(hash_schema)
end

- (Object) merge!(hash_schema)

In-place version of #merge. This schema is returned.



180
181
182
183
184
# File 'lib/respect/hash_schema.rb', line 180

def merge!(hash_schema)
  @options.merge!(hash_schema.options)
  @properties.merge!(hash_schema.properties)
  self
end

- (Object) optional_properties

Return the optional properties (e.g. those that are not required).



170
171
172
# File 'lib/respect/hash_schema.rb', line 170

def optional_properties
  @properties.select{|name, schema| schema.optional? }
end

- (Object) pattern_properties

Return all the properties identified by a regular expression.



175
176
177
# File 'lib/respect/hash_schema.rb', line 175

def pattern_properties
  @properties.select{|name, schema| name.is_a?(Regexp) }
end

- (Object) validate(object)

Overwritten method. See Schema#validate.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/respect/hash_schema.rb', line 77

def validate(object)
  # Handle nil case.
  if object.nil?
    if allow_nil?
      self.sanitized_object = nil
      return true
    else
      raise ValidationError, "object is nil but this #{self.class} does not allow nil"
    end
  end
  # Validate object format.
  unless object.is_a?(Hash)
    raise ValidationError, "object is not a hash but a #{object.class}"
  end
  sanitized_object = {}.with_indifferent_access
  # Validate expected properties.
  @properties.each do |name, schema|
    case name
    when Symbol
      validate_property_with_options(name.to_s, schema, object, sanitized_object)
    when String
      validate_property_with_options(name, schema, object, sanitized_object)
    when Regexp
      object.select{|prop, schema| prop =~ name }.each do |prop, value|
        validate_property(prop, schema, object, sanitized_object)
      end
    end
  end
  if options[:strict]
    # Check whether there are extra properties.
    object.each do |name, schema|
      unless sanitized_object.has_key? name
        raise ValidationError, "unexpected key `#{name}'"
      end
    end
  end
  self.sanitized_object = sanitized_object
  true
rescue ValidationError => e
  # Reset sanitized object.
  self.sanitized_object = nil
  raise e
end