dkastner-moneta 0.7.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ moneta-*.gem
2
+ .idea
3
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/README CHANGED
@@ -1,3 +1,12 @@
1
+ Moneta::Monetas
2
+ ===============
3
+
4
+ store = Moneta::Monetas.new(:stores => [
5
+ Moneta::Memory.new,
6
+ Moneta::BasicFile.new(...)])
7
+
8
+ Read operations select a random cache, write operations write to all caches.
9
+
1
10
  Moneta: A unified interface for key/value stores
2
11
  ================================================
3
12
 
@@ -53,4 +62,46 @@ The API:
53
62
  #update_key(key, options):: updates an existing key with a new :expires_in option.
54
63
  if the key has already expired, it will not be updated.
55
64
 
56
- #clear:: clear all keys in this store
65
+ #clear:: clear all keys in this store
66
+
67
+
68
+ Moneta::ActiveRecord
69
+ ====================
70
+ You need a migration:
71
+
72
+ class AddMonetaTable < ActiveRecord::Migration
73
+ # Only needed if you want to keep your cache in a separate database
74
+ class TheStore < ActiveRecord::Base
75
+ establish_connection "#{Rails.env}_store"
76
+ end
77
+
78
+ def self.connection
79
+ TheStore.connection
80
+ end
81
+
82
+ def self.up
83
+ create_table 'moneta_store', :id => false do |t|
84
+ t.string :key, :null => false
85
+ t.text :value
86
+ end
87
+
88
+ if mysql?
89
+ execute "alter table moneta_store modify column `key` varchar(255) primary key"
90
+ else
91
+ execute "alter table moneta_store add primary key (key)"
92
+ end
93
+ end
94
+
95
+ def self.down
96
+ drop_table 'moneta_store'
97
+ end
98
+
99
+ def self.mysql?
100
+ ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'mysql'
101
+ end
102
+ end
103
+
104
+ Initialize like this:
105
+
106
+ store = Moneta::ActiveRecord.new(:connection => ActiveRecord::Base.configurations["#{Rails.env}_store"])
107
+
data/Rakefile CHANGED
@@ -1,60 +1,13 @@
1
1
  require 'rubygems'
2
- require 'rake/gempackagetask'
3
- require 'rubygems/specification'
4
- require 'spec/rake/spectask'
5
- require 'date'
2
+ require 'bundler'
3
+ Bundler.setup
6
4
 
7
- GEM = "moneta"
8
- GEM_VERSION = "0.5.0"
9
- AUTHOR = "Yehuda Katz"
10
- EMAIL = "wycats@gmail.com"
11
- HOMEPAGE = "http://www.yehudakatz.com"
12
- SUMMARY = "A unified interface to key/value stores"
5
+ Bundler::GemHelper.install_tasks
13
6
 
14
- spec = Gem::Specification.new do |s|
15
- s.name = GEM
16
- s.version = GEM_VERSION
17
- s.platform = Gem::Platform::RUBY
18
- s.has_rdoc = true
19
- s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
20
- s.summary = SUMMARY
21
- s.description = s.summary
22
- s.author = AUTHOR
23
- s.email = EMAIL
24
- s.homepage = HOMEPAGE
25
-
26
- # Uncomment this to add a dependency
27
- # s.add_dependency "foo"
28
-
29
- s.require_path = 'lib'
30
- s.autorequire = GEM
31
- s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,specs}/**/*")
32
- end
33
-
34
- Rake::GemPackageTask.new(spec) do |pkg|
35
- pkg.gem_spec = spec
36
- end
37
-
38
- desc "install the gem locally"
39
- task :install => [:package] do
40
- sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
41
- end
42
-
43
- desc "create a gemspec file"
44
- task :make_spec do
45
- File.open("#{GEM}.gemspec", "w") do |file|
46
- file.puts spec.to_ruby
47
- end
7
+ require 'rspec/core/rake_task'
8
+ desc "Run all examples (or a specific spec with TASK=xxxx)"
9
+ RSpec::Core::RakeTask.new(:examples) do |c|
10
+ c.rspec_opts = '-Ispec'
48
11
  end
49
12
 
