Module: Collapsium::IndifferentAccess

Extended by:
Support::Methods
Included in:
UberHash
Defined in:
lib/collapsium/indifferent_access.rb

Overview

Provides indifferent access to string/symbol keys in a Hash. That is, if your hash contains a string key, you can also access it via a symbol and vice versa.

Constant Summary collapse

METHODS =
(
    ::Collapsium::Support::HashMethods::KEYED_READ_METHODS \
    + ::Collapsium::Support::HashMethods::KEYED_WRITE_METHODS
).freeze
INDIFFERENT_ACCESS =
proc do |wrapped_method, *args, &block|
  # Bail out early if the receiver is not a Hash. Do the same if we have
  # no key.
  receiver = wrapped_method.receiver
  if not receiver.is_a? Hash or args.empty?
    next wrapped_method.call(*args, &block)
  end

  # Definitely try the key as given first. Then, depending on the key's
  # type and value, we want to try it as a Symbol, String and/or Integer
  key = args.shift
  tries = IndifferentAccess.key_permutations(key)

  # With the variations to try assembled, go through them one by one. We
  # define an inner class here for undefined results because 'nil' can be
  # a legitimate result from the wrapped method.
  class Undefined; end
  result = Undefined
  tries.each do |try|
    if receiver.keys.include?(try)
      result = wrapped_method.call(try, *args, &block)
      break
    end
  end

  # If any of the above yielded a result, great, return that. Otherwise
  # yield to the default implementation (i.e. wrapped_method).
  if result != Undefined
    next result
  end
  next wrapped_method.call(key, *args, &block)
end.freeze

Constants included from Support::Methods

Support::Methods::BUILTINS, Support::Methods::WRAPPER_HASH

Class Method Summary collapse

Methods included from Support::Methods

builtins, loop_detected?, repeated, resolve_helpers, wrap_method, wrappers

Class Method Details

.enhance(base) ⇒ Object



124
125
126
127
128
129
130
131
132
133
# File 'lib/collapsium/indifferent_access.rb', line 124

def enhance(base)
  # Make the capabilities of classes using IndifferentAccess viral.
  base.extend(ViralCapabilities)

  # Wrap all accessor functions to deal with paths
  METHODS.each do |method|
    wrap_method(base, method, raise_on_missing: false,
                &INDIFFERENT_ACCESS)
  end
end

.extended(base) ⇒ Object



116
117
118
# File 'lib/collapsium/indifferent_access.rb', line 116

def extended(base)
  enhance(base)
end

.included(base) ⇒ Object



112
113
114
# File 'lib/collapsium/indifferent_access.rb', line 112

def included(base)
  enhance(base)
end

.key_permutations(key) ⇒ Object

Given a key, returns all indifferent permutations to try.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/collapsium/indifferent_access.rb', line 31

def key_permutations(key)
  tries = [key]
  if key.is_a? Symbol
    key_s = key.to_s
    tries << key_s
    if key_s =~ /^[0-9]/
      tries << key_s.to_i
    end
  elsif key.is_a? String
    tries << key.to_sym
    if key =~ /^[0-9]/
      tries << key.to_i
    end
  elsif key.is_a? Integer
    tries += [key.to_s, key.to_s.to_sym]
  end

  return tries
end

.prepended(base) ⇒ Object



120
121
122
# File 'lib/collapsium/indifferent_access.rb', line 120

def prepended(base)
  enhance(base)
end

.sorted_keys(keys, &block) ⇒ Object

Sort the given keys indifferently. This will sort Integers first, Symbols second and Strings third. Everything else comes last. This is done because that’s the order in which comparsion time increases.



63
64
65
66
67
68
69
70
71
72
# File 'lib/collapsium/indifferent_access.rb', line 63

def sorted_keys(keys, &block)
  # Sorting sucks because we can't compare Strings and Symbols. So in
  # order to get this right, we'll have to sort each type individually,
  # then concatenate the results.
  sorted = []
  [Integer, Symbol, String].each do |klass|
    sorted += keys.select { |key| key.is_a?(klass) }.sort(&block)
  end
  return sorted
end

.unique_keys(keys) ⇒ Object

Make the given keys unique according to the logic of this module.



53
54
55
56
57
# File 'lib/collapsium/indifferent_access.rb', line 53

def unique_keys(keys)
  # The simplest way is to stringify all keys before making them
  # unique. That works for Integer as well as Symbol.
  return keys.map(&:to_s).uniq
end