Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .rvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if [[ -n "$rvm_environments_path" && -s "$rvm_environments_path/ruby-1.9.2-p136@mongo_store" ]] ; then
\. "$rvm_environments_path/ruby-1.9.2-p136@mongo_store"
else
rvm --create use "ruby-1.9.2-p136@mongo_store"
fi
4 changes: 4 additions & 0 deletions History.markdown
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
0.3.1 / 2011-03-17
==================
* Added in an optimised 'read_multi' function for Rails 3 - performs a single db call rather than one for each key

0.3.0 / 2010-08-30
==================
* We have a History file now
Expand Down
16 changes: 0 additions & 16 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,6 @@ rescue LoadError
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
end

require 'spec/rake/spectask'
Spec::Rake::SpecTask.new(:spec) do |spec|
spec.libs << 'lib' << 'spec'
spec.spec_files = FileList['spec/**/*_spec.rb']
end

Spec::Rake::SpecTask.new(:rcov) do |spec|
spec.libs << 'lib' << 'spec'
spec.pattern = 'spec/**/*_spec.rb'
spec.rcov = true
end

task :spec => :check_dependencies

task :default => :spec

require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
Expand Down
103 changes: 61 additions & 42 deletions lib/active_support/cache/mongo_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,52 +24,51 @@ def read(key, local_options=nil)
doc['value']
end
end

# Takes the specified value out of the collection.
def delete(key, local_options=nil)
super
opts = local_options ? options.merge(local_options) : options
collection.remove({'_id' => namespaced_key(key,opts)})
end


# Takes the value matching the pattern out of the collection.
def delete_matched(key, local_options=nil)
super
opts = local_options ? options.merge(local_options) : options
collection.remove({'_id' => key_matcher(key,opts)})
end


protected

# Lifted from Rails 3 ActiveSupport::Cache::Store
def namespaced_key(key, options)
namespace = options[:namespace] if options
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
key = "#{prefix}:#{key}" if prefix
key
end

# Lifted from Rails 3 ActiveSupport::Cache::Store
def key_matcher(pattern, options)
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
if prefix
source = pattern.source
if source.start_with?('^')
source = source[1, source.length]
protected

# Lifted from Rails 3 ActiveSupport::Cache::Store
def namespaced_key(key, options)
namespace = options[:namespace] if options
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
key = "#{prefix}:#{key}" if prefix
key
end

# Lifted from Rails 3 ActiveSupport::Cache::Store
def key_matcher(pattern, options)
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
if prefix
source = pattern.source
if source.start_with?('^')
source = source[1, source.length]
else
source = ".*#{source[0, source.length]}"
end
Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
else
source = ".*#{source[0, source.length]}"
pattern
end
Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
else
pattern
end
end

end

module Rails3

def write_entry(key, entry, options)
expires = Time.now + options[:expires_in]
value = entry.value
Expand All @@ -80,20 +79,39 @@ def write_entry(key, entry, options)
value = value.to_s and retry unless value.is_a? String
end
end

def read_entry(key, options=nil)
doc = collection.find_one('_id' => key, 'expires' => {'$gt' => Time.now})
ActiveSupport::Cache::Entry.new(doc['value']) if doc
end

def read_multi(*keys)
docs = {}
keys.each do |key|
docs[key] = nil
end

collection.find({ '_id' => {'$in' => keys}, 'expires' => {'$gt' => Time.now} }, :timeout => false ) do |cursor|
cursor.each do |doc|
docs[ doc['_id'] ] = doc['value']
end
end

return docs
end

def delete_entry(key, options=nil)
collection.remove({'_id' => key})
end

def delete_matched(pattern, options=nil)
options = merged_options(options)
instrument(:delete_matched, pattern.inspect) do
matcher = key_matcher(pattern, options) # Handles namespacing with regexes
delete_entry(matcher, options)
end
end

end

module Store
Expand Down Expand Up @@ -154,7 +172,7 @@ def initialize(collection = nil, options = nil)
def collection
@collection ||= make_collection
end

# Removes old cached values that have expired. Set this up to run occasionally in delayed_job, etc., if you
# start worrying about space. (In practice, because we favor updating over inserting, space is only wasted
# if the key itself never gets cached again. It also means you can _reduce_ efficiency by running this
Expand All @@ -169,25 +187,26 @@ def clear
end

private
def mongomapper?
Kernel.const_defined?(:MongoMapper) && MongoMapper.respond_to?(:database) && MongoMapper.database
end

def make_collection
db = case options[:db]
when Mongo::DB then options[:db]
when String then Mongo::DB.new(options[:db], Mongo::Connection.new)
else
if mongomapper?
MongoMapper.database

def mongomapper?
Kernel.const_defined?(:MongoMapper) && MongoMapper.respond_to?(:database) && MongoMapper.database
end

