Skip to content

Instantly share code, notes, and snippets.

@IronSavior
Last active May 11, 2016 02:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IronSavior/8406cc52ae8a6eb69c68e84455171d34 to your computer and use it in GitHub Desktop.
Save IronSavior/8406cc52ae8a6eb69c68e84455171d34 to your computer and use it in GitHub Desktop.
Fun with Hash-like objects
class HashlikeFetchCache
include HashlikeOverlay
def initialize( context )
@overlay = Hash[]
@context = context
freeze
end
def []( key )
super.tap{ |value| update_cache key, value }
end
def fetch( key, * )
super.tap{ |value| update_cache key, value }
end
def clear_cache
overlay.clear
end
private
attr_reader :overlay, :context
def update_cache( key, value )
overlay[key] = value
end
end
# Hash-like interface composed of two Hash-like objects where one is an overlay for the other. Common Hash
# accessor methods like #fetch and #[] will search the overlay and then fall back to the context. Classes
# including this module must implement #overlay and #context.
module HashlikeOverlay
# Retrieve value associated with the given key. Defaulting behavior is delegated to context.
# Note: If context is a genuine Hash, #[] will likely return nil when the key is not found.
# @param key [Object]
# @return [Object] Value associated with the given key, or a default
def []( key )
overlay.key?(key) ? overlay[key] : context[key]
end
# Retrieve value associated with the given key. Defaulting behavior is delegated to context.
# Note: If context is a genuine Hash, #fetch may raise KeyError when the key is not found.
# @param key [Object]
# @param default [Object] Value returned when key not found
# @param [Block] Invoked when key not found (overrides non-block default param)
# @return [Object] Value associated with the given key, or a default
def fetch( key, *default, &blk )
overlay.fetch(key){ context.fetch key, *default, &blk }
end
# @param key [Object] Key to search for
# @return [Boolean] True when the given key is found in the overlay or context
def key?( key )
overlay.key?(key) || context.key?(key)
end
alias_method :has_key?, :key?
# @param value [Object] Value to search for
# @return [Boolean] True when the given value is found in the overlay or context
def value?( value )
overlay.value?(value) || context.value?(value)
end
alias_method :has_value?, :value?
# @return [Array] Unique all keys from overlay and context
def keys
overlay.keys | context.keys
end
# @return [Boolean] True when context and overlay are both empty
def empty?
overlay.empty? && context.empty?
end
# @return [Integer] Count of unique keys between context and overlay
def size
keys.size
end
# Explicit conversion to Hash
# @return [Hash] Union of context with overlay
def to_h
keys.reduce Hash[] do |h, k|
h.tap{ h[k] = self[k] }
end
end
# Implicit conversion to Hash. This works only when implicit conversion is supported by both overlay and context.
# @return [Hash] Union of context with overlay
def to_hash
context.to_hash.merge overlay.to_hash
end
end
require 'forwardable'
class LexicalScope
include HashlikeOverlay
extend Forwardable
delegate [:store, :[]=] => :overlay
def initialize( context = {} )
@overlay = Hash[]
@context = context
end
def inner_scope
self.class.new self
end
private
attr_reader :overlay, :context
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment