Class: Hash

Inherits:
Object show all
Defined in:
lib/y_support/name_magic/hash.rb,
lib/y_support/core_ext/hash/misc.rb,
lib/y_support/typing/hash/typing.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.method_added(sym) ⇒ Object



116
117
118
119
120
# File 'lib/y_support/core_ext/hash/misc.rb', line 116

def method_added( sym )
  if sym == :slice then
    fail LoadError, "Attempt to overwrite YSupport's Hash##{sym} method!"
  end
end

Instance Method Details

#aE_has(key, options = {}, &b) ⇒ Object

This method behaves exactly like #aT_has, but with the difference, that it raises ArgumentError instead of TypeError



69
70
71
72
73
74
75
# File 'lib/y_support/typing/hash/typing.rb', line 69

def aE_has key, options={}, &b
  begin
    options.empty? ? aT_has( key, &b ) : aT_has( key, options, &b )
  rescue TypeError => e
    raise AErr, e.message
  end
end

#aT_has(key, options = {}, &b) ⇒ Object Also known as: must_have

This enforcer method (aka. runtime assertion) raises TypeError when:

  1. Neither the required key nor any of its synonyms are present.

  2. The supplied criterion block, if any, returns false when applied

to the value of the key in question. If the block takes an argument (or more arguments), the value is passed in. If the block takes no arguments (arity 0), it is executed inside the singleton class of the receiver (using #instance_exec method).

Raises:



54
55
56
57
58
59
60
61
62
63
# File 'lib/y_support/typing/hash/typing.rb', line 54

def aT_has key, options={}, &b
  raise TErr, "Key '#{key}' absent!" unless has? key, options
  # Now validate self[key] using the supplied block
  if block_given?
    m = "Value for #{key} fails its duck type!"
    raise TErr, m unless ( b.arity == 0 ? self[key].instance_exec( &b ) :
                             b.( self[key] ) )
  end
  return self[key]
end

#dot!(overwrite_methods: false) ⇒ Object

Makes hash keys accessible as methods. If the hash keys collide with its methods, ArgumentError is raised, unless :overwrite_methods option == true.



87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/y_support/core_ext/hash/misc.rb', line 87

def dot! overwrite_methods: false
  keys.each do |key|
    msg = "key #{key} of #dot!-ted hash is not convertible to a symbol"
    fail ArgumentError, msg unless key.respond_to? :to_sym
    msg = "#dot!-ted hash must not have key names colliding with its methods"
    fail ArgumentError, msg if methods.include? key.to_sym unless
      overwrite_methods
    define_singleton_method key.to_sym do self[key] end
    define_singleton_method "#{key}=".to_sym do |value| self[key] = value end
  end
  return self
end

#has?(key, options = {}) ⇒ Boolean

This method behaves similarly to #may_have, with the difference that it does not return the value of the key, only true / false to indicate whether the key or any synonym has been found.

Returns:

  • (Boolean)


42
43
44
# File 'lib/y_support/typing/hash/typing.rb', line 42

def has? key, options={}
  !merge_synonym_keys!( key, *options[:syn!] ).nil?
end

#keys_to_namesObject

Maps a hash into a hash, whose keys have been replaced with names of the key objects (which are assumed to respond to #name method).



7
8
9
# File 'lib/y_support/name_magic/hash.rb', line 7

def keys_to_names
  with_keys do |key| key.name || key end
end

#keys_to_names!Object

Modifies a hash in place so that the keys are replaced with key names (key objects are assumed to respond to #name method).



14
15
16
# File 'lib/y_support/name_magic/hash.rb', line 14

def keys_to_names!
  with_keys! do |key| key.name || key end
end

#may_have(key, options = {}) ⇒ Object

This method uses #merge_synonym_keys! method first and then returns the value under the key. The first argument is the main key. Synonyms may be supplied as a named argument :syn!. (Bang indicates that the synonym keys will be merged with the main key, modifying the hash.)



33
34
35
36
# File 'lib/y_support/typing/hash/typing.rb', line 33

def may_have key, options={}
  merge_synonym_keys!( key, *options[:syn!] ).nil?
  return self[key]
end

#merge_synonym_keys!(key, *synonyms) ⇒ Object

Merges the synonymous hash keys into a single key - useful for argument validation. Returns nil if neither main key, nor synonyms are found. Returns false (no merging) if the main key was found, but no synonym keys. Returns true (yes merging) if any of the synonym keys is found and renamed/merged to the main key. Value collisions in synonym keys (detected by #==) raise ArgumentError.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/y_support/typing/hash/typing.rb', line 11

def merge_synonym_keys!( key, *synonyms )
  synonyms.reduce has_key?( key ) ? false : nil do |acc, syn|
    next acc unless has_key? syn
    if acc.nil? then
      self[key] = self[syn]
      delete syn
      next true
    end
    if self[key] == self[syn] then
      delete syn
      next true
    else
      raise TErr, "Value collision between #{key} and its synonym #{syn}!"
    end
  end
end

#modifyObject

Like #map that returns a hash.



57
58
59
60
61
62
# File 'lib/y_support/core_ext/hash/misc.rb', line 57

def modify
  each_with_object self.class.new do |hash_pair, 

#modify_keysObject

The difference from #with_keys is that modify_keys expects block that takes 2 arguments (key: value pair) and returns the new key.



20
21
22
23
24
# File 'lib/y_support/core_ext/hash/misc.rb', line 20

def modify_keys
  each_with_object self.class.new do |hash_pair, hsh|
    hsh[ yield( hash_pair ) ] = self[ hash_pair[0] ]
  end
end

#modify_valuesObject

The difference from #do_with_values is that modify_values expects block that takes 2 arguments (key: value pair) and returns the new value.



41
42
43
44
45
# File 'lib/y_support/core_ext/hash/misc.rb', line 41

def modify_values
  each_with_object self.class.new do |hash_pair, 

#modify_values!Object

Like #modify_values, but modifies the receiver.



49
50
51
52
53
# File 'lib/y_support/core_ext/hash/misc.rb', line 49

def modify_values!
  each_with_object self do |hash_pair, 

#pretty_print_numeric_values(gap: 0, precision: 2) ⇒ Object

Pretty-prints the hash consisting of names as keys, and numeric values. Takes 2 named arguments: :gap and :precision.



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/y_support/core_ext/hash/misc.rb', line 103

def pretty_print_numeric_values gap: 0, precision: 2
  key_strings = key.map &:to_s
  value_strings = values.map do |n| "%.#{precision}e" % n rescue "%s" % s end
  lmax, rmax = keys_strings.map( &:size ).max, values_strings.map( &:size ).max
  lgap = gap / 2
  rgap = gap - lgap
  key_strings.zip( value_strings ).map do |k

#slice(matcher) ⇒ Object

A bit like Array#slice, but only takes 1 argument, which is either a Range, or an Array, and returns the selection of the hash for the keys that match the range or are present in the array.



74
75
76
77
78
79
80
81
# File 'lib/y_support/core_ext/hash/misc.rb', line 74

def slice matcher
  self.class[ case matcher
              when Array then
                select { |key, _| matcher.include? key }
              else
                select { |key, _| matcher === key }
              end ]
end

#with_keysObject

Applies a block as a mapping on all keys, returning a new hash.



11
12
13
14
15
# File 'lib/y_support/core_ext/hash/misc.rb', line 11

def with_keys
  keys.each_with_object self.class.new do |hash_key, hsh|
    hsh[ yield hash_key ] = self[ hash_key ]
  end
end

#with_valuesObject

Applies a block as a mapping on all values, returning a new hash.



28
29
30
# File 'lib/y_support/core_ext/hash/misc.rb', line 28

def with_values
  each_with_object self.class.new do |(k, v), hsh| hsh[ k ] = yield v end
end

#with_values!Object

Like #do_with_values, but modifies the receiver.



34
35
36
# File 'lib/y_support/core_ext/hash/misc.rb', line 34

def with_values!
  each_with_object self do |(k, v), hsh| hsh[ k ] = yield v end
end