bin 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ log
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'bson_ext', :require => false
4
+
5
+ gemspec
6
+
7
+ gem 'i18n'
8
+ gem 'rake'
9
+ gem 'rspec'
10
+ gem 'log_buddy'
11
+ gem 'SystemTimer'
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bin (0.6.3)
5
+ activesupport (~> 3.1.0)
6
+ mongo (~> 1.3.0)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ SystemTimer (1.2.1)
12
+ activesupport (3.1.0)
13
+ multi_json (~> 1.0)
14
+ bson (1.3.1)
15
+ bson_ext (1.3.1)
16
+ diff-lcs (1.1.3)
17
+ i18n (0.6.0)
18
+ log_buddy (0.6.0)
19
+ mongo (1.3.1)
20
+ bson (>= 1.3.1)
21
+ multi_json (1.0.3)
22
+ rake (0.9.2)
23
+ rspec (2.6.0)
24
+ rspec-core (~> 2.6.0)
25
+ rspec-expectations (~> 2.6.0)
26
+ rspec-mocks (~> 2.6.0)
27
+ rspec-core (2.6.4)
28
+ rspec-expectations (2.6.0)
29
+ diff-lcs (~> 1.1.2)
30
+ rspec-mocks (2.6.0)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ SystemTimer
37
+ bin!
38
+ bson_ext
39
+ i18n
40
+ log_buddy
41
+ rake
42
+ rspec
@@ -4,8 +4,29 @@ ActiveSupport MongoDB Cache store.
4
4
 
5
5
  == Supports
6
6
 
7
- * Ruby 1.8.7 and Ruby 1.9.1.
8
- * ActiveSupport 2 and 3
7
+ * Ruby 1.8.7 and 1.9.2+
8
+ * ActiveSupport 3+
9
+
10
+ == Installation and Usage
11
+
12
+ gem install bin
13
+
14
+ require 'bin'
15
+
16
+ collection = Mongo::Connection.new.db('testing')['testing']
17
+ collection.remove
18
+
19
+ bin = Bin::Store.new(collection)
20
+ bin.write('abc', 123)
21
+ pp bin.read('abc')
22
+
23
+ pp bin.read('def')
24
+ pp bin.fetch('def') { 456 }
25
+ pp bin.read('def')
26
+
27
+ Using bin with rails is as easy as:
28
+
29
+ config.cache_store = :bin, Mongo::Connection.new.db('myapp')['cache']
9
30
 
10
31
  == Note on Patches/Pull Requests
11
32
 
data/Rakefile CHANGED
@@ -1,45 +1,7 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'spec/rake/spectask'
4
- require File.expand_path('../lib/bin/version', __FILE__)
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
5
3
 
6
- namespace :spec do
7
- Spec::Rake::SpecTask.new(:all) do |t|
8
- t.ruby_opts << '-rubygems'
9
- t.verbose = true
10
- end
11
-
12
- task :as2 do
13
- sh 'ACTIVE_SUPPORT_VERSION="<= 2.3.8" rake spec:all'
14
- end
15
-
16
- task :as3 do
17
- sh 'ACTIVE_SUPPORT_VERSION=">= 3.0.0.beta3" rake spec:all'
18
- end
19
- end
20
-
21
- desc 'Runs all specs against Active Support 2 and 3'
22
- task :spec do
23
- Rake::Task['spec:as2'].invoke
24
- Rake::Task['spec:as3'].invoke
25
- end
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new
26
6
 
27
7
  task :default => :spec
