Skip to content
This repository has been archived by the owner on May 29, 2024. It is now read-only.

Commit

Permalink
Merge pull request #13 from Sage/optimise-mock-store
Browse files Browse the repository at this point in the history
Optimise mock cache store
  • Loading branch information
vaughanbrittonsage authored Feb 13, 2018
2 parents 35a3674 + fbdc0d5 commit ee7f862
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 132 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.bundle
*.swp
118 changes: 44 additions & 74 deletions cache_store_base/lib/cache_store.rb
Original file line number Diff line number Diff line change
@@ -1,130 +1,100 @@
require "cache_store/version"
# frozen_string_literal: true

require 'cache_store/version'
require 'date'
require 'time'
#This class is used to define the contract that CacheStore implementations must adhere to.
class CacheStoreContract

# Defines the contract that CacheStore implementations must adhere to.
class CacheStoreContract
def initialize(namespace = '')
@namespace = namespace
end

#This method is called to set a value within this cache store by it's key.
# Sets a value within this cache store by its key.
#
# @param [String] This is the unique key to reference the value being set within this cache store.
# @param [Object] This is the value to set within this cache store.
# @param [Integer] This is the number of seconds from the current time that this value should expire.
def set(key, value, expires_in = 0)

end

#This method is called to get a value from this cache store by it's unique key.
# Gets a value from this cache store by its unique key.
#
# @param [String] This is the unique key to reference the value to fetch from within this cache store.
# @param [Integer] This is the number of seconds from the current time that this value should expire. (This is used in conjunction with the block to hydrate the cache key if it is empty.)
# @param [Block] This block is provided to hydrate this cache store with the value for the request key when it is not found.
def get(key, expires_in = 0, &block)

end

# This method is called to remove a value from this cache store by it's unique key.
# Removes a value from this cache store by its unique key.
#
# @param [String] This is the unique key to reference the value to remove from this cache store.
def remove(key)

end

# This method is called to check if a value exists within this cache store for a specific key.
# Checks if a value exists within this cache store for a specific key.
#
# @param [String] This is the unique key to reference the value to check for within this cache store.
def exist?(key)

end
end

#This class is used to implement a local in memory cache store.
# Implements a local in-memory cache store.
class LocalCacheStore

attr_accessor :store

def initialize(namespace = nil)
@store = Array.new
@namespace = namespace
def initialize(_namespace = nil)
@store = {}
end

#This method is called to set a value within this cache store by it's key.
# Stores a value in this cache store by its key.
#
# @param key [String] This is the unique key to reference the value being set within this cache store.
# @param value [Object] This is the value to set within this cache store.
# @param expires_in [Integer] This is the number of seconds from the current time that this value should expire.
# @param key [String] The unique key to reference the value being set.
# @param value [Object] The value to store.
# @param expires_in [Integer] The number of seconds from the current time that this value should expire.
def set(key, value, expires_in = 0)
remove(key)
expires = nil
if expires_in > 0
expires = Time.now.utc + expires_in
expires = Time.now + expires_in
end
@store.push({ key: build_key(key), value: value, expires: expires})
@store.store(key, {value: value, expires: expires})
end

#This method is called to get a value from this cache store by it's unique key.
# Gets a value from this cache store by its unique key.
#
# @param key [String] This is the unique key to reference the value to fetch from within this cache store.
# @param expires_in [Integer] This is the number of seconds from the current time that this value should expire. (This is used in conjunction with the block to hydrate the cache key if it is empty.)
# @param &block [Block] This block is provided to hydrate this cache store with the value for the request key when it is not found.
# @param key [String] Unique key to reference the value to fetch from within this cache store.
# @param &block [Block] This block is provided to populate this cache store with the value for the request key when it is not found.
# @return [Object] The value for the specified unique key within the cache store.
def get(key, expires_in = 0, &block)

#look for the cache item in the store
items = @store.select { |i| i[:key] == build_key(key) }
item = if !items.empty? then items[0] else nil end
#check if a valid item was found in the store
if item == nil || (item[:expires] != nil && item[:expires] <= Time.now.utc)
#a valid item wasn't found so check if a hydration block was specified.
if block_given?
#create the item from the block
value = yield
#put the item in the store
set(build_key(key), value, expires_in)
return value
else
#no hydration block was specified

#check if an expired item was found
if item != nil
#remove the expired item from the store
remove(build_key(key))
def get(key, expires_in = 0)
item = @store[key]
if item
if item[:expires] && item[:expires] < Time.now # An expired entry has been found
if block_given?
value = yield
set(key, value, expires_in)
return value
else
remove(key)
return nil
end
return nil
else # An item was found which has not expired
return item[:value]
end
elsif block_given?
value = yield
set(key, value, expires_in)
return value
end

#return the item
return item[:value]
end

# This method is called to remove a value from this cache store by it's unique key.
# Removes a value from this cache store by its unique key.
#
# @param key [String] This is the unique key to reference the value to remove from this cache store.
# @param key [String] The unique key to remove from this cache store.
def remove(key)
@store.delete_if { |i| i[:key] == build_key(key) }
@store.delete key
end

