Module: Collapsium::ViralCapabilities

Extended by:
Support::HashMethods, Support::Methods
Includes:
Support::HashMethods, Support::Methods
Included in:
RecursiveDup, RecursiveMerge, RecursiveSort, UberHash
Defined in:
lib/collapsium/viral_capabilities.rb

Overview

Tries to make extended Hash capabilities viral, i.e. provides the same features to nested Hash structures as the Hash that includes this module.

Virality is ensured by changing the return value of various methods; if it is derived from Hash, it is attempted to convert it to the including class.

The module uses HashMethods to decide which methods to make viral in this manner.

There are two ways for using this module: a) in a ‘Class`, either include, prepend or extend it. b) in a `Module`, extend this module. The resulting module can be included,

prepended or extended in a `Class` again.

Constant Summary

Constants included from Support::HashMethods

Support::HashMethods::KEYED_READ_METHODS, Support::HashMethods::KEYED_WRITE_METHODS, Support::HashMethods::READ_METHODS, Support::HashMethods::WRITE_METHODS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Support::Methods

loop_detected?, repeated, resolve_helpers, wrap_method

Class Method Details

.enhance(base) ⇒ Object

Enhance the base by wrapping all READ_METHODS and WRITE_METHODS in a wrapper that uses enhance_hash_value to, well, enhance Hash results.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/collapsium/viral_capabilities.rb', line 68

def enhance(base)
  # rubocop:disable Style/ClassVars
  @@write_block ||= proc do |wrapped_method, *args, &block|
    arg_copy = args.map do |arg|
      enhance_hash_value(wrapped_method.receiver, arg)
    end
    result = wrapped_method.call(*arg_copy, &block)
    next enhance_hash_value(wrapped_method.receiver, result)
  end
  @@read_block ||= proc do |wrapped_method, *args, &block|
    result = wrapped_method.call(*args, &block)
    next enhance_hash_value(wrapped_method.receiver, result)
  end
  # rubocop:enable Style/ClassVars

  READ_METHODS.each do |method_name|
    wrap_method(base, method_name, raise_on_missing: false, &@@read_block)
  end

  WRITE_METHODS.each do |method_name|
    wrap_method(base, method_name, raise_on_missing: false, &@@write_block)
  end
end

.enhance_hash_value(outer_hash, value) ⇒ Object

Given an outer Hash and a value, enhance Hash values so that they have the same capabilities as the outer Hash. Non-Hash values are returned unchanged.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/collapsium/viral_capabilities.rb', line 96

def enhance_hash_value(outer_hash, value)
  # If the value is not a Hash, we don't do anything.
  if not value.is_a? Hash
    return value
  end

  # If the value is a different type of Hash from ourself, we want to
  # create an instance of our own type with the same values.
  # XXX: DO NOT replace the loop with :merge! or :merge - those are
  #      potentially wrapped write functions, leading to an infinite
  #      recursion.
  if value.class != outer_hash.class
    new_value = outer_hash.class.new

    value.each do |key, inner_val|
      if not inner_val.is_a? Hash
        new_value[key] = inner_val
        next
      end

      if inner_val.class == outer_hash.class
        new_value[key] = inner_val
        next
      end

      new_inner_value = outer_hash.class.new
      new_inner_value.merge!(inner_val)
      new_value[key] = new_inner_value
    end
    value = new_value
  end

  # Next, we want to extend all the modules in self. That might be a
  # no-op due to the above block, but not necessarily so.
  value_mods = (class << value; self end).included_modules
  own_mods = (class << outer_hash; self end).included_modules
  (own_mods - value_mods).each do |mod|
    value.extend(mod)
  end

  # If we have a default_proc and the value doesn't, we want to use our
  # own. This *can* override a perfectly fine default_proc with our own,
  # which might suck.
  value.default_proc ||= outer_hash.default_proc

  # Finally, the class can define its own virality function.
  if outer_hash.respond_to?(:virality)
    value = outer_hash.virality(value)
  end

  return value
end

.extended(base) ⇒ Object



61
62
63
# File 'lib/collapsium/viral_capabilities.rb', line 61

def extended(base)
  enhance(base)
end

.included(base) ⇒ Object



57
58
59
# File 'lib/collapsium/viral_capabilities.rb', line 57

def included(base)
  enhance(base)
end

.prepended(base) ⇒ Object

When prepended, included or extended, enhance the base.



53
54
55
# File 'lib/collapsium/viral_capabilities.rb', line 53

def prepended(base)
  enhance(base)
end

Instance Method Details

#extended(base) ⇒ Object



43
44
45
# File 'lib/collapsium/viral_capabilities.rb', line 43

def extended(base)
  ViralCapabilities.enhance(base)
end

#included(base) ⇒ Object



39
40
41
# File 'lib/collapsium/viral_capabilities.rb', line 39

def included(base)
  ViralCapabilities.enhance(base)
end

#prepended(base) ⇒ Object

When prepended, included or extended, enhance the base.



35
36
37
# File 'lib/collapsium/viral_capabilities.rb', line 35

def prepended(base)
  ViralCapabilities.enhance(base)
end