50
- desc "Run all examples (or a specific spec with TASK=xxxx)"
51
- Spec::Rake::SpecTask.new('spec') do |t|
52
- t.spec_opts = ["-cfs"]
53
- t.spec_files = begin
54
- if ENV["TASK"]
55
- ENV["TASK"].split(',').map { |task| "spec/**/#{task}_spec.rb" }
56
- else
57
- FileList['spec/**/*_spec.rb']
58
- end
59
- end
60
- end
13
+ task :default => :examples
data/SPEC.markdown ADDED
@@ -0,0 +1,99 @@
1
+ # Moneta Specification
2
+
3
+ The purpose of the moneta specification is to create a general-purpose API for interacting with key-value stores. In general, libraries that need to interact with key-value stores should be able to specify that they can use any "moneta-compliant store".
4
+
5
+ # Moneta Executable Specs
6
+
7
+ Moneta ships with a set of executable specs which you can use to verify spec-compliance with your moneta adapter.
8
+
9
+ # Moneta Library
10
+
11
+ Moneta ships with proof-of-concept adapters for over a dozen key-value stores, including in-memory, memcache, database-backed and S3. These adapters are meant as proofs-of-concept, and while the moneta project intends to maintain them (and will accept patches to improve them), you should not consider them the core of the project.
12
+
13
+ # Requirements for a Moneta Adapter
14
+
15
+ (See RFC 2119 for use of MUST, SHOULD, MAY, MUST NOT, and SHOULD NOT)
16
+
17
+ A Moneta adapter must expose a class with the following characteristics:
18
+
19
+ ## Class Methods
20
+
21
+ ### <code>new(options[Hash]) => Object</code>
22
+
23
+ Return an instance of the moneta adapter, with the instance methods listed below. The <code>options</code> hash is a required parameter, and the adapter may specify whatever additional requirements it needs to properly instantiate it.
24
+
25
+ ## Instance Methods
26
+
27
+ ### <code>\[\](key[Object]) => Object</code>
28
+
29
+ Return the value stored in the key-value-store under the provided key. Adapters MUST return a duplicate of the original value, and consumers should expect that adapters might serialize and deserialize the key and value. As a result, both the key and value MUST be objects that can be serialized using Ruby's Marshal system.
30
+
31
+ ### <code>\[\]=(key[Object], value[Object]) => Object(value)</code>
32
+
33
+ Store the value in the key-value-store under the provided key. Adapters MAY serialize the value using Ruby's Marshal system, and MUST NOT store a reference to the original value in the store, unless Ruby disallows duplication of the original value. Adapters SHOULD NOT simply call <code>dup</code> on the value, unless the value stores no references to other Object. For example, an adapter MAY store a <code>dup</code> of a String, but SHOULD NOT store a <code>dup</code> of <code>["hello", "world"]</code>.
34
+
35
+ ### <code>fetch(key[Object]) => Object</code>
36
+
37
+ Return the value stored in the key-value-store under the provided key. If no value is stored under the provided key, the adapter MUST raise an IndexError.
38
+
39
+ ### <code>fetch(key[Object], &block) => Object</code>
40
+
41
+ Return the value stored in the key-value-store under the provided key. If no value is stored under the provided key, the adapter MUST yield to the block, and return the value. The adapter MUST NOT store the value returned from the block in the key-value-store.
42
+
43
+ ### <code>fetch(key[Object], value[Object]) => Object</code>
44
+
45
+ Return the value stored in the key-value-store under the provided key. If no value is stored under the provided key, the adapter MUST return the default value provided. The adapter MUST NOT store the default value in the key-value-store.
46
+
47
+ ### <code>delete(key[Object]) => Object</code>
48
+
49
+ Delete the value stored in the key-value-store for the key provided, and return the value previously stored there. After this operation, the key-value-store MUST behave as though no value was stored for the provided key.
50
+
51
+ ### <code>key?(key[Object]) => [TrueClass, FalseClass]</code>
52
+
53
+ Determine whether a value exists in the key-value-store for the key provided. If a value exists, the adapter MUST return <code>true</code>. Otherwise, the adapter MUST return <code>false</code>.
54
+
55
+ ### <code>store(key[Object], value[Object]) => Object(value)</code>
56
+
57
+ Behaves the same as <code>[]=</code>, but allows the client to send additional options which can be specified by the adapter (and which may be specified by extensions to this specification).
58
+
59
+ ### <code>update_key(key[Object], options[Hash]) => nil</code>
60
+
61
+ In this specification, this operation does nothing. However, extensions to this specification may specify semantics for certain values of the <code>options</code> Hash.
62
+
63
+ ### <code>clear</code>
64
+
65
+ Completely empty all keys and values from the key-value-store. Adapters MAY allow a namespace during initialization, which can scope this operation to a particular subset of keys. After calling <code>clear</code>, a <code>[]</code> operation MUST return nil for every possible key, and a <code>key?</code> query MUST return false for every possible key.
66
+
67
+ # Additional Options Hashes
68
+
69
+ The following methods may all take an additional Hash as a final argument. This allows the client to send additional options which can be specified by the adapter (and which may be specified by extensions to this specification).
70
+
71
+ * fetch
72
+ * store
73
+ * delete
74
+ * key?
75
+ * clear
76
+
77
+ In the case of methods with optional arguments, the Hash MUST be provided as the final argument, and all optional arguments MUST be specified.
78
+
79
+ Keys in this Hash MUST be Strings or Symbols. If they are Strings, they MUST be prefixed with a unique namespace. Namespaces MUST be separated from the name of the key with a single ".". The namespace SHOULD be the name of the gem that exposes the key.
80
+
81
+ Keys in this Hash MUST NOT be Symbols unless this specification or an official extension to this specification defines a Symbol key.
82
+
83
+ # Key Equality
84
+
85
+ Adapters MUST consider keys as equal to one another if and only if the value of <code>Marshal.dump(keya)</code> is the same (byte-for-byte) as <code>Marshal.dump(keyb)</code>. This does not mean that adapters are required to use <code>Marshal.dump</code> to calculate the key to use for a given key specified by the consumer of the adapter. However, if an adapter does not, it MUST guarantee that the value returned for every key is identical to the value that would be returned if it did a byte-for-byte comparison of the result of <code>Marshal.dump</code> for every operation involving a key.
86
+
87
+ # Storage and Serialization
88
+
89
+ In a Moneta-compliant adapter, any Ruby object that can be serialized using Ruby's marshalling system may be used for keys or values.
90
+
91
+ Adapters MAY use the marshalling system to serialize Ruby objects. Adapters MUST NOT return an Object from a fetch operation that existed on the heap prior to the fetch operation. The intention of this requirement is to prevent adapters that use the heap for persistence to store direct references to Objects passed into the <code>store</code> or <code>[]=</code> methods.
92
+
93
+ # Atomicity
94
+
95
+ The base Moneta specification does not specify any atomicity guarantees. However, extensions to this spec may specify extensions that define additional guarantees for any of the defined operations.
96
+
97
+ # Expiry
98
+
99
+ The base Moneta specification does not specify any mechanism for time-based expiry. However, extensions to this spec may specify mechanisms (using <code>store</code> and <code>update_key</code>) to provide expiration semantics.
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env ruby
2
+ require 'benchmark'
3
+ require "rubygems"
4
+
5
+ # Hacked arrays
6
+ # Array modifications
7
+ class HackedArray < Array
8
+ # Random keys/values
9
+ attr_reader :keys_used
10
+ def random_key(no_repeat = true, clean_keys_used = false)
11
+ @keys_used = [] if clean_keys_used or @keys_used.nil? or @keys_used.size == self.size
12
+ begin key = rand(self.size) end while no_repeat and @keys_used.include?(key)
13
+ @keys_used << key
14
+ return key
15
+ end
16
+
17
+ def random_value(no_repeat = true, clean_keys_used = false)
18
+ values_at(random_key(no_repeat, clean_keys_used)).first
19
+ end
20
+ alias_method :random, :random_value
21
+
22
+ def random_subset(n, no_repeat = true, clean_keys_used = true)
23
+ (1..n).map{|x| random_value(no_repeat, (clean_keys_used && x == 1))}
24
+ end
25
+
26
+ def self.new_from_string(str)
27
+ res = new
28
+ str.split('').each{|x| res << x}
29
+ res
30
+ end
31
+ end
32
+
33
+ require "../lib/moneta"
34
+ require "moneta/memcache"
35
+ require "moneta/tyrant"
36
+ require "moneta/berkeley"
37
+
38
+
39
+ stores = {
40
+ 'Redis' => { },
41
+ 'Memcached' => { :class_name => "Memcache", :server => "localhost:11211", :namespace => 'moneta_bench' },
42
+ 'Tyrant' => { :host => 'localhost', :port => 1978 }, # Breaks for any n > 50 on my machine
43
+ 'MongoDB' => { :host => 'localhost', :port => 27017, :db => 'moneta_bench' },
44
+ 'LMC' => { :filename => "bench.lmc" },
45
+ 'Berkeley' => { :file => "bench.bdb" },
46
+ 'Rufus' => {:file => "bench.rufus"},
47
+ 'Memory' => { },
48
+ 'DataMapper' => { :setup => "sqlite3::memory:" },
49
+ # 'Couch' => {:db => "couch_test"},
50
+ 'TC (Tyrant)' =>
51
+ {:name => "test.tieredtyrant", :backup => Moneta::Tyrant.new(:host => "localhost", :port => 1978), :class_name => "TieredCache"},
52
+ 'TC (Memcached)' =>
53
+ {:name => "test.tieredmc", :backup => Moneta::Memcache.new(:server => "localhost:11211", :namespace => "various"), :class_name => "TieredCache"}
54
+ }
55
+
56
+ stats, keys, data, errors, summary = {}, [], HackedArray.new, HackedArray.new, HackedArray.new
57
+ dict = HackedArray.new_from_string 'abcdefghijklnopq123456789'
58
+ n = ARGV[0] ? ARGV[0].to_i : 100
59
+ m = ARGV[1] ? ARGV[1].to_i : 10
60
+ c = ARGV[2] ? ARGV[2].to_i : 3
61
+ vlen_min, vlen_max, vlen_ttl, vlen_avg = 99999, 0, 0, 0
62
+ ds = dict.size
63
+
64
+ puts "======================================================================"
65
+ puts "Comparison of write/read between Moneta Stores"
66
+ puts "======================================================================"
67
+
68
+ puts "Data loading..."
69
+ n.times do |x|
70
+ klen = 6 + rand(3)
71
+ vlen = (rand(m) + 1) * (rand(m) + rand(m) + 1)
72
+ key = dict.random_subset(klen).join
73
+ keys << key
74
+ value = key * vlen
75
+ data << [key, value]
76
+ vs = value.size
77
+ vlen_min = vs if vs < vlen_min
78
+ vlen_max = vs if vs > vlen_max
79
+ vlen_ttl = vlen_ttl + vs
80
+ end
81
+ vlen_avg = vlen_ttl / n
82
+
83
+ puts "----------------------------------------------------------------------"
84
+ #puts data.inspect
85
+ puts "Total keys: #{keys.size}, unique: #{keys.uniq.size}"
86
+ #puts keys.sort.inspect
87
+
88
+ puts "----------------------------------------------------------------------"
89
+ puts " Minimum Maximum Total Average xps "
90
+ puts "----------------------------------------------------------------------"
91
+ puts "Lenght Stats % 10i % 10i % 10i % 10i " % [vlen_min, vlen_max, vlen_ttl, vlen_avg]
92
+
93
+ module Moneta
94
+ class TieredCache
95
+ include Moneta::Defaults
96
+
97
+ def initialize(options)
98
+ @bdb = Moneta::Berkeley.new(:file => File.join(File.dirname(__FILE__), options[:name]))
99
+ @mc = options[:backup]
100
+ # @mc = Moneta::Tyrant.new(:host => "localhost", :port => 1978)
101
+ # @mc = Moneta::Memcache.new(:server => "localhost:11211", :namespace => options[:name])
102
+ end
103
+
104
+ def [](key)
105
+ val = @bdb[key]
106
+ unless val
107
+ @bdb[key] = val if val = @mc[key]
108
+ end
109
+ val
110
+ end
111
+
112
+ def []=(key, val)
113
+ @bdb[key] = val
114
+ @mc[key] = val
115
+ end
116
+
117
+ def store(key, value, options = {})
118
+ @bdb.store(key, value, options)
119
+ @mc.store(key, value, options)
120
+ end
121
+
122
+ def delete(key)
123
+ bdb_val = @bdb.delete(key)
124
+ mc_val = @mc.delete(key)
125
+ bdb_val || mc_val
126
+ end
127
+
128
+ def clear
129
+ @mc.clear
130
+ @bdb.clear
131
+ end
132
+
133
+ def update_key(name, options)
134
+ @mc.update_key(name, options)
135
+ @bdb.update_key(name, options)
136
+ end
137
+
138
+ def key?(key)
139
+ @bdb.key?(key) || @mc.key?(key)
140
+ end
141
+ end
142
+ end
143
+
144
+ stores.each do |name, options|
145
+ cname = options.delete(:class_name) || name
146
+ puts "======================================================================"
147
+ puts name
148
+ puts "----------------------------------------------------------------------"
149
+ begin
150
+ require "../lib/moneta/#{cname.downcase}"
151
+ rescue LoadError
152
+ end
153
+ klass = Moneta.const_get(cname)
154
+ @cache = klass.new(options)
155
+ stats[name] = {
156
+ :writes => [],
157
+ :reads => [],
158
+ :totals => [],
159
+ :avgs => [],
160
+ }
161
+ c.times do |round|
162
+ @cache.clear
163
+ print "[#{round + 1}] R"
164
+ m1 = Benchmark.measure do
165
+ n.times do
166
+ key, value = data.random
167
+
168
+ @cache[key] = value
169
+ end
170
+ end
171
+ stats[name][:writes] << m1.real
172
+ print "W "
173
+ m2 = Benchmark.measure do
174
+ n.times do
175
+ key, value = data.random
176
+ res = @cache[key]
177
+ errors << [name, key, value, res] unless res == value
178
+ end
179
+ end
180
+ stats[name][:reads] << m2.real
181
+ stats[name][:totals] << (m1.real + m2.real)
182
+ stats[name][:avgs] << (m1.real + m2.real)
183
+ end
184
+ print "\n"
185
+ puts "----------------------------------------------------------------------"
186
+ puts " Minimum Maximum Total Average xps "
187
+ puts "----------------------------------------------------------------------"
188
+ tcmin, tcmax, tctot, tcavg = 99999, 0, 0, 0
189
+ [:writes, :reads].each do |sname|
190
+ cmin, cmax, ctot, cavg = 99999, 0, 0, 0
191
+ stats[name][sname].each do |val|
192
+ cmin = val if val < cmin
193
+ tcmin = val if val < tcmin
194
+ cmax = val if val > cmax
195
+ tcmax = val if val > tcmax
196
+ ctot = ctot + val
197
+ tctot = tctot + val
198
+ end
199
+ cavg = ctot / c
200
+ puts "%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f " % ["#{name} #{sname}", cmin, cmax, ctot, cavg, n / cavg]
201
+ end
202
+ tcavg = tctot / (c * 2)
203
+ puts "%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f " % ["#{name} avgs", tcmin, tcmax, tctot, tcavg, n / tcavg]
204
+ summary << [name, tcmin, tcmax, tctot, tcavg, n / tcavg]
205
+ end
206
+ puts "----------------------------------------------------------------------"
207
+ if errors.size > 0
208
+ puts "Errors : #{errors.size}"
209
+ # puts errors.inspect
210
+ else
211
+ puts "No errors in reading!"
212
+ end
213
+ puts "======================================================================"
214
+ puts "Summary :: #{c} runs, #{n} keys"
215
+ puts "======================================================================"
216
+ puts " Minimum Maximum Total Average xps "
217
+ puts "----------------------------------------------------------------------"
218
+ summary.each do |sry|
219
+ puts "%-14.14s % 10.4f % 10.4f % 10.4f % 10.4f % 10.4f " % sry
220
+ end
221
+ puts "======================================================================"
222
+ puts "THE END"
223
+ puts "======================================================================"
224
+
225
+ #======================================================================
226
+ #Summary :: 3 runs, 1000 keys
227
+ #======================================================================
228
+ # Minimum Maximum Total Average xps
229
+ #----------------------------------------------------------------------
230
+ #MemcacheDB 0.6202 2.7850 7.0099 1.1683 855.9366
231
+ #Memcached 0.4483 0.6563 3.3251 0.5542 1804.4385
232
+ #Redis 0.3282 0.5221 2.2965 0.3828 2612.6444
233
+ #MongoDB 0.6660 1.0539 5.1667 0.8611 1161.2745
234
+ #======================================================================
@@ -0,0 +1,55 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'moneta/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'dkastner-moneta'
7
+ s.version = Moneta::VERSION
8
+ s.authors = ['Yehuda Katz', 'Derek Kastner']
9
+ s.email = %w{wycats@gmail.com dkastner@gmail.com}
10
+ s.date = '2011-02-10'
11
+ s.description = %q{A unified interface to key/value stores}
12
+ s.extra_rdoc_files = %w{README LICENSE}
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.homepage = 'http://github.com/dkastner/moneta'
17
+ s.require_paths = ["lib"]
18
+ s.summary = %q{A unified interface to key/value stores, including MongoDB, Redis, Tokyo, and ActiveRecord}
19
+
20
+ s.add_development_dependency 'bundler'
21
+ s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'rspec'
23
+
24
+ # Adapter-specific requirements:
25
+ if ENV['ACTIVERECORD']
26
+ s.add_development_dependency 'activerecord'
27
+ s.add_development_dependency 'sqlite3-ruby'
28
+ end
29
+
30
+ s.add_development_dependency 'couchrest' if ENV['COUCH']
31
+
32
+ if ENV['DM']
33
+ s.add_development_dependency 'datamapper'
34
+ s.add_development_dependency 'dm-core'
35
+ s.add_development_dependency 'dm-migrations'
36
+ end
37
+
38
+ s.add_development_dependency 'fog' if ENV['FOG']
39
+
40
+ if ENV['MEMCACHE']
41
+ s.add_development_dependency 'localmemcache'
42
+ s.add_development_dependency 'memcache-client'
43
+ end
44
+
45
+ s.add_development_dependency 'mongo' if ENV['MONGO']
46
+
47
+ s.add_development_dependency 'redis' if ENV['REDIS']
48
+
49
+ if ENV['TOKYO']
50
+ s.add_development_dependency 'tokyocabinet'
51
+ s.add_development_dependency 'tokyotyrant'
52
+ end
53
+
54
+ s.add_development_dependency 'xattr' if ENV['XATTR']
55
+ end
@@ -0,0 +1,57 @@
1
+ begin
2
+ require 'active_record'
3
+ rescue LoadError
4
+ puts "You need the activerecord gem in order to use the ActiveRecord moneta store"
5
+ end
6
+
7
+ module Moneta
8
+ class ActiveRecord
9
+ include Moneta::Defaults
10
+
11
+ def initialize(options = {})
12
+ @options = options
13
+ unless self.class.const_defined?('Store')
14
+ self.class.const_set('Store', Class.new(::ActiveRecord::Base)) # this prevents loading issues when active_record gem is unavailable
15
+ end
16
+ Store.establish_connection(@options[:connection] || raise("Must specify :connection"))
17
+ Store.set_table_name(@options[:table] || 'moneta_store')
18
+ end
19
+
20
+ def migrate
21
+ unless Store.table_exists?
22
+ Store.connection.create_table Store.table_name, :id => false do |t|
23
+ t.string 'key', :primary => :true
24
+ t.string 'value'
25
+ end
26
+ end
27
+ end
28
+
29
+ def key?(key)
30
+ record = Store.find_by_key key_for(key)
31
+ !record.nil?
32
+ end
33
+
34
+ def [](key)
35
+ record = Store.find_by_key key_for(key)
36
+ record ? deserialize(record.value) : nil
37
+ end
38
+
39
+ def delete(key)
40
+ record = Store.find_by_key key_for(key)
41
+ if record
42
+ Store.where(:key => key_for(record.key)).delete_all
43
+ deserialize record.value
44
+ end
45
+ end
46
+
47
+ def store(key, value, options = {})
48
+ record = Store.new :key => key_for(key), :value => serialize(value)
49
+ record.save!
50
+ value
51
+ end
52
+
53
+ def clear
54
+ Store.delete_all
55
+ end
56
+ end
57
+ end
@@ -2,10 +2,11 @@ begin
2
2
  require "memcached"