# This method is called to check if a value exists within this cache store for a specific key.
# Checks if a value exists within this cache store for a specific key.
#
# @param key [String] This is the unique key to reference the value to check for within this cache store.
# @param key [String] The unique key to reference the value to check for within this cache store.
# @return [Boolean] True or False to specify if the key exists in the cache store.
def exist?(key)
!@store.select { |i| i[:key] == build_key(key) }.empty?
end

private

def build_key(key)
k = ''
if @namespace != nil
k = @namespace + ':' + key.to_s
elsif
k = key.to_s
end
k
@store.key? key
end
end
80 changes: 22 additions & 58 deletions cache_store_base/spec/cache_store_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require 'spec_helper'
require_relative '../lib/cache_store'
require 'cache_store'

describe LocalCacheStore do
describe '#set' do
Expand All @@ -10,18 +10,14 @@

it "should add an item to the cache store that doesn't expire when no [expires_in] is specified." do
subject.set(key, value)

expect(subject.store[0][:key ]).to eq(key )
expect(subject.store[0][:value ]).to eq(value)
expect(subject.store[0][:expires]).to eq(nil )
expect(subject.get(key)).to eq(value)
end

it "should add an item to the cache store and set the expiry when specified." do
subject.set(key, value, expires_in)
subject.set(key, value, 0.001)

expect(subject.store[0][:key ]).to eq(key)
expect(subject.store[0][:value ]).to eq(value)
expect(subject.store[0][:expires]).to be > now
sleep 0.002
expect(subject.get(key)).to be_nil
end

context "when item already exists" do
Expand All @@ -46,55 +42,51 @@
let(:now ) { Time.now.utc }

it 'should return a value from the cache store for the specified key when a value is found.' do
subject.store.push({ key: key, value: value, expires: nil })
subject.set key, value

expect(subject.get(key)).to eq(value)
end

it 'should return nil from the cache store for the specified key when no value is found and no hydration block is specified.' do
it 'should return nil from the cache store for the specified key when no value is found and no block is specified.' do
expect(subject.get(key)).to eq(nil)
end

it 'should hydrate the cache store with a value for the specified key when no value is found and a hydration block is provided.' do

it 'should populate the cache store with a value for the specified key when no value is found and a block is provided.' do
result = subject.get(key, expires_in) do
value
end

expect(result).to eq(value)
expect(subject.store.length).to eq(1)
expect(subject.store[0][:expires]).to be > now
end

it 'should hydrate the cache store with a value for the specified key when the value has expired and a hydration block is provided.' do
subject.store.push({ key: key, value: 'old_value', expires: Time.now.utc })
it 'should populate cache store with a value for the specified key when the value has expired and a block is provided.' do
subject.set(key, 'old_value', 0.001)
sleep 0.002

result = subject.get(key) do
value
end

expect(result).to eq(value)
expect(subject.store.length).to eq(1)

end

it 'should return nil from the cache store for the specified key when a value is expired.' do
subject.store.push({ key: key, value: value, expires: Time.now.utc })
it 'should return nil from the cache store for the specified key when a value is expired' do
subject.set(key, value, 0.001)
sleep(0.001)

expect(subject.get(key)).to eq(nil)
expect(subject.store.length).to eq(0)
end
end

describe '#remove' do
let(:key ) { 'key123' }
let(:value) { 'value123' }

it "should remove a value by it's specified key" do
subject.store.push({ key: key, value: value })
it "should remove a value by its specified key" do
subject.set key, value

subject.remove(key)
expect(subject.store.length).to eq(0)
expect(subject.get(key)).to be_nil
end
end

Expand All @@ -103,7 +95,7 @@
let(:value) { 'value123' }

context 'when a value exists for a specified key' do
before { subject.store.push({ key: key, value: value}) }
before { subject.set key, value }

it 'should return true' do
expect(subject.exist?(key)).to eq(true)
Expand All @@ -124,38 +116,10 @@

subject { LocalCacheStore.new('test') }

it 'should set a value and append the namespace to the key' do
subject.set(key, value)

expect(subject.store[0][:key]).to eq('test:key123')
end

context 'when a namespace has been specified' do
before { subject.store.push({key: 'test:' + key, value: value }) }

it 'should get a value' do
result = subject.get(key)

expect(result).to eq(value)
end

it 'should remove a value' do
subject.remove(key)

expect(subject.store.length).to eq(0)
end

it 'should return true when check if a key exists' do
expect(subject.exist?(key)).to eq(true)
end

it "updates item with new value" do
expect(subject.get(key)).to eq(value)

subject.set(key, new_value)

expect(subject.get(key)).to eq(new_value)
end
it 'accepts the namespace in the initializer' do
store = LocalCacheStore.new('test')
store.set key, value
expect(store.get(key)).to eq(value)
end
end
end

0 comments on commit ee7f862

Please sign in to comment.