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.
@@ -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,14 @@
1
+ class ObjectCache
2
+ class ClassCacher
3
+ @@cache = {}
4
+
5
+ def self.cache(klass, id)
6
+ ObjectCache.cache_store.fetch(klass, id)
7
+ end
8
+
9
+ def self.get_cache
10
+ @@cache
11
+ end
12
+
13
+ end
14
+ 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
@@ -1,4 +1,5 @@
1
1
  require 'yaml'
2
+ require 'object_cache'
2
3
 
3
4
  class ObjectCache
4
5
  class Reader
@@ -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
@@ -1,3 +1,3 @@
1
- module ObjectCache
2
- VERSION = "0.0.3"
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
- class ObjectCache
4
- @@cache ||= {}
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
@@ -17,4 +17,5 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.add_development_dependency(%q<rspec>, [">= 0"])
19
19
  gem.add_development_dependency(%q<rake>, ["~> 0.9.2"])
20
+ gem.add_development_dependency(%q<timecop>)
20
21
  end
@@ -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
@@ -47,6 +47,7 @@ describe ObjectCache do
47
47
 
48
48
  ObjectCache.foo.should == u1
49
49
  end
50
+
50
51
  end
51
52
 
52
53
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "object_cache"
2
+ require "timecop"
2
3
  RSpec.configure do |config|
3
4
  # config.mock_with :mocha
4
5
  # config.mock_with :flexmock
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.3
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-17 00:00:00.000000000Z
12
+ date: 2012-07-20 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70189857627440 !ruby/object:Gem::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: *70189857627440
24
+ version_requirements: *70116759086560
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70189857679980 !ruby/object:Gem::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: *70189857679980
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