3
3
  MemCache = Memcached
4
4
  rescue LoadError
5
- require "memcache"
6
- rescue
7
- puts "You need either the `memcached` or `memcache-client` gem to use the Memcache moneta store"
8
- exit
5
+ begin
6
+ require "memcache"
7
+ rescue LoadError
8
+ puts "You need either the `memcached` or `memcache-client` gem to use the Memcache moneta store"
9
+ end
9
10
  end
10
11
 
11
12
  module Moneta
@@ -6,7 +6,7 @@ module Moneta
6
6
  include Moneta::Defaults
7
7
 
8
8
  def initialize(options = {})
9
- options[:path] ||= File.join Dir.pwd, 'data.pstore'
9
+ options[:path] ||= ::File.join Dir.pwd, 'data.pstore'
10
10
  super options[:path]
11
11
  end
12
12
 
@@ -7,9 +7,9 @@ module Moneta
7
7
  include Moneta::Defaults
8
8
 
9
9
  def initialize(options = {})
10
- @file = File.expand_path(options[:path])
10
+ @file = ::File.expand_path(options[:path])
11
11
  unless ::File.exists?(@file)
12
- File.open(@file, "w") { |file| file << {}.to_yaml }
12
+ ::File.open(@file, "w") { |file| file << {}.to_yaml }
13
13
  end
14
14
  end
15
15