wycats-moneta 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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,44 @@
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
+
14
+ All stores support key expiration, but only memcache supports it natively. All other stores
15
+ emulate expiration.
16
+
17
+ The Moneta API is purposely extremely similar to the Hash API. In order so support an
18
+ identical API across stores, it does not support iteration or partial matches, but that
19
+ might come in a future release.
20
+
21
+ The API:
22
+
23
+ #initialize(options):: options differs per-store, and is used to set up the store
24
+
25
+ #[](key):: retrieve a key. if the key is not available, return nil
26
+
27
+ #[]=(key, value):: set a value for a key. if the key is already used, clobber it.
28
+ keys set using []= will never expire
29
+
30
+ #delete(key):: delete the key from the store and return the current value
31
+
32
+ #key?(key):: true if the key exists, false if it does not
33
+
34
+ #has_key?(key):: alias for key?
35
+
36
+ #store(key, value, options):: same as []=, but you can supply an :expires_in option,
37
+ which will specify a number of seconds before the key
38
+ should expire. In order to support the same features
39
+ across all stores, only full seconds are supported
40
+
41
+ #update_key(key, options):: updates an existing key with a new :expires_in option.
42
+ if the key has already expired, it will not be updated.
43
+
44
+ #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.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"
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,50 @@
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 has_key?(key)
11
+ check_expired(key)
12
+ super
13
+ end
14
+
15
+ def key?(key)
16
+ has_key?(key)
17
+ end
18
+
19
+ def [](key)
20
+ check_expired(key)
21
+ super
22
+ end
23
+
24
+ def fetch(key, default)
25
+ check_expired(key)
26
+ super
27
+ end
28
+
29
+ def delete(key)
30
+ check_expired(key)
31
+ super
32
+ end
33
+
34
+ def update_key(key, options)
35
+ update_options(key, options)
36
+ end
37
+
38
+ def store(key, value, options = {})
39
+ super(key, value)
40
+ update_options(key, options)
41
+ end
42
+
43
+ private
44
+ def update_options(key, options)
45
+ if options[:expires_in]
46
+ @expiration[key] = (Time.now + options[:expires_in])
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,107 @@
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
+ def self.default_repository_name
13
+ :moneta
14
+ end
15
+
16
+ property :the_key, String, :key => true
17
+ property :value, Object, :lazy => false
18
+ property :expires, Time
19
+
20
+ def self.value(key)
21
+ obj = self.get(key)
22
+ obj && obj.value
23
+ end
24
+ end
25
+
26
+ module Moneta
27
+ class DataMapper
28
+ class Expiration
29
+ def initialize(klass)
30
+ @klass = klass
31
+ end
32
+
33
+ def [](key)
34
+ if obj = get(key)
35
+ obj.expires
36
+ end
37
+ end
38
+
39
+ def []=(key, value)
40
+ obj = get(key)
41
+ obj.expires = value
42
+ obj.save
43
+ end
44
+
45
+ def delete(key)
46
+ obj = get(key)
47
+ obj.expires = nil
48
+ obj.save
49
+ end
50
+
51
+ private
52
+ def get(key)
53
+ @klass.get(key)
54
+ end
55
+ end
56
+
57
+ def initialize(options = {})
58
+ ::DataMapper.setup(:moneta, options[:setup])
59
+ MonetaHash.auto_upgrade!
60
+ @hash = MonetaHash
61
+ @expiration = Expiration.new(MonetaHash)
62
+ end
63
+
64
+ module Implementation
65
+ def key?(key)
66
+ !!@hash.get(key)
67
+ end
68
+
69
+ def has_key?(key)
70
+ !!@hash.get(key)
71
+ end
72
+
73
+ def [](key)
74
+ @hash.value(key)
75
+ end
76
+
77
+ def []=(key, value)
78
+ obj = @hash.get(key)
79
+ if obj
80
+ obj.update(key, value)
81
+ else
82
+ @hash.create(:the_key => key, :value => value)
83
+ end
84
+ end
85
+
86
+ def fetch(key, default)
87
+ self[key] || default
88
+ end
89
+
90
+ def delete(key)
91
+ value = self[key]
92
+ @hash.all(:the_key => key).destroy!
93
+ value
94
+ end
95
+
96
+ def store(key, value, options = {})
97
+ self[key] = value
98
+ end
99
+
100
+ def clear
101
+ @hash.all.destroy!
102
+ end
103
+ end
104
+ include Implementation
105
+ include Expires
106
+ end
107
+ end
@@ -0,0 +1,100 @@
1
+ begin
2
+ require "fileutils"
3
+ require "xattr"
4
+ rescue LoadError
5
+ puts "You need the xattr gem to use the File moneta store"
6
+ exit
7
+ end
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(@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
+ def has_key?(key)
55
+ ::File.exist?(path(key))
56
+ end
57
+
58
+ def [](key)
59
+ if ::File.exist?(path(key))
60
+ Marshal.load(::File.read(path(key)))
61
+ end
62
+ end
63
+
64
+ def []=(key, value)
65
+ ::File.open(path(key), "w") do |file|
66
+ contents = Marshal.dump(value)
67
+ file.puts(contents)
68
+ end
69
+ end
70
+
71
+ def fetch(key, default)
72
+ self[key] || default
73
+ end
74
+
75
+ def delete(key)
76
+ value = self[key]
77
+ FileUtils.rm(path(key))
78
+ value
79
+ rescue Errno::ENOENT
80
+ end
81
+
82
+ def store(key, value, options = {})
83
+ self[key] = value
84
+ end
85
+
86
+ def clear
87
+ FileUtils.rm_rf(@directory)
88
+ FileUtils.mkdir(@directory)
89
+ end
90
+
91
+ private
92
+ def path(key)
93
+ ::File.join(@directory, key)
94
+ end
95
+ end
96
+ include Implementation
97
+ include Expires
98
+
99
+ end
100
+ end
@@ -0,0 +1,52 @@
1
+ begin
2
+ require "memcache"
3
+ rescue LoadError
4
+ puts "You need the memcache gem to use the Memcache moneta store"
5
+ exit
6
+ end
7
+
8
+ module Moneta
9
+ class Memcache
10
+ def initialize(options = {})
11
+ @cache = MemCache.new(options.delete(:server), options)
12
+ end
13
+
14
+ def key?(key)
15
+ !self["key"].nil?
16
+ end
17
+
18
+ alias has_key? key?
19
+
20
+ def [](key)
21
+ @cache.get(key)
22
+ end
23
+
24
+ def []=(key, value)
25
+ store(key, value)
26
+ end
27
+
28
+ def fetch(key, default)
29
+ self[key] || default
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,67 @@
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
+
12
+ def initialize(options = {})
13
+ file = options[:file]
14
+ @hash = ::Xattr.new(file)
15
+ FileUtils.mkdir_p(::File.dirname(file))
16
+ FileUtils.touch(file)
17
+ unless options[:skip_expires]
18
+ @expiration = Moneta::Xattr.new(:file => "#{file}_expiration", :skip_expires => true)
19
+ self.extend(Expires)
20
+ end
21
+ end
22
+
23
+ module Implementation
24
+
25
+ def key?(key)
26
+ @hash.list.include?(key)
27
+ end
28
+
29
+ def has_key?(key)
30
+ @hash.list.include?(key)
31
+ end
32
+
33
+ def [](key)
34
+ return nil unless key?(key)
35
+ Marshal.load(@hash.get(key))
36
+ end
37
+
38
+ def []=(key, value)
39
+ @hash.set(key, Marshal.dump(value))
40
+ end
41
+
42
+ def fetch(key, value)
43
+ self[key] || value
44
+ end
45
+
46
+ def delete(key)
47
+ return nil unless key?(key)
48
+ value = self[key]
49
+ @hash.remove(key)
50
+ value
51
+ end
52
+
53
+ def store(key, value, options = {})
54
+ self[key] = value
55
+ end
56
+
57
+ def clear
58
+ @hash.list.each do |item|
59
+ @hash.remove(item)
60
+ end
61
+ end
62
+
63
+ end
64
+ include Implementation
65
+
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wycats-moneta
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Yehuda Katz
8
+ autorequire: moneta
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-12 00:00:00 -08: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/datamapper.rb
33
+ - lib/moneta/file.rb
34
+ - lib/moneta/memcache.rb
35
+ - lib/moneta/memory.rb
36
+ - lib/moneta/xattr.rb
37
+ - lib/moneta.rb
38
+ has_rdoc: true
39
+ homepage: http://www.yehudakatz.com
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.2.0
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: A unified interface to key/value stores
64
+ test_files: []
65
+