Created
September 10, 2009 21:19
-
-
Save joshaven/184837 to your computer and use it in GitHub Desktop.
Volatile Ruby Hash > Vash
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############################################################################# | |
# Class: Vash (Ruby Volatile Hash) | |
# Hash that returns values only for a short time. This is useful as a cache | |
# where I/O is involved. The primary goal of this object is to reduce I/O | |
# access and due to the nature of I/O being slower then memory, you should also | |
# see a gain in quicker response times. | |
# | |
# For example, if Person.first found the first person from the database & cache | |
# was an instance of Vash then the following would only contact the database for | |
# the first iteration: | |
# | |
# > cache = Vash.new | |
# > 1000.times {cache[:person] ||= Person.first} | |
# | |
# However if you did the following immediately following that command it would | |
# hit the database again: | |
# | |
# > sleep 61 | |
# > cache[:person] ||= Person.first | |
# | |
# The reason is that there is a default Time-To-Live of 60 seconds. You can | |
# also set a custom TTL of 10 seconds like so: | |
# | |
# > cache[:person, 10] = Person.first | |
# | |
# The Vash object will forget any answer that is requested after the specified | |
# TTL. It is a good idea to manually clean things up from time to time because | |
# it is possible that you'll cache data but never again access it and therefor | |
# it will stay in memory after the TTL has expired. To clean up the Vash object, | |
# call the method: cleanup! | |
# | |
# > sleep 11 # At this point the prior person ttl will be expired | |
# # but the person key and value will still exist. | |
# > cache # This will still show the the entire set of keys | |
# # regardless of the TTL, the :person will still exist | |
# > cache.cleanup! # All of the TTL's will be inspected and the expired | |
# # :person key will be deleted. | |
# | |
# The cleanup must be manually called because the purpose of the Vash is to | |
# lessen needless I/O calls and gain speed not to slow it down with regular | |
# maintenance. | |
class Vash < Hash | |
def initialize(constructor = {}) | |
@register ||= {} # remembers expiration time of every key | |
if constructor.is_a?(Hash) | |
super() | |
merge(constructor) | |
else | |
super(constructor) | |
end | |
end | |
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) | |
alias_method :regular_reader, :[] unless method_defined?(:regular_reader) | |
def [](key) | |
sterilize(key) | |
clear(key) if expired?(key) | |
regular_reader(key) | |
end | |
def []=(key, *args) | |
# a little bit o variable hacking to support (h[key, ttl] = value), which will come | |
# accross as (key, [ttl, value]) whereas (h[key]=value) comes accross as (key, [value]) | |
if args.length == 2 | |
value, ttl = args[1], args[0] | |
elsif args.length == 1 | |
value, ttl = args[0], 60 | |
else | |
raise ArgumentError, "Wrong number of arguments, expected 2 or 3, received: #{args.length+1}\n"+ | |
"Example Usage: volatile_hash[:key]=value OR volatile_hash[:key, ttl]=value" | |
end | |
sterilize(key) | |
ttl(key, ttl) | |
regular_writer(key, value) | |
end | |
def merge(hsh) | |
hsh.map {|key,value| self[sterile(key)] = hsh[key]} | |
self | |
end | |
def cleanup! | |
now = Time.now.to_i | |
@register.map {|k,v| clear(k) if v < now} | |
end | |
def clear(key) | |
sterilize(key) | |
@register.delete key | |
self.delete key | |
end | |
private | |
def expired?(key) | |
Time.now.to_i > @register[key].to_i | |
end | |
def ttl(key, secs=60) | |
@register[key] = Time.now.to_i + secs.to_i | |
end | |
def sterile(key) | |
String === key ? key.chomp('!').chomp('=') : key.to_s.chomp('!').chomp('=').to_sym | |
end | |
def sterilize(key) | |
key = sterile(key) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nice