object_cache 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|