persistent-cache 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .idea
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.rvmrc ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 1.9.3" > .rvmrc
9
+ environment_id="ruby-1.9.3-p194@persistent-hash"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.14.3 (stable)" # 1.10.1 seams as a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
27
+ \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
28
+ else
29
+ # If the environment file has not yet been created, use the RVM CLI to select.
30
+ rvm --create "$environment_id" || {
31
+ echo "Failed to create RVM environment '${environment_id}'."
32
+ return 1
33
+ }
34
+ fi
35
+
36
+ # If you use bundler, this might be useful to you:
37
+ # if [[ -s Gemfile ]] && {
38
+ # ! builtin command -v bundle >/dev/null ||
39
+ # builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
40
+ # }
41
+ # then
42
+ # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
43
+ # gem install bundler
44
+ # fi
45
+ # if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
46
+ # then
47
+ # bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
48
+ # fi
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rspec'
4
+
5
+ # Specify your gem's dependencies in persistent-cache.gemspec
6
+ gemspec
7
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Wynand van Dyk, Ernst van Graan, Hetzner (Pty) Ltd
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # Persistent::Cache
2
+
3
+ Persistent cache behaves like a hash, with a sqlite3 storage back-end. Values in the cache have a default
4
+ freshness period of 15465600 ms. This can be configured in the cache initializer. Setting fresh = nil indicates that
5
+ data remains fresh for-ever. If stale data is requested from the cache, nil is returned. Updates to the cache are
6
+ written to the sqlite3 storage, with SQL driver timeout set to 30 seconds. Data is marshalled before storage. If a key
7
+ is not found in the cache, nil is returned. Setting the value of a key in the cache to nil deletes the entry from the
8
+ sqlite database
9
+
10
+ This gem is sponsored by Hetzner (Pty) Ltd - http://hetzner.co.za
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'persistent-cache'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install persistent-cache
25
+
26
+ ## Usage
27
+
28
+ cache = Persistent::Cache.new("/tmp/my-persistent-cache", 3600) # 1 hour freshness
29
+
30
+ cache["testkey"] = "testvalue"
31
+ puts cache["testkey"] # testvalue
32
+
33
+ cache["testkey"] = "newvalue"
34
+ puts cache["testkey"] # newvalue
35
+
36
+ cache["testkey"] = nil
37
+ puts cache["testkey"] #
38
+
39
+ cache["testkey"] = "one"
40
+ cache["testkey2"] = "two"
41
+ cache["testkey3"] = 3
42
+
43
+ cache.each do |key|
44
+ puts "#{key} - #{cache[key]}"
45
+ end
46
+
47
+ #testkey - one
48
+ #testkey2 - two
49
+ #testkey3 - 3
50
+
51
+ puts cache.size # 3
52
+
53
+ puts cache.keys
54
+ #testkey
55
+ #testkey2
56
+ #testkey3
57
+
58
+ cache.clear # []
59
+
60
+ puts cache.size #0
61
+
62
+ cache = Persistent::Cache.new("/tmp/my-persistent-cache") # 15465600 (179 days) freshness
63
+
64
+ cache = Persistent::Cache.new("/tmp/my-persistent-cache", nil) # for-ever fresh
65
+
66
+ Please send feedback and comments to the authors at:
67
+ Wynand van Dyk <wynand.van.dyk@hetzner.co.za>
68
+ Ernst van Graan <ernst.van.graan@hetzner.co.za>
69
+
70
+ ## Contributing
71
+
72
+ 1. Fork it
73
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
74
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
75
+ 4. Push to the branch (`git push origin my-new-feature`)
76
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,104 @@
1
+ require "persistent-cache/version"
2
+ require "sqlite3"
3
+
4
+ module Persistent
5
+ class Cache
6
+ # Fresh is 1 day less than the bacula default job retention time. If this is configured differently, FRESH should be updated as well.
7
+ FRESH = 15465600; FRESH.freeze
8
+ DB_TABLE = "key_value"; DB_TABLE.freeze
9
+
10
+ attr_accessor :database_details
11
+
12
+ def initialize(database_details, fresh = FRESH)
13
+ @database_details = database_details
14
+ @db_handle = connect_to_database
15
+ @db_handle.busy_timeout = 30000
16
+ @fresh = fresh
17
+ end
18
+
19
+ def []=(key, value)
20
+ if value.nil?
21
+ delete_entry(Marshal.dump(key))
22
+ else
23
+ save_key_value_pair(Marshal.dump(key), Marshal.dump(value))
24
+ end
25
+ end
26
+
27
+ def [](key)
28
+ lookup_key(Marshal.dump(key))
29
+ end
30
+
31
+ def each(&block)
32
+ keys.each do |key|
33
+ yield key, lookup_key(Marshal.dump(key))
34
+ end
35
+ end
36
+
37
+ def size
38
+ @db_handle.execute("SELECT value FROM #{DB_TABLE}").size
39
+ end
40
+
41
+ def keys
42
+ @db_handle.execute("SELECT key FROM #{DB_TABLE}").collect { |key|
43
+ Marshal.load(key[0])
44
+ }
45
+ end
46
+
47
+ def clear
48
+ @db_handle.execute("DELETE FROM #{DB_TABLE}")
49
+ end
50
+
51
+ # Methods not supported by this implementation
52
+ private
53
+
54
+ def save_key_value_pair(serialized_key, serialized_value)
55
+ delete_entry(serialized_key)
56
+ @db_handle.execute("INSERT INTO #{DB_TABLE} (key, value, timestamp) VALUES(?, ?, ?)",serialized_key, serialized_value, Time.now.to_s)
57
+ end
58
+
59
+ def lookup_key(serialized_key)
60
+ result = @db_handle.execute("SELECT value, timestamp FROM #{DB_TABLE} WHERE key=?", serialized_key)
61
+ return nil if nil_result?(result)
62
+ return nil if stale_entry?(serialized_key, result)
63
+
64
+ return Marshal.load(result[0][0])
65
+ end
66
+
67
+ def stale_entry?(serialized_key, result)
68
+ return false if @fresh.nil?
69
+ timestamp = Time.parse(result[0][1])
70
+ if ((Time.now - timestamp) > FRESH)
71
+ delete_entry(serialized_key)
72
+ return true
73
+ end
74
+ return false
75
+ end
76
+
77
+ def delete_entry(serialized_key)
78
+ @db_handle.execute("DELETE FROM #{DB_TABLE} WHERE key=?", serialized_key)
79
+ end
80
+
81
+ def open_database
82
+ @handle = SQLite3::Database.open(@database_details)
83
+ end
84
+
85
+ def create_database
86
+ @handle = SQLite3::Database.new(@database_details)
87
+ create_table
88
+ @handle
89
+ end
90
+
91
+ def create_table
92
+ @handle.execute("CREATE TABLE #{DB_TABLE}(key TEXT PRIMARY KEY, value TEXT, timestamp TEXT)")
93
+ @handle
94
+ end
95
+
96
+ def connect_to_database
97
+ File.exists?(@database_details) ? open_database : create_database
98
+ end
99
+
100
+ def nil_result?(result)
101
+ result.nil? or result[0].nil? or result[0][0].nil?
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,5 @@
1
+ module Persistent
2
+ class Cache
3
+ VERSION = "0.0.6"
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/persistent-cache/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Wynand van Dyk", "Ernst van Graan"]
6
+ gem.email = ["wvd@hetzner.co.za", "ernst.van.graan@hetzner.co.za"]
7
+ gem.description = %q{Persistent Cache using SQLite}
8
+ gem.summary = %q{Persistent Cache has a default freshness threshold of 179 days after which entries are no longer returned}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "persistent-cache"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Persistent::Cache::VERSION
17
+ gem.add_development_dependency 'rspec', '2.12.0'
18
+ gem.add_dependency 'sqlite3', '1.3.7'
19
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+ require 'persistent-cache'
3
+ require 'tempfile'
4
+
5
+ describe Persistent::Cache do
6
+ def get_database_name
7
+ path = Tempfile.new("persistent-cache-spec-testdb").path
8
+ FileUtils.rm_f(path)
9
+ path
10
+ end
11
+
12
+ before :each do
13
+ @db_name = get_database_name
14
+ end
15
+
16
+
17
+ context "When assigning a value to a key" do
18
+ it "should store the key/value pair in the db, with a timestamp" do
19
+ start_time = Time.now - 1
20
+ @pcache = Persistent::Cache.new(@db_name)
21
+ @pcache["testkey"] = "testvalue"
22
+ handle = SQLite3::Database.open(@db_name)
23
+ result = handle.execute "select value, timestamp from #{Persistent::Cache::DB_TABLE} where key=?", Marshal.dump("testkey")
24
+ result.nil?.should == false
25
+ result[0].nil?.should == false
26
+ result[0][0].should == Marshal.dump("testvalue")
27
+ test_time = Time.parse(result[0][1])
28
+ test_time.should > start_time and test_time.should < start_time + 600
29
+ end
30
+
31
+ it "should overwrite the existing key/value pair if they already exist" do
32
+ @pcache = Persistent::Cache.new(@db_name)
33
+ @pcache["testkey"] = "testvalue"
34
+ @pcache["testkey"] = "testvalue2"
35
+ handle = SQLite3::Database.open(@db_name)
36
+ result = handle.execute "select value from #{Persistent::Cache::DB_TABLE} where key=?", Marshal.dump("testkey")
37
+ result.nil?.should == false
38
+ result[0].nil?.should == false
39
+ result.size.should == 1
40
+ result[0][0].should == Marshal.dump("testvalue2")
41
+ end
42
+
43
+ it "should serialize the key and value for persistence" do
44
+ @pcache = Persistent::Cache.new(@db_name)
45
+ @pcache.should_receive(:save_key_value_pair).with(Marshal.dump("testkey"), Marshal.dump("testvalue"))
46
+ @pcache["testkey"] = "testvalue"
47
+ end
48
+ end
49
+
50
+ context "When looking up a value given its key" do
51
+ it "should retrieve the value from the database and deserialize the value" do
52
+ @pcache = Persistent::Cache.new(@db_name)
53
+ @pcache["testkey"] = "testvalue2"
54
+ result = @pcache["testkey"]
55
+ result.should == "testvalue2"
56
+ end
57
+
58
+ it "should return nil if a value exists but it not fresh" do
59
+ fetch_stale_entry
60
+ end
61
+
62
+ it "should remove from the cache an entry it encounters that is not fresh" do
63
+ fetch_stale_entry
64
+ result = @handle.execute("SELECT value FROM #{Persistent::Cache::DB_TABLE} WHERE key=?", Marshal.dump("oldkey"))
65
+ result.size.should == 0
66
+ end
67
+
68
+ it "should return nil if a key is not in the database" do
69
+ @pcache = Persistent::Cache.new(@db_name)
70
+ @pcache["testkey"] = "testvalue2"
71
+ result = @pcache["testkey2"]
72
+ result.nil?.should == true
73
+ end
74
+
75
+ it "should serialize the key for lookup" do
76
+ @pcache = Persistent::Cache.new(@db_name)
77
+ @pcache["testkey"] = "testvalue"
78
+ @pcache.should_receive(:lookup_key).with(Marshal.dump("testkey"))
79
+ @pcache["testkey"]
80
+ end
81
+
82
+ def fetch_stale_entry
83
+ @pcache = Persistent::Cache.new(@db_name)
84
+ timestamp = Time.now - Persistent::Cache::FRESH
85
+ @handle = SQLite3::Database.open(@db_name)
86
+ @handle.execute("INSERT INTO #{Persistent::Cache::DB_TABLE} (key, value, timestamp) VALUES ('#{Marshal.dump("oldkey")}', '#{Marshal.dump("oldvalue")}', '#{timestamp}')")
87
+ @pcache["oldkey"].nil?.should == true
88
+ end
89
+ end
90
+
91
+ context "when constructing" do
92
+ it "should receive database connection details" do
93
+ @pcache = Persistent::Cache.new(@db_name)
94
+ @pcache.class.should == Persistent::Cache
95
+ @pcache.database_details.should == @db_name
96
+ end
97
+
98
+ it "should create the database if it does not exist" do
99
+ FileUtils.rm_f(@db_name)
100
+ Persistent::Cache.new(@db_name)
101
+ File.exists?(@db_name).should be_true
102
+ end
103
+
104
+ it "should create a key_value table with key (TEXT) and value (TEXT) and timestamp (TEXT) columns" do
105
+ FileUtils.rm_f(@db_name)
106
+ Persistent::Cache.new(@db_name)
107
+ handle = SQLite3::Database.open(@db_name)
108
+ result = handle.execute "PRAGMA table_info(#{Persistent::Cache::DB_TABLE})"
109
+ result[0][1].should == "key"
110
+ result[0][2].should == "TEXT"
111
+ result[1][1].should == "value"
112
+ result[1][2].should == "TEXT"
113
+ result[2][1].should == "timestamp"
114
+ result[2][2].should == "TEXT"
115
+ end
116
+
117
+
118
+ it "should use the existing database if it does exist" do
119
+ FileUtils.rm_f(@db_name)
120
+ handle = SQLite3::Database.new(@db_name)
121
+ handle.execute "create table test123 ( id int );"
122
+ handle.close
123
+ Persistent::Cache.new(@db_name)
124
+ handle = SQLite3::Database.open(@db_name)
125
+ result = handle.execute "select name from sqlite_master where type='table'"
126
+ result[0][0].should == "test123"
127
+ end
128
+ end
129
+
130
+ context "it should behave like a cache" do
131
+ it "should return the correct size" do
132
+ setup_cache
133
+ @pcache.size.should == 3
134
+ end
135
+
136
+ it "should return the list of keys when asked" do
137
+ setup_cache
138
+ @pcache.keys.should == ["one", "two", "three"]
139
+ end
140
+
141
+ it "should allow iteration through each" do
142
+ setup_cache
143
+ test = []
144
+ @pcache.each do |key, value|
145
+ test << "#{key} => #{value}"
146
+ end
147
+ test.should == ["one => value one", "two => value two", "three => value three"]
148
+ end
149
+
150
+ it "should delete all entries in the database when asked to clear" do
151
+ setup_cache
152
+ @pcache.clear
153
+ @pcache.size.should == 0
154
+ end
155
+
156
+ it "should be able to handle multiple accesses to the same db" do
157
+ #pending "find a better way to test this, currently you need to spawn multiple rspecs running this test to hit the error if the busy_timeout is not specified"
158
+ pcache = Persistent::Cache.new("multidb")
159
+ pcache["multi_test"] = 0
160
+
161
+ threads = []
162
+ 100.times do |i|
163
+ threads << Thread.new do
164
+ Thread.current['pcache'] = Persistent::Cache.new("multidb")
165
+ Thread.current['pcache']["multi_test"] += 1
166
+ end
167
+ end
168
+ threads.each { |t| t.join }
169
+
170
+ p pcache["multi_test"]
171
+
172
+ end
173
+
174
+ def setup_cache
175
+ FileUtils.rm_f(@db_name)
176
+ @pcache = Persistent::Cache.new(@db_name)
177
+ @pcache["one"] = "value one"
178
+ @pcache["two"] = "value two"
179
+ @pcache["three"] = "value three"
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,23 @@
1
+ require 'rspec'
2
+ require 'rspec/mocks'
3
+
4
+ # This file was generated by the `rspec --init` command. Conventionally, all
5
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
6
+ # Require this file using `require "spec_helper"` to ensure that it is only
7
+ # loaded once.
8
+ #
9
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
10
+
11
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'persistent-cache'))
12
+
13
+ RSpec.configure do |config|
14
+ config.treat_symbols_as_metadata_keys_with_true_values = true
15
+ config.run_all_when_everything_filtered = true
16
+ config.filter_run :focus
17
+
18
+ # Run specs in random order to surface order dependencies. If you find an
19
+ # order dependency and want to debug it, you can fix the order by providing
20
+ # the seed, which is printed after each run.
21
+ # --seed 1234
22
+ config.order = 'random'
23
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: persistent-cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Wynand van Dyk
9
+ - Ernst van Graan
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-03-07 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - '='
21
+ - !ruby/object:Gem::Version
22
+ version: 2.12.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - '='
29
+ - !ruby/object:Gem::Version
30
+ version: 2.12.0
31
+ - !ruby/object:Gem::Dependency
32
+ name: sqlite3
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - '='
37
+ - !ruby/object:Gem::Version
38
+ version: 1.3.7
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - '='
45
+ - !ruby/object:Gem::Version
46
+ version: 1.3.7
47
+ description: Persistent Cache using SQLite
48
+ email:
49
+ - wvd@hetzner.co.za
50
+ - ernst.van.graan@hetzner.co.za
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - .rspec
57
+ - .rvmrc
58
+ - Gemfile
59
+ - LICENSE
60
+ - README.md
61
+ - Rakefile
62
+ - lib/persistent-cache.rb
63
+ - lib/persistent-cache/version.rb
64
+ - persistent-cache.gemspec
65
+ - spec/persistent-cache_spec.rb
66
+ - spec/spec_helper.rb
67
+ homepage: ''
68
+ licenses: []
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 1.8.24
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Persistent Cache has a default freshness threshold of 179 days after which
91
+ entries are no longer returned
92
+ test_files:
93
+ - spec/persistent-cache_spec.rb
94
+ - spec/spec_helper.rb