Class: HashDelegator

Inherits:
Object
  • Object
show all
Defined in:
lib/hash_delegator.rb,
lib/hash_delegator/version.rb

Overview

Provides delegation and basic validation for Hashes

Constant Summary collapse

MUTATING_METHODS =

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

Set[
  :clear,
  :delete,
  :update,
  :delete_if,
  :keep_if,
  :compact!,
  :filter!,
  :merge!,
  :reject!,
  :select!,
  :transform_keys!,
  :transform_values!,
  :default=,
  :default_proc=,
  :compare_by_identity,
  :rehash,
  :replace,
  :initialize_copy,
  :shift,
  :store
].freeze
CLOSED_METHODS =

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

Set[
  :compact,
  :merge
].freeze
VERSION =
"0.1.2"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash = EMPTY_HASH) ⇒ HashDelegator

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).

Parameters:

  • hash (Hash) (defaults to: EMPTY_HASH)


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
# 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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

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.

Parameters:

  • method (Symbol)
  • args (Array)
  • block (Proc)

See Also:



273
274
275
276
277
278
279
280
281
282
283
284
# 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

Class Method Details

.default(value = nil, &block) ⇒ HashDelegator

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

Parameters:

  • value (Object) (defaults to: nil)

    default value

  • block (Proc)

    default proc

Returns:



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 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_valueObject

Return the default value



63
64
65
66
67
# 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_transformerObject

Return the key transformer



75
76
77
78
79
# 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

.require(*attributes) ⇒ Object

Deprecated.


35
36
37
38
# File 'lib/hash_delegator.rb', line 35

def require(*attributes)
  warn 'HashDelegator.require is deprecated'
  required(*attrbutes)
end

.required(*attributes) ⇒ HashDelegator

Specifiy required attributes

Parameters:

  • attributes (Array)

Returns:



23
24
25
26
27
28
29
30
31
32
# 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_attributesArray?

Return required attributes or nil

Returns:

  • (Array, nil)


13
14
15
16
17
# 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) ⇒ Object

Specify the key transformer



70
71
72
# File 'lib/hash_delegator.rb', line 70

def transform_keys(&block)
  @key_transformer = block
end

Instance Method Details

#==(other) ⇒ Boolean

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

Parameters:

  • other

Returns:

  • (Boolean)


242
243
244
# File 'lib/hash_delegator.rb', line 242

def ==(other)
  other.instance_of?(self.class) && eql?(other)
end

#===(other) ⇒ Object

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

Parameters:

  • other


229
230
231
232
233
234
# 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) ⇒ Object

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).

Parameters:

  • key


203
204
205
206
207
208
209
# 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) ⇒ Boolean

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

Returns:

  • (Boolean)


222
223
224
# File 'lib/hash_delegator.rb', line 222

def eql?(other)
  @hash.hash == other.hash
end

#except(*keys) ⇒ Hash, HashDelegator

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.

Parameters:

  • keys (Array)

Returns:



157
158
159
160
161
162
163
164
165
# 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

#hashInteger

Return the numerical hash of the decorated hash.

Returns:

  • (Integer)


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

def hash
  @hash.hash
end

#respond_to_missing?(method, include_all) ⇒ Boolean

Note:

DO NOT USE DIRECTLY

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.

Parameters:

  • method (Symbol)
  • include_all (Boolean)

Returns:

  • (Boolean)

See Also:

  • Object#respond_to?
  • Object#respond_to_missing?


257
258
259
# File 'lib/hash_delegator.rb', line 257

def respond_to_missing?(method, include_all)
  super || key?(method) || hash_respond_to?(method)
end

#slice(*keys) ⇒ Hash, HashDelegator

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.

Parameters:

  • keys (Array)

Returns:



174
175
176
177
178
179
180
181
182
183
# 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_hashHash Also known as: to_h

Return a duplicate of the delegated hash.

Returns:



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

def to_hash
  @hash.dup
end

#to_sObject Also known as: inspect



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

def to_s
  "#<#{self.class} #{@hash.inspect}>"
end