diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2dfced9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.bundle +*.swp diff --git a/cache_store_base/lib/cache_store.rb b/cache_store_base/lib/cache_store.rb index 6fdf0eb..a08779d 100644 --- a/cache_store_base/lib/cache_store.rb +++ b/cache_store_base/lib/cache_store.rb @@ -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 diff --git a/cache_store_base/spec/cache_store_spec.rb b/cache_store_base/spec/cache_store_spec.rb index f2fa5b7..ef249c6 100644 --- a/cache_store_base/spec/cache_store_spec.rb +++ b/cache_store_base/spec/cache_store_spec.rb @@ -1,5 +1,5 @@ require 'spec_helper' -require_relative '../lib/cache_store' +require 'cache_store' describe LocalCacheStore do describe '#set' do @@ -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 @@ -46,43 +42,39 @@ 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 @@ -90,11 +82,11 @@ 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 @@ -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) @@ -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