object-cache 0.0.1
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 +7 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +10 -0
- data/.wercker.yml +7 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +162 -0
- data/Rakefile +16 -0
- data/lib/object/cache.rb +97 -0
- data/lib/object/cache/core_extension.rb +8 -0
- data/lib/object/cache/version.rb +5 -0
- data/object-cache.gemspec +27 -0
- data/test/cache_test.rb +130 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b329bb499b49a3b608f4ba5a756e09a246228c28
|
4
|
+
data.tar.gz: e5bf39f2fd0025bd30568d4fe87aaf29eb2e077b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e14b55bf9cf2fa92ca9d307740ad8d31df097616a8f0642e362b4b1398ac7eef4480646df252e3abfdf154de5f9b06610406426fdcffa61bc32bef2007114a71
|
7
|
+
data.tar.gz: 4ab4b813991e17132477fa97531636d8a04778eeb14ef221a85afbe0503061e85671e6b195f80bca42f6962cf1b60d297c7672a683bc28528273375721b73276
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.wercker.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at jean@blendle.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Blendle
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# Object::Cache [](https://app.wercker.com/project/bykey/8f5b16e230784fd4bd75f267d5d62e85)
|
2
|
+
|
3
|
+
Easy caching of Ruby objects, using [Redis](http://redis.io) as a backend store.
|
4
|
+
|
5
|
+
* [Installation](#installation)
|
6
|
+
* [Quick Start](#quick-start)
|
7
|
+
* [Usage](#usage)
|
8
|
+
* [marshaling data](#marshaling-data)
|
9
|
+
* [ttl](#ttl)
|
10
|
+
* [namespaced keys](#namespaced-keys)
|
11
|
+
* [redis replicas](#redis-replicas)
|
12
|
+
* [core extension](#core-extension)
|
13
|
+
* [License](#license)
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'object-cache'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
```shell
|
26
|
+
bundle
|
27
|
+
```
|
28
|
+
|
29
|
+
Or install it yourself as:
|
30
|
+
|
31
|
+
```shell
|
32
|
+
gem install object-cache
|
33
|
+
```
|
34
|
+
|
35
|
+
## Quick Start
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# require the proper libraries in your project
|
39
|
+
require 'redis'
|
40
|
+
require 'object/cache'
|
41
|
+
|
42
|
+
# set the backend to a new Redis instance
|
43
|
+
Cache.backend = Redis.new
|
44
|
+
|
45
|
+
# wrap your object in a `Cache.new` block to store the object on first usage,
|
46
|
+
# and retrieve it again on subsequent usages
|
47
|
+
Cache.new { 'hello world' }
|
48
|
+
|
49
|
+
# add the core extension for easier access
|
50
|
+
require 'object/cache/core_extension'
|
51
|
+
cache { 'hello world' }
|
52
|
+
```
|
53
|
+
|
54
|
+
## Usage
|
55
|
+
|
56
|
+
Using `Object::Cache`, you can cache objects in Ruby that have a heavy cost
|
57
|
+
attached to initializing them, and then replay the recorded object on any
|
58
|
+
subsequent requests.
|
59
|
+
|
60
|
+
For example, database query results can be cached, or HTTP requests to other
|
61
|
+
services within your infrastructure.
|
62
|
+
|
63
|
+
Caching an object is as easy as wrapping that object in a `Cache.new` block:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
Cache.new { 'hello world' }
|
67
|
+
```
|
68
|
+
|
69
|
+
Here, the object is of type `String`, but it can be any type of object that can
|
70
|
+
be marshalled using the Ruby [`Marshal`][marshal] library.
|
71
|
+
|
72
|
+
#### marshaling data
|
73
|
+
|
74
|
+
You can only marshal _data_, not _code_, so anything that produces code that is
|
75
|
+
executed later to return data (like Procs) cannot be cached. You can still wrap
|
76
|
+
those in a `Cache.new` block, and the block will return the Proc as expected,
|
77
|
+
but no caching will occur, so there's no point in doing so.
|
78
|
+
|
79
|
+
#### ttl
|
80
|
+
|
81
|
+
By default, a cached object has a `ttl` (time to live) of one week. This means
|
82
|
+
that every request after the first request uses the value from the cached
|
83
|
+
object. After one week, the cached value becomes stale, and the first request
|
84
|
+
after that will again store the (possibly changed) object in the cache store.
|
85
|
+
|
86
|
+
You can easily modify the `ttl` using the keyword argument by that same name:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
Cache.new(ttl: 60) { 'remember me for 60 seconds!' }
|
90
|
+
```
|
91
|
+
|
92
|
+
Or, if you want the cached object to never go stale, disable the TTL entirely:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
Cache.new(ttl: nil) { 'I am forever in your cache!' }
|
96
|
+
Cache.new(ttl: 0) { 'me too!' }
|
97
|
+
```
|
98
|
+
|
99
|
+
#### namespaced keys
|
100
|
+
|
101
|
+
When storing the key/value object into Redis, the key name is based on the file
|
102
|
+
name and line number where the cache was initiated. This allows you to cache
|
103
|
+
objects without specifying any namespacing yourself.
|
104
|
+
|
105
|
+
If however, you are storing an object that changes based on input, you need to
|
106
|
+
add a unique namespace to the cache, to make sure the correct object is returned
|
107
|
+
from cache:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
Cache.new(email) { User.find(email: email) }
|
111
|
+
```
|
112
|
+
|
113
|
+
In the above case, we use the customer's email to correctly namespace the
|
114
|
+
returned object in the cache store. The provided namespace argument is still
|
115
|
+
merged together with the file name and line number of the cache request, so you
|
116
|
+
can re-use that same `email` namespace in different locations, without worrying
|
117
|
+
about any naming collisions.
|
118
|
+
|
119
|
+
#### redis replicas
|
120
|
+
|
121
|
+
Before, we used the following setup to connect `Object::Cache` to a redis
|
122
|
+
backend:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
Cache.backend = Redis.new
|
126
|
+
```
|
127
|
+
|
128
|
+
The Ruby Redis library has primary/replicas support [buit-in using Redis
|
129
|
+
Sentinel][sentinel].
|
130
|
+
|
131
|
+
If however, you have your own setup, and want the writes and reads to be
|
132
|
+
separated between different Redis instances, you can pass in a hash to the
|
133
|
+
backend config, with a `primary` and `replicas` key:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
Cache.backend = { primary: Redis.new, replicas: [Redis.new, Redis.new] }
|
137
|
+
```
|
138
|
+
|
139
|
+
The above example obiously only works if the replicas receive the written data
|
140
|
+
from the primary instance.
|
141
|
+
|
142
|
+
#### core extension
|
143
|
+
|
144
|
+
Finally, if you want, you can extend `Object` with a `cache` method, for
|
145
|
+
convenient access to the cache object:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
require 'object/cache/core_extension'
|
149
|
+
|
150
|
+
# these are the same:
|
151
|
+
cache('hello', ttl: 60) { 'hello world' }
|
152
|
+
Cache.new('hello', ttl: 60) { 'hello world' }
|
153
|
+
```
|
154
|
+
|
155
|
+
That's it!
|
156
|
+
|
157
|
+
## License
|
158
|
+
|
159
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
160
|
+
|
161
|
+
[marshal]: http://ruby-doc.org/core-2.3.0/Marshal.html
|
162
|
+
[sentinel]: https://github.com/redis/redis-rb#sentinel-support
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rubocop/rake_task'
|
5
|
+
|
6
|
+
RuboCop::RakeTask.new do |t|
|
7
|
+
t.options = %w(--display-cop-names --extra-details --display-style-guide)
|
8
|
+
end
|
9
|
+
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << 'test'
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.test_files = FileList['test/**/*_test.rb']
|
14
|
+
end
|
15
|
+
|
16
|
+
task default: %i(test rubocop)
|
data/lib/object/cache.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest/sha1'
|
4
|
+
require 'object/cache/version'
|
5
|
+
|
6
|
+
# Caching of objects in a Redis store
|
7
|
+
class Cache
|
8
|
+
DEFAULT_TTL = (7 * 24 * 60 * 60) # 1 week
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :backend
|
12
|
+
|
13
|
+
# new
|
14
|
+
#
|
15
|
+
# Finds the correct value (based on the provided key) in the cache store, or
|
16
|
+
# calls the original code, and stores the result in cache.
|
17
|
+
#
|
18
|
+
# The TTL of the cached content is provided with the optional `ttl` named
|
19
|
+
# argument. If left blank, the `DEFAULT_TTL` ttl value will be used.
|
20
|
+
#
|
21
|
+
# The caching key will be determined by creating a SHA digest of the
|
22
|
+
# original code's file location and line number within that file. This makes
|
23
|
+
# it easier to provide short caching keys like uid's, or ids, and still
|
24
|
+
# receive a unique caching key under which the data is stored.
|
25
|
+
#
|
26
|
+
# The cache key can optionally be left blank. This should **only be done**
|
27
|
+
# if the provided data by the method will never changes based on some form
|
28
|
+
# of input.
|
29
|
+
#
|
30
|
+
# For example: caching an `Item` should _always_ be done by providing a
|
31
|
+
# unique item identifier as the caching key, otherwise the cache will return
|
32
|
+
# the same item every time, even if a different one is stored the second
|
33
|
+
# time.
|
34
|
+
#
|
35
|
+
# good:
|
36
|
+
#
|
37
|
+
# Cache.new { 'hello world' } # stored object is always the same
|
38
|
+
# Cache.new(item.id) { item } # stored item is namespaced using its id
|
39
|
+
#
|
40
|
+
# bad:
|
41
|
+
#
|
42
|
+
# Cache.new { item } # item is only stored once, and then always
|
43
|
+
# # retrieved, even if it is a different item
|
44
|
+
#
|
45
|
+
def new(key = nil, ttl: DEFAULT_TTL)
|
46
|
+
return yield unless replica
|
47
|
+
|
48
|
+
key = Digest::SHA1.hexdigest([key, Proc.new.source_location].flatten.join)[0..5]
|
49
|
+
|
50
|
+
if (cached_value = replica.get(key)).nil?
|
51
|
+
yield.tap { |value| update_cache(key, value, ttl: ttl) }
|
52
|
+
else
|
53
|
+
Marshal.load(cached_value)
|
54
|
+
end
|
55
|
+
rescue TypeError
|
56
|
+
# if `TypeError` is raised, the data could not be Marshal dumped. In that
|
57
|
+
# case, delete anything left in the cache store, and get the data without
|
58
|
+
# caching.
|
59
|
+
#
|
60
|
+
delete(key)
|
61
|
+
yield
|
62
|
+
rescue
|
63
|
+
yield
|
64
|
+
end
|
65
|
+
|
66
|
+
def include?(key)
|
67
|
+
replica.exists(key)
|
68
|
+
rescue
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
72
|
+
def delete(key)
|
73
|
+
return false unless include?(key)
|
74
|
+
|
75
|
+
primary.del(key)
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_cache(key, value, ttl: DEFAULT_TTL)
|
80
|
+
return unless primary && (value = Marshal.dump(value))
|
81
|
+
|
82
|
+
ttl.to_i.zero? ? primary.set(key, value) : primary.setex(key, ttl.to_i, value)
|
83
|
+
end
|
84
|
+
|
85
|
+
def primary
|
86
|
+
backend.is_a?(Hash) ? backend[:primary] : backend
|
87
|
+
end
|
88
|
+
|
89
|
+
def replicas
|
90
|
+
[backend.is_a?(Hash) ? backend[:replicas] : backend].flatten
|
91
|
+
end
|
92
|
+
|
93
|
+
def replica
|
94
|
+
replicas.sample
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'object/cache'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'object-cache'
|
9
|
+
spec.version = Cache::VERSION
|
10
|
+
spec.authors = %w(Jean Mertz)
|
11
|
+
spec.email = %w(jean@mertz.fm)
|
12
|
+
|
13
|
+
spec.summary = 'Caching of objects, using a Redis store.'
|
14
|
+
spec.description = 'Easily cache objects in Ruby, using a Redis store backend'
|
15
|
+
spec.homepage = 'https://github.com/blendle/object-cache'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.require_paths = %w(lib)
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.12'
|
21
|
+
spec.add_development_dependency 'm', '~> 1.5'
|
22
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
23
|
+
spec.add_development_dependency 'mock_redis', '~> 0.16'
|
24
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
spec.add_development_dependency 'rubocop', '~> 0.40'
|
27
|
+
end
|
data/test/cache_test.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
|
4
|
+
require 'mock_redis'
|
5
|
+
require 'object/cache'
|
6
|
+
require 'minitest/autorun'
|
7
|
+
|
8
|
+
# :no-doc:
|
9
|
+
class CacheTest < Minitest::Test
|
10
|
+
def setup
|
11
|
+
Cache.backend = MockRedis.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def redis
|
15
|
+
Cache.primary
|
16
|
+
end
|
17
|
+
|
18
|
+
def key_to_value(key)
|
19
|
+
Marshal.load(redis.get(key))
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_cache_returns_object
|
23
|
+
assert_equal 'hello world', Cache.new { 'hello world' }
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_cache_stores_object
|
27
|
+
Cache.new { 'hello world' }
|
28
|
+
assert redis.keys.one?
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_cache_stores_correct_value
|
32
|
+
assert_equal Cache.new { 'hello world' }, key_to_value(redis.keys.first)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_does_not_cache_non_marshalable_objects
|
36
|
+
Cache.new { -> { 'hello world' } }
|
37
|
+
assert redis.keys.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_return_original_value_for_non_marshalable_objects
|
41
|
+
assert_equal 'hello world', Cache.new { -> { 'hello world' } }.call
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_returns_cache_on_same_file_line_without_custom_key
|
45
|
+
Cache.new { 'hello world' } && Cache.new { 'hello universe' }
|
46
|
+
assert_equal 'hello world', key_to_value(redis.keys.first)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_store_multiple_objects_cached_in_same_file_but_different_lines
|
50
|
+
Cache.new { 'hello world' }
|
51
|
+
Cache.new { 'hello universe' }
|
52
|
+
|
53
|
+
assert_equal 'hello world', key_to_value(redis.keys.first)
|
54
|
+
assert_equal 'hello universe', key_to_value(redis.keys.last)
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_custom_cache_key_on_same_file_and_line
|
58
|
+
Cache.new('hello') { 'world' } && Cache.new('hi') { 'world' }
|
59
|
+
assert_equal 2, redis.keys.count
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_custom_cache_key_on_same_file_but_different_lines
|
63
|
+
Cache.new('hello') { 'world' }
|
64
|
+
Cache.new('hi') { 'world' }
|
65
|
+
assert_equal 2, redis.keys.count
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_cache_without_ttl
|
69
|
+
Cache.new(ttl: nil) { 'hello world' }
|
70
|
+
assert_equal(-1, redis.ttl(redis.keys.first))
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_cache_without_ttl_using_zero
|
74
|
+
Cache.new(ttl: 0) { 'hello world' }
|
75
|
+
assert_equal(-1, redis.ttl(redis.keys.first))
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_cache_default_ttl
|
79
|
+
Cache.new { 'hello world' }
|
80
|
+
assert_equal Cache::DEFAULT_TTL, redis.ttl(redis.keys.first)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_cache_with_ttl
|
84
|
+
Cache.new(ttl: 60) { 'hello world' }
|
85
|
+
assert_equal 60, redis.ttl(redis.keys.first)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_core_extension
|
89
|
+
load 'object/cache/core_extension.rb'
|
90
|
+
assert_equal 'hello world', cache { 'hello world' }
|
91
|
+
assert Object.send(:remove_method, :cache)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_core_extension_options
|
95
|
+
load 'object/cache/core_extension.rb'
|
96
|
+
cache(ttl: 60) { 'hello world' }
|
97
|
+
assert_equal 60, redis.ttl(redis.keys.first)
|
98
|
+
assert Object.send(:remove_method, :cache)
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_backend_with_replicas
|
102
|
+
Cache.backend = { primary: redis, replicas: [redis, redis] }
|
103
|
+
|
104
|
+
Cache.new { 'hello world' } && assert_equal('hello world', Cache.new { 'hello world' })
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_backend_with_primary_without_replicas
|
108
|
+
Cache.backend = { primary: MockRedis.new }
|
109
|
+
|
110
|
+
Cache.new { 'hello world' } && assert_equal('hello world', Cache.new { 'hello world' })
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_backend_with_primary_and_single_replica
|
114
|
+
redis = MockRedis.new
|
115
|
+
Cache.backend = { primary: redis, replicas: redis }
|
116
|
+
|
117
|
+
Cache.new { 'hello world' } && assert_equal('hello world', Cache.new { 'hello world' })
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_backend_with_replicas_not_having_primary_data
|
121
|
+
primary = MockRedis.new
|
122
|
+
replica = MockRedis.new
|
123
|
+
Cache.backend = { primary: primary, replicas: replica }
|
124
|
+
|
125
|
+
Cache.new { 'hello world' } && Cache.new { 'hello world' }
|
126
|
+
|
127
|
+
assert_equal 1, primary.keys.count
|
128
|
+
assert_equal 0, replica.keys.count
|
129
|
+
end
|
130
|
+
end
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: object-cache
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jean
|
8
|
+
- Mertz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-05-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.12'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.12'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: m
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '1.5'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.5'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: minitest
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '5.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '5.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: mock_redis
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.16'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.16'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: pry
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.10'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.10'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rake
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '10.0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '10.0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: rubocop
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0.40'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0.40'
|
112
|
+
description: Easily cache objects in Ruby, using a Redis store backend
|
113
|
+
email:
|
114
|
+
- jean@mertz.fm
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- ".rubocop.yml"
|
121
|
+
- ".wercker.yml"
|
122
|
+
- CODE_OF_CONDUCT.md
|
123
|
+
- Gemfile
|
124
|
+
- LICENSE.txt
|
125
|
+
- README.md
|
126
|
+
- Rakefile
|
127
|
+
- lib/object/cache.rb
|
128
|
+
- lib/object/cache/core_extension.rb
|
129
|
+
- lib/object/cache/version.rb
|
130
|
+
- object-cache.gemspec
|
131
|
+
- test/cache_test.rb
|
132
|
+
homepage: https://github.com/blendle/object-cache
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.6.4
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: Caching of objects, using a Redis store.
|
156
|
+
test_files: []
|
157
|
+
has_rdoc:
|