Skip to content

Instantly share code, notes, and snippets.

@mariusbutuc
Forked from nickhoffman/cache.rb
Last active April 29, 2016 20:57
Show Gist options
  • Save mariusbutuc/3acebc6b58361a0b8686be46f851f014 to your computer and use it in GitHub Desktop.
Save mariusbutuc/3acebc6b58361a0b8686be46f851f014 to your computer and use it in GitHub Desktop.
Code Kata #4: Cache
class Cache
attr_accessor :data
def initialize(data: {})
@data = data
end
def get(key)
return if data.fetch(key, {}).fetch(:expires_at, nil) <= now
value_for(key)
end
def set(key, value, expires_at: default_expires_at)
data[key] = { value: value, expires_at: expires_at }
end
def delete(key)
data.delete(key)
end
def fetch(key, &block)
value = value_for(key)
data[key] = { value: yield, expires_at: default_expires_at } if value.nil?
value_for(key)
end
private
def now
# timezones? UTC?
Time.now
end
def default_expires_at
now + 100
end
def value_for(key)
data.fetch(key, {}).fetch(:value, nil)
end
end
require './cache.rb'
cache = Cache.new
tests = [
# 0
Proc.new do |cache|
cache.get(:foo).nil?
end,
# 1
Proc.new do |cache|
cache.set(:foo, 'FOO') == 'FOO'
end,
# 2
Proc.new do |cache|
cache.set(:foo, 'FOO')
cache.get(:foo) == 'FOO'
end,
# 3
Proc.new do |cache|
cache.set(:foo, 'FOO')
cache.delete(:foo) == 'FOO'
end,
# 4
Proc.new do |cache|
cache.set(:foo, 'FOO')
cache.delete(:foo)
cache.get(:foo).nil?
end,
# 5
Proc.new do |cache|
num_calls = 0
calculation = -> { num_calls += 1 }
cache.fetch(:foo, &calculation) == 1 && cache.fetch(:foo, &calculation) == 1
end,
# 6
Proc.new do |cache|
now = Time.now
expires_at = now + 100
cache.set(:foo, 'FOO', expires_at: expires_at)
cache.get(:foo) == 'FOO'
end,
# 7
Proc.new do |cache|
now = Time.now
expires_at = now - 1
cache.set(:foo, 'FOO', expires_at: expires_at)
cache.get(:foo).nil?
end,
# 8
Proc.new do |cache|
now = Time.now
expires_at = now + 1
cache.set(:foo, 'FOO', expires_at: expires_at)
sleep(1.2)
cache.get(:foo).nil?
end,
]
tests.each_with_index do |test, i|
cache = Cache.new
print "Test #{i}: "
begin
if test.call(cache)
puts 'success'
else
puts 'fail'
end
rescue StandardError => exception
puts "error: #{exception.inspect}"
end
end
$ ruby run.rb
Test 0: error: #<NoMethodError: undefined method `<=' for nil:NilClass>
Test 1: fail
Test 2: success
Test 3: fail
Test 4: error: #<NoMethodError: undefined method `<=' for nil:NilClass>
Test 5: success
Test 6: success
Test 7: success
Test 8: success
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment