Class: ShapeOf::Hash

Inherits:
Shape
  • Object
show all
Defined in:
lib/shape_of.rb

Overview

Hash[key: shape, …] denotes it is a hash of shapes with a very specific structure. Hash (without square brackets) is just a hash with any shape. This, along with Array, are the core components of this module. Note that the keys are converted to strings for comparison for both the shape and object provided.

Class Method Summary collapse

Methods inherited from Shape

#initialize, required?

Constructor Details

This class inherits a constructor from ShapeOf::Shape

Class Method Details

.[](shape = {}) ⇒ Object

Raises:

  • (TypeError)


300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/shape_of.rb', line 300

def self.[](shape = {})
  raise TypeError, "Shape must be Hash, was #{shape.class.name}" unless shape.instance_of? ::Hash

  Class.new(self) do
    @class_name = "#{superclass.name}[#{shape.map { |(k, v)| "#{k.to_s}: #{v.inspect}" }.join(', ')}]"
    @shape = stringify_rb_hash_keys(shape)
    @internal_class = superclass.instance_variable_get(:@internal_class)

    def self.name
      @class_name
    end

    def self.to_s
      @class_name
    end

    def self.inspect
      @class_name
    end

    def self.shape_of?(hash, validator: Validator.new(shape: self, object: hash))
      return false unless super

      each_is_shape_of = true
      rb_hash = stringify_rb_hash_keys(hash)

      rb_hash.keys.each do |key|
        has_key = @shape.key?(key)
        unless has_key
          validator.push_key(key)
          validator.add_error("unexpected key")
          validator.pop_key
          each_is_shape_of = false
        end
      end

      @shape.each do |key, shape|
        unless rb_hash.key?(key) || shape.respond_to?(:required?) && !shape.required?
          validator.push_key(key)
          validator.add_error("required key not present")
          validator.pop_key
          each_is_shape_of = false
        end
      end

      rb_hash.each do |key, elem|
        shape_elem = @shape[key]
        validator.push_key(key)

        is_shape_of = if shape_elem.respond_to? :shape_of?
          shape_elem.shape_of?(elem, validator: validator)
        elsif shape_elem.is_a? ::Array
          Array[shape_elem.first].shape_of?(elem, validator: validator)
        elsif shape_elem.is_a? ::Hash
          Hash[shape_elem].shape_of?(elem, validator: validator)
        elsif shape_elem.is_a? Class
          is_instance_of = elem.instance_of?(shape_elem)
          validator.add_error(elem.inspect + " is not instance of " + shape_elem.inspect) unless is_instance_of

          is_instance_of
        else
          is_equal_to = elem == shape_elem
          validator.add_error(elem.inspect + " is not equal to (==) " + shape_elem.inspect) unless is_equal_to

          is_equal_to
        end

        validator.pop_key
        each_is_shape_of &&= is_shape_of
      end
      each_is_shape_of
    end
  end
end

.shape_of?(object, validator: Validator.new(shape: self, object: object)) ⇒ Boolean



293
294
295
296
297
298
# File 'lib/shape_of.rb', line 293

def self.shape_of?(object, validator: Validator.new(shape: self, object: object))
  is_instance_of = object.instance_of?(@internal_class)
  validator.add_error(object.inspect + " is not instance of " + @internal_class.inspect) unless is_instance_of

  is_instance_of
end