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.
@@ -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