28
-
29
- desc 'Builds the gem'
30
- task :build do
31
- sh "gem build bin.gemspec"
32
- end
33
-
34
- desc 'Builds and installs the gem'
35
- task :install => :build do
36
- sh "gem install bin-#{Bin::Version}"
37
- end
38
-
39
- desc 'Tags version, pushes to remote, and pushes gem'
40
- task :release => :build do
41
- sh "git tag v#{Bin::Version}"
42
- sh "git push origin master"
43
- sh "git push origin v#{Bin::Version}"
44
- sh "gem push bin-#{Bin::Version}.gem"
45
- end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path('../lib/bin/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'bin'
6
+ s.version = Bin::Version
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['John Nunemaker']
9
+ s.email = ['nunemaker@gmail.com']
10
+ s.homepage = 'http://github.com/jnunemaker/bin'
11
+ s.summary = 'ActiveSupport MongoDB Cache store.'
12
+ s.description = 'ActiveSupport MongoDB Cache store.'
13
+
14
+ s.add_dependency 'mongo', '~> 1.3.0'
15
+ s.add_dependency 'activesupport', '~> 3.1.0'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
@@ -0,0 +1,18 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
2
+ require 'active_support/all'
3
+ require 'bin'
4
+ require 'pp'
5
+
6
+ collection = Mongo::Connection.new.db('testing')['testing']
7
+ collection.remove
8
+
9
+ bin = Bin::Store.new(collection)
10
+ bin.write('abc', 123)
11
+ pp bin.read('abc')
12
+
13
+ pp bin.read('def')
14
+ pp bin.fetch('def') { 456 }
15
+ pp bin.read('def')
16
+
17
+ bin.delete('abc')
18
+ bin.delete('def')
@@ -0,0 +1,82 @@
1
+ require 'mongo'
2
+
3
+ module ActiveSupport
4
+ module Cache
5
+ class Bin < Store
6
+ attr_reader :collection, :options
7
+
8
+ def initialize(collection, options={})
9
+ @collection, @options = collection, options
10
+ end
11
+
12
+ def expires_in
13
+ @expires_in ||= options[:expires_in] || 1.year
14
+ end
15
+
16
+ def delete_matched(matcher, options=nil)
17
+ collection.remove(:_id => matcher)
18
+ end
19
+
20
+ def increment(key, amount=1, options=nil)
21
+ counter_key_upsert(:increment, key, amount, options)
22
+ end
23
+
24
+ def decrement(key, amount=1, options=nil)
25
+ counter_key_upsert(:decrement, key, -amount.abs, options)
26
+ end
27
+
28
+ def clear
29
+ collection.remove
30
+ end
31
+
32
+ def stats
33
+ collection.stats
34
+ end
35
+
36
+ private
37
+ def counter_key_upsert(action, key, amount, options)
38
+ options = merged_options(options)
39
+ instrument(action, key, :amount => amount) do
40
+ key = namespaced_key(key, options)
41
+ collection.update(
42
+ {:_id => key}, {
43
+ '$inc' => {:value => amount},
44
+ '$set' => {
45
+ :expires_at => Time.now.utc + 1.year,
46
+ :raw => true
47
+ },
48
+ }, :upsert => true)
49
+ end
50
+ end
51
+
52
+ def deserialize_doc(doc)
53
+ return nil if doc.nil?
54
+
55
+ entry = doc['raw'] ? doc['value'] : Marshal.load(doc['value'].to_s)
56
+ entry.is_a?(Entry) ? entry : Entry.new(entry)
57
+ end
58
+
59
+ def read_entry(key, options)
60
+ query = {:_id => key, :expires_at => {'$gt' => Time.now.utc}}
61
+ deserialize_doc(collection.find_one(query))
62
+ end
63
+
64
+ def write_entry(key, entry, options)
65
+ expires = Time.now.utc + options.fetch(:expires_in, expires_in)
66
+ value = options[:raw] ? entry.value : BSON::Binary.new(Marshal.dump(entry.value))
67
+ query = {:_id => key}
68
+ updates = {'$set' => {
69
+ :value => value,
70
+ :expires_at => expires,
71
+ :raw => options[:raw],
72
+ }}
73
+
74
+ collection.update(query, updates, :upsert => true)
75
+ end
76
+
77
+ def delete_entry(key, options)
78
+ collection.remove(:_id => key)
79
+ end
80
+ end
81
+ end
82
+ end
data/lib/bin.rb CHANGED
@@ -1,10 +1,5 @@
1
- # encoding: UTF-8
2
- require 'active_support/all'
3
- require 'active_support/version'
4
- require 'mongo'
1
+ require 'active_support/cache/bin'
5
2
 
6
3
  module Bin
7
- autoload :Compatibility, 'bin/compatibility'
8
- autoload :Store, 'bin/store'
9
- autoload :Version, 'bin/version'
4
+ Store = ActiveSupport::Cache::Bin
10
5
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  module Bin
3
- Version = '0.6.3'
2
+ Version = '0.7.0'
4
3
  end
@@ -1,15 +1,207 @@
1
1
  require 'helper'
2
2
 
3
- describe Bin do
4
- it "autoloads Store" do
5
- lambda { Bin::Store }.should_not raise_error(NameError)
3
+ describe ActiveSupport::Cache::Bin do
4
+ let(:collection) { DB['bin_cache'] }
5
+ let(:store) { ActiveSupport::Cache::Bin.new(collection) }
6
+
7
+ it "has a collection" do
8
+ store.collection.should == collection
9
+ end
10
+
11
+ it "should also work with Bin::Store for backwards compat" do
12
+ Bin::Store.should == ActiveSupport::Cache::Bin
13
+ end
14
+
15
+ it "defaults expires_in to 1.year" do
16
+ store.expires_in.should == 1.year
17
+ end
18
+
19
+ it "can set default expires_in" do
20
+ ActiveSupport::Cache::Bin.new(collection, :expires_in => 5.minutes).expires_in.should == 5.minutes
21
+ end
22
+
23
+ describe "#write" do
24
+ before(:each) do
25
+ store.write('foo', 'bar')
26
+ end
27
+ let(:document) { collection.find_one(:_id => 'foo') }
28
+
29
+ it "sets _id to key" do
30
+ document['_id'].should == 'foo'
31
+ end
32
+
33
+ it "sets value key to value" do
34
+ store.read('foo').should == 'bar'
35
+ end
36
+
37
+ it "should marshal value by default" do
38
+ document['value'].to_s.should == BSON::Binary.new(Marshal.dump('bar')).to_s
39
+ document['raw'].should be_false
40
+ end
41
+
42
+ it "should be able to store in raw format" do
43
+ store.write('foo', 'bar', :raw => true)
44
+ document['value'].should == 'bar'
45
+ document['raw'].should be_true
46
+ end
47
+
48
+ it "sets expires in to default if not provided" do
49
+ document['expires_at'].to_i.should == (Time.now.utc + 1.year).to_i
50
+ end
51
+
52
+ it "sets expires_at if expires_in provided" do
53
+ store.write('foo', 'bar', :expires_in => 5.seconds)
54
+ document['expires_at'].to_i.should == (Time.now.utc + 5.seconds).to_i
55
+ end
56
+
57
+ it "always sets key as string" do
58
+ store.write(:baz, 'wick')
59
+ doc = collection.find_one(:_id => 'baz')
60
+ doc.should_not be_nil
61
+ doc['_id'].should be_instance_of(String)
62
+ end
6
63
  end
7
64
 
8
- it "autoloads Compatibility" do
9
- lambda { Bin::Compatibility }.should_not raise_error(NameError)
65
+ describe "#read" do
66
+ before(:each) do
67
+ store.write('foo', 'bar')
68
+ end
69
+ let(:document) { collection.find_one(:_id => 'foo') }
70
+
71
+ it "returns nil for key not found" do
72
+ store.read('non:existent:key').should be_nil
73
+ end
74
+
75
+ it "returns unmarshalled value key value for key found" do
76
+ store.read('foo').should == 'bar'
77
+ end
78
+
79
+ it "returns raw value if document raw key is true" do
80
+ store.write('foo', 'bar', :raw => true)
81
+ store.read('foo').should == 'bar'
82
+ end
83
+
84
+ it "returns nil for existing but expired key" do
85
+ collection.save(:_id => 'foo', :value => 'bar', :expires_at => 5.seconds.ago)
86
+ store.read('foo').should be_nil
87
+ end
88
+
89
+ it "return value for existing and not expired key" do
90
+ store.write('foo', 'bar', :expires_in => 20.seconds)
91
+ store.read('foo').should == 'bar'
92
+ end
93
+
94
+ it "works with symbol" do
95
+ store.read(:foo).should == 'bar'
96
+ end
97
+ end
98
+
99
+ describe "#delete" do
100
+ before(:each) do
101
+ store.write('foo', 'bar')
102
+ end
103
+
104
+ it "delete key from cache" do
105
+ store.read('foo').should_not be_nil
106
+ store.delete('foo')
107
+ store.read('foo').should be_nil
108
+ end
109
+
110
+ it "works with symbol" do
111
+ store.read(:foo).should_not be_nil
112
+ store.delete(:foo)
113
+ store.read(:foo).should be_nil
114
+ end
115
+ end
116
+
117
+ describe "#delete_matched" do
118
+ before(:each) do
119
+ store.write('foo1', 'bar')
120
+ store.write('foo2', 'bar')
121
+ store.write('baz', 'wick')
122
+ end
123
+
124
+ it "deletes matching keys" do
125
+ store.read('foo1').should_not be_nil
126
+ store.read('foo2').should_not be_nil
127
+ store.delete_matched(/foo/)
128
+ store.read('foo1').should be_nil
129
+ store.read('foo2').should be_nil
130
+ end
131
+
132
+ it "does not delete unmatching keys" do
133
+ store.delete_matched('foo')
134
+ store.read('baz').should_not be_nil
135
+ end
136
+ end
137
+
138
+ describe "#exist?" do
139
+ before(:each) do
140
+ store.write('foo', 'bar')
141
+ end
142
+
143
+ it "returns true if key found" do
144
+ store.exist?('foo').should be_true
145
+ end
146
+
147
+ it "returns false if key not found" do
148
+ store.exist?('not:found:key').should be_false
149
+ end
150
+
151
+ it "works with symbol" do
152
+ store.exist?(:foo).should be_true
153
+ store.exist?(:notfoundkey).should be_false
154
+ end
155
+ end
156
+
157
+ describe "#clear" do
158
+ before(:each) do
159
+ store.write('foo', 'bar')
160
+ store.write('baz', 'wick')
161
+ end
162
+
163
+ it "clear all keys" do
164
+ collection.count.should == 2
165
+ store.clear
166
+ collection.count.should == 0
167
+ end
168
+ end
169
+
170
+ describe "#increment" do
171
+ it "increment key by amount" do
172
+ store.increment('views', 1)
173
+ store.read('views').should == 1
174
+ store.increment('views', 2)
175
+ store.read('views').should == 3
176
+ end
177
+
178
+ it "works with symbol" do
179
+ store.increment(:views, 2)
180
+ store.read(:views).should == 2
181
+ end
182
+ end
183
+
184
+ describe "#decrement" do
185
+ it "decrement key by amount" do
186
+ store.increment('views', 5)
187
+ store.decrement('views', 2)
188
+ store.read('views').should == 3
189
+ store.decrement('views', 2)
190
+ store.read('views').should == 1
191
+ end
192
+
193
+ it "works with symbol" do
194
+ store.increment(:views, 2)
195
+ store.decrement(:views, 1)
196
+ store.read(:views).should == 1
197
+ end
10
198
  end
11
199
 
12
- it "autoloads Version" do
13
- lambda { Bin::Version }.should_not raise_error(NameError)
200
+ describe "#stats" do
201
+ it "returns stats" do
202
+ %w[ns count size].each do |key|
203
+ store.stats.should have_key(key)
204
+ end
205
+ end
14
206
  end
15
207
  end
@@ -1,11 +1,21 @@
1
- gem 'activesupport', ENV['ACTIVE_SUPPORT_VERSION']
2
-
3
1
  $:.unshift(File.expand_path('../../lib', __FILE__))
4
2
 
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ require 'active_support/all'
6
+
7
+ Bundler.require(:default)
8
+
5
9
  require 'bin'
6
- require 'spec'
7
10
 
8
11
  connection = Mongo::Connection.new
9
- DB = connection.db('bin-store-test')
12
+ DB = connection.db('test')
10
13
 
11
- puts "\n--- Active Support Version: #{ActiveSupport::VERSION::STRING} ---\n"
14
+ RSpec.configure do |c|
15
+ c.before(:each) do
16
+ DB.collections.each do |collection|
17
+ collection.remove
18
+ collection.drop_indexes
19
+ end
20
+ end
21
+ end
@@ -1,4 +1,3 @@
1
1
  --colour
2
2
  --timeout
3
- 20
4
- --diff
3
+ 20
@@ -0,0 +1,46 @@
1
+ def growl(title, msg, img)
2
+ %x{growlnotify -m #{ msg.inspect} -t #{title.inspect} --image ~/.watchr/#{img}.png}
3
+ end
4
+
5
+ def form_growl_message(str)
6
+ msg = str.split("\n").last
7
+ if msg =~ /(\d)\sfailure/
8
+ img = $1.to_i > 0 ? 'fail' : 'pass'
9
+ end
10
+ growl 'Results', msg, img
11
+ end
12
+
13
+ def run(cmd)
14
+ puts cmd
15
+ output = ""
16
+ IO.popen(cmd) do |com|
17
+ com.each_char do |c|
18
+ print c
19
+ output << c
20
+ $stdout.flush
21
+ end
22
+ end
23
+ form_growl_message output
24
+ end
25
+
26
+ def run_spec(path)
27
+ path.gsub!('lib/', 'spec/')
28
+ path.gsub!('_spec', '')
29
+ file_name = File.basename(path, '.rb')
30
+ path.gsub!(file_name, file_name + "_spec")
31
+ run %Q(spec #{path})
32
+ end
33
+
34
+ watch('spec/helper\.rb') { system('clear'); run('rake') }
35
+ watch('lib/.*\.rb') { |m| system('clear'); run_spec(m[0]) }
36
+ watch('spec/.*_spec\.rb') { |m| system('clear'); run_spec(m[0]) }
37
+
38
+ # Ctrl-\
39
+ Signal.trap('QUIT') do
40
+ puts " --- Running all tests ---\n\n"
41
+ run('rake')
42
+ end
43
+
44
+ # Ctrl-C
45
+ Signal.trap('INT') { abort("\n") }
46
+
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bin
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 3
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
- - 6
8
- - 3
9
- version: 0.6.3
8
+ - 7
9
+ - 0
10
+ version: 0.7.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - John Nunemaker
@@ -14,51 +15,41 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-06-15 00:00:00 -04:00
18
- default_executable:
18
+ date: 2011-09-17 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: mongo
22
22
  prerelease: false
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
24
25
  requirements:
25
26
  - - ~>
26
27
  - !ruby/object:Gem::Version
28
+ hash: 27
27
29
  segments:
28
30
  - 1
31
+ - 3
29
32
  - 0
30
- - 1
31
- version: 1.0.1
33
+ version: 1.3.0
32
34
  type: :runtime
33
35
  version_requirements: *id001
34
36
  - !ruby/object:Gem::Dependency
35
37
  name: activesupport
36
38
  prerelease: false
37
39
  requirement: &id002 !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- segments:
42
- - 2
43
- - 3
44
- version: "2.3"
45
- type: :runtime
46
- version_requirements: *id002
47
- - !ruby/object:Gem::Dependency
48
- name: rspec
49
- prerelease: false
50
- requirement: &id003 !ruby/object:Gem::Requirement
40
+ none: false
51
41
  requirements:
52
42
  - - ~>
53
43
  - !ruby/object:Gem::Version
44
+ hash: 3
54
45
  segments:
55
- - 1
56
46
  - 3
47
+ - 1
57
48
  - 0
58
- version: 1.3.0
59
- type: :development
60
- version_requirements: *id003
61
- description:
49
+ version: 3.1.0
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: ActiveSupport MongoDB Cache store.
62
53
  email:
63
54
  - nunemaker@gmail.com
64
55
  executables: []
@@ -68,18 +59,21 @@ extensions: []
68
59
  extra_rdoc_files: []
69
60
 
70
61
  files:
71
- - lib/bin/compatibility.rb
72
- - lib/bin/store.rb
73
- - lib/bin/version.rb
62
+ - .gitignore
63
+ - Gemfile
64
+ - Gemfile.lock
65
+ - LICENSE
66
+ - README.rdoc
67
+ - Rakefile
68
+ - bin.gemspec
69
+ - examples/read_write_delete.rb
70
+ - lib/active_support/cache/bin.rb
74
71
  - lib/bin.rb
75
- - spec/bin/store_spec.rb
72
+ - lib/bin/version.rb
76
73
  - spec/bin_spec.rb
77
74
  - spec/helper.rb
78
75
  - spec/spec.opts
79
- - LICENSE
80
- - Rakefile
81
- - README.rdoc
82
- has_rdoc: true
76
+ - specs.watchr
83
77
  homepage: http://github.com/jnunemaker/bin
84
78
  licenses: []
85
79
 
@@ -89,25 +83,31 @@ rdoc_options: []
89
83
  require_paths:
90
84
  - lib
91
85
  required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
92
87
  requirements:
93
88
  - - ">="
94
89
  - !ruby/object:Gem::Version
90
+ hash: 3
95
91
  segments:
96
92
  - 0
97
93
  version: "0"
98
94
  required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
99
96
  requirements:
100
97
  - - ">="
101
98
  - !ruby/object:Gem::Version
99
+ hash: 3
102
100
  segments:
103
101
  - 0
104
102
  version: "0"
105
103
  requirements: []
106
104
 
107
105
  rubyforge_project:
108
- rubygems_version: 1.3.6
106
+ rubygems_version: 1.8.9
109
107
  signing_key:
110
108
  specification_version: 3
111
109
  summary: ActiveSupport MongoDB Cache store.
112
- test_files: []
113
-
110
+ test_files:
111
+ - spec/bin_spec.rb
112
+ - spec/helper.rb
113
+ - spec/spec.opts
@@ -1,41 +0,0 @@
1
- # encoding: UTF-8
2
- module Bin
3
- class Compatibility < ActiveSupport::Cache::Store
4
- def increment(key, amount=1)
5
- yield
6
- end
7
-
8
- def decrement(key, amount=1)
9
- yield
10
- end
11
- end
12
-
13
- if ActiveSupport::VERSION::STRING < '3'
14
- class Compatibility
15
- def write(key, value, options=nil, &block)
16
- super(key, value, options)
17
- yield
18
- end
19
-
20
- def read(key, options=nil, &block)
21
- super
22
- yield
23
- end
24
-
25
- def delete(key, options=nil, &block)
26
- super
27
- yield
28
- end
29
-
30
- def delete_matched(matcher, options=nil, &block)
31
- super
32
- yield
33
- end
34
-
35
- def exist?(key, options=nil, &block)
36
- super
37
- yield
38
- end
39
- end
40
- end
41
- end
@@ -1,84 +0,0 @@
1
- # encoding: UTF-8
2
- module Bin
3
- class Store < Compatibility
4
- attr_reader :collection, :options
5
-
6
- def initialize(collection, options={})
7
- @collection, @options = collection, options
8
- end
9
-
10
- def expires_in
11
- @expires_in ||= options[:expires_in] || 1.year
12
- end
13
-
14
- def write(key, value, options={})
15
- key = key.to_s
16
- super do
17
- expires = Time.now.utc + ((options && options[:expires_in]) || expires_in)
18
- raw = !!options[:raw]
19
- value = raw ? value : BSON::Binary.new(Marshal.dump(value))
20
- doc = {:_id => key, :value => value, :expires_at => expires, :raw => raw}
21
- collection.save(doc)
22
- end
23
- end
24
-
25
- def read(key, options=nil)
26
- super do
27
- if doc = collection.find_one(:_id => key.to_s, :expires_at => {'$gt' => Time.now.utc})
28
- doc['raw'] ? doc['value'] : Marshal.load(doc['value'].to_s)
29
- end
30
- end
31
- end
32
-
33
- def delete(key, options=nil)
34
- super do
35
- collection.remove(:_id => key.to_s)
36
- end
37
- end
38
-
39
- def delete_matched(matcher, options=nil)
40
- super do
41
- collection.remove(:_id => matcher)
42
- end
43
- end
44
-
45
- def exist?(key, options=nil)
46
- super do
47
- !read(key, options).nil?
48
- end
49
- end
50
-
51
- def increment(key, amount=1)
52
- super do
53
- counter_key_upsert(key, amount)
54
- end
55
- end
56
-
57
- def decrement(key, amount=1)
58
- super do
59
- counter_key_upsert(key, -amount.abs)
60
- end
61
- end
62
-
63
- def clear
64
- collection.remove
65
- end
66
-
67
- def stats
68
- collection.stats
69
- end
70
-
71
- private
72
- def counter_key_upsert(key, amount)
73
- key = key.to_s
74
- collection.update(
75
- {:_id => key}, {
76
- '$inc' => {:value => amount},
77
- '$set' => {
78
- :expires_at => Time.now.utc + 1.year,
79
- :raw => true
80
- },
81
- }, :upsert => true)
82
- end
83
- end
84
- end
@@ -1,210 +0,0 @@
1
- require 'helper'
2
-
3
- describe Bin::Store do
4
- before(:each) do
5
- DB.collections.each do |collection|
6
- collection.remove
7
- collection.drop_indexes
8
- end
9
- end
10
-
11
- let(:collection) { DB['bin_cache'] }
12
- let(:store) { Bin::Store.new(collection) }
13
-
14
- it "has a collection" do
15
- store.collection.should == collection
16
- end
17
-
18
- it "defaults expires_in to 1.year" do
19
- store.expires_in.should == 1.year
20
- end
21
-
22
- it "can set default expires_in" do
23
- Bin::Store.new(collection, :expires_in => 5.minutes).expires_in.should == 5.minutes
24
- end
25
-
26
- describe "#write" do
27
- before(:each) do
28
- store.write('foo', 'bar')
29
- end
30
- let(:document) { collection.find_one(:_id => 'foo') }
31
-
32
- it "sets _id to key" do
33
- document['_id'].should == 'foo'
34
- end
35
-
36
- it "sets value key to value" do
37
- store.read('foo').should == 'bar'
38
- end
39
-
40
- it "should marshal value by default" do
41
- document['value'].to_s.should == BSON::Binary.new(Marshal.dump('bar')).to_s
42
- document['raw'].should be_false
43
- end
44
-
45
- it "should be able to store in raw format" do
46
- store.write('foo', 'bar', :raw => true)
47
- document['value'].should == 'bar'
48
- document['raw'].should be_true
49
- end
50
-
51
- it "sets expires in to default if not provided" do
52
- document['expires_at'].to_i.should == (Time.now.utc + 1.year).to_i
53
- end
54
-
55
- it "sets expires_at if expires_in provided" do
56
- store.write('foo', 'bar', :expires_in => 5.seconds)
57
- document['expires_at'].to_i.should == (Time.now.utc + 5.seconds).to_i
58
- end
59
-
60
- it "always sets key as string" do
61
- store.write(:baz, 'wick')
62
- doc = collection.find_one(:_id => 'baz')
63
- doc.should_not be_nil
64
- doc['_id'].should be_instance_of(String)
65
- end
66
- end
67
-
68
- describe "#read" do
69
- before(:each) do
70
- store.write('foo', 'bar')
71
- end
72
- let(:document) { collection.find_one(:_id => 'foo') }
73
-
74
- it "returns nil for key not found" do
75
- store.read('non:existent:key').should be_nil
76
- end
77
-
78
- it "returns unmarshalled value key value for key found" do
79
- store.read('foo').should == 'bar'
80
- end
81
-
82
- it "returns raw value if document raw key is true" do
83
- store.write('foo', 'bar', :raw => true)
84
- store.read('foo').should == 'bar'
85
- end
86
-
87
- it "returns nil for existing but expired key" do
88
- collection.save(:_id => 'foo', :value => 'bar', :expires_at => 5.seconds.ago)
89
- store.read('foo').should be_nil
90
- end
91
-
92
- it "return value for existing and not expired key" do
93
- store.write('foo', 'bar', :expires_in => 20.seconds)
94
- store.read('foo').should == 'bar'
95
- end
96
-
97
- it "works with symbol" do
98
- store.read(:foo).should == 'bar'
99
- end
100
- end
101
-
102
- describe "#delete" do
103
- before(:each) do
104
- store.write('foo', 'bar')
105
- end
106
-
107
- it "delete key from cache" do
108
- store.read('foo').should_not be_nil
109
- store.delete('foo')
110
- store.read('foo').should be_nil
111
- end
112
-
113
- it "works with symbol" do
114
- store.read(:foo).should_not be_nil
115
- store.delete(:foo)
116
- store.read(:foo).should be_nil
117
- end
118
- end
119
-
120
- describe "#delete_matched" do
121
- before(:each) do
122
- store.write('foo1', 'bar')
123
- store.write('foo2', 'bar')
124
- store.write('baz', 'wick')
125
- end
126
-
127
- it "deletes matching keys" do
128
- store.read('foo1').should_not be_nil
129
- store.read('foo2').should_not be_nil
130
- store.delete_matched(/foo/)
131
- store.read('foo1').should be_nil
132
- store.read('foo2').should be_nil
133
- end
134
-
135
- it "does not delete unmatching keys" do
136
- store.delete_matched('foo')
137
- store.read('baz').should_not be_nil
138
- end
139
- end
140
-
141
- describe "#exist?" do
142
- before(:each) do
143
- store.write('foo', 'bar')
144
- end
145
-
146
- it "returns true if key found" do
147
- store.exist?('foo').should be_true
148
- end
149
-
150
- it "returns false if key not found" do
151
- store.exist?('not:found:key').should be_false
152
- end
153
-
154
- it "works with symbol" do
155
- store.exist?(:foo).should be_true
156
- store.exist?(:notfoundkey).should be_false
157
- end
158
- end
159
-
160
- describe "#clear" do
161
- before(:each) do
162
- store.write('foo', 'bar')
163
- store.write('baz', 'wick')
164
- end
165
-
166
- it "clear all keys" do
167
- collection.count.should == 2
168
- store.clear
169
- collection.count.should == 0
170
- end
171
- end
172
-
173
- describe "#increment" do
174
- it "increment key by amount" do
175
- store.increment('views', 1)
176
- store.read('views').should == 1
177
- store.increment('views', 2)
178
- store.read('views').should == 3
179
- end
180
-
181
- it "works with symbol" do
182
- store.increment(:views, 2)
183
- store.read(:views).should == 2
184
- end
185
- end
186
-
187
- describe "#decrement" do
188
- it "decrement key by amount" do
189
- store.increment('views', 5)
190
- store.decrement('views', 2)
191
- store.read('views').should == 3
192
- store.decrement('views', 2)
193
- store.read('views').should == 1
194
- end
195
-
196
- it "works with symbol" do
197
- store.increment(:views, 2)
198
- store.decrement(:views, 1)
199
- store.read(:views).should == 1
200
- end
201
- end
202
-
203
- describe "#stats" do
204
- it "returns stats" do
205
- %w[ns count size].each do |key|
206
- store.stats.should have_key(key)
207
- end
208
- end
209
- end
210
- end