mini_cache 1.0.1 → 1.1.0
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.
- 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
|
+
[](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:
|