def make_collection
db = case options[:db]
when Mongo::DB then options[:db]
when String then Mongo::DB.new(options[:db], Mongo::Connection.new)
else
Mongo::DB.new(options[:db_name], Mongo::Connection.new)
if mongomapper?
MongoMapper.database
else
Mongo::DB.new(options[:db_name], Mongo::Connection.new)
end
end
coll = db.create_collection(options[:collection_name])
coll.create_index([['_id',Mongo::ASCENDING], ['expires',Mongo::DESCENDING]]) if options[:create_index]
coll
end
coll = db.create_collection(options[:collection_name])
coll.create_index([['_id',Mongo::ASCENDING], ['expires',Mongo::DESCENDING]]) if options[:create_index]
coll
end

end
end
Expand Down
2 changes: 1 addition & 1 deletion mongo_store.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Gem::Specification.new do |s|
s.name = %q{mongo_store}
s.version = "0.3.0"
s.version = "0.3.1"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Stephen Eley"]
Expand Down
34 changes: 20 additions & 14 deletions spec/active_support/cache/mongo_store_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ module Cache
describe "initializing" do
it "can take a Mongo::Collection object" do
db = Mongo::DB.new('mongo_store_test', Mongo::Connection.new)
coll = Mongo::Collection.new(db, 'foostore')
coll = Mongo::Collection.new('foostore', db)
store = ActiveSupport::Cache.lookup_store(:mongo_store, coll)
store.collection.should == coll
end

it "can take a collection name" do
store = ActiveSupport::Cache.lookup_store(:mongo_store, 'foo')
store.collection.name.should == 'foo'
Expand Down Expand Up @@ -50,7 +50,6 @@ module Cache
MongoMapper.expects(:database).at_least_once.returns(lazy)
store.collection.db.should == lazy
end


it "defaults the database name to 'rails_cache'" do
store = ActiveSupport::Cache.lookup_store(:mongo_store)
Expand Down Expand Up @@ -88,38 +87,44 @@ module Cache
store = ActiveSupport::Cache.lookup_store(:mongo_store, 'foo', :expires_in => 1.day)
store.expires_in.should == 1.day
end



after(:all) do
c = Mongo::Connection.new
%w(rails_cache mongo_store_test_name mongo_store_test_deebee mongo_store_test_mappy mongo_store_test_lazy).each do |db|
c.drop_database(db)
end
end
end

describe "caching" do
before(:all) do
@store = ActiveSupport::Cache.lookup_store(:mongo_store, 'mongo_store_test', :db => 'mongo_store_test')
end

it "can write values" do
@store.write('fnord', 'I am vaguely disturbed.')
@store.collection.find_one('_id' => 'fnord')['value'].should == "I am vaguely disturbed."
end

it "can read values" do
@store.collection.insert({'_id' => 'yoo', :value => 'yar', :expires => (Time.now + 10)})
@store.read('yoo').should == 'yar'
end


it "can read multiple values" do
@store.collection.insert({'_id' => 'multi1', :value => 'weee', :expires => (Time.now + 10)})
@store.collection.insert({'_id' => 'multi2', :value => 'wooo', :expires => (Time.now + 10)})
@store.read_multi('multi1','multi2').should == { 'multi1' => 'weee', 'multi2' => 'wooo' }
@store.read_multi('multi1','multi2','multi3').should == { 'multi1' => 'weee', 'multi2' => 'wooo', 'multi3' => nil }
end

it "can delete keys" do
@store.write('foo', 'bar')
@store.read('foo').should == 'bar'
@store.delete('foo')
@store.read('foo').should be_nil
end

it "can delete keys matching a regular expression" do
@store.write('foo', 'bar')
@store.write('fodder', 'bother')
Expand All @@ -135,7 +140,7 @@ module Cache
@store.read('fodder').should == 'bother'
@store.read('yoo').should be_nil
end

it "can expire a value with the :expires_in option" do
@store.write('ray', 'dar', :expires_in => 2.seconds)
@store.read('ray').should == 'dar'
Expand Down Expand Up @@ -164,14 +169,15 @@ module Cache
@store.clean_expired
@store.collection.count.should == 1
end

it "can clear the whole cache" do
@store.write('foo', 'bar')
@store.write('yoo', 'yar', :expires_in => 2.days)
@store.collection.count.should == 2
@store.clear
@store.collection.count.should == 0
end

describe "namespacing" do
before(:each) do
@store.options[:namespace] = 'ns1'
Expand Down Expand Up @@ -227,9 +233,9 @@ module Cache
@store.read('too', :namespace => 'ns2').should be_nil
@store.read('foz', :namespace => 'ns2').should == 'bat'
end

end

after(:each) do
@store.collection.remove # Clear our records
end
Expand Down