class HashDelegator

Provides delegation and basic validation for Hashes

Constants

CLOSED_METHODS

Methods that are closed (in the algebraic sense) meaning that they will not remove required keys.

EMPTY_HASH
MUTATING_METHODS

Methods that mutate the internal hash, these cannot be called publicly.

VERSION

Public Class Methods

default(value = nil, &block) click to toggle source

Specify the default value if the value is a Proc or a block is passed each hash's default_proc attribute will be set.

@param value [Object] default value @param block [Proc] default proc @return [HashDelegator]

# File lib/hash_delegator.rb, line 46
def default(value = nil, &block)
  if block
    @default_value = block
    return self
  end

  if value.is_a?(Proc) && value.lambda? && value.arity != 2
    lambda = value
    value  = ->(*args) { lambda.call(*args.slice(0, lambda.arity)) }
  end

  @default_value = value

  self
end
default_value() click to toggle source

Return the default value

# File lib/hash_delegator.rb, line 63
def default_value
  return @default_value if @default_value

  superclass.default_value if superclass.respond_to?(:default_value)
end
key_transformer() click to toggle source

Return the key transformer

# File lib/hash_delegator.rb, line 75
def key_transformer
  return @key_transformer if @key_transformer

  superclass.key_transformer if superclass.respond_to?(:key_transformer)
end
new(hash = EMPTY_HASH) click to toggle source

Initialize the HashDelegator with the given hash. If the hash is not frozen it will be duplicated. If a key transformer is specified the hashes keys will be processed with it (duplicating the original hash). The hash will be validated for the existance of the required attributes (note that a key with a nil value still exists in the hash).

@param hash [Hash]

# File lib/hash_delegator.rb, line 124
def initialize(hash = EMPTY_HASH)
  raise 'HashDelegator should not be initialized' if instance_of?(HashDelegator)

  @hash =
    if self.class.key_transformer
      hash.transform_keys(&self.class.key_transformer)
    elsif hash.frozen?
      hash
    else
      hash.dup
    end

  if self.class.default_value.is_a?(Proc)
    @hash.default_proc = self.class.default_value
  else
    @hash.default = self.class.default_value
  end

  if self.class.required_attributes
    self.class.required_attributes.each do |attribute|
      attribute = self.class.key_transformer.call(attribute) if self.class.key_transformer
      raise "#{attribute.inspect} is required, but is missing" unless key?(attribute)
    end
  end
end
require(*attributes) click to toggle source

@deprecated

# File lib/hash_delegator.rb, line 35
def require(*attributes)
  warn 'HashDelegator.require is deprecated'
  required(*attrbutes)
end
required(*attributes) click to toggle source

Specifiy required attributes

@param attributes [Array] @return [HashDelegator]

# File lib/hash_delegator.rb, line 23
def required(*attributes)
  @required_attributes =
    if superclass.respond_to?(:required_attributes) && !superclass.required_attributes.nil?
      superclass.required_attributes + attributes
    else
      attributes
    end

  self
end
required_attributes() click to toggle source

Return required attributes or nil

@return [Array, nil]

# File lib/hash_delegator.rb, line 13
def required_attributes
  return @required_attributes if @required_attributes

  superclass.required_attributes if superclass.respond_to?(:required_attributes)
end
transform_keys(&block) click to toggle source

Specify the key transformer

# File lib/hash_delegator.rb, line 70
def transform_keys(&block)
  @key_transformer = block
end

Public Instance Methods

==(other) click to toggle source

Return true if the other object is of the same class and the numerical hash of the other object and this object are equal.

@param other

@return [Boolean]

# File lib/hash_delegator.rb, line 242
def ==(other)
  other.instance_of?(self.class) && eql?(other)
end
===(other) click to toggle source

Return true if the other object has all of this objects required attributes.

@param other

# File lib/hash_delegator.rb, line 229
def ===(other)
  required = self.class.required_attributes

  other.respond_to?(:keys) && (common = other.keys & required) &&
    common.size == other.keys.size && common.size == required.size
