Class: Contours::BlendedHash

Inherits:
Hash
  • Object
show all
Defined in:
lib/contours/blended_hash.rb

Overview

BlendedHash is a utility class that allows you to merge two hashes. It may be used to specify how to combine values that are specified more than once. For example, if you have a BlendedHash like this:

class CssConfig < BlendedHash
  @blended_keys = %i[class]
  def blend_class(original, extras)
    [original, extras].flatten.compact.uniq.join(" ")
  end
end

CssConfig.new({class: "foo"})

And you merge it with another hash like this:

CssConfig.new({class: "foo"}).merge({class: "bar"})

The result will be:

{
  class: "foo bar"
}

See the source of this #name class for more details.

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.blended_keysObject (readonly)

Returns the value of attribute blended_keys.



145
146
147
# File 'lib/contours/blended_hash.rb', line 145

def blended_keys
  @blended_keys
end

Class Method Details

.blend(key, with: nil, &block) ⇒ Object

Define a method that will be used to blend the value of a key. The method name will be “blend_#key”. If a block is given, it will be used as the implementation of the method. Otherwise, the default implementation will be used where the value of the with argument is expected to receive ‘init’ with the original value and then ‘merge’ with the extras value.

Examples:

class CssConfig < BlendedHash
  @blended_keys = %i[class]
  blend :class do |original, extras|
    [original, extras].flatten.compact.uniq.join(" ")
  end
end

CssConfig.new({class: "foo"}).merge({class: "bar"})
class CssConfig < BlendedHash
  @blended_keys = %i[class]
  blend :class, with: CssConfig
end

CssConfig.new({class: "foo"}).merge({class: "bar"})

Parameters:

  • key (Symbol)

    the key that should be blended

  • with (BlendedHash) (defaults to: nil)

    the class that should be used to blend the value

  • block (Proc)

    the implementation of the blend method



68
69
70
71
72
73
74
75
76
# File 'lib/contours/blended_hash.rb', line 68

def self.blend(key, with: nil, &block)
  if block
    define_method(:"blend_#{key}", &block)
  else
    define_method(:"blend_#{key}") do |original, extras|
      with.init(original).merge(extras)
    end
  end
end

.init(initial_hash) ⇒ Object

Ensure that the initial hash is a BlendedHash



31
32
33
34
35
36
37
# File 'lib/contours/blended_hash.rb', line 31

def self.init(initial_hash)
  if initial_hash.is_a?(BlendedHash)
    initial_hash
  else
    new({}).merge(initial_hash)
  end
end

Instance Method Details

#[]Object

Ensure that the return value of these methods is a BlendedHash



114
115
116
117
118
119
120
121
# File 'lib/contours/blended_hash.rb', line 114

def [](...)
  super_result = super
  if super_result.is_a?(Hash)
    self.class.init(super_result)
  else
    super_result
  end
end

#blend(original, extra) ⇒ Object

Default implementation of the blend method. Override this in subclasses with a custom blend_#key method to customize the blending behavior for a specific key.



153
154
155
156
157
158
159
160
161
162
# File 'lib/contours/blended_hash.rb', line 153

def blend(original, extra)
  case original
  when BlendedHash, Hash
    self.class.init(original).merge(extra)
  when Array
    [original, extra].flatten.compact.uniq
  else
    extra
  end
end

#blended_keysObject



147
# File 'lib/contours/blended_hash.rb', line 147

def blended_keys = self.class.blended_keys

#digObject



132
133
134
135
136
137
138
139
# File 'lib/contours/blended_hash.rb', line 132

def dig(...)
  super_result = super
  if super_result.is_a?(Hash)
    self.class.init(super_result)
  else
    super_result
  end
end

#fetchObject



123
124
125
126
127
128
129
130
# File 'lib/contours/blended_hash.rb', line 123

def fetch(...)
  super_result = super
  if super_result.is_a?(Hash)
    self.class.init(super_result)
  else
    super_result
  end
end

#merge(overrides) ⇒ BlendedHash

Recursively check for keys that are specified as blended and apply the blend method to them or execute the blend_#key method if it exists to set the new value.

Examples:

class CssConfig < BlendedHash
  @blended_keys = %i[class]
  blend :class do |original, extras|
    [original, extras].flatten.compact.uniq.join(" ")
  end
end

config = CssConfig.new({class: "foo"}).merge({class: "bar"})
config[:class] # => "foo bar"

Parameters:

  • overrides (Hash)

    the hash to merge with

Returns:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/contours/blended_hash.rb', line 97

def merge(overrides)
  return self if overrides.nil? || overrides.empty?
  self.class.new(overrides.each_with_object(to_hash.dup) do |(key, value), hash|
    hash[key] = if blended_keys.include?(key)
      if respond_to?(:"blend_#{key}")
        send(:"blend_#{key}", hash[key], value)
      else
        blend(hash[key], value)
      end
    else
      value
    end
    hash
  end)
end

#to_hashObject



164
165
166
# File 'lib/contours/blended_hash.rb', line 164

def to_hash
  deep_stringify_structured_strings(__getobj__)
end