mongo_cache_store 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .rvmrc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mongo_cache_store.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kevin McGrath
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # MongoCacheStore
2
+
3
+ A MongoDB cache store for ActiveSupport 3
4
+
5
+ ## Description
6
+
7
+ MongoCacheStore uses pluggable backends to expose MongoDB as a cache store to ActiveSupport applications. Each backend allows the application to customize how the cache operates. Support is available for standard, capped and TTL collections.
8
+
9
+ **WARNING** This gem is in the early stages of development and should be treated as such. Checking the version of the gem will help with what could be many changes to the backends, options, etc...
10
+
11
+ While in beta, the major version will always be 0. The minor version will be increased for anything that breaks the current API. The patch version will be increased for all changes within a minor revision that add to or fix the current release without changing how the gem is configured or used.
12
+
13
+
14
+ ## Backends
15
+
16
+ A Backend controls how MongoDB collections are used and manipulated as a cache store.
17
+
18
+ MongoCacheStore ships with 4 backends, TTL, Capped, Standard and MultiTTL.
19
+
20
+ Planned: GridFS
21
+ * For very large entries. Ex: Generate a large report (PDF, DOC, etc...), keep it around for a limited amount of time and have it automatically flush upon expiration.
22
+
23
+
24
+ The major difference between each backend involves how entries are flushed. The core driver will always respect ActiveSupport's *:expires_in* parameter for hits and misses whether the entry has actually been flushed from the backend or not.
25
+
26
+ #### Core Options
27
+ From ActiveSupport
28
+ * :namespace
29
+ * :expires_in
30
+
31
+ For MongoCacheStore
32
+ * :db - A Mongo::DB instance
33
+ * :db_name - Name of database to be created. Only used if *db* is not set
34
+ * :connection - A Mongo::Connection. Only used if *db* is not set. Defaults to Mongo::Connection.new()
35
+ * serialize: (*:always*, :on\_fail, :never)
36
+ * :always (default) - Serialize all entries
37
+ * :on\_fail - Try to save the entry in a native MongoDB format. If that fails, then serialize the entry.
38
+ * :never - only save the entry if it can be saved natively by MongoDB.
39
+ * *NOTE:* Without serialization class structures and instances that cannot be converted to a native MongoDB type will not be stored. Also, without serialization MongoDB converts all symbols to strings. Therefore a hash with symbols as keys will have strings as keys when read.
40
+
41
+
42
+ ### TTL
43
+ Entries are kept in a namespaced TTL collection that will automatically flush any entries as they pass their expiration time. This keeps the size of the cache in check over time.
44
+
45
+ * *Requires MongoDB 2.0 or higher*
46
+
47
+ #### Options
48
+ No Extra options
49
+
50
+ ### Standard
51
+ Entreis are kept in a namespaced MongoDB collection. In a standard collection entries are only flushed from the collection with an explicit delete call or if auto_flush is enabled. If auto_flush is enabled the cache will flush all expired entries when auto\_flush\_threshold is reached. The threshold is based on a set number of cache instance writes.
52
+
53
+ #### Options
54
+ * auto_flush: (*true*|false)
55
+ * auto\_flush\_threshold: *10_000*
56
+
57
+
58
+ ### Capped (TODO)
59
+ This should only be used if limiting the size of the cache is the greatest concern. Entries are flushed from the cache on a FIFO basis, regardless of the entries expiration time. Delete operations set an entry to expired, but it will not be flushed until it is automatically removed by MongoDB.
60
+
61
+
62
+ ### MultiTTL
63
+ Entries are stored in multiple namespaced TTL collections. A namespaced TTL collection is created for each unique expiration time. For example all entries with an expiration time of 300 seconds will be kept in the same collection while entries with a 900 second expiration time will be kept in another. This requires the use of a *key index* collection that keeps track of which TTL collection a entry resides in.
64
+
65
+ #### Downsides
66
+ * Cache set operations require 2 MongoDB write calls. One for the key index, one for the TTL collection. (unless *use_index* is false, see below)
67
+ * Cache read operations will require 1 or 2 MongoDB calls depending on whether the 'expires_in' option is set for the read.
68
+
69
+ #### Benefits
70
+ * Ability to flush cache based on expire time (TODO)
71
+
72
+ #### Options
73
+ use\_index: (*true*, false)
74
+ * This should only be set to *false* if all fetch and/or read operations are passed the *:expires_in* option. If so, this will eliminate the need for the key index collection and only one write and one read operation is necessary.
75
+
76
+
77
+
78
+ ## Installation
79
+
80
+ Add this line to your application's Gemfile:
81
+
82
+ gem 'mongo_cache_store'
83
+
84
+ And then execute:
85
+
86
+ $ bundle
87
+
88
+ Or install it yourself as:
89
+
90
+ $ gem install mongo_cache_store
91
+
92
+ ## Usage
93
+
94
+
95
+
96
+ ## Contributing
97
+
98
+ 1. Fork it
99
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
100
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
101
+ 4. Push to the branch (`git push origin my-new-feature`)
102
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,41 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "mongo_cache_store/version"
3
+ require "mongo"
4
+ require "active_support/cache"
5
+
6
+ module ActiveSupport
7
+ module Cache
8
+
9
+ class MongoCacheStore < Store
10
+
11
+ def initialize (backend=:Standard, options = {})
12
+
13
+ options = {
14
+ :db_name => 'cache_store',
15
+ :db => nil,
16
+ :namespace => nil,
17
+ :connection => nil,
18
+ :serialize => :always
19
+ }.merge(options)
20
+
21
+ @db = options.delete :db
22
+
23
+ if (@db.nil?)
24
+ @db = Mongo::DB.new(options[:db_name], options[:connection] || Mongo::Connection.new)
25
+ end
26
+
27
+ extend ActiveSupport::Cache::MongoCacheStore::Backend.const_get(backend)
28
+
29
+ build_backend(options)
30
+
31
+ super(options)
32
+
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ require "active_support/cache/mongo_cache_store/backend/capped"
39
+ require "active_support/cache/mongo_cache_store/backend/standard"
40
+ require "active_support/cache/mongo_cache_store/backend/ttl"
41
+ require "active_support/cache/mongo_cache_store/backend/multi_ttl"
@@ -0,0 +1,111 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module ActiveSupport
4
+ module Cache
5
+ class MongoCacheStore
6
+ module Backend
7
+ # Base methods used by all MongoCacheStore backends
8
+ module Base
9
+
10
+ # No public methods are defined for this module
11
+
12
+ private
13
+
14
+ def expand_key(key)
15
+ return key
16
+ end
17
+
18
+ def namespaced_key(key, options)
19
+ return key
20
+ end
21
+
22
+ def read_entry(key,options)
23
+ col = get_collection(options)
24
+
25
+ safe_rescue do
26
+ query = {
27
+ :_id => key,
28
+ :expires_at => {
29
+ '$gt' => Time.now
30
+ }
31
+ }
32
+
33
+ response = col.find_one(query)
34
+ return nil if response.nil?
35
+
36
+ entry_options = {
37
+ :compressed => response[:compressed],
38
+ :expires_in => response[:expires_in]
39
+ }
40
+ if response['serialized']
41
+ r_value = response['value'].to_s
42
+ else
43
+ r_value = Marshal.dump(response['value'])
44
+ end
45
+ ActiveSupport::Cache::Entry.create(r_value,response[:created_at],entry_options)
46
+ end
47
+ end
48
+
49
+
50
+ def write_entry(key,entry,options)
51
+ col = get_collection(options)
52
+ serialize = options[:serialize] == :always ? true : false
53
+ try_cnt = 0
54
+
55
+ save_doc = {
56
+ :_id => key,
57
+ :created_at => Time.at(entry.created_at),
58
+ :expires_in => entry.expires_in,
59
+ :expires_at => entry.expires_in.nil? ? Time.utc(9999) : Time.at(entry.expires_at),
60
+ :compressed => entry.compressed?,
61
+ :serialized => serialize,
62
+ :value => serialize ? BSON::Binary.new(entry.raw_value) : entry.value
63
+ }.merge(options[:xentry] || {})
64
+ #puts save_doc.inspect
65
+
66
+ safe_rescue do
67
+ begin
68
+ col.save(save_doc)
69
+ rescue BSON::InvalidDocument => ex
70
+ if (options[:serialize] == :on_fail and try_cnt < 2)
71
+ save_doc[:serialized] = true
72
+ save_doc[:value] = BSON::Binary.new(entry.raw_value)
73
+ try_cnt += 1
74
+ retry
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ def delete_entry(key,options)
81
+ col = get_collection(options)
82
+ safe_rescue do
83
+ col.remove({'_id' => key})
84
+ end
85
+ end
86
+
87
+ def get_collection_name(options = {})
88
+ name_parts = ['cache']
89
+ name_parts.push(backend_name)
90
+ name_parts.push options[:namespace] unless options[:namespace].nil?
91
+ return name_parts.join('.')
92
+ end
93
+
94
+ def get_collection(options)
95
+ return options[:collection] if options[:collection].is_a? Mongo::Collection
96
+ end
97
+
98
+ def safe_rescue
99
+ begin
100
+ yield
101
+ rescue => e
102
+ warn e
103
+ logger.error("FileStoreError (#{e}): #{e.message}") if logger
104
+ false
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,44 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'active_support/cache/mongo_cache_store/backend/base'
4
+
5
+ module ActiveSupport
6
+ module Cache
7
+ class MongoCacheStore
8
+ module Backend
9
+ # MongoCacheStoreBackend for capped collections
10
+ #
11
+ # == Capped Collections
12
+ #
13
+ module Capped
14
+ include Base
15
+
16
+ def clear(options = {})
17
+ col = get_collection(options)
18
+ ret = safe_rescue do
19
+ col.update({},{:expires_at => Time.new})
20
+ end
21
+ ret ? true : false
22
+ end
23
+
24
+ private
25
+
26
+ def backend_name
27
+ "capped"
28
+ end
29
+
30
+ def get_collection(options)
31
+ @db[get_collection_name(options)]
32
+ end
33
+
34
+ def delete_entry(key,options)
35
+ col = get_collection(options)
36
+ safe_rescue do
37
+ col.update({:_id => key}, {:expires_at => Time.new})
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,136 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'active_support/cache/mongo_cache_store/backend/base'
4
+
5
+
6
+ module ActiveSupport
7
+ module Cache
8
+ class MongoCacheStore
9
+ module Backend
10
+ # MongoCacheStoreBackend for TTL collections
11
+ #
12
+ # == Time To Live (TTL) collections
13
+ #
14
+ module MultiTTL
15
+ include Base
16
+
17
+ alias :get_collection_prefix :get_collection_name
18
+
19
+ def clear(options = {})
20
+ @db.collection_names.each do |cname|
21
+ prefix = get_collection_prefix
22
+ if prefix.match(/^cache/) and cname.match(/^#{get_collection_prefix(options)}/)
23
+ safe_rescue do
24
+ @db[cname].drop
25
+ end
26
+ end
27
+ end
28
+ true
29
+ end
30
+
31
+ protected
32
+
33
+ def read_entry(key, options)
34
+ if options[:expires_in]
35
+ options[:collection] = get_collection(options)
36
+ else
37
+ options[:collection] = get_collection_from_index(key,options)
38
+ end
39
+
40
+ super(key, options)
41
+ end
42
+
43
+
44
+ def write_entry(key, entry, options)
45
+ options[:collection] = get_collection(options)
46
+
47
+ super(key, entry, options)
48
+
49
+ if (options[:use_index])
50
+ ki = get_key_index_collection(options)
51
+ indexed = safe_rescue do
52
+ ki.save({
53
+ :_id => key,
54
+ :collection => options[:collection].name,
55
+ :expires_at => entry.expires_at
56
+ })
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ def delete_entry(key, options)
63
+ options[:collection] = get_collection_from_index(key,options)
64
+ super(key, options)
65
+ end
66
+
67
+
68
+ private
69
+
70
+ def backend_name
71
+ "multi_ttl"
72
+ end
73
+
74
+ def get_collection(options)
75
+
76
+ col = super
77
+ return col unless col.nil?
78
+
79
+ name_parts = [get_collection_prefix(options)]
80
+ expires_in = options[:expires_in]
81
+ name_parts.push expires_in.nil? ? 'forever' : expires_in.to_i
82
+ collection_name = name_parts.join('.')
83
+
84
+ collection = @collection_map[collection_name]
85
+
86
+ collection ||= create_collection(collection_name, expires_in)
87
+ return collection
88
+ end
89
+
90
+
91
+ def get_key_index_collection(options)
92
+ name_parts = [get_collection_prefix(options)]
93
+ name_parts.push options[:namespace] unless options[:namespace].nil?
94
+ name_parts.push 'key_index'
95
+
96
+ col = @db[name_parts.join('.')]
97
+ col.ensure_index('expires_at',{ expireAfterSeconds: 0})
98
+ return col
99
+ end
100
+
101
+ def get_collection_from_index(key,options)
102
+ if (options[:use_index])
103
+ ki = get_key_index_collection(options)
104
+
105
+ options[:collection] = safe_rescue do
106
+ response = ki.find_one({
107
+ :_id => key,
108
+ })
109
+ return nil if response.nil?
110
+
111
+ return @db[response['collection']]
112
+ end
113
+ end
114
+ nil
115
+ end
116
+
117
+
118
+ def create_collection(name, expires_in)
119
+ collection = @db[name]
120
+ collection.ensure_index('created_at',{ expireAfterSeconds: expires_in.to_i }) unless expires_in.nil?
121
+ return collection
122
+ end
123
+
124
+ def build_backend(options)
125
+ options.replace({
126
+ :use_index => true
127
+ }.merge(options))
128
+
129
+ @collection_map = {}
130
+ end
131
+
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,65 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'active_support/cache/mongo_cache_store/backend/base'
4
+
5
+
6
+ module ActiveSupport
7
+ module Cache
8
+ class MongoCacheStore
9
+ module Backend
10
+ # MongoCacheStoreBackend for standard collections
11
+ #
12
+ # == Standard collections
13
+ #
14
+ module Standard
15
+ include Base
16
+
17
+ def clear(options = {})
18
+ col = get_collection(options)
19
+ ret = safe_rescue do
20
+ col.remove
21
+ end
22
+ ret ? true : false
23
+ end
24
+
25
+ private
26
+
27
+ def backend_name
28
+ "standard"
29
+ end
30
+
31
+ def get_collection(options)
32
+ @db[get_collection_name(options)]
33
+ end
34
+
35
+ def write_entry(key, entry, options)
36
+ ret = super
37
+ @write_cnt += 1
38
+ if options[:auto_flush] and @write_cnt > options[:auto_flush_threshold]
39
+ safe_rescue do
40
+ col = get_collection(options)
41
+ col.delete({
42
+ :expires_at => {
43
+ '$gt' => Time.now
44
+ }
45
+ })
46
+ end
47
+ @write_cnt = 0
48
+ end
49
+ return ret
50
+ end
51
+
52
+ def build_backend(options)
53
+ options.replace({
54
+ :auto_flush => true,
55
+ :auto_flush_threshold => 10_000,
56
+ :collection => nil
57
+ }.merge(options))
58
+
59
+ @write_cnt = 0
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,65 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'active_support/cache/mongo_cache_store/backend/base'
4
+
5
+ module ActiveSupport
6
+ module Cache
7
+ class MongoCacheStore
8
+ module Backend
9
+ # MongoCacheStoreBackend for OneTTL collections
10
+ #
11
+ # == OneTTL collections
12
+ #
13
+ module TTL
14
+ include Base
15
+
16
+ def clear(options = {})
17
+ col = get_collection(options)
18
+ ret = safe_rescue do
19
+ col.remove
20
+ end
21
+ ret ? true : false
22
+ end
23
+
24
+ protected
25
+
26
+ def write_entry(key,entry,options)
27
+
28
+ # Set all time based entries here.
29
+ # This ensures all fields are based on the same Time
30
+ now = Time.now
31
+ options[:xentry] = {
32
+ :created_at => now,
33
+ :expires_at => entry.expires_in.nil? ? Time.utc(9999) : now + entry.expires_in.to_i
34
+ }
35
+
36
+ super(key,entry,options)
37
+ end
38
+
39
+
40
+ private
41
+
42
+ def backend_name
43
+ "ttl"
44
+ end
45
+
46
+ def get_collection(options)
47
+ return @collection if @collection.is_a? Mongo::Collection
48
+ collection = super
49
+ return collection unless collection.nil?
50
+
51
+ collection = @db[get_collection_name(options)]
52
+ collection.ensure_index('expires_at',{ expireAfterSeconds: 0 })
53
+ @collection = collection
54
+ end
55
+
56
+ def build_backend(options)
57
+
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module MongoCacheStore
3
+ VERSION = "0.2.0"
4
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mongo_cache_store/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "mongo_cache_store"
8
+ gem.version = MongoCacheStore::VERSION
9
+ gem.authors = ["Kevin McGrath"]
10
+ gem.email = ["kmcgrath@baknet.com"]
11
+ gem.description = %q{A MongoDB ActiveSupport Cache}
12
+ gem.summary = %q{A MongoDB ActiveSupport Cache}
13
+ gem.homepage = "https://github.com/kmcgrath/mongo_cache_store"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'mongo'
21
+ gem.add_dependency 'activesupport'
22
+ gem.add_development_dependency 'rspec'
23
+ end
@@ -0,0 +1,160 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+
5
+ class MongoCacheStoreTestSaveClass
6
+
7
+ attr_reader :set_me
8
+ def initialize(set_me)
9
+ @set_me = set_me
10
+ end
11
+
12
+ end
13
+
14
+
15
+ module ActiveSupport
16
+ module Cache
17
+ class MongoCacheStoreTest
18
+ shared_examples "a cache store" do
19
+ describe "Caching" do
20
+
21
+ it "can write values" do
22
+ @store.write('forever!', 'I live forever!')
23
+ @store.fetch('forever!').should == "I live forever!"
24
+ end
25
+
26
+ it "can expire" do
27
+ @store.fetch 'testkey', expires_in: 2.minutes do
28
+ "I will expire."
29
+ end
30
+ @store.exist?("testkey").should == true
31
+
32
+ sleep 60
33
+
34
+ response = @store.fetch 'testkey'
35
+ response.should == "I will expire."
36
+
37
+ sleep 120
38
+
39
+ response = @store.fetch 'testkey'
40
+ response.should == nil
41
+ end
42
+
43
+ it "can cache a hash" do
44
+ @store.write('hashme',
45
+ {"1"=>{:name=>"Uptime", :zenoss_instance=>"cust", :path=>"/zport/RenderServer/render?gopts=k4VJvRM2K1Dahl3RzJWcqoicTSIz0QJ1Ja10idqC412ptG5_lrmcroRy3BDIqioB561tB4UjtjSg1ib_YaGOXBWOEHBhPbEmA6ZXXjd8LiIOmItqTDZjuFkZvOV38_CF_YEi7t9xUtFKOIxibyVo67jzbyK6P-ORc7i07rK5GUYeA2CnJxm4UDCNKcKRpDCzmdzR-NwcOp0w55KH8YeU6NpgHyM_tpu8G42d7nBQebs1DKvn20sktezqy_LiIizKk3eMzDW-c8w1EZTESWa4rIihXfHrrv9PNxvCw41wU9f3dxQt2c3OFfhSx6riSUbeVsUNEo6s_35KdKJkbr6rEZaaxL2RGR47eLBzifIcZAg="}, "2"=>{:name=>"CPU Utilization", :zenoss_instance=>"cust", :path=>"/zport/RenderServer/render?gopts=WN-Oc9wB_CFbVKzzN75ij-qQcZ6lSOC3jj_ALTzkdMEZ_EzgcvfWOdU7vmlrm9MFY6zrOUeW2Z_uzyJkhRrl_81bVs96xoa7y2S29NVU_oSIIxQj1rUElV04u3wGRXnEbfyYPjLp0m6QEDOthqCs3SLcc7jKkCI15V0Js1b6FpFleWdmO9dCMLvKqdBiE-yhSjfwPS7Rh0wPLAoxXTyIkFFHHBXIu_g69_xnESZhYz0rAWn0W0Ag5N46LuNqwbzW1PwIfczY1Hqw2iKQAq1C4fMry3DNiQvnRmUZdF_w11quhIo-XccH1em3iopVdyy6ik83vMvJN3DfhLUbXcZjnA=="}, "3"=>{:name=>"Memory Utilization", :instance=>"cust", :path=>"/zport/RenderServer/render?gopts=_E6b8KAZjy9xzEYtckX_RDQOsGfIRZnG5WRu7ekbUIlsgWmNZNJ8AgEfr7AM0rV6lZMC4PzCPacRN1zeVQ-nHgTBBDbq4sIQuUMl3ETW9DRM2wgjrmnCRwcl7Dz_qGL6PWkhsJzhQ231SuNi7pM6mDw8dbE_pmZ3F6xKa7_8frJLWoMGLji8sIklFf45YckRkIohkMsffZKMsIspx8JEOvR9mP24vGs0Wv4gRXxqFoE1FlgchuKf2KDvWbwp92RrdOmhOAqMxHWmNA2faAC75072ao9bf6UnxZUu5WFUcNsvJJFcEl2U30TEX5vyjf_9C-1Rvnyf8Q6ajvRKA1VNIl6fpItv8dsKTNViX27RWsI="}, "4"=>{:name=>"Swap", :instance=>"cust", :path=>"/zport/RenderServer/render?gopts=nBpbINZ6hhzV8IUJq49zWOTsQ4hX5F5ZGPyoHZ6KWPUx_4Xk0_URWbZPQO4yicu14u81lDFqfRCoFTwofkA0NJIqoOQfAx2gWrwbE7RtulqYeMol47KWQpSITLSV4Mkh1Sbp9VZqpc4YVjiu1EvWSOIHCeUPXZPSBWFh0R2VoPB_VutBricdimwkvRsHbU9-BlKyJcT-_vEnTMi2MHlB3iKWY329OkN-J2chI3MtoLvSw9sN2LJbrO4ynFQPX4WFY9D18Zhv1bzOapn0XcCsxj88XHZ2eZeEZ0uv33_9EKSVxKtOOYo3Y08mjEKduOme"}, "5"=>{:name=>"Load Average", :instance=>"cust", :path=>"/zport/RenderServer/render?gopts=0y-dExjiyMFT0yWeOf-FR3hBIgIVZZortlKSFUETQYjwMuSDfhB-JhsEYI-feAC0Bmh5T8fZQRuYi5MkD3C2S6Qwd_kzvPrjwxCfxjTaQzidN-DxlWjqTkTfZ-AiPQ3F8JVXr-8kP8ZA10XDcV2VpIHJwZatzxDBVk7isEdKAXEJm-1X_TY7BCdU9uFlcBT1XGEYESM5anyR3282Z77yiYX6PsUcoPwFtLzg1l6Ql8cda8n4d2zLK4ZBX5G25ZQoC0MAmHZPyXRIsI-GGZjKcvXHjZ350tEpyvPiSX8a7PG-dRtWIGEEecM9czU3MF5Xzl-qOVfoRWc8pmlc-5E37Z5TcAoZpUHSlssJdjwoLfOlhFkLGyAVZpVXRCVRWdXNxilLS9N4GbuIEqhsEILyM0MzPvEHwPO3rcPSlzcS4s_SXnjp6XLg2bB6NZjAvbz5Vyb6OPg-8b9C-MmzcpWzVHzP5nYo9RVPR_lpFcmOBUSO_cJO22pIqqMyn1vXbtVz"}, "6"=>{:name=>"Paging", :instance=>"cust", :path=>"/zport/RenderServer/render?gopts=N7sjMwpMD3JE0ZNya07IgWj6t7yn7uX4GJ1XHlmyhRcqcea0HpfXTen6ptASVAZIvWT7fW4kufkvtNBsxKfDMX9JMzqoR-kpfWqoRRfI3eGmqYecg6gcQgXSEnu5Mzf4r-lbo1_Cqp8dYuSmC37uWrKi0gTWVr54ZmQ6w4dSymQ4xSkCH2-MO1X8wxZdtm804NJy6yQ4l7JZYBkWD9rABoPhVfZq4mZpHmZN27UlbK14Z9R3EEF6MG0jKCbJ7gJVEjpOoX_y5ka4S3aRULFSByrZIA290_ZMO21DAnsJbJyMXAEP9r8UanaotdVrPLSOXJkYD4WvdcdDeZ3wMtvdL42oHdhwLDyG9J8i9GkcvwsU04MlTTl270LkX31HJjxjB8IChpDke-9LEmxG_1icmGcAcWr26gTk9HOULwTyso4sX_ZqqCnrrnRRQLCpCZtlqxIB9iAMUtSdkEUdZlsAuw=="}},
46
+ expires_in: 1.hour
47
+ )
48
+
49
+ hash = @store.read('hashme')
50
+ hash["1"].should be_a_kind_of(Hash)
51
+
52
+ end
53
+
54
+ it "can cache class instances" do
55
+ @store.fetch 'my_class', expires_in: 30.seconds do
56
+ MongoCacheStoreTestSaveClass.new('what did i say?')
57
+ end
58
+
59
+ my_class = @store.fetch 'my_class'
60
+ my_class.set_me.should == 'what did i say?'
61
+ end
62
+
63
+ it "can use a hash as a key" do
64
+ hash_key = {
65
+ :class_name => 'my_class',
66
+ :option2 => 2
67
+ }
68
+
69
+ miss_key = {
70
+ :class_name => 'my_class',
71
+ :option2 => 1
72
+ }
73
+
74
+ hit_key = {
75
+ :class_name => 'my_class',
76
+ :option2 => 2
77
+ }
78
+
79
+
80
+ @store.fetch(hash_key, expires_in: 30.seconds) do
81
+ MongoCacheStoreTestSaveClass.new('what did i say?')
82
+ end
83
+
84
+ my_class = @store.fetch(hash_key)
85
+ my_class.set_me.should == 'what did i say?'
86
+
87
+ my_class = @store.fetch(hit_key)
88
+ my_class.set_me.should == 'what did i say?'
89
+
90
+ my_class = @store.fetch(miss_key)
91
+ my_class.should == nil
92
+
93
+ end
94
+
95
+ after(:all) do
96
+ @store.clear
97
+ end
98
+ end
99
+ end
100
+
101
+
102
+ describe MongoCacheStore do
103
+ describe "initializing" do
104
+ it "can take a Mongo::DB object" do
105
+ db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
106
+ store = ActiveSupport::Cache::MongoCacheStore.new(:TTL, :db => db)
107
+ end
108
+ end
109
+
110
+ describe "TTL Caching" do
111
+ it_behaves_like "a cache store"
112
+ before(:all) do
113
+ db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
114
+ @store = ActiveSupport::Cache::MongoCacheStore.new(:TTL, :db => db)
115
+ end
116
+ end
117
+
118
+ describe "TTL Caching On Fail Serialization" do
119
+ it_behaves_like "a cache store"
120
+ before(:all) do
121
+ db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
122
+ @store = ActiveSupport::Cache::MongoCacheStore.new(:TTL, :db => db, :serialize => :on_fail)
123
+ end
124
+ end
125
+
126
+ describe "MultiTTL Caching" do
127
+ it_behaves_like "a cache store"
128
+ before(:all) do
129
+ db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
130
+ @store = ActiveSupport::Cache::MongoCacheStore.new(:MultiTTL, :db => db)
131
+ end
132
+ end
133
+
134
+ describe "MultiTTL Caching On Fail Serialization" do
135
+ it_behaves_like "a cache store"
136
+ before(:all) do
137
+ db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
138
+ @store = ActiveSupport::Cache::MongoCacheStore.new(:MultiTTL, :db => db, :serialize => :on_fail)
139
+ end
140
+ end
141
+
142
+ describe "Standard Caching" do
143
+ it_behaves_like "a cache store"
144
+ before(:all) do
145
+ db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
146
+ @store = ActiveSupport::Cache::MongoCacheStore.new(:Standard, :db => db)
147
+ end
148
+ end
149
+
150
+ describe "Standard Caching On Fail Serialization" do
151
+ it_behaves_like "a cache store"
152
+ before(:all) do
153
+ db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
154
+ @store = ActiveSupport::Cache::MongoCacheStore.new(:Standard, :db => db, :serialize => :on_fail)
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,6 @@
1
+ # -*- encoding : utf-8 -*-
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ require 'mongo'
5
+ require 'active_support/cache/mongo_cache_store'
6
+ require 'rspec'
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongo_cache_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kevin McGrath
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongo
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activesupport
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A MongoDB ActiveSupport Cache
63
+ email:
64
+ - kmcgrath@baknet.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE.txt
72
+ - README.md
73
+ - Rakefile
74
+ - lib/active_support/cache/mongo_cache_store.rb
75
+ - lib/active_support/cache/mongo_cache_store/backend/base.rb
76
+ - lib/active_support/cache/mongo_cache_store/backend/capped.rb
77
+ - lib/active_support/cache/mongo_cache_store/backend/multi_ttl.rb
78
+ - lib/active_support/cache/mongo_cache_store/backend/standard.rb
79
+ - lib/active_support/cache/mongo_cache_store/backend/ttl.rb
80
+ - lib/mongo_cache_store/version.rb
81
+ - mongo_cache_store.gemspec
82
+ - spec/active_support/cache/mongo_cache_store_spec.rb
83
+ - spec/spec_helper.rb
84
+ homepage: https://github.com/kmcgrath/mongo_cache_store
85
+ licenses: []
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.24
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: A MongoDB ActiveSupport Cache
108
+ test_files:
109
+ - spec/active_support/cache/mongo_cache_store_spec.rb
110
+ - spec/spec_helper.rb