Last active
December 17, 2015 03:09
-
-
Save mweppler/5541289 to your computer and use it in GitHub Desktop.
Ruby implementation of the python Cache class from Box CRE "The 'One' Thing" by Peter Potrebic
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
#!/usr/bin/env ruby | |
# Ruby implementation of the python Cache class from Box CRE "The 'One' Thing" by Peter Potrebic | |
$store = {} | |
$namespace = 'rcache' | |
class Cache < Object | |
def initialize(cache_name) | |
registration_table_key = "#{$namespace}.registration_table" | |
registration_table = $store[registration_table_key] | |
cache_info = registration_table[cache_name] | |
@_cache_name = cache_name | |
@_version, @_data_type, @_timeout = cache_info | |
end | |
def self.register(cache_name, data_type, timeout) | |
if (data_type != Fixnum && data_type != Float && data_type != Bignum && data_type != String && data_type != Complex) | |
raise TypeError, 'Unsupported type' | |
end | |
registration_table_key = "#{$namespace}.registration_table" | |
if $store.has_key? registration_table_key | |
registration_table = $store.fetch(registration_table_key) | |
else | |
$store.default = {} | |
registration_table = $store[registration_table_key] | |
end | |
unless registration_table.has_key?(cache_name) | |
cache_info = [1, data_type, timeout] # should this be a struct | |
registration_table[cache_name] = cache_info | |
end | |
Cache.new(cache_name) | |
end | |
def timeout | |
@_timeout | |
end | |
def cache_name | |
@_cache_name | |
end | |
def version | |
@_version | |
end | |
def bump_version | |
registration_table_key = "#{$namespace}.registration_table" | |
registration_table = $store[registration_table_key] | |
version, _, _ = registration_table[@_cache_name] # the _, _ is just a throw away of elements 1 & 2 (0 is the version) | |
@_version = version + 1 | |
registration_table[@_cache_name] = [@_version, @_data_type, @_timeout] | |
end | |
def write(key, datum) | |
unless datum.class == @_data_type | |
raise TypeError | |
end | |
full_key = "#{$namespace}.#{@_cache_name}.#{@_version}.#{key}" | |
ttl = Time.now() + @_timeout | |
$store[full_key] = { :datum => datum, :ttl => ttl } | |
end | |
def write_collection(key, datum_collection) | |
# To maintain 'memcache' like semantics, make a copy of collection | |
collection = datum_collection.collect { |datum| datum } | |
collection.each { |datum| raise TypeError unless datum.class == @_data_type } | |
full_key = "#{$namespace}.#{@_cache_name}.#{@_version}.#{key}" | |
ttl = Time.now() + @_timeout | |
$store[full_key] = { :collection => collection, :ttl => ttl } | |
end | |
def read(key) | |
full_key = "#{$namespace}.#{@_cache_name}.#{@_version}.#{key}" | |
datum, ttl = $store[full_key][:datum], $store[full_key][:ttl] | |
if ttl < Time.now() # simplistic expiration logic | |
$store[full_key] = nil | |
raise KeyError | |
end | |
raise TypeError unless datum.class == @_data_type | |
ttl = Time.now() + @_timeout | |
$store[full_key] = { :datum => datum, :ttl => ttl } | |
datum | |
end | |
def read_collection(key) | |
full_key = "#{$namespace}.#{@_cache_name}.#{@_version}.#{key}" | |
collection, ttl = store[full_key][:collection], store[full_key][:ttl] | |
if ttl < Time.now() # simplistic expiration logic | |
$store[full_key] = nil | |
raise KeyError | |
end | |
collection.each { |datum| raise TypeError unless datum.class == @_data_type } | |
ttl = Time.now() + @_timeout | |
$store[full_key] = { :collection => collection, :ttl => ttl } | |
collection | |
end | |
end | |
require 'rspec' | |
describe Cache do | |
let(:cache) { Cache.register('test', String, 60) } | |
it "registers a new cache" do | |
expect(cache.class).to eq Cache | |
end | |
it "writes to the cache" do | |
result = cache.write 'test', 'this is a test' | |
expect(result[:datum]).to eq 'this is a test' | |
end | |
it "reads a cache" do | |
expect(cache.read 'test').to eq 'this is a test' | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment