aeden-moneta 0.6.2

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Yehuda Katz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,50 @@
1
+ Moneta: A unified interface for key/value stores
2
+ ================================================
3
+
4
+ Moneta provides a standard interface for interacting with various kinds of key/value stores.
5
+
6
+ Out of the box, it supports:
7
+
8
+ * File store
9
+ * Memcache store
10
+ * In-memory store
11
+ * The xattrs in a file system
12
+ * DataMapper
13
+ * S3
14
+ * Berkeley DB
15
+ * Redis
16
+ * SDBM
17
+ * Tokyo
18
+ * CouchDB
19
+
20
+ All stores support key expiration, but only memcache supports it natively. All other stores
21
+ emulate expiration.
22
+
23
+ The Moneta API is purposely extremely similar to the Hash API. In order so support an
24
+ identical API across stores, it does not support iteration or partial matches, but that
25
+ might come in a future release.
26
+
27
+ The API:
28
+
29
+ #initialize(options):: options differs per-store, and is used to set up the store
30
+
31
+ #[](key):: retrieve a key. if the key is not available, return nil
32
+
33
+ #[]=(key, value):: set a value for a key. if the key is already used, clobber it.
34
+ keys set using []= will never expire
35
+
36
+ #delete(key):: delete the key from the store and return the current value
37
+
38
+ #key?(key):: true if the key exists, false if it does not
39
+
40
+ #has_key?(key):: alias for key?
41
+
42
+ #store(key, value, options):: same as []=, but you can supply an :expires_in option,
43
+ which will specify a number of seconds before the key
44
+ should expire. In order to support the same features
45
+ across all stores, only full seconds are supported
46
+
47
+ #update_key(key, options):: updates an existing key with a new :expires_in option.
48
+ if the key has already expired, it will not be updated.
49
+
50
+ #clear:: clear all keys in this store
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'spec/rake/spectask'
5
+ require 'date'
6
+
7
+ GEM = "moneta"
8
+ GEM_VERSION = "0.6.2"
9
+ AUTHOR = "Yehuda Katz"
10
+ EMAIL = "wycats@gmail.com"
11
+ HOMEPAGE = "http://www.yehudakatz.com"
12
+ SUMMARY = "A unified interface to key/value stores"
13
+
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
48
+ end
49
+
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
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ TODO:
2
+ Fix LICENSE with your name
3
+ Fix Rakefile with your name and contact info
4
+ Add your code to lib/<%= name %>.rb
data/lib/moneta.rb ADDED
@@ -0,0 +1,76 @@
1
+ module Moneta
2
+ module Expires
3
+ def check_expired(key)
4
+ if @expiration[key] && Time.now > @expiration[key]
5
+ @expiration.delete(key)
6
+ self.delete(key)
7
+ end
8
+ end
9
+
10
+ def key?(key)
11
+ check_expired(key)
12
+ super
13
+ end
14
+
15
+ def [](key)
16
+ check_expired(key)
17
+ super
18
+ end
19
+
20
+ def fetch(key, default = nil, &blk)
21
+ check_expired(key)
22
+ super
23
+ end
24
+
25
+ def delete(key)
26
+ check_expired(key)
27
+ super
28
+ end
29
+
30
+ def update_key(key, options)
31
+ update_options(key, options)
32
+ end
33
+
34
+ def store(key, value, options = {})
35
+ ret = super(key, value)
36
+ update_options(key, options)
37
+ ret
38
+ end
39
+
40
+ private
41
+ def update_options(key, options)
42
+ if options[:expires_in]
43
+ @expiration[key] = (Time.now + options[:expires_in])
44
+ end
45
+ end
46
+ end
47
+
48
+ module StringExpires
49
+ include Expires
50
+
51
+ def check_expired(key)
52
+ if @expiration[key] && Time.now > Time.at(@expiration[key].to_i)
53
+ @expiration.delete(key)
54
+ delete(key)
55
+ end
56
+ end
57
+
58
+ private
59
+ def update_options(key, options)
60
+ if options[:expires_in]
61
+ @expiration[key] = (Time.now + options[:expires_in]).to_i.to_s
62
+ end
63
+ end
64
+ end
65
+
66
+ module Defaults
67
+ def fetch(key, value = nil)
68
+ value ||= block_given? ? yield(key) : default
69
+ self[key] || value
70
+ end
71
+
72
+ def store(key, value, options = {})
73
+ self[key] = value
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,53 @@
1
+ begin
2
+ require 'bdb'
3
+ rescue LoadError
4
+ puts "You need bdb gem to use Bdb moneta store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+
10
+ class Berkeley
11
+ include Defaults
12
+
13
+ def initialize(options={})
14
+ file = @file = options[:file]
15
+ @db = Bdb::Db.new()
16
+ @db.open(nil, file, nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
17
+ unless options[:skip_expires]
18
+ @expiration = Moneta::Berkeley.new(:file => "#{file}_expiration", :skip_expires => true )
19
+ self.extend(StringExpires)
20
+ end
21
+ end
22
+
23
+ module Implementation
24
+ def key?(key)
25
+ nil | self[key]
26
+ end
27
+
28
+ alias has_key? key?
29
+
30
+ def []=(key,value)
31
+ @db[key] = value
32
+ end
33
+
34
+ def [](key)
35
+ @db[key]
36
+ end
37
+
38
+ def delete(key)
39
+ value = self[key]
40
+ @db.del(nil,key,0) if value
41
+ value
42
+ end
43
+
44
+ def clear
45
+ @db.truncate(nil)
46
+ end
47
+ end
48
+
49
+ include Implementation
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,63 @@
1
+ begin
2
+ require "couchrest"
3
+ rescue LoadError
4
+ puts "You need the couchrest gem to use the CouchDB store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+ class Couch
10
+ include Defaults
11
+
12
+ def initialize(options = {})
13
+ @db = ::CouchRest.database!(options[:db])
14
+ unless options[:skip_expires]
15
+ @expiration = Moneta::Couch.new(:db => "#{options[:db]}_expiration", :skip_expires => true)
16
+ self.extend(StringExpires)
17
+ end
18
+ end
19
+
20
+ def key?(key)
21
+ !self[key].nil?
22
+ rescue RestClient::ResourceNotFound
23
+ false
24
+ end
25
+
26
+ alias has_key? key?
27
+
28
+ def [](key)
29
+ @db.get(key)["data"]
30
+ rescue RestClient::ResourceNotFound
31
+ nil
32
+ end
33
+
34
+ def []=(key, value)
35
+ @db.save_doc("_id" => key, :data => value)
36
+ rescue RestClient::RequestFailed
37
+ self[key]
38
+ end
39
+
40
+ def delete(key)
41
+ value = @db.get(key)
42
+ @db.delete_doc({"_id" => value["_id"], "_rev" => value["_rev"]}) if value
43
+ value["data"]
44
+ rescue RestClient::ResourceNotFound
45
+ nil
46
+ end
47
+
48
+ def update_key(key, options = {})
49
+ val = self[key]
50
+ self.store(key, val, options)
51
+ rescue RestClient::ResourceNotFound
52
+ nil
53
+ end
54
+
55
+ def clear
56
+ @db.recreate!
57
+ end
58
+
59
+ def delete_store
60
+ @db.delete!
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,117 @@
1
+ begin
2
+ gem "dm-core", "0.9.10"
3
+ require "dm-core"
4
+ rescue LoadError
5
+ puts "You need the dm-core gem in order to use the DataMapper moneta store"
6
+ exit
7
+ end
8
+
9
+ class MonetaHash
10
+ include DataMapper::Resource
11
+
12
+ property :the_key, String, :key => true
13
+ property :value, Object, :lazy => false
14
+ property :expires, Time
15
+
16
+ def self.value(key)
17
+ obj = self.get(key)
18
+ obj && obj.value
19
+ end
20
+ end
21
+
22
+ module Moneta
23
+ class DataMapper
24
+ class Expiration
25
+ def initialize(klass, repository)
26
+ @klass = klass
27
+ @repository = repository
28
+ end
29
+
30
+ def [](key)
31
+ if obj = get(key)
32
+ obj.expires
33
+ end
34
+ end
35
+
36
+ def []=(key, value)
37
+ obj = get(key)
38
+ obj.expires = value
39
+ obj.save(@repository)
40
+ end
41
+
42
+ def delete(key)
43
+ obj = get(key)
44
+ obj.expires = nil
45
+ obj.save(@repository)
46
+ end
47
+
48
+ private
49
+ def get(key)
50
+ repository(@repository) { @klass.get(key) }
51
+ end
52
+ end
53
+
54
+ def initialize(options = {})
55
+ @repository = options.delete(:repository) || :moneta
56
+ ::DataMapper.setup(@repository, options[:setup])
57
+ repository_context { MonetaHash.auto_upgrade! }
58
+ @hash = MonetaHash
59
+ @expiration = Expiration.new(MonetaHash, @repository)
60
+ end
61
+
62
+ module Implementation
63
+ def key?(key)
64
+ repository_context { !!@hash.get(key) }
65
+ end
66
+
67
+ def has_key?(key)
68
+ repository_context { !!@hash.get(key) }
69
+ end
70
+
71
+ def [](key)
72
+ repository_context { @hash.value(key) }
73
+ end
74
+
75
+ def []=(key, value)
76
+ repository_context {
77
+ obj = @hash.get(key)
78
+ if obj
79
+ obj.update(key, value)
80
+ else
81
+ @hash.create(:the_key => key, :value => value)
82
+ end
83
+ }
84
+ end
85
+
86
+ def fetch(key, value = nil)
87
+ repository_context {
88
+ value ||= block_given? ? yield(key) : default
89
+ self[key] || value
90
+ }
91
+ end
92
+
93
+ def delete(key)
94
+ repository_context {
95
+ value = self[key]
96
+ @hash.all(:the_key => key).destroy!
97
+ value
98
+ }
99
+ end
100
+
101
+ def store(key, value, options = {})
102
+ repository_context { self[key] = value }
103
+ end
104
+
105
+ def clear
106
+ repository_context { @hash.all.destroy! }
107
+ end
108
+
109
+ private
110
+ def repository_context
111
+ repository(@repository) { yield }
112
+ end
113
+ end
114
+ include Implementation
115
+ include Expires
116
+ end
117
+ end
@@ -0,0 +1,91 @@
1
+ begin
2
+ require "xattr"
3
+ rescue LoadError
4
+ puts "You need the xattr gem to use the File moneta store"
5
+ exit
6
+ end
7
+ require "fileutils"
8
+
9
+ module Moneta
10
+ class File
11
+ class Expiration
12
+ def initialize(directory)
13
+ @directory = directory
14
+ end
15
+
16
+ def [](key)
17
+ attrs = xattr(key)
18
+ ret = Marshal.load(attrs.get("moneta_expires"))
19
+ rescue Errno::ENOENT, SystemCallError
20
+ end
21
+
22
+ def []=(key, value)
23
+ attrs = xattr(key)
24
+ attrs.set("moneta_expires", Marshal.dump(value))
25
+ end
26
+
27
+ def delete(key)
28
+ attrs = xattr(key)
29
+ attrs.remove("moneta_expires")
30
+ end
31
+
32
+ private
33
+ def xattr(key)
34
+ ::Xattr.new(::File.join(@directory, key))
35
+ end
36
+ end
37
+
38
+ def initialize(options = {})
39
+ @directory = options[:path]
40
+ if ::File.file?(@directory)
41
+ raise StandardError, "The path you supplied #{@directory} is a file"
42
+ elsif !::File.exists?(@directory)
43
+ FileUtils.mkdir_p(@directory)
44
+ end
45
+
46
+ @expiration = Expiration.new(@directory)
47
+ end
48
+
49
+ module Implementation
50
+ def key?(key)
51
+ ::File.exist?(path(key))
52
+ end
53
+
54
+ alias has_key? key?
55
+
56
+ def [](key)
57
+ if ::File.exist?(path(key))
58
+ Marshal.load(::File.read(path(key)))
59
+ end
60
+ end
61
+
62
+ def []=(key, value)
63
+ ::File.open(path(key), "w") do |file|
64
+ contents = Marshal.dump(value)
65
+ file.puts(contents)
66
+ end
67
+ end
68
+
69
+ def delete(key)
70
+ value = self[key]
71
+ FileUtils.rm(path(key))
72
+ value
73
+ rescue Errno::ENOENT
74
+ end
75
+
76
+ def clear
77
+ FileUtils.rm_rf(@directory)
78
+ FileUtils.mkdir(@directory)
79
+ end
80
+
81
+ private
82
+ def path(key)
83
+ ::File.join(@directory, key)
84
+ end
85
+ end
86
+ include Implementation
87
+ include Defaults
88
+ include Expires
89
+
90
+ end
91
+ end
data/lib/moneta/lmc.rb ADDED
@@ -0,0 +1,52 @@
1
+ begin
2
+ require "localmemcache"
3
+ rescue LoadError
4
+ puts "You need the localmemcache gem to use the LMC moneta store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+ class Expiration
10
+ def initialize(hash)
11
+ @hash = hash
12
+ end
13
+
14
+ def [](key) @hash["#{key}__!__expiration"] end
15
+ def []=(key, value) @hash["#{key}__!__expiration"] = value end
16
+
17
+ def delete(key)
18
+ key = "#{key}__!__expiration"
19
+ value = @hash[key]
20
+ @hash.delete(key)
21
+ value
22
+ end
23
+ end
24
+
25
+ class LMC
26
+ include Defaults
27
+
28
+ module Implementation
29
+ def initialize(options = {})
30
+ @hash = LocalMemCache.new(:filename => options[:filename])
31
+ @expiration = Expiration.new(@hash)
32
+ end
33
+
34
+ def [](key) @hash[key] end
35
+ def []=(key, value) @hash[key] = value end
36
+ def clear() @hash.clear end
37
+
38
+ def key?(key)
39
+ @hash.keys.include?(key)
40
+ end
41
+
42
+ def delete(key)
43
+ value = @hash[key]
44
+ @hash.delete(key)
45
+ value
46
+ end
47
+ end
48
+ include Implementation
49
+ include StringExpires
50
+
51
+ end
52
+ end
@@ -0,0 +1,52 @@
1
+ begin
2
+ require "memcached"
3
+ rescue LoadError
4
+ require "memcache"
5
+ rescue LoadError
6
+ puts "You need the memcache gem to use the Memcache moneta store"
7
+ exit
8
+ end
9
+
10
+ module Moneta
11
+ class Memcache
12
+ include Defaults
13
+
14
+ def initialize(options = {})
15
+ @cache = MemCache.new(options.delete(:server), options)
16
+ end
17
+
18
+ def key?(key)
19
+ !self[key].nil?
20
+ end
21
+
22
+ alias has_key? key?
23
+
24
+ def [](key)
25
+ @cache.get(key)
26
+ end
27
+
28
+ def []=(key, value)
29
+ store(key, value)
30
+ end
31
+
32
+ def delete(key)
33
+ value = self[key]
34
+ @cache.delete(key) if value
35
+ value
36
+ end
37
+
38
+ def store(key, value, options = {})
39
+ args = [key, value, options[:expires_in]].compact
40
+ @cache.set(*args)
41
+ end
42
+
43
+ def update_key(key, options = {})
44
+ val = self[key]
45
+ self.store(key, val, options)
46
+ end
47
+
48
+ def clear
49
+ @cache.flush_all
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,11 @@
1
+ module Moneta
2
+ class Memory < Hash
3
+ include Expires
4
+
5
+ def initialize(*args)
6
+ @expiration = {}
7
+ super
8
+ end
9
+
10
+ end
11
+ end
@@ -0,0 +1,58 @@
1
+ begin
2
+ require "mongo"
3
+ rescue LoadError
4
+ puts "You need the mongo gem to use the MongoDB moneta store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+ class MongoDB
10
+ include Defaults
11
+
12
+ def initialize(options = {})
13
+ options = {
14
+ :host => ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
15
+ :port => ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT,
16
+ :db => 'cache',
17
+ :collection => 'cache'
18
+ }.update(options)
19
+ conn = XGen::Mongo::Driver::Mongo.new(options[:host], options[:port])
20
+ @cache = conn.db(options[:db]).collection(options[:collection])
21
+ end
22
+
23
+ def key?(key)
24
+ !!self[key]
25
+ end
26
+
27
+ def [](key)
28
+ res = @cache.find_first('_id' => key)
29
+ res = nil if res && res['expires'] && Time.now > res['expires']
30
+ res && res['data']
31
+ end
32
+
33
+ def []=(key, value)
34
+ store(key, value)
35
+ end
36
+
37
+ def delete(key)
38
+ value = self[key]
39
+ @cache.remove('_id' => key) if value
40
+ value
41
+ end
42
+
43
+ def store(key, value, options = {})
44
+ exp = options[:expires_in] ? (Time.now + options[:expires_in]) : nil
45
+ @cache.repsert({ '_id' => key }, { '_id' => key, 'data' => value, 'expires' => exp })
46
+ end
47
+
48
+ def update_key(key, options = {})
49
+ val = self[key]
50
+ self.store(key, val, options)
51
+ end
52
+
53
+ def clear
54
+ @cache.clear
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,49 @@
1
+ begin
2
+ require "redis"
3
+ rescue LoadError
4
+ puts "You need the redis gem to use the Redis store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+ class Redis
10
+ include Defaults
11
+
12
+ def initialize(options = {})
13
+ @cache = ::Redis.new(options)
14
+ end
15
+
16
+ def key?(key)
17
+ !@cache[key].nil?
18
+ end
19
+
20
+ alias has_key? key?
21
+
22
+ def [](key)
23
+ @cache.get(key)
24
+ end
25
+
26
+ def []=(key, value)
27
+ store(key, value)
28
+ end
29
+
30
+ def delete(key)
31
+ value = @cache[key]
32
+ @cache.delete(key) if value
33
+ value
34
+ end
35
+
36
+ def store(key, value, options = {})
37
+ @cache.set(key, value, options[:expires_in])
38
+ end
39
+
40
+ def update_key(key, options = {})
41
+ val = @cache[key]
42
+ self.store(key, val, options)
43
+ end
44
+
45
+ def clear
46
+ @cache.flush_db
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,41 @@
1
+ begin
2
+ require "rufus/tokyo"
3
+ rescue LoadError
4
+ puts "You need the rufus gem to use the Rufus moneta store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+ class BasicRufus < ::Rufus::Tokyo::Cabinet
10
+ include Defaults
11
+
12
+ def initialize(options = {})
13
+ file = options[:file]
14
+ super("#{file}.tch")
15
+ end
16
+
17
+ def key?(key)
18
+ !!self[key]
19
+ end
20
+
21
+ def [](key)
22
+ if val = super
23
+ Marshal.load(val.unpack("m")[0])
24
+ end
25
+ end
26
+
27
+ def []=(key, value)
28
+ super(key, [Marshal.dump(value)].pack("m"))
29
+ end
30
+ end
31
+
32
+ class Rufus < BasicRufus
33
+ include Expires
34
+
35
+ def initialize(options = {})
36
+ file = options[:file]
37
+ @expiration = BasicRufus.new(:file => "#{file}_expires")
38
+ super
39
+ end
40
+ end
41
+ end
data/lib/moneta/s3.rb ADDED
@@ -0,0 +1,164 @@
1
+ begin
2
+ require "right_aws"
3
+ rescue LoadError
4
+ puts "You need the RightScale AWS gem to use the S3 moneta store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+ # An S3 implementation of Moneta
10
+ #
11
+ # Example usage:
12
+ #
13
+ # require 'rubygems'
14
+ # require 'moneta'
15
+ # require 'moneta/s3'
16
+ #
17
+ # store = Moneta::S3.new(
18
+ # :access_key_id => 'ACCESS_KEY_ID',
19
+ # :secret_access_key => 'SECRET_ACCESS_KEY',
20
+ # :bucket => 'a_bucket'
21
+ # )
22
+ # store['somefile']
23
+ class S3
24
+ include Defaults
25
+
26
+ # Initialize the Moneta::S3 store.
27
+ #
28
+ # Required values passed in the options hash:
29
+ # * <tt>:access_key_id</tt>: The access id key
30
+ # * <tt>:secret_access_key</tt>: The secret key
31
+ # * <tt>:bucket</tt>: The name of bucket. Will be created if it doesn't
32
+ # exist.
33
+ # * <tt>:multi_thread</tt>: Set to true if using threading
34
+ def initialize(options = {})
35
+ validate_options(options)
36
+ s3 = RightAws::S3.new(
37
+ options[:access_key_id],
38
+ options[:secret_access_key],
39
+ {
40
+ :logger => logger,
41
+ :multi_thread => options.delete(:multi_thread) || false
42
+ }
43
+ )
44
+ @bucket = s3.bucket(options.delete(:bucket), true)
45
+ end
46
+
47
+ def key?(key)
48
+ !s3_key(key).nil?
49
+ end
50
+
51
+ alias has_key? key?
52
+
53
+ def [](key)
54
+ get(key)
55
+ end
56
+
57
+ def []=(key, value)
58
+ store(key, value)
59
+ end
60
+
61
+ def delete(key)
62
+ k = s3_key(key)
63
+ if k
64
+ value = k.get
65
+ k.delete
66
+ value
67
+ end
68
+ end
69
+
70
+ # Store the key/value pair.
71
+ #
72
+ # Options:
73
+ # *<tt>:meta_headers</tt>: Meta headers passed to S3
74
+ # *<tt>:perms</tt>: Permissions passed to S3
75
+ # *<tt>:headers</tt>: Headers sent as part of the PUT request
76
+ # *<tt>:expires_in</tt>: Number of seconds until expiration
77
+ def store(key, value, options = {})
78
+ debug "store(key=#{key}, value=#{value}, options=#{options.inspect})"
79
+ meta_headers = meta_headers_from_options(options)
80
+ perms = options[:perms]
81
+ headers = options[:headers] || {}
82
+
83
+ case value
84
+ when IO
85
+ @bucket.put(key, value.read, meta_headers, perms, headers)
86
+ else
87
+ @bucket.put(key, value, meta_headers, perms, headers)
88
+ end
89
+ end
90
+
91
+ def update_key(key, options = {})
92
+ debug "update_key(key=#{key}, options=#{options.inspect})"
93
+ k = s3_key(key, false)
94
+ k.save_meta(meta_headers_from_options(options)) unless k.nil?
95
+ end
96
+
97
+ def clear
98
+ @bucket.clear
99
+ end
100
+
101
+ protected
102
+ def logger
103
+ @logger ||= begin
104
+ logger = Logger.new(STDOUT)
105
+ logger.level = Logger::FATAL
106
+ logger
107
+ end
108
+ end
109
+
110
+ private
111
+ def validate_options(options)
112
+ unless options[:access_key_id]
113
+ raise RuntimeError, ":access_key_id is required in options"
114
+ end
115
+ unless options[:secret_access_key]
116
+ raise RuntimeError, ":secret_access_key is required in options"
117
+ end
118
+ unless options[:bucket]
119
+ raise RuntimeError, ":bucket is required in options"
120
+ end
121
+ end
122
+
123
+ def get(key)
124
+ k = s3_key(key)
125
+ k.nil? ? nil : k.get
126
+ end
127
+
128
+ def s3_key(key, nil_if_expired=true)
129
+ begin
130
+ s3_key = @bucket.key(key, true)
131
+ if s3_key.exists?
132
+ logger.debug "[Moneta::S3] key exists: #{key}"
133
+ if s3_key.meta_headers.has_key?('expires-at')
134
+ expires_at = Time.parse(s3_key.meta_headers['expires-at'])
135
+ if Time.now > expires_at && nil_if_expired
136
+ # TODO delete the object?
137
+ debug "key expired: #{key} (@#{s3_key.meta_headers['expires-at']})"
138
+ return nil
139
+ end
140
+ end
141
+ return s3_key
142
+ else
143
+ debug "key does not exist: #{key}"
144
+ end
145
+ rescue RightAws::AwsError => e
146
+ debug "key does not exist: #{key}"
147
+ end
148
+ nil
149
+ end
150
+
151
+ def meta_headers_from_options(options={})
152
+ meta_headers = options[:meta_headers] || {}
153
+ if options[:expires_in]
154
+ meta_headers['expires-at'] = (Time.now + options[:expires_in]).rfc2822
155
+ end
156
+ debug "setting expires-at: #{meta_headers['expires-at']}"
157
+ meta_headers
158
+ end
159
+
160
+ def debug(message)
161
+ logger.debug "[Moneta::S3] #{message}"
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,33 @@
1
+ require "sdbm"
2
+
3
+ module Moneta
4
+ class BasicSDBM < ::SDBM
5
+ include Defaults
6
+
7
+ def [](key)
8
+ if val = super
9
+ Marshal.load(val)
10
+ end
11
+ end
12
+
13
+ def []=(key, value)
14
+ super(key, Marshal.dump(value))
15
+ end
16
+
17
+ def delete(key)
18
+ if val = super
19
+ Marshal.load(val)
20
+ end
21
+ end
22
+ end
23
+
24
+ class SDBM < BasicSDBM
25
+ include Expires
26
+
27
+ def initialize(options = {})
28
+ raise "No :file option specified" unless file = options[:file]
29
+ @expiration = BasicSDBM.new("#{file}_expires")
30
+ super(file)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,58 @@
1
+ begin
2
+ require "rufus/tokyo/tyrant"
3
+ rescue LoadError
4
+ puts "You need the rufus gem to use the Tyrant moneta store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+ class Tyrant < ::Rufus::Tokyo::Tyrant
10
+ include Defaults
11
+
12
+ module Implementation
13
+ def initialize(options = {})
14
+ host = options[:host]
15
+ port = options[:port]
16
+ super(host, port)
17
+ end
18
+
19
+ def key?(key)
20
+ !!self[key]
21
+ end
22
+
23
+ def [](key)
24
+ if val = super
25
+ Marshal.load(val.unpack("m")[0])
26
+ end
27
+ end
28
+
29
+ def []=(key, value)
30
+ super(key, [Marshal.dump(value)].pack("m"))
31
+ end
32
+ end
33
+
34
+ include Implementation
35
+ include Expires
36
+
37
+ def initialize(options = {})
38
+ super
39
+ @expiration = Expiration.new(options)
40
+ end
41
+
42
+ class Expiration < ::Rufus::Tokyo::Tyrant
43
+ include Implementation
44
+
45
+ def [](key)
46
+ super("#{key}__expiration")
47
+ end
48
+
49
+ def []=(key, value)
50
+ super("#{key}__expiration", value)
51
+ end
52
+
53
+ def delete(key)
54
+ super("#{key}__expiration")
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,58 @@
1
+ begin
2
+ require "xattr"
3
+ rescue LoadError
4
+ puts "You need the xattr gem to use the Xattr moneta store"
5
+ exit
6
+ end
7
+ require "fileutils"
8
+
9
+ module Moneta
10
+ class Xattr
11
+ include Defaults
12
+
13
+ def initialize(options = {})
14
+ file = options[:file]
15
+ @hash = ::Xattr.new(file)
16
+ FileUtils.mkdir_p(::File.dirname(file))
17
+ FileUtils.touch(file)
18
+ unless options[:skip_expires]
19
+ @expiration = Moneta::Xattr.new(:file => "#{file}_expiration", :skip_expires => true)
20
+ self.extend(Expires)
21
+ end
22
+ end
23
+
24
+ module Implementation
25
+
26
+ def key?(key)
27
+ @hash.list.include?(key)
28
+ end
29
+
30
+ alias has_key? key?
31
+
32
+ def [](key)
33
+ return nil unless key?(key)
34
+ Marshal.load(@hash.get(key))
35
+ end
36
+
37
+ def []=(key, value)
38
+ @hash.set(key, Marshal.dump(value))
39
+ end
40
+
41
+ def delete(key)
42
+ return nil unless key?(key)
43
+ value = self[key]
44
+ @hash.remove(key)
45
+ value
46
+ end
47
+
48
+ def clear
49
+ @hash.list.each do |item|
50
+ @hash.remove(item)
51
+ end
52
+ end
53
+
54
+ end
55
+ include Implementation
56
+
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aeden-moneta
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.2
5
+ platform: ruby
6
+ authors:
7
+ - Yehuda Katz
8
+ autorequire: moneta
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-03 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A unified interface to key/value stores
17
+ email: wycats@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - LICENSE
25
+ - TODO
26
+ files:
27
+ - LICENSE
28
+ - README
29
+ - Rakefile
30
+ - TODO
31
+ - lib/moneta
32
+ - lib/moneta/berkeley.rb
33
+ - lib/moneta/couch.rb
34
+ - lib/moneta/datamapper.rb
35
+ - lib/moneta/file.rb
36
+ - lib/moneta/lmc.rb
37
+ - lib/moneta/memcache.rb
38
+ - lib/moneta/memory.rb
39
+ - lib/moneta/mongodb.rb
40
+ - lib/moneta/redis.rb
41
+ - lib/moneta/rufus.rb
42
+ - lib/moneta/s3.rb
43
+ - lib/moneta/sdbm.rb
44
+ - lib/moneta/tyrant.rb
45
+ - lib/moneta/xattr.rb
46
+ - lib/moneta.rb
47
+ has_rdoc: true
48
+ homepage: http://www.yehudakatz.com
49
+ post_install_message:
50
+ rdoc_options: []
51
+
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.2.0
70
+ signing_key:
71
+ specification_version: 2
72
+ summary: A unified interface to key/value stores
73
+ test_files: []
74
+