wycats-moneta 0.5.0

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,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
+