end
[](key) click to toggle source

Return the value associated with the given key. If a key transformer is special the key will be transformed first. If the key is missing the default value will be return (nil unless specified).

@param key

# File lib/hash_delegator.rb, line 203
def [](key)
  if self.class.key_transformer
    @hash[self.class.key_transformer.call(key)]
  else
    @hash[key]
  end
end
eql?(other) click to toggle source

Return true if the other object has the same numerical hash as this object.

@return [Boolean]

# File lib/hash_delegator.rb, line 222
def eql?(other)
  @hash.hash == other.hash
end
except(*keys) click to toggle source

If the given keys include any required attributes the hash will be duplicated and except will be called on the duplicated hash. Otherwise a new instance of the HashDelegator will be return without the specified keys.

@param keys [Array] @return [Hash, HashDelegator]

# File lib/hash_delegator.rb, line 157
def except(*keys)
  common = keys & self.class.required_attributes

  if common.empty?
    self.class.new(@hash.except(*keys))
  else
    to_hash.except(*keys)
  end
end
hash() click to toggle source

Return the numerical hash of the decorated hash.

@return [Integer]

# File lib/hash_delegator.rb, line 214
def hash
  @hash.hash
end
inspect()
Alias for: to_s
method_missing(method, *args, &block) click to toggle source

If the method is a key of the internal hash return it's value. If the internal hash responds to the method forward the method to the hash. If the method is 'closed' retrun a new HashDelegator otherwise return the raw result. If none of these conditions hold call the superclass' method_missing.

@see CLOSED_METHODS @see Object#method_missing

@param method [Symbol] @param args [Array] @param block [Proc]

Calls superclass method
# File lib/hash_delegator.rb, line 273
def method_missing(method, *args, &block)
  return @hash[method] if @hash.key?(method)

  if hash_respond_to?(method)
    result = @hash.public_send(method, *args, &block)
    return result unless CLOSED_METHODS.include?(method)

    return self.class.new(result)
  end

  super
end
respond_to_missing?(method, include_all) click to toggle source

Return true if the superclass responds to the method or if the method is a key of the internal hash or if the hash responds to this method. Otherwise return false.

@note DO NOT USE DIRECTLY

@see Object#respond_to? @see Object#respond_to_missing?

@param method [Symbol] @param include_all [Boolean]

Calls superclass method
# File lib/hash_delegator.rb, line 257
def respond_to_missing?(method, include_all)
  super || key?(method) || hash_respond_to?(method)
end
slice(*keys) click to toggle source

If the given keys include all of the required attributes a new HashDelegator will be returned with only the specified keys. Otherwise a internal hash will be duplicated and slice will be called on the duplicated hash.

@param keys [Array] @return [Hash, HashDelegator]

# File lib/hash_delegator.rb, line 174
def slice(*keys)
  required = self.class.required_attributes
  common   = keys & required

  if keys.size == common.size && common.size == required.size
    self.class.new(@hash.slice(*keys))
  else
    to_hash.slice(*keys)
  end
end
to_h()
Alias for: to_hash
to_hash() click to toggle source

Return a duplicate of the delegated hash.

@return [Hash]

# File lib/hash_delegator.rb, line 188
def to_hash
  @hash.dup
end
Also aliased as: to_h
to_s() click to toggle source
# File lib/hash_delegator.rb, line 193
def to_s
  "#<#{self.class} #{@hash.inspect}>"
end
Also aliased as: inspect

Protected Instance Methods

[]=(key, value) click to toggle source

Set the key of the internal hash to the given value.

@param key @param value

# File lib/hash_delegator.rb, line 298
def []=(key, value)
  @hash[key] = value
end

Private Instance Methods

hash_respond_to?(method) click to toggle source
# File lib/hash_delegator.rb, line 288
def hash_respond_to?(method)
  !MUTATING_METHODS.include?(method) && @hash.respond_to?(method)
end