object_cache 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/object_cache/cache_store.rb +52 -0
- data/lib/object_cache/class_cacher.rb +14 -0
- data/lib/object_cache/memory_store.rb +55 -0
- data/lib/object_cache/object_cache.rb +69 -0
- data/lib/{object_reader.rb → object_cache/object_reader.rb} +1 -0
- data/lib/object_cache/rails_store.rb +51 -0
- data/lib/object_cache/version.rb +2 -2
- data/lib/object_cache.rb +4 -63
- data/object_cache.gemspec +1 -0
- data/spec/class_cacher_spec.rb +58 -0
- data/spec/object_cache_spec.rb +1 -0
- data/spec/spec_helper.rb +1 -0
- metadata +25 -7
@@ -0,0 +1,52 @@
|
|
1
|
+
class ObjectCache
|
2
|
+
class CacheStore
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def fetch(klass, ids)
|
6
|
+
raise NotImplementedError
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(klass, id)
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def set(klass, id, value)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
def hash_get(key)
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
def hash_set(key, value)
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_from_array(klass, ids)
|
26
|
+
ObjectCache.constantize(klass).find(ids)
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_fresh_results(klass, to_set)
|
30
|
+
#puts to_set.inspect
|
31
|
+
return nil if to_set.size == 0
|
32
|
+
results = []
|
33
|
+
fresh_results = fetch_from_array(klass, to_set)
|
34
|
+
|
35
|
+
fresh_results.each do |item|
|
36
|
+
results << set(klass, item.id, item)
|
37
|
+
end
|
38
|
+
|
39
|
+
results
|
40
|
+
end
|
41
|
+
|
42
|
+
def array_or_value(results, ids)
|
43
|
+
if ids.class == Array
|
44
|
+
results
|
45
|
+
else
|
46
|
+
results.first
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "object_cache/cache_store"
|
2
|
+
|
3
|
+
class ObjectCache
|
4
|
+
class MemoryStore < ObjectCache::CacheStore
|
5
|
+
@@cache = {}
|
6
|
+
@@hash = {}
|
7
|
+
|
8
|
+
def self.fetch(klass, ids)
|
9
|
+
results = []
|
10
|
+
to_set = []
|
11
|
+
[*ids].each do |id|
|
12
|
+
if @@cache[klass] && @@cache[klass][id] && (Time.now - @@cache[klass][id].last) < ObjectCache.ttl
|
13
|
+
results << get(klass, id)
|
14
|
+
else
|
15
|
+
to_set << id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
fresh_results = fetch_fresh_results(klass, to_set)
|
20
|
+
results += fresh_results if fresh_results
|
21
|
+
results.compact!
|
22
|
+
|
23
|
+
# if we're asking for one object, we want it,
|
24
|
+
# if asking for array, we expect array of objects back
|
25
|
+
if ids.class == Array
|
26
|
+
results
|
27
|
+
else
|
28
|
+
results.first
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.get(klass, id)
|
33
|
+
@@cache[klass][id].first
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.set(klass, id, item)
|
37
|
+
@@cache[klass] ||= {}
|
38
|
+
@@cache[klass][id] = [item, Time.now]
|
39
|
+
@@cache[klass][id].first
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.hash_empty?
|
43
|
+
@@hash.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.hash_get(key)
|
47
|
+
@@hash[key]
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.hash_set(key, value)
|
51
|
+
@@hash[key] = value
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
|
2
|
+
class ObjectCache
|
3
|
+
@@ttl ||= 300
|
4
|
+
@@file_or_hash ||= nil
|
5
|
+
@@reader ||= nil
|
6
|
+
@@expires_at ||= nil
|
7
|
+
@@cache_store ||= ObjectCache::MemoryStore
|
8
|
+
|
9
|
+
|
10
|
+
def self.source(file_or_hash)
|
11
|
+
@@file_or_hash = file_or_hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.expire_after(ttl)
|
15
|
+
@@ttl = ttl
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.ttl
|
19
|
+
@@ttl
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.cache_store
|
23
|
+
@@cache_store
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.cache_store=(store)
|
27
|
+
@@cache_store = store
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.refresh_the_cache
|
31
|
+
@@reader ||= Reader.new(@@file_or_hash)
|
32
|
+
|
33
|
+
groups = @@reader.grouped_objects
|
34
|
+
groups.each do |klass, ids|
|
35
|
+
#we need both ids and items to be in the same order for this to work
|
36
|
+
ids.sort!{|a,b| a.last <=> b.last}
|
37
|
+
items = constantize(klass).find(ids.collect(&:last))
|
38
|
+
items.sort!{|a,b| a.id <=> b.id}
|
39
|
+
|
40
|
+
@i = 0
|
41
|
+
items.each do |item|
|
42
|
+
cache_store.hash_set(ids[@i].first.to_s, item)
|
43
|
+
@i+=1
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
@@expires_at = Time.now + (@@ttl || 300)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.constantize(camel_cased_word)
|
51
|
+
names = camel_cased_word.split('::')
|
52
|
+
names.shift if names.empty? || names.first.empty?
|
53
|
+
|
54
|
+
constant = Object
|
55
|
+
names.each do |name|
|
56
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
57
|
+
end
|
58
|
+
constant
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.method_missing(method_name)
|
62
|
+
if cache_store.hash_empty? || @@expires_at < Time.now
|
63
|
+
refresh_the_cache
|
64
|
+
end
|
65
|
+
|
66
|
+
cache_store.hash_get(method_name.to_s)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "object_cache/cache_store"
|
2
|
+
|
3
|
+
class ObjectCache
|
4
|
+
class RailsStore < ObjectCache::CacheStore
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def fetch(klass, ids)
|
8
|
+
results = []
|
9
|
+
ids_to_refresh = []
|
10
|
+
|
11
|
+
[*ids].each do |id|
|
12
|
+
if Rails.cache.exist?(cache_key(klass, id))
|
13
|
+
results << get(klass, id)
|
14
|
+
else
|
15
|
+
ids_to_refresh << id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
fresh_results = fetch_fresh_results(klass, ids_to_refresh)
|
20
|
+
results += fresh_results if fresh_results
|
21
|
+
|
22
|
+
array_or_value(results, ids)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(klass, id)
|
26
|
+
Rails.cache.read(cache_key(klass, id))
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(klass, id, value)
|
30
|
+
Rails.cache.write(cache_key(klass, id), value, expires_at: ObjectCache.ttl)
|
31
|
+
end
|
32
|
+
|
33
|
+
def cache_key(klass, id)
|
34
|
+
"object_cache:#{klass}:#{id}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def hash_get(key)
|
38
|
+
Rails.cache.read(hash_key(key))
|
39
|
+
end
|
40
|
+
|
41
|
+
def hash_set(key, value)
|
42
|
+
Rails.cache.write(hash_key(key), value, expires_at: ObjectCache.ttl)
|
43
|
+
end
|
44
|
+
|
45
|
+
def hash_key(key)
|
46
|
+
"object_cache:#{key}"
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/object_cache/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.0.
|
1
|
+
class ObjectCache
|
2
|
+
VERSION = "0.0.4"
|
3
3
|
end
|
data/lib/object_cache.rb
CHANGED
@@ -1,63 +1,4 @@
|
|
1
|
-
require "object_reader"
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
@@ttl ||= 60
|
6
|
-
@@file_or_hash ||= nil
|
7
|
-
@@reader ||= nil
|
8
|
-
@@expires_at ||= nil
|
9
|
-
|
10
|
-
|
11
|
-
def self.source(file_or_hash)
|
12
|
-
@@file_or_hash = file_or_hash
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.expire_after(ttl)
|
16
|
-
@@ttl = ttl
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.refresh_the_cache
|
20
|
-
@@reader ||= Reader.new(@@file_or_hash)
|
21
|
-
@@cache ||= {}
|
22
|
-
|
23
|
-
groups = @@reader.grouped_objects
|
24
|
-
groups.each do |klass, ids|
|
25
|
-
#we need both ids and items to be in the same order for this to work
|
26
|
-
ids.sort!{|a,b| a.last <=> b.last}
|
27
|
-
items = constantize(klass).find(ids.collect(&:last))
|
28
|
-
items.sort!{|a,b| a.id <=> b.id}
|
29
|
-
|
30
|
-
@i = 0
|
31
|
-
items.each do |item|
|
32
|
-
@@cache[ids[@i].first.to_s] = item
|
33
|
-
@i+=1
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
@@expires_at = Time.now + (@@ttl || 5.minutes)
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.constantize(camel_cased_word)
|
41
|
-
names = camel_cased_word.split('::')
|
42
|
-
names.shift if names.empty? || names.first.empty?
|
43
|
-
|
44
|
-
constant = Object
|
45
|
-
names.each do |name|
|
46
|
-
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
47
|
-
end
|
48
|
-
constant
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.hash
|
52
|
-
@@cache
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.method_missing(method_name)
|
56
|
-
if @@cache.empty? || @@expires_at < Time.now
|
57
|
-
refresh_the_cache
|
58
|
-
end
|
59
|
-
|
60
|
-
@@cache[method_name.to_s]
|
61
|
-
end
|
62
|
-
|
63
|
-
end
|
1
|
+
require "object_cache/object_reader"
|
2
|
+
require "object_cache/class_cacher"
|
3
|
+
require "object_cache/memory_store"
|
4
|
+
require "object_cache/object_cache"
|
data/object_cache.gemspec
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Foo
|
4
|
+
def self.find(ids)
|
5
|
+
return ids
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# we assume the cached objects have ids,
|
10
|
+
# and for testing we use integers, so we shamelessly fake it :)
|
11
|
+
class Integer
|
12
|
+
def id
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe ObjectCache::ClassCacher do
|
18
|
+
describe "calling of cache" do
|
19
|
+
|
20
|
+
it "should not perform a query after the object is cached" do
|
21
|
+
ObjectCache::ClassCacher.cache("Foo", 1)
|
22
|
+
Foo.should_not_receive(:find)
|
23
|
+
ObjectCache::ClassCacher.cache("Foo", 1).should == 1
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should perform a query after ttl has passed" do
|
27
|
+
ObjectCache::ClassCacher.cache("Foo", 1)
|
28
|
+
Foo.should_receive(:find).with([1]).and_return([1])
|
29
|
+
Timecop.freeze(Time.now+30000) do
|
30
|
+
ObjectCache::ClassCacher.cache("Foo", 1).should == 1
|
31
|
+
Foo.should_not_receive(:find)
|
32
|
+
ObjectCache::ClassCacher.cache("Foo", 1)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should allow caching of multiple objects" do
|
37
|
+
ObjectCache::ClassCacher.cache("Foo", [1,2,3]).should == [1,2,3]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should allow caching of multiple objects" do
|
41
|
+
ObjectCache::ClassCacher.cache("Foo", [1,2,3])
|
42
|
+
Foo.should_not_receive(:find)
|
43
|
+
|
44
|
+
ObjectCache::ClassCacher.cache("Foo", [1,3]).should == [1,3]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should call the find method only for uncached objects" do
|
48
|
+
ObjectCache::ClassCacher.cache("Foo", [1,2,3])
|
49
|
+
|
50
|
+
Foo.should_receive(:find).with([4,5]).and_return([4,5])
|
51
|
+
ObjectCache::ClassCacher.cache("Foo", [1,3,4,5]).should == [1,3,4,5]
|
52
|
+
|
53
|
+
Foo.should_not_receive(:find)
|
54
|
+
ObjectCache::ClassCacher.cache("Foo", [1,2,3,4,5])
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/spec/object_cache_spec.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: object_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-20 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &70116759086560 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70116759086560
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &70116759086040 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,7 +32,18 @@ dependencies:
|
|
32
32
|
version: 0.9.2
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70116759086040
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: timecop
|
38
|
+
requirement: &70116759085660 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70116759085660
|
36
47
|
description: simple object cache for ruby
|
37
48
|
email:
|
38
49
|
- tcaspy@gmail.com
|
@@ -48,9 +59,15 @@ files:
|
|
48
59
|
- README.rdoc
|
49
60
|
- Rakefile
|
50
61
|
- lib/object_cache.rb
|
62
|
+
- lib/object_cache/cache_store.rb
|
63
|
+
- lib/object_cache/class_cacher.rb
|
64
|
+
- lib/object_cache/memory_store.rb
|
65
|
+
- lib/object_cache/object_cache.rb
|
66
|
+
- lib/object_cache/object_reader.rb
|
67
|
+
- lib/object_cache/rails_store.rb
|
51
68
|
- lib/object_cache/version.rb
|
52
|
-
- lib/object_reader.rb
|
53
69
|
- object_cache.gemspec
|
70
|
+
- spec/class_cacher_spec.rb
|
54
71
|
- spec/fixtures/foo.yml
|
55
72
|
- spec/object_cache_spec.rb
|
56
73
|
- spec/spec_helper.rb
|
@@ -79,6 +96,7 @@ signing_key:
|
|
79
96
|
specification_version: 3
|
80
97
|
summary: hash table for object cache with defined TTL
|
81
98
|
test_files:
|
99
|
+
- spec/class_cacher_spec.rb
|
82
100
|
- spec/fixtures/foo.yml
|
83
101
|
- spec/object_cache_spec.rb
|
84
102
|
- spec/spec_helper.rb
|