lru-redux-1.1.0/0000755000175100017510000000000013625723103012470 5ustar pravipravilru-redux-1.1.0/.gitignore0000644000175100017510000000024013625723103014454 0ustar pravipravi*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp *.swp lru-redux-1.1.0/Rakefile0000644000175100017510000000016613625723103014140 0ustar pravipravirequire "bundler/gem_tasks" require 'rake/testtask' Rake::TestTask.new do |t| t.pattern = "test/**/*_test.rb" end lru-redux-1.1.0/lru_redux.gemspec0000644000175100017510000000227013625723103016047 0ustar pravipravi# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'lru_redux/version' Gem::Specification.new do |spec| spec.name = "lru_redux" spec.version = LruRedux::VERSION spec.authors = ["Sam Saffron", "Kaijah Hougham"] spec.email = ["sam.saffron@gmail.com", "github@seberius.com"] spec.description = %q{An efficient implementation of an lru cache} spec.summary = %q{An efficient implementation of an lru cache} spec.homepage = "https://github.com/SamSaffron/lru_redux" spec.license = "MIT" spec.required_ruby_version = ">= 1.9.3" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test)/}) spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" spec.add_development_dependency "minitest" spec.add_development_dependency "guard-minitest" spec.add_development_dependency "guard" spec.add_development_dependency "rb-inotify" spec.add_development_dependency "timecop", "~> 0.7" end lru-redux-1.1.0/Guardfile0000644000175100017510000000023513625723103014315 0ustar pravipraviguard 'minitest', :focus_on_failed => true do watch(%r{^test/.+_test\.rb$}) watch(%r{^lib/lru_redux/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" } end lru-redux-1.1.0/lib/0000755000175100017510000000000013625723103013236 5ustar pravipravilru-redux-1.1.0/lib/lru_redux.rb0000644000175100017510000000035113625723103015573 0ustar pravipravirequire "lru_redux/util" require "lru_redux/cache" require "lru_redux/cache_legacy" if RUBY_ENGINE == "ruby" && RUBY_VERSION < "2.1.0" require "lru_redux/thread_safe_cache" require "lru_redux/ttl" require "lru_redux/version" lru-redux-1.1.0/lib/lru_redux/0000755000175100017510000000000013625723103015247 5ustar pravipravilru-redux-1.1.0/lib/lru_redux/util/0000755000175100017510000000000013625723103016224 5ustar pravipravilru-redux-1.1.0/lib/lru_redux/util/safe_sync.rb0000644000175100017510000000267513625723103020535 0ustar pravipravirequire 'monitor' module LruRedux module Util module SafeSync include MonitorMixin def initialize(*args) super(*args) end def max_size=(max_size) synchronize do super(max_size) end end def ttl=(ttl) synchronize do super(ttl) end end def getset(key) synchronize do super(key) end end def fetch(key) synchronize do super(key) end end def [](key) synchronize do super(key) end end def []=(key, value) synchronize do super(key, value) end end def each synchronize do super end end def to_a synchronize do super end end def delete(key) synchronize do super(key) end end def evict(key) synchronize do super(key) end end def key?(key) synchronize do super(key) end end def has_key?(key) synchronize do super(key) end end def clear synchronize do super end end def count synchronize do super end end def valid? synchronize do super end end end end end lru-redux-1.1.0/lib/lru_redux/util/safe_sync_jruby.rb0000644000175100017510000000042413625723103021736 0ustar pravipravimodule LruRedux module Util module SafeSync def getset(key, &block) synchronize do super(key, &block) end end def fetch(key, &block) synchronize do super(key, &block) end end end end end lru-redux-1.1.0/lib/lru_redux/cache_legacy.rb0000644000175100017510000000172713625723103020172 0ustar pravipravi# Ruby 1.9 makes our life easier, Hash is already ordered # # This is an ultra efficient 1.9 freindly implementation class LruRedux::Cache def max_size=(size) raise ArgumentError.new(:max_size) if @max_size < 1 @max_size = size if @max_size < @data.size @data.keys[0..@max_size-@data.size].each do |k| @data.delete(k) end end end def getset(key) found = true value = @data.delete(key){ found = false } if found @data[key] = value else result = @data[key] = yield # this may seem odd see: http://bugs.ruby-lang.org/issues/8312 @data.delete(@data.first[0]) if @data.length > @max_size result end end def []=(key,val) @data.delete(key) @data[key] = val # this may seem odd see: http://bugs.ruby-lang.org/issues/8312 @data.delete(@data.first[0]) if @data.length > @max_size val end # for cache validation only, ensures all is sound def valid? true end end lru-redux-1.1.0/lib/lru_redux/cache.rb0000644000175100017510000000333313625723103016641 0ustar pravipravi# Ruby 1.9 makes our life easier, Hash is already ordered # # This is an ultra efficient 1.9 freindly implementation class LruRedux::Cache def initialize(*args) max_size, _ = args raise ArgumentError.new(:max_size) if max_size < 1 @max_size = max_size @data = {} end def max_size=(max_size) max_size ||= @max_size raise ArgumentError.new(:max_size) if max_size < 1 @max_size = max_size @data.shift while @data.size > @max_size end def ttl=(_) nil end def getset(key) found = true value = @data.delete(key){ found = false } if found @data[key] = value else result = @data[key] = yield @data.shift if @data.length > @max_size result end end def fetch(key) found = true value = @data.delete(key){ found = false } if found @data[key] = value else yield if block_given? end end def [](key) found = true value = @data.delete(key){ found = false } if found @data[key] = value else nil end end def []=(key,val) @data.delete(key) @data[key] = val @data.shift if @data.length > @max_size val end def each array = @data.to_a array.reverse!.each do |pair| yield pair end end # used further up the chain, non thread safe each alias_method :each_unsafe, :each def to_a array = @data.to_a array.reverse! end def delete(key) @data.delete(key) end alias_method :evict, :delete def key?(key) @data.key?(key) end alias_method :has_key?, :key? def clear @data.clear end def count @data.size end protected # for cache validation only, ensures all is sound def valid? true end end lru-redux-1.1.0/lib/lru_redux/util.rb0000644000175100017510000000020413625723103016545 0ustar pravipravirequire 'lru_redux/util/safe_sync' require 'lru_redux/util/safe_sync_jruby' if RUBY_ENGINE == 'jruby' && JRUBY_VERSION < '9.0' lru-redux-1.1.0/lib/lru_redux/ttl/0000755000175100017510000000000013625723103016052 5ustar pravipravilru-redux-1.1.0/lib/lru_redux/ttl/cache.rb0000644000175100017510000000671613625723103017454 0ustar pravipravimodule LruRedux module TTL class Cache attr_reader :max_size, :ttl def initialize(*args) max_size, ttl = args ttl ||= :none raise ArgumentError.new(:max_size) if max_size < 1 raise ArgumentError.new(:ttl) unless ttl == :none || ((ttl.is_a? Numeric) && ttl >= 0) @max_size = max_size @ttl = ttl @data_lru = {} @data_ttl = {} end def max_size=(max_size) max_size ||= @max_size raise ArgumentError.new(:max_size) if max_size < 1 @max_size = max_size resize end def ttl=(ttl) ttl ||= @ttl raise ArgumentError.new(:ttl) unless ttl == :none || ((ttl.is_a? Numeric) && ttl >= 0) @ttl = ttl ttl_evict end def getset(key) ttl_evict found = true value = @data_lru.delete(key){ found = false } if found @data_lru[key] = value else result = @data_lru[key] = yield @data_ttl[key] = Time.now.to_f if @data_lru.size > @max_size key, _ = @data_lru.first @data_ttl.delete(key) @data_lru.delete(key) end result end end def fetch(key) ttl_evict found = true value = @data_lru.delete(key){ found = false } if found @data_lru[key] = value else yield if block_given? end end def [](key) ttl_evict found = true value = @data_lru.delete(key){ found = false } if found @data_lru[key] = value else nil end end def []=(key, val) ttl_evict @data_lru.delete(key) @data_ttl.delete(key) @data_lru[key] = val @data_ttl[key] = Time.now.to_f if @data_lru.size > @max_size key, _ = @data_lru.first @data_ttl.delete(key) @data_lru.delete(key) end val end def each ttl_evict array = @data_lru.to_a array.reverse!.each do |pair| yield pair end end # used further up the chain, non thread safe each alias_method :each_unsafe, :each def to_a ttl_evict array = @data_lru.to_a array.reverse! end def delete(key) ttl_evict @data_lru.delete(key) @data_ttl.delete(key) end alias_method :evict, :delete def key?(key) ttl_evict @data_lru.key?(key) end alias_method :has_key?, :key? def clear @data_lru.clear @data_ttl.clear end def expire ttl_evict end def count @data_lru.size end protected # for cache validation only, ensures all is sound def valid? @data_lru.size == @data_ttl.size end def ttl_evict return if @ttl == :none ttl_horizon = Time.now.to_f - @ttl key, time = @data_ttl.first until time.nil? || time > ttl_horizon @data_ttl.delete(key) @data_lru.delete(key) key, time = @data_ttl.first end end def resize ttl_evict while @data_lru.size > @max_size key, _ = @data_lru.first @data_ttl.delete(key) @data_lru.delete(key) end end end end end lru-redux-1.1.0/lib/lru_redux/ttl/thread_safe_cache.rb0000644000175100017510000000014313625723103021765 0ustar pravipraviclass LruRedux::TTL::ThreadSafeCache < LruRedux::TTL::Cache include LruRedux::Util::SafeSync end lru-redux-1.1.0/lib/lru_redux/version.rb0000644000175100017510000000005013625723103017254 0ustar pravipravimodule LruRedux VERSION = "1.1.0" end lru-redux-1.1.0/lib/lru_redux/ttl.rb0000644000175100017510000000014213625723103016374 0ustar pravipravirequire "lru_redux/util" require "lru_redux/ttl/cache" require "lru_redux/ttl/thread_safe_cache" lru-redux-1.1.0/lib/lru_redux/thread_safe_cache.rb0000644000175100017510000000013113625723103021157 0ustar pravipraviclass LruRedux::ThreadSafeCache < LruRedux::Cache include LruRedux::Util::SafeSync end lru-redux-1.1.0/bench/0000755000175100017510000000000013625723103013547 5ustar pravipravilru-redux-1.1.0/bench/bench.rb0000644000175100017510000000165113625723103015156 0ustar pravipravirequire 'bundler' require 'benchmark' require 'lru' require 'lru_cache' require 'threadsafe-lru' Bundler.require # Lru lru = Cache::LRU.new(max_elements: 1_000) # LruCache lru_cache = LRUCache.new(1_000) # ThreadSafeLru thread_safe_lru = ThreadSafeLru::LruCache.new(1_000) # LruRedux redux = LruRedux::Cache.new(1_000) redux_thread_safe = LruRedux::ThreadSafeCache.new(1_000) puts "** LRU Benchmarks **" Benchmark.bmbm do |bm| bm.report 'ThreadSafeLru' do 1_000_000.times { thread_safe_lru.get(rand(2_000)) { :value } } end bm.report 'LRU' do 1_000_000.times { lru[rand(2_000)] ||= :value } end bm.report 'LRUCache' do 1_000_000.times { lru_cache[rand(2_000)] ||= :value } end bm.report 'LruRedux::Cache' do 1_000_000.times { redux.getset(rand(2_000)) { :value } } end bm.report 'LruRedux::ThreadSafeCache' do 1_000_000.times { redux_thread_safe.getset(rand(2_000)) { :value } } end end lru-redux-1.1.0/bench/bench_ttl.rb0000644000175100017510000000154613625723103016044 0ustar pravipravirequire 'bundler' require 'benchmark' require 'fast_cache' Bundler.require # FastCache fast_cache = FastCache::Cache.new(1_000, 5 * 60) # LruRedux redux_ttl = LruRedux::TTL::Cache.new(1_000, 5 * 60) redux_ttl_thread_safe = LruRedux::TTL::ThreadSafeCache.new(1_000, 5 * 60) redux_ttl_disabled = LruRedux::TTL::Cache.new(1_000, :none) puts puts "** TTL Benchmarks **" Benchmark.bmbm do |bm| bm.report 'FastCache' do 1_000_000.times { fast_cache.fetch(rand(2_000)) { :value } } end bm.report 'LruRedux::TTL::Cache' do 1_000_000.times { redux_ttl.getset(rand(2_000)) { :value } } end bm.report 'LruRedux::TTL::ThreadSafeCache' do 1_000_000.times { redux_ttl_thread_safe.getset(rand(2_000)) { :value } } end bm.report 'LruRedux::TTL::Cache (TTL disabled)' do 1_000_000.times { redux_ttl_disabled.getset(rand(2_000)) { :value } } end endlru-redux-1.1.0/README.md0000644000175100017510000002277413625723103013763 0ustar pravipravi# LruRedux [![Gem Version](https://badge.fury.io/rb/lru_redux.svg)](http://badge.fury.io/rb/lru_redux) An efficient, thread safe LRU cache. - [Installation](#installation) - [Usage](#usage) - [TTL Cache](#ttl-cache) - [Cache Methods](#cache-methods) - [Benchmarks](#benchmarks) - [Other Caches](#other-caches) - [Contributing](#contributing) - [Changelog](#changelog) ## Installation Add this line to your application's Gemfile: gem 'lru_redux' And then execute: $ bundle Or install it yourself as: $ gem install lru_redux Ruby 1.8 - v0.8.4 is the last compatible release: gem 'lru_redux', '~> 0.8.4' ## Usage ```ruby require 'lru_redux' # non thread safe cache = LruRedux::Cache.new(100) cache[:a] = "1" cache[:b] = "2" cache.to_a # [[:b,"2"],[:a,"1"]] # note the order matters here, last accessed is first cache[:a] # a pushed to front # "1" cache.to_a # [[:a,"1"],[:b,"2"]] cache.delete(:a) cache.each {|k,v| p "#{k} #{v}"} # b 2 cache.max_size = 200 # cache now stores 200 items cache.clear # cache has no items cache.getset(:a){1} cache.to_a #[[:a,1]] # already set so don't call block cache.getset(:a){99} cache.to_a #[[:a,1]] # for thread safe access, all methods on cache # are protected with a mutex cache = LruRedux::ThreadSafeCache.new(100) ``` #### TTL Cache The TTL cache extends the functionality of the LRU cache with a Time To Live eviction strategy. TTL eviction occurs on every access and takes precedence over LRU eviction, meaning a 'live' value will never be evicted over an expired one. ```ruby # Timecop is gem that allows us to change Time.now # and is used for demonstration purposes. require 'lru_redux' require 'timecop' # Create a TTL cache with a size of 100 and TTL of 5 minutes. # The first argument is the size and # the second optional argument is the TTL in seconds. cache = LruRedux::TTL::Cache.new(100, 5 * 60) Timecop.freeze(Time.now) cache[:a] = "1" cache[:b] = "2" cache.to_a # => [[:b,"2"],[:a,"1"]] # Now we advance time 5 min 30 sec into the future. Timecop.freeze(Time.now + 330) # And we see that the expired values have been evicted. cache.to_a # => [] # The TTL can be updated on a live cache using #ttl=. # Currently cached items will be evicted under the new TTL. cache[:a] = "1" cache[:b] = "2" Timecop.freeze(Time.now + 330) cache.ttl = 10 * 60 # Since ttl eviction is triggered by access, # the items are still cached when the ttl is changed and # are now under the 10 minute TTL. cache.to_a # => [[:b,"2"],[:a,"1"]] # TTL eviction can be triggered manually with the #expire method. Timecop.freeze(Time.now + 330) cache.expire cache.to_a # => [] Timecop.return # The behavior of a TTL cache with the TTL set to `:none` # is identical to the LRU cache. cache = LruRedux::TTL::Cache.new(100, :none) # The TTL argument is optional and defaults to `:none`. cache = LruRedux::TTL::Cache.new(100) # A thread safe version is available. cache = LruRedux::TTL::ThreadSafeCache.new(100, 5 * 60) ``` ## Cache Methods - `#getset` Takes a key and block. Will return a value if cached, otherwise will execute the block and cache the resulting value. - `#fetch` Takes a key and optional block. Will return a value if cached, otherwise will execute the block and return the resulting value or return nil if no block is provided. - `#[]` Takes a key. Will return a value if cached, otherwise nil. - `#[]=` Takes a key and value. Will cache the value under the key. - `#delete` Takes a key. Will return the deleted value, otherwise nil. - `#evict` Alias for `#delete`. - `#clear` Clears the cache. Returns nil. - `#each` Takes a block. Executes the block on each key-value pair in LRU order (most recent first). - `#to_a` Return an array of key-value pairs (arrays) in LRU order (most recent first). - `#key?` Takes a key. Returns true if the key is cached, otherwise false. - `#has_key?` Alias for `#key?`. - `#count` Return the current number of items stored in the cache. - `#max_size` Returns the current maximum size of the cache. - `#max_size=` Takes a positive number. Changes the current max_size and triggers a resize. Also triggers TTL eviction on the TTL cache. #### TTL Cache Specific - `#ttl` Returns the current TTL of the cache. - `#ttl=` Takes `:none` or a positive number. Changes the current ttl and triggers a TTL eviction. - `#expire` Triggers a TTL eviction. ## Benchmarks see: benchmark directory (a million random lookup / store) #### LRU ##### Ruby 2.2.1 ``` $ ruby ./bench/bench.rb Rehearsal ------------------------------------------------------------- ThreadSafeLru 4.500000 0.030000 4.530000 ( 4.524213) LRU 2.250000 0.000000 2.250000 ( 2.249670) LRUCache 1.720000 0.010000 1.730000 ( 1.728243) LruRedux::Cache 0.960000 0.000000 0.960000 ( 0.961292) LruRedux::ThreadSafeCache 2.180000 0.000000 2.180000 ( 2.187714) --------------------------------------------------- total: 11.650000sec user system total real ThreadSafeLru 4.390000 0.020000 4.410000 ( 4.415703) LRU 2.140000 0.010000 2.150000 ( 2.149626) LRUCache 1.680000 0.010000 1.690000 ( 1.688564) LruRedux::Cache 0.910000 0.000000 0.910000 ( 0.913108) LruRedux::ThreadSafeCache 2.200000 0.010000 2.210000 ( 2.212108) ``` ##### Ruby 2.0.0-p643 Implementation is slightly different for Ruby versions before 2.1 due to a Ruby bug. http://bugs.ruby-lang.org/issues/8312 ``` $ ruby ./bench/bench.rb Rehearsal ------------------------------------------------------------- ThreadSafeLru 4.790000 0.040000 4.830000 ( 4.828370) LRU 2.170000 0.010000 2.180000 ( 2.180630) LRUCache 1.810000 0.000000 1.810000 ( 1.814737) LruRedux::Cache 1.330000 0.010000 1.340000 ( 1.325554) LruRedux::ThreadSafeCache 2.770000 0.000000 2.770000 ( 2.777754) --------------------------------------------------- total: 12.930000sec user system total real ThreadSafeLru 4.710000 0.060000 4.770000 ( 4.773233) LRU 2.120000 0.010000 2.130000 ( 2.135111) LRUCache 1.780000 0.000000 1.780000 ( 1.781392) LruRedux::Cache 1.190000 0.010000 1.200000 ( 1.201908) LruRedux::ThreadSafeCache 2.650000 0.010000 2.660000 ( 2.652580) ``` #### TTL ##### Ruby 2.2.1 ``` $ ruby ./bench/bench_ttl.rb Rehearsal ----------------------------------------------------------------------- FastCache 6.240000 0.070000 6.310000 ( 6.302569) LruRedux::TTL::Cache 4.700000 0.010000 4.710000 ( 4.712858) LruRedux::TTL::ThreadSafeCache 6.300000 0.010000 6.310000 ( 6.319032) LruRedux::TTL::Cache (TTL disabled) 2.460000 0.010000 2.470000 ( 2.470629) ------------------------------------------------------------- total: 19.800000sec user system total real FastCache 6.470000 0.070000 6.540000 ( 6.536193) LruRedux::TTL::Cache 4.640000 0.010000 4.650000 ( 4.661793) LruRedux::TTL::ThreadSafeCache 6.310000 0.020000 6.330000 ( 6.328840) LruRedux::TTL::Cache (TTL disabled) 2.440000 0.000000 2.440000 ( 2.446269) ``` ## Other Caches This is a list of the caches that are used in the benchmarks. #### LRU - RubyGems: https://rubygems.org/gems/lru - Homepage: http://lru.rubyforge.org/ #### LRUCache - RubyGems: https://rubygems.org/gems/lru_cache - Homepage: https://github.com/brendan/lru_cache #### ThreadSafeLru - RubyGems: https://rubygems.org/gems/threadsafe-lru - Homepage: https://github.com/draganm/threadsafe-lru #### FastCache - RubyGems: https://rubygems.org/gems/fast_cache - Homepage: https://github.com/swoop-inc/fast_cache ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request ## Changlog ###version 1.1.0 - 30-Mar-2015 - New: TTL cache added. This cache is LRU like with the addition of time-based eviction. Check the Usage -> TTL Cache section in README.md for details. ###version 1.0.0 - 26-Mar-2015 - Ruby Support: Ruby 1.9+ is now required by LruRedux. If you need to use LruRedux in Ruby 1.8, please specify gem version 0.8.4 in your Gemfile. v0.8.4 is the last 1.8 compatible release and included a number of fixes and performance improvements for the Ruby 1.8 implementation. @Seberius - Perf: improve performance in Ruby 2.1+ on the MRI @Seberius ###version 0.8.4 - 20-Feb-2015 - Fix: regression of ThreadSafeCache under JRuby 1.7 @Seberius ###version 0.8.3 - 20-Feb-2015 - Perf: improve ThreadSafeCache performance @Seberius ###version 0.8.2 - 16-Feb-2015 - Perf: use #size instead of #count when checking length @Seberius - Fix: Cache could grow beyond its size in Ruby 1.8 @Seberius - Fix: #each could deadlock in Ruby 1.8 @Seberius ###version 0.8.1 - 7-Sep-2013 - Fix #each implementation - Fix deadlocks with ThreadSafeCache - Version jump is because its been used in production for quite a while now ###version 0.0.6 - 24-April-2013 - Fix bug in getset, overflow was not returning the yeilded val ###version 0.0.5 - 23-April-2013 - Added getset and fetch - Optimised implementation so it 20-30% faster on Ruby 1.9+ ###version 0.0.4 - 23-April-2013 - Initial version lru-redux-1.1.0/test/0000755000175100017510000000000013625723103013447 5ustar pravipravilru-redux-1.1.0/test/cache_test.rb0000644000175100017510000000416613625723103016105 0ustar pravipravirequire 'lru_redux' require 'minitest/autorun' require 'minitest/pride' class CacheTest < MiniTest::Test def setup @c = LruRedux::Cache.new(3) end def teardown assert_equal true, @c.send(:valid?) end def test_drops_old @c[:a] = 1 @c[:b] = 2 @c[:c] = 3 @c[:d] = 4 assert_equal [[:d,4],[:c,3],[:b,2]], @c.to_a assert_nil @c[:a] end def test_fetch @c[:a] = nil @c[:b] = 2 assert_equal @c.fetch(:a){1}, nil assert_equal @c.fetch(:c){3}, 3 assert_equal [[:a,nil],[:b,2]], @c.to_a end def test_getset assert_equal @c.getset(:a){1}, 1 @c.getset(:b){2} assert_equal @c.getset(:a){11}, 1 @c.getset(:c){3} assert_equal @c.getset(:d){4}, 4 assert_equal [[:d,4],[:c,3],[:a,1]], @c.to_a end def test_pushes_lru_to_back @c[:a] = 1 @c[:b] = 2 @c[:c] = 3 @c[:a] @c[:d] = 4 assert_equal [[:d,4],[:a,1],[:c,3]], @c.to_a assert_nil @c[:b] end def test_delete @c[:a] = 1 @c[:b] = 2 @c[:c] = 3 @c.delete(:a) assert_equal [[:c,3],[:b,2]], @c.to_a assert_nil @c[:a] # Regression test for a bug in the legacy delete method @c.delete(:b) @c[:d] = 4 @c[:e] = 5 @c[:f] = 6 assert_equal [[:f,6],[:e,5],[:d,4]], @c.to_a assert_nil @c[:b] end def test_key? @c[:a] = 1 @c[:b] = 2 assert_equal true, @c.key?(:a) assert_equal false, @c.key?(:c) end def test_update @c[:a] = 1 @c[:b] = 2 @c[:c] = 3 @c[:a] = 99 assert_equal [[:a,99],[:c,3],[:b,2]], @c.to_a end def test_clear @c[:a] = 1 @c[:b] = 2 @c[:c] = 3 @c.clear assert_equal [], @c.to_a end def test_grow @c[:a] = 1 @c[:b] = 2 @c[:c] = 3 @c.max_size = 4 @c[:d] = 4 assert_equal [[:d,4],[:c,3],[:b,2],[:a,1]], @c.to_a end def test_shrink @c[:a] = 1 @c[:b] = 2 @c[:c] = 3 @c.max_size = 1 assert_equal [[:c,3]], @c.to_a end def test_each @c.max_size = 2 @c[:a] = 1 @c[:b] = 2 @c[:c] = 3 pairs = [] @c.each do |pair| pairs << pair end assert_equal [[:c,3],[:b, 2]], pairs end end lru-redux-1.1.0/test/ttl/0000755000175100017510000000000013625723103014252 5ustar pravipravilru-redux-1.1.0/test/ttl/cache_test.rb0000644000175100017510000000256413625723103016710 0ustar pravipravirequire 'timecop' class TTLCacheTest < CacheTest def setup Timecop.freeze(Time.now) @c = LruRedux::TTL::Cache.new 3, 5 * 60 end def teardown Timecop.return assert_equal true, @c.send(:valid?) end def test_ttl assert_equal 300, @c.ttl @c.ttl = 10 * 60 assert_equal 600, @c.ttl end # TTL tests using Timecop def test_ttl_eviction_on_access @c[:a] = 1 @c[:b] = 2 Timecop.freeze(Time.now + 330) @c[:c] = 3 assert_equal([[:c, 3]], @c.to_a) end def test_ttl_eviction_on_expire @c[:a] = 1 @c[:b] = 2 Timecop.freeze(Time.now + 330) @c.expire assert_equal([], @c.to_a) end def test_ttl_eviction_on_new_max_size @c[:a] = 1 @c[:b] = 2 Timecop.freeze(Time.now + 330) @c.max_size = 10 assert_equal([], @c.to_a) end def test_ttl_eviction_on_new_ttl @c[:a] = 1 @c[:b] = 2 Timecop.freeze(Time.now + 330) @c.ttl = 10 * 60 assert_equal([[:b, 2], [:a, 1]], @c.to_a) @c.ttl = 2 * 60 assert_equal([], @c.to_a) end def test_ttl_precedence_over_lru @c[:a] = 1 Timecop.freeze(Time.now + 60) @c[:b] = 2 @c[:c] = 3 @c[:a] assert_equal [[:a, 1], [:c, 3], [:b, 2]], @c.to_a Timecop.freeze(Time.now + 270) @c[:d] = 4 assert_equal [[:d, 4], [:c, 3], [:b, 2]], @c.to_a end endlru-redux-1.1.0/test/ttl/thread_safe_cache_test.rb0000644000175100017510000000042213625723103021224 0ustar pravipraviclass TTLThreadSafeCacheTest < TTLCacheTest def setup Timecop.freeze(Time.now) @c = LruRedux::TTL::ThreadSafeCache.new 3, 5 * 60 end def test_recursion @c[:a] = 1 @c[:b] = 2 # should not blow up @c.each do |k, _| @c[k] end end endlru-redux-1.1.0/test/thread_safe_cache_test.rb0000644000175100017510000000036013625723103020422 0ustar pravipraviclass ThreadSafeCacheTest < CacheTest def setup @c = LruRedux::ThreadSafeCache.new(3) end def test_recursion @c[:a] = 1 @c[:b] = 2 # should not blow up @c.each do |k, _| @c[k] end end end lru-redux-1.1.0/Gemfile0000644000175100017510000000013613625723103013763 0ustar pravipravisource 'https://rubygems.org' # Specify your gem's dependencies in lru_redux.gemspec gemspec lru-redux-1.1.0/LICENSE.txt0000644000175100017510000000205413625723103014314 0ustar pravipraviCopyright (c) 2013 Sam Saffron MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.