mini_cache 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.rubocop_todo.yml +24 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/LICENSE +2 -2
- data/README.md +68 -46
- data/Rakefile +13 -5
- data/lib/mini_cache.rb +5 -2
- data/lib/mini_cache/data.rb +21 -0
- data/lib/mini_cache/store.rb +49 -22
- data/lib/mini_cache/version.rb +3 -1
- data/mini_cache.gemspec +18 -11
- data/test/mini_cache/data_test.rb +67 -0
- data/test/mini_cache/store_test.rb +261 -127
- data/test/test_helper.rb +6 -2
- metadata +71 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a985472b71d6f27c56bbc2f4eb78e9a6587d3a46
|
4
|
+
data.tar.gz: dbf5cc0c90cf4d555cb315c526dbf12d67d8e177
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6940e77b4988ca034d6c031c564613ed79e0ffe2d140d06b70a2b59ef6ae7dcdb10e19b75f91a08c9bd872a942a391e583529ee948c1911089a8ad9373b7af2
|
7
|
+
data.tar.gz: e7e1687c3b0c58b7d189ac43f0d5bd8fa06a3224650393faf415e660239694fa9cf1ea6e4e836a3dfe5e8108ebb10bf34b96956d11b8973d87ffff9978c2854c
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2017-04-22 10:39:26 -0500 using RuboCop version 0.46.0.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 3
|
10
|
+
# Configuration parameters: CountComments.
|
11
|
+
Metrics/BlockLength:
|
12
|
+
Max: 68
|
13
|
+
|
14
|
+
# Offense count: 1
|
15
|
+
# Configuration parameters: CountComments.
|
16
|
+
Metrics/ClassLength:
|
17
|
+
Max: 227
|
18
|
+
|
19
|
+
# Offense count: 1
|
20
|
+
Style/Documentation:
|
21
|
+
Exclude:
|
22
|
+
- 'spec/**/*'
|
23
|
+
- 'test/**/*'
|
24
|
+
- 'lib/mini_cache/data.rb'
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2017 Derrick Reimer
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
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.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
# MiniCache
|
2
2
|
|
3
|
-
|
4
|
-
This gem has no external dependencies and has been tested in MRI 1.9.x and
|
5
|
-
JRuby 1.9 mode.
|
3
|
+
[![Build Status](https://travis-ci.org/djreimer/mini_cache.svg?branch=master)](https://travis-ci.org/djreimer/mini_cache)
|
6
4
|
|
7
|
-
|
5
|
+
MiniCache is a lightweight in-memory key-value store for Ruby objects.
|
6
|
+
This gem requires Ruby version 2.3.0 or higher.
|
8
7
|
|
9
8
|
## Motivation
|
10
9
|
|
11
|
-
It is common practice to cache certain values on an object that are
|
12
|
-
computationally expensive to obtain, such as a property that requires a
|
13
|
-
database query.
|
10
|
+
It is common practice to cache certain values on an object that are
|
11
|
+
computationally expensive to obtain, such as a property that requires a
|
12
|
+
database query.
|
14
13
|
|
15
14
|
The simplest way to do this is by storing the value in an instance variable:
|
16
15
|
|
@@ -19,14 +18,14 @@ class Account
|
|
19
18
|
def calculate_balance
|
20
19
|
# Do something expensive.
|
21
20
|
end
|
22
|
-
|
21
|
+
|
23
22
|
def balance
|
24
23
|
@balance ||= self.calculate_balance
|
25
24
|
end
|
26
25
|
end
|
27
26
|
```
|
28
27
|
|
29
|
-
While this method works in many scenarios, it fails when the value you
|
28
|
+
While this method works in many scenarios, it fails when the value you
|
30
29
|
need to cache is:
|
31
30
|
|
32
31
|
- Either `nil` or `false`
|
@@ -39,7 +38,7 @@ class Account
|
|
39
38
|
def lookup_role(user)
|
40
39
|
# Execute a database query to find the user's role.
|
41
40
|
end
|
42
|
-
|
41
|
+
|
43
42
|
def role(user)
|
44
43
|
# Perform the lookup once and cache the value. We can't use
|
45
44
|
#
|
@@ -53,14 +52,14 @@ class Account
|
|
53
52
|
self.lookup_role(user)
|
54
53
|
end
|
55
54
|
end
|
56
|
-
|
55
|
+
|
57
56
|
def cache
|
58
57
|
@cache ||= MiniCache::Store.new
|
59
58
|
end
|
60
59
|
end
|
61
60
|
```
|
62
61
|
|
63
|
-
The `#get_or_set` method works similarly to the `||=` operator, except it
|
62
|
+
The `#get_or_set` method works similarly to the `||=` operator, except it
|
64
63
|
knows how to handle `false` and `nil` values and it's keyed off of a unique string ID.
|
65
64
|
Problem solved!
|
66
65
|
|
@@ -86,29 +85,77 @@ To create a new MiniCache store object, just initialize it:
|
|
86
85
|
store = MiniCache::Store.new
|
87
86
|
|
88
87
|
# Optionally pass in a Hash of data
|
89
|
-
store = MiniCache::Store.new(:
|
88
|
+
store = MiniCache::Store.new(name: 'Derrick', occupation: 'Developer')
|
90
89
|
```
|
91
90
|
|
92
91
|
Set and retrieve data using `#get` and `#set`:
|
93
92
|
|
94
93
|
```ruby
|
95
94
|
# Pass in the value as an argument or block
|
96
|
-
store.set(
|
97
|
-
store.set(
|
95
|
+
store.set('age', 24)
|
96
|
+
store.set('birth_year') { 1988 }
|
97
|
+
|
98
|
+
store.get('age')
|
99
|
+
# => 24
|
100
|
+
|
101
|
+
store.get('birth_year')
|
102
|
+
# => 1988
|
103
|
+
|
104
|
+
# Sets an expiration time to cache (in seconds)
|
105
|
+
store.set('age', 24, expires_in: 60)
|
106
|
+
store.set('day', expires_in: 60) { 12 }
|
107
|
+
store.set('birth_year') { MiniCache::Data.new(1988, 60) }
|
108
|
+
|
109
|
+
store.get('age')
|
110
|
+
# => 24
|
111
|
+
|
112
|
+
store.get('day')
|
113
|
+
# => 12
|
114
|
+
|
115
|
+
store.get('birth_year')
|
116
|
+
# => 1988
|
117
|
+
|
118
|
+
sleep(60)
|
119
|
+
|
120
|
+
store.get('age')
|
121
|
+
# => nil
|
122
|
+
|
123
|
+
store.get('day')
|
124
|
+
# => nil
|
98
125
|
|
99
|
-
store.get(
|
100
|
-
|
126
|
+
store.get('birth_year')
|
127
|
+
#=> nil
|
101
128
|
```
|
102
129
|
|
103
130
|
Use the `#get_or_set` method to either set the value if it hasn't already been
|
104
131
|
set, or get the value that was already set.
|
105
132
|
|
106
133
|
```ruby
|
107
|
-
store.set(
|
108
|
-
|
134
|
+
store.set('birth_year') { 1988 }
|
135
|
+
#=> 1988
|
109
136
|
|
110
|
-
store.get_or_set(
|
111
|
-
|
137
|
+
store.get_or_set('birth_year') { 1964 }
|
138
|
+
#=> 1988 # Did not overwrite previously set value
|
139
|
+
|
140
|
+
# You may also set an expiration time (in seconds):
|
141
|
+
|
142
|
+
store.get_or_set('age', expires_in: 60) { 24 }
|
143
|
+
#=> 24
|
144
|
+
|
145
|
+
store.get_or_set('birth_year') do
|
146
|
+
MiniCache::Data.new(1988, expires_in: 60)
|
147
|
+
end
|
148
|
+
#=> 1988
|
149
|
+
|
150
|
+
sleep(60)
|
151
|
+
|
152
|
+
store.get_or_set('age', expires_in: 60) { 28 }
|
153
|
+
#=> 28
|
154
|
+
|
155
|
+
store.get_or_set('birth_year') do
|
156
|
+
MiniCache::Data.new(1964, expires_in: 60)
|
157
|
+
end
|
158
|
+
#=> 1964
|
112
159
|
```
|
113
160
|
|
114
161
|
Other convenience methods:
|
@@ -125,28 +172,3 @@ Other convenience methods:
|
|
125
172
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
126
173
|
4. Push to the branch (`git push origin my-new-feature`)
|
127
174
|
5. Create new Pull Request
|
128
|
-
|
129
|
-
## License
|
130
|
-
|
131
|
-
Copyright © 2012 Derrick Reimer
|
132
|
-
|
133
|
-
MIT License
|
134
|
-
|
135
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
136
|
-
a copy of this software and associated documentation files (the
|
137
|
-
"Software"), to deal in the Software without restriction, including
|
138
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
139
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
140
|
-
permit persons to whom the Software is furnished to do so, subject to
|
141
|
-
the following conditions:
|
142
|
-
|
143
|
-
The above copyright notice and this permission notice shall be
|
144
|
-
included in all copies or substantial portions of the Software.
|
145
|
-
|
146
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
147
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
148
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
149
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
150
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
151
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
152
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/gem_tasks'
|
3
5
|
require 'rake/testtask'
|
6
|
+
require 'rubocop/rake_task'
|
4
7
|
|
5
8
|
Rake::TestTask.new do |t|
|
6
|
-
t.libs <<
|
7
|
-
t.pattern =
|
9
|
+
t.libs << 'lib'
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
8
11
|
t.verbose = true
|
9
12
|
end
|
10
13
|
|
11
|
-
|
12
|
-
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
|
16
|
+
desc 'Run tests'
|
17
|
+
task :default do
|
18
|
+
Rake::Task['rubocop'].invoke
|
19
|
+
Rake::Task['test'].invoke
|
20
|
+
end
|
data/lib/mini_cache.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniCache
|
4
|
+
class Data
|
5
|
+
attr_reader :value
|
6
|
+
attr_reader :expires_in
|
7
|
+
|
8
|
+
def initialize(value, expires_in: nil)
|
9
|
+
@value = value
|
10
|
+
@expires_in = expires_in.nil? ? nil : Time.now + expires_in
|
11
|
+
end
|
12
|
+
|
13
|
+
def expired?
|
14
|
+
!@expires_in.nil? && Time.now > @expires_in
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
other.is_a?(MiniCache::Data) && @value == other.value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/mini_cache/store.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MiniCache
|
2
|
-
class Store
|
4
|
+
class Store # :nodoc:
|
3
5
|
include Enumerable
|
4
6
|
|
5
7
|
# Public: Returns the hash of key-value pairs.
|
@@ -8,11 +10,12 @@ module MiniCache
|
|
8
10
|
# Public: Initializes a new MiniCache object.
|
9
11
|
#
|
10
12
|
# data - A Hash of key-value pairs (optional).
|
13
|
+
# The values can be String or MiniCache::Data.
|
11
14
|
#
|
12
15
|
# Returns nothing.
|
13
16
|
def initialize(data = {})
|
14
17
|
@data = {}
|
15
|
-
|
18
|
+
load(data)
|
16
19
|
end
|
17
20
|
|
18
21
|
# Public: Retrieves the value for a given key.
|
@@ -23,7 +26,8 @@ module MiniCache
|
|
23
26
|
# set, returns nil.
|
24
27
|
def get(key)
|
25
28
|
check_key!(key)
|
26
|
-
|
29
|
+
expires!(key)
|
30
|
+
@data[key.to_s]&.value
|
27
31
|
end
|
28
32
|
|
29
33
|
# Public: Sets a value for a given key either as
|
@@ -31,22 +35,37 @@ module MiniCache
|
|
31
35
|
#
|
32
36
|
# key - A String or Symbol representing the key.
|
33
37
|
# value - Any object that represents the value (optional).
|
38
|
+
# The value can be a MiniCache::Data.
|
34
39
|
# Not used if a block is given.
|
35
|
-
# block - A block of code that returns the value to set
|
36
|
-
#
|
40
|
+
# block - A block of code that returns the value to set (optional).
|
41
|
+
# Can be set a MiniCache::Data in the block.
|
42
|
+
# expires_in - Time, in seconds, to expire the cache (optional).
|
43
|
+
# If not set, the cache never expires.
|
37
44
|
#
|
38
45
|
# Examples
|
39
46
|
#
|
40
47
|
# cache.set("name", "Derrick")
|
41
48
|
# => "Derrick"
|
42
49
|
#
|
50
|
+
# cache.set("name", "Derrick", expires_in: 60)
|
51
|
+
# => "Derrick"
|
52
|
+
#
|
43
53
|
# cache.set("name") { "Joe" }
|
44
54
|
# => "Joe"
|
45
55
|
#
|
56
|
+
# cache.set("name") { MiniCache::Data.new("Joe", 60) }
|
57
|
+
# => "Joe"
|
58
|
+
#
|
46
59
|
# Returns the value given.
|
47
|
-
def set(key, value = nil)
|
60
|
+
def set(key, value = nil, expires_in: nil)
|
48
61
|
check_key!(key)
|
49
|
-
|
62
|
+
data = block_given? ? yield : value
|
63
|
+
@data[key.to_s] = if data.is_a?(MiniCache::Data)
|
64
|
+
data
|
65
|
+
else
|
66
|
+
MiniCache::Data.new(data, expires_in: expires_in)
|
67
|
+
end
|
68
|
+
get(key)
|
50
69
|
end
|
51
70
|
|
52
71
|
# Public: Determines whether a value has been set for
|
@@ -57,6 +76,7 @@ module MiniCache
|
|
57
76
|
# Returns a Boolean.
|
58
77
|
def set?(key)
|
59
78
|
check_key!(key)
|
79
|
+
expires!(key)
|
60
80
|
@data.keys.include?(key.to_s)
|
61
81
|
end
|
62
82
|
|
@@ -66,9 +86,12 @@ module MiniCache
|
|
66
86
|
#
|
67
87
|
# key - A String or Symbol representing the key.
|
68
88
|
# value - Any object that represents the value (optional).
|
89
|
+
# The value can be a MiniCache::Data.
|
69
90
|
# Not used if a block is given.
|
70
|
-
# block - A block of code that returns the value to set
|
71
|
-
#
|
91
|
+
# block - A block of code that returns the value to set (optional).
|
92
|
+
# Can be set a MiniCache::Data in the block.
|
93
|
+
# expires_in - Time, in seconds, to expire the cache (optional).
|
94
|
+
# If not set, the cache never expires.
|
72
95
|
#
|
73
96
|
# Examples
|
74
97
|
#
|
@@ -85,9 +108,9 @@ module MiniCache
|
|
85
108
|
# => "Engineer"
|
86
109
|
#
|
87
110
|
# Returns the value.
|
88
|
-
def get_or_set(key, value = nil)
|
111
|
+
def get_or_set(key, value = nil, expires_in: nil)
|
89
112
|
return get(key) if set?(key)
|
90
|
-
set(key, block_given? ? yield : value)
|
113
|
+
set(key, block_given? ? yield : value, expires_in: expires_in)
|
91
114
|
end
|
92
115
|
|
93
116
|
# Public: Removes the key-value pair from the cache
|
@@ -114,7 +137,7 @@ module MiniCache
|
|
114
137
|
# and value of each pair.
|
115
138
|
#
|
116
139
|
# Yields the String key and value.
|
117
|
-
def each
|
140
|
+
def each
|
118
141
|
@data.each { |k, v| yield(k, v) }
|
119
142
|
end
|
120
143
|
|
@@ -126,20 +149,24 @@ module MiniCache
|
|
126
149
|
def load(data)
|
127
150
|
data.each do |key, value|
|
128
151
|
check_key!(key)
|
129
|
-
|
152
|
+
set(key, value)
|
130
153
|
end
|
131
154
|
end
|
132
155
|
|
133
156
|
private
|
134
157
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
158
|
+
# Internal: Raises an error if the key is not a String
|
159
|
+
# or a Symbol.
|
160
|
+
#
|
161
|
+
# key - A key provided by the user.
|
162
|
+
def check_key!(key)
|
163
|
+
return if key.is_a?(String) || key.is_a?(Symbol)
|
164
|
+
raise TypeError, 'key must be a String or Symbol'
|
165
|
+
end
|
166
|
+
|
167
|
+
# Internal: Verifies if data is expired and unset it
|
168
|
+
def expires!(key)
|
169
|
+
unset(key) if @data[key.to_s]&.expired?
|
170
|
+
end
|
144
171
|
end
|
145
172
|
end
|
data/lib/mini_cache/version.rb
CHANGED
data/mini_cache.gemspec
CHANGED
@@ -1,19 +1,26 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require File.expand_path('../lib/mini_cache/version', __FILE__)
|
3
5
|
|
4
6
|
Gem::Specification.new do |gem|
|
5
|
-
gem.authors = [
|
6
|
-
gem.email = [
|
7
|
-
gem.description =
|
8
|
-
gem.summary =
|
9
|
-
|
7
|
+
gem.authors = ['Derrick Reimer']
|
8
|
+
gem.email = ['derrickreimer@gmail.com']
|
9
|
+
gem.description = 'A lightweight, in-memory cache for Ruby objects'
|
10
|
+
gem.summary = 'MiniCache is a lightweight, in-memory key-value store' \
|
11
|
+
'for Ruby objects'
|
12
|
+
gem.homepage = 'https://github.com/djreimer/mini_cache'
|
10
13
|
|
11
|
-
gem.files = `git ls-files`.split(
|
12
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
|
+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
13
16
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
-
gem.name =
|
15
|
-
gem.require_paths = [
|
17
|
+
gem.name = 'mini_cache'
|
18
|
+
gem.require_paths = ['lib']
|
16
19
|
gem.version = MiniCache::VERSION
|
17
|
-
|
18
|
-
gem.add_development_dependency
|
20
|
+
|
21
|
+
gem.add_development_dependency 'shoulda-context'
|
22
|
+
gem.add_development_dependency 'timecop', '~> 0.8'
|
23
|
+
gem.add_development_dependency 'rake'
|
24
|
+
gem.add_development_dependency 'minitest'
|
25
|
+
gem.add_development_dependency 'rubocop', '~> 0.48'
|
19
26
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../../test_helper.rb', __FILE__)
|
4
|
+
|
5
|
+
module MiniCache
|
6
|
+
class DataTest < MiniTest::Test
|
7
|
+
context 'initialize' do
|
8
|
+
should 'return initialized value' do
|
9
|
+
data = MiniCache::Data.new('Gunter')
|
10
|
+
assert_equal('Gunter', data.value)
|
11
|
+
end
|
12
|
+
|
13
|
+
should 'return a nil expires_in as default' do
|
14
|
+
data = MiniCache::Data.new('Gunter')
|
15
|
+
assert_nil(data.expires_in)
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'return initialized expires_in' do
|
19
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
20
|
+
data = MiniCache::Data.new('Gunter', expires_in: 60)
|
21
|
+
assert_equal(Time.now + 60, data.expires_in)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context '#equals' do
|
27
|
+
should 'be compared only by value and be the same' do
|
28
|
+
data1 = MiniCache::Data.new('Finn', expires_in: 10)
|
29
|
+
data2 = MiniCache::Data.new('Finn', expires_in: 30)
|
30
|
+
assert_equal(data1, data2)
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'be compared only by value and not be the same' do
|
34
|
+
data1 = MiniCache::Data.new('Finn', expires_in: 10)
|
35
|
+
data2 = MiniCache::Data.new('Grass Finn', expires_in: 10)
|
36
|
+
refute_equal(data1, data2)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context '#expired?' do
|
41
|
+
should 'be expired' do
|
42
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
43
|
+
data = MiniCache::Data.new('Gunter', expires_in: 60)
|
44
|
+
Timecop.travel(data.expires_in) do
|
45
|
+
assert_equal(true, data.expired?)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
should 'not be expired' do
|
51
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
52
|
+
data = MiniCache::Data.new('Gunter', expires_in: 60)
|
53
|
+
Timecop.travel(data.expires_in - 1) do
|
54
|
+
assert_equal(false, data.expired?)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'not be expired because expires_in is nil' do
|
60
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
61
|
+
data = MiniCache::Data.new('Gunter')
|
62
|
+
assert_equal(false, data.expired?)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,138 +1,272 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
should "default to empty data" do
|
10
|
-
store = MiniCache::Store.new
|
11
|
-
assert_equal Hash.new, store.data
|
12
|
-
end
|
13
|
-
|
14
|
-
should "load seed data" do
|
15
|
-
data = { "name" => "Derrick" }
|
16
|
-
store = MiniCache::Store.new(data)
|
17
|
-
assert_equal data, store.data
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context "#get" do
|
22
|
-
should "return a value if set" do
|
23
|
-
@store.set("name", "Derrick")
|
24
|
-
assert_equal "Derrick", @store.get("name")
|
25
|
-
end
|
26
|
-
|
27
|
-
should "return nil if not set" do
|
28
|
-
assert_nil @store.get("name")
|
29
|
-
end
|
30
|
-
|
31
|
-
should "raise a TypeError if key is not valid" do
|
32
|
-
assert_raises(TypeError) { @store.get([1, 2]) }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
context "#set" do
|
37
|
-
should "accept the value as an argument" do
|
38
|
-
@store.set("name", "Derrick")
|
39
|
-
assert_equal "Derrick", @store.get("name")
|
40
|
-
end
|
41
|
-
|
42
|
-
should "accept the value as a block" do
|
43
|
-
@store.set("name") { "Derrick" }
|
44
|
-
assert_equal "Derrick", @store.get("name")
|
45
|
-
end
|
46
|
-
|
47
|
-
should "raise a TypeError if key is not valid" do
|
48
|
-
assert_raises(TypeError) { @store.set([1, 2], "foo") }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context "#set?" do
|
53
|
-
should "be true if key has been set" do
|
54
|
-
@store.set("name", "Derrick")
|
55
|
-
assert_equal true, @store.set?("name")
|
56
|
-
end
|
57
|
-
|
58
|
-
should "be false if key has not been set" do
|
59
|
-
assert_equal false, @store.set?("foobar")
|
60
|
-
end
|
61
|
-
|
62
|
-
should "raise a TypeError if key is not valid" do
|
63
|
-
assert_raises(TypeError) { @store.set?([1, 2]) }
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context "#get_or_set" do
|
68
|
-
should "set the value if it hasn't already been set" do
|
69
|
-
@store.get_or_set("name", "Derrick")
|
70
|
-
assert_equal "Derrick", @store.get("name")
|
71
|
-
end
|
72
|
-
|
73
|
-
should "not set the value if it has already been set" do
|
74
|
-
@store.set("name", "Derrick")
|
75
|
-
@store.get_or_set("name", "Joe")
|
76
|
-
assert_equal "Derrick", @store.get("name")
|
77
|
-
end
|
78
|
-
|
79
|
-
should "return the value if not already set" do
|
80
|
-
assert_equal "Derrick", @store.get_or_set("name", "Derrick")
|
3
|
+
require File.expand_path('../../test_helper.rb', __FILE__)
|
4
|
+
|
5
|
+
module MiniCache
|
6
|
+
class StoreTest < MiniTest::Test
|
7
|
+
def setup
|
8
|
+
@store = MiniCache::Store.new
|
81
9
|
end
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
10
|
+
|
11
|
+
context 'initialize' do
|
12
|
+
should 'default to empty data' do
|
13
|
+
store = MiniCache::Store.new
|
14
|
+
assert_equal({}, store.data)
|
15
|
+
end
|
16
|
+
|
17
|
+
should 'load seed data' do
|
18
|
+
data = { 'name' => 'Derrick' }
|
19
|
+
store = MiniCache::Store.new(data)
|
20
|
+
assert_equal(
|
21
|
+
{ data.keys.first => MiniCache::Data.new(data.values.first) },
|
22
|
+
store.data
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'load seed data using MiniCache::Data' do
|
27
|
+
data = { 'name' => MiniCache::Data.new('Derrick', expires_in: 60) }
|
28
|
+
store = MiniCache::Store.new(data)
|
29
|
+
assert_equal(
|
30
|
+
data,
|
31
|
+
store.data
|
32
|
+
)
|
33
|
+
end
|
86
34
|
end
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
35
|
+
|
36
|
+
context '#get' do
|
37
|
+
should 'return a value if set' do
|
38
|
+
@store.set('name', 'Derrick')
|
39
|
+
assert_equal 'Derrick', @store.get('name')
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'return nil if not set' do
|
43
|
+
assert_nil @store.get('name')
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'raise a TypeError if key is not valid' do
|
47
|
+
assert_raises(TypeError) { @store.get([1, 2]) }
|
48
|
+
end
|
49
|
+
|
50
|
+
should 'return an not expired value' do
|
51
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
52
|
+
@store.set('name', 'Derrick', expires_in: 60)
|
53
|
+
Timecop.travel(Time.now + 59) do
|
54
|
+
assert_equal('Derrick', @store.get('name'))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'not return an expired value' do
|
60
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
61
|
+
@store.set('name', 'Derrick', expires_in: 60)
|
62
|
+
Timecop.travel(Time.now + 60) do
|
63
|
+
assert_nil @store.get('name')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
91
67
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
68
|
+
|
69
|
+
context '#set' do
|
70
|
+
should 'accept the value as an argument' do
|
71
|
+
@store.set('name', 'Derrick')
|
72
|
+
assert_equal 'Derrick', @store.get('name')
|
73
|
+
end
|
74
|
+
|
75
|
+
should 'accept the value as a block' do
|
76
|
+
@store.set('name') { 'Derrick' }
|
77
|
+
assert_equal 'Derrick', @store.get('name')
|
78
|
+
end
|
79
|
+
|
80
|
+
should 'accept the value as a MiniCache::Data argument' do
|
81
|
+
@store.set('name', MiniCache::Data.new('Derrick'))
|
82
|
+
assert_equal 'Derrick', @store.get('name')
|
83
|
+
end
|
84
|
+
|
85
|
+
should 'accept the value as a block with MiniCache::Data' do
|
86
|
+
@store.set('name') { MiniCache::Data.new('Derrick') }
|
87
|
+
assert_equal 'Derrick', @store.get('name')
|
88
|
+
end
|
89
|
+
|
90
|
+
should 'raise a TypeError if key is not valid' do
|
91
|
+
assert_raises(TypeError) { @store.set([1, 2], 'foo') }
|
92
|
+
end
|
93
|
+
|
94
|
+
should 'returns cached value' do
|
95
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
96
|
+
@store.set('name', 'Derrick', expires_in: 60)
|
97
|
+
Timecop.travel(Time.now + 59) do
|
98
|
+
assert_equal('Derrick', @store.get('name'))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
should 'returns nil, because cache was expired' do
|
104
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
105
|
+
@store.set('name', 'Derrick', expires_in: 60)
|
106
|
+
Timecop.travel(Time.now + 60) do
|
107
|
+
assert_nil(@store.get('name'))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
should 'returns cached value. Using block' do
|
113
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
114
|
+
@store.get_or_set('name', expires_in: 60) do
|
115
|
+
'Derrick'
|
116
|
+
end
|
117
|
+
Timecop.travel(Time.now + 59) do
|
118
|
+
assert_equal('Derrick', @store.get('name'))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
should 'returns nil, because cache was expired. Using block' do
|
124
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
125
|
+
@store.get_or_set('name', expires_in: 60) do
|
126
|
+
'Derrick'
|
127
|
+
end
|
128
|
+
Timecop.travel(Time.now + 60) do
|
129
|
+
assert_nil(@store.get('name'))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
95
133
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
134
|
+
|
135
|
+
context '#set?' do
|
136
|
+
should 'be true if key has been set' do
|
137
|
+
@store.set('name', 'Derrick')
|
138
|
+
assert_equal true, @store.set?('name')
|
139
|
+
end
|
140
|
+
|
141
|
+
should 'be false if key has not been set' do
|
142
|
+
assert_equal false, @store.set?('foobar')
|
143
|
+
end
|
144
|
+
|
145
|
+
should 'raise a TypeError if key is not valid' do
|
146
|
+
assert_raises(TypeError) { @store.set?([1, 2]) }
|
147
|
+
end
|
103
148
|
end
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
149
|
+
|
150
|
+
context '#get_or_set' do
|
151
|
+
should "set the value if it hasn't already been set" do
|
152
|
+
@store.get_or_set('name', 'Derrick')
|
153
|
+
assert_equal 'Derrick', @store.get('name')
|
154
|
+
end
|
155
|
+
|
156
|
+
should 'not set the value if it has already been set' do
|
157
|
+
@store.set('name', 'Derrick')
|
158
|
+
@store.get_or_set('name', 'Joe')
|
159
|
+
assert_equal 'Derrick', @store.get('name')
|
160
|
+
end
|
161
|
+
|
162
|
+
should 'return the value if not already set' do
|
163
|
+
assert_equal('Derrick',
|
164
|
+
@store.get_or_set('name', 'Derrick'))
|
165
|
+
end
|
166
|
+
|
167
|
+
should 'return the value if already set' do
|
168
|
+
@store.set('name', 'Derrick')
|
169
|
+
assert_equal 'Derrick', @store.get_or_set('name', 'Joe')
|
170
|
+
end
|
171
|
+
|
172
|
+
should 'accept the value as a block' do
|
173
|
+
@store.get_or_set('name') { 'Joe' }
|
174
|
+
assert_equal 'Joe', @store.get('name')
|
175
|
+
end
|
176
|
+
|
177
|
+
should 'raise a TypeError if key is not valid' do
|
178
|
+
assert_raises(TypeError) { @store.get_or_set([1, 2], 'foo') }
|
179
|
+
end
|
180
|
+
|
181
|
+
should 'returns first set value' do
|
182
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
183
|
+
@store.set('name', 'Derrick', expires_in: 60)
|
184
|
+
Timecop.travel(Time.now + 59) do
|
185
|
+
@store.get_or_set('name', 'Gunter', expires_in: 60)
|
186
|
+
assert_equal('Derrick', @store.get('name'))
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
should 'returns next set value' do
|
192
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
193
|
+
@store.set('name', 'Derrick', expires_in: 60)
|
194
|
+
Timecop.travel(Time.now + 60) do
|
195
|
+
@store.get_or_set('name', 'Gunter', expires_in: 60)
|
196
|
+
assert_equal('Gunter', @store.get('name'))
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
should 'returns first set value. Using block' do
|
202
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
203
|
+
@store.get_or_set('name', expires_in: 60) do
|
204
|
+
'Derrick'
|
205
|
+
end
|
206
|
+
Timecop.travel(Time.now + 59) do
|
207
|
+
@store.get_or_set('name', expires_in: 60) do
|
208
|
+
'Gunter'
|
209
|
+
end
|
210
|
+
assert_equal('Derrick', @store.get('name'))
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
should 'returns next set value. Using block' do
|
216
|
+
Timecop.freeze(Time.local(2010, 4, 5, 12, 0, 0)) do
|
217
|
+
@store.get_or_set('name', expires_in: 60) do
|
218
|
+
'Derrick'
|
219
|
+
end
|
220
|
+
Timecop.travel(Time.now + 60) do
|
221
|
+
@store.get_or_set('name', expires_in: 60) do
|
222
|
+
'Derrick'
|
223
|
+
end
|
224
|
+
assert_equal('Derrick', @store.get('name'))
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
111
228
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
@store.load(data)
|
120
|
-
|
121
|
-
all_data = { "title" => "Mr.",
|
122
|
-
"name" => "Derrick", "occupation" => "Developer" }
|
123
|
-
assert_equal all_data, @store.data
|
229
|
+
|
230
|
+
context '#unset' do
|
231
|
+
should 'remove the key-value pair' do
|
232
|
+
@store.set('name', 'Derrick')
|
233
|
+
@store.unset('name')
|
234
|
+
assert !@store.data.keys.include?('name')
|
235
|
+
end
|
124
236
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
237
|
+
|
238
|
+
context '#reset' do
|
239
|
+
should 'remove all data' do
|
240
|
+
@store.set('name', 'Derrick')
|
241
|
+
@store.reset
|
242
|
+
assert_equal({}, @store.data)
|
243
|
+
end
|
131
244
|
end
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
245
|
+
|
246
|
+
context '#load' do
|
247
|
+
should 'append the data to the cache' do
|
248
|
+
@store.set('title', 'Mr.')
|
249
|
+
|
250
|
+
data = { 'name' => 'Derrick', 'occupation' => 'Developer' }
|
251
|
+
@store.load(data)
|
252
|
+
|
253
|
+
all_data = { 'title' => MiniCache::Data.new('Mr.'),
|
254
|
+
'name' => MiniCache::Data.new('Derrick'),
|
255
|
+
'occupation' => MiniCache::Data.new('Developer') }
|
256
|
+
assert_equal all_data, @store.data
|
257
|
+
end
|
258
|
+
|
259
|
+
should 'stringify the keys' do
|
260
|
+
data = { name: 'Derrick' }
|
261
|
+
@store.load(data)
|
262
|
+
stringified_data = { 'name' => MiniCache::Data.new('Derrick') }
|
263
|
+
assert_equal stringified_data, @store.data
|
264
|
+
end
|
265
|
+
|
266
|
+
should 'raise a TypeError if an invalid key is encountered' do
|
267
|
+
data = { [1, 2] => 'Derrick' }
|
268
|
+
assert_raises(TypeError) { @store.load(data) }
|
269
|
+
end
|
136
270
|
end
|
137
271
|
end
|
138
|
-
end
|
272
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
4
|
+
|
2
5
|
require 'mini_cache'
|
3
6
|
require 'minitest/autorun'
|
4
|
-
require 'shoulda-context'
|
7
|
+
require 'shoulda-context'
|
8
|
+
require 'timecop'
|
metadata
CHANGED
@@ -1,29 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Derrick Reimer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: shoulda-context
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: timecop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.8'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.48'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.48'
|
27
83
|
description: A lightweight, in-memory cache for Ruby objects
|
28
84
|
email:
|
29
85
|
- derrickreimer@gmail.com
|
@@ -31,15 +87,20 @@ executables: []
|
|
31
87
|
extensions: []
|
32
88
|
extra_rdoc_files: []
|
33
89
|
files:
|
34
|
-
- .gitignore
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rubocop.yml"
|
92
|
+
- ".rubocop_todo.yml"
|
93
|
+
- ".travis.yml"
|
35
94
|
- Gemfile
|
36
95
|
- LICENSE
|
37
96
|
- README.md
|
38
97
|
- Rakefile
|
39
98
|
- lib/mini_cache.rb
|
99
|
+
- lib/mini_cache/data.rb
|
40
100
|
- lib/mini_cache/store.rb
|
41
101
|
- lib/mini_cache/version.rb
|
42
102
|
- mini_cache.gemspec
|
103
|
+
- test/mini_cache/data_test.rb
|
43
104
|
- test/mini_cache/store_test.rb
|
44
105
|
- test/test_helper.rb
|
45
106
|
homepage: https://github.com/djreimer/mini_cache
|
@@ -51,21 +112,21 @@ require_paths:
|
|
51
112
|
- lib
|
52
113
|
required_ruby_version: !ruby/object:Gem::Requirement
|
53
114
|
requirements:
|
54
|
-
- -
|
115
|
+
- - ">="
|
55
116
|
- !ruby/object:Gem::Version
|
56
117
|
version: '0'
|
57
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
119
|
requirements:
|
59
|
-
- -
|
120
|
+
- - ">="
|
60
121
|
- !ruby/object:Gem::Version
|
61
122
|
version: '0'
|
62
123
|
requirements: []
|
63
124
|
rubyforge_project:
|
64
|
-
rubygems_version: 2.
|
125
|
+
rubygems_version: 2.5.1
|
65
126
|
signing_key:
|
66
127
|
specification_version: 4
|
67
|
-
summary: MiniCache is a lightweight, in-memory key-value
|
128
|
+
summary: MiniCache is a lightweight, in-memory key-value storefor Ruby objects
|
68
129
|
test_files:
|
130
|
+
- test/mini_cache/data_test.rb
|
69
131
|
- test/mini_cache/store_test.rb
|
70
132
|
- test/test_helper.rb
|
71
|
-
has_rdoc:
|