class ShapeOf::Hash

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.

Public Class Methods

[](shape = {}) click to toggle source
# File lib/shape_of.rb, line 189
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)
      return false unless super

      rb_hash = stringify_rb_hash_keys(hash)

      rb_hash.keys.each do |key|
        return false unless @shape.key?(key)
      end

      @shape.each do |key, shape|
        return false unless rb_hash.key?(key) || shape.respond_to?(:required?) && !shape.required?
      end

      rb_hash.all? do |key, elem|
        if @shape[key].respond_to? :shape_of?
          @shape[key].shape_of? elem
        elsif @shape[key].is_a? ::Array
          Array[@shape[key].first].shape_of? elem
        elsif @shape[key].is_a? ::Hash
          Hash[@shape[key]].shape_of? elem
        elsif @shape[key].is_a? Class
          elem.instance_of? @shape[key]
        else
          elem == @shape[key]
        end
      end
    end
  end
end
inspect() click to toggle source
# File lib/shape_of.rb, line 205
def self.inspect
  @class_name
end
name() click to toggle source
# File lib/shape_of.rb, line 197
def self.name
  @class_name
end
shape_of?(object) click to toggle source
# File lib/shape_of.rb, line 185
def self.shape_of?(object)
  object.instance_of? @internal_class
end
to_s() click to toggle source
# File lib/shape_of.rb, line 201
def self.to_s
  @class_name
end

Private Class Methods

stringify_rb_hash_keys(rb_hash) click to toggle source
# File lib/shape_of.rb, line 241
def self.stringify_rb_hash_keys(rb_hash)
  rb_hash.to_a.map { |k, v| [k.to_s, v] }.to_h
end