Class: Respect::ArraySchema

Inherits:
Schema
  • Object
show all
Defined in:
lib/respect/array_schema.rb

Overview

A schema to specify the structure of an array.

They are two approaches to specify the structure of an array.

If the items of your array have all the same structure then you should use the #item= method to set their schema.

Example:

# An array where all items are integer greater than 42.
s = ArraySchema.define do |s|
  s.item do |s|
    s.integer greater_than: 42
  end
end
s.validate?([])              #=> true
s.validate?([ 43 ])          #=> true
s.validate?([ 43, 44 ])      #=> true
s.validate?([ 43, 44, 30 ])  #=> false

Otherwise, you should use the #items= and #extra_items=. This is called "tuple" typing.

Example:

# An array where first item is an integer and the second one
# is a string.
ArraySchema.define do |s|
  s.items do |s|
    s.integer
    s.string
  end
end
s.validate?([])              #=> false
s.validate?([ 43 ])          #=> false
s.validate?([ 43, "foo" ])   #=> true
s.validate?([ 43, 44 ])      #=> false

You cannot mix tuple typing and single item typing.

You can pass several options when creating an ArraySchema:

uniq

if true, duplicated items are forbidden (false by default).

min_size

if set the array must have at least the given number of items (nil by default). This option apply only in non-tuple typing.

max_size

if set the array must have at most the given number of items (nil by default). This option apply only in non-tuple typing.

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

- (ArraySchema) initialize(options = {})

A new instance of ArraySchema



59
60
61
# File 'lib/respect/array_schema.rb', line 59

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

Instance Attribute Details

- (Object) extra_items

Get the extra schema items.



106
107
108
# File 'lib/respect/array_schema.rb', line 106

def extra_items
  @extra_items
end

- (Object) item

Get the schema that all items in the array must validate.



79
80
81
# File 'lib/respect/array_schema.rb', line 79

def item
  @item
end

- (Object) items

Get the array of schema that the corresponding items must validate.



91
92
93
# File 'lib/respect/array_schema.rb', line 91

def items
  @items
end

Class Method Details

+ (Object) default_options

Overwritten method. See Schema.default_options



52
53
54
55
56
# File 'lib/respect/array_schema.rb', line 52

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

Instance Method Details

- (Object) ==(other)



187
188
189
# File 'lib/respect/array_schema.rb', line 187

def ==(other)
  super && @item == other.item && @items == other.items && @extra_items == other.extra_items
end

- (Object) initialize_copy(other)



63
64
65
66
67
# File 'lib/respect/array_schema.rb', line 63

def initialize_copy(other)
  super
  @items = other.items.dup unless other.items.nil?
  @extra_items = other.extra_items.dup unless other.extra_items.nil?
end

- (Object) validate(object)

Overwritten method. See Schema#validate



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/respect/array_schema.rb', line 109

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 type.
  unless object.is_a?(Array)
    raise ValidationError, "object is not an array but a #{object.class}"
  end
  # At this point we are sure @item and (@items or @extra_items) cannot be
  # defined both. (see the setters).
  sanitized_object = []
  # Validate expected item.
  if @item
    if options[:min_size] && object.size < options[:min_size]
      raise ValidationError,
            "expected at least #{options[:min_size]} item(s) but got #{object.size}"
    end
    if options[:max_size] && object.size > options[:max_size]
      raise ValidationError,
            "expected at most #{options[:min_size]} item(s) but got #{object.size}"
    end
    object.each_with_index do |item, i|
      validate_item(i, @item, object, sanitized_object)
    end
  end
  # Validate object items count.
  if @items || @extra_items
    if @extra_items
      min_size = @items ? @items.size : 0
      unless min_size <= object.size
        raise ValidationError,
              "array size should be at least #{min_size} but is #{object.size}"
      end
    else
      if @items.size != object.size
        raise ValidationError,
              "array size should be #{@items.size} but is #{object.size}"
      end
    end
  end
  # Validate expected multiple items.
  if @items
    @items.each_with_index do |schema, i|
      validate_item(i, schema, object, sanitized_object)
    end
  end
  # Validate extra items.
  if @extra_items
    @extra_items.each_with_index do |schema, i|
      if @items.size + i < object.size
        validate_item(@items.size + i, schema, object, sanitized_object)
      end
    end
  end
  # Validate all items are unique.
  if options[:uniq]
    s = Set.new
    object.each_with_index do |e, i|
      if s.add?(e).nil?
        raise ValidationError,
              "duplicated item number #{i}"
      end
    end
  end
  self.sanitized_object = sanitized_object
  true
rescue ValidationError => e
  # Reset sanitized object.
  self.sanitized_object = nil
  raise e
end