gmalamid-key_value_store 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2009 nutrun.com, George Malamidis, Andy Kent, Mike Jones
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,42 @@
1
+ = KeyValueStore - Simple Tokyo Cabinet interface
2
+
3
+ == Dependencies
4
+
5
+ KeyValueStore Depends on Tokyo Cabinet ( http://tokyocabinet.sourceforge.net/index.html ) and the Tokyo Cabinet Ruby bindings ( http://tokyocabinet.sourceforge.net/rubypkg ). Also, the json gem ( http://json.rubyforge.org ).
6
+
7
+ == Usage
8
+
9
+ KeyValueStore is a minimal interface for persisting Ruby objects in Tokyo Cabinet key-value
10
+ stores. Objects are serialized in JSON on storage and deserialized on retrieval. An object needs to implement three methods in order to be successfully stored and retrieved : <tt>storage_key</tt>, <tt>to_storage_hash</tt> and <tt>self.from_storage_hash(h)</tt>.
11
+
12
+ == Usage example
13
+
14
+ class Guitar
15
+ def initialize(model, color)
16
+ @model, @color = model, color
17
+ end
18
+
19
+ def storage_key
20
+ @model
21
+ end
22
+
23
+ def to_storage_hash
24
+ {'model' => @model, 'color' => @color}
25
+ end
26
+
27
+ def self.from_storage_hash(h)
28
+ self.new(h['model'], h['color'])
29
+ end
30
+ end
31
+
32
+ store = KeyValueStore::Hash.new('guitars', Guitar)
33
+ les_paul = Guitar.new('les_paul', 'sunburst')
34
+
35
+ #store
36
+ store.put(les_paul)
37
+
38
+ #retrieve
39
+ les_paul = store.get('les_paul')
40
+
41
+ #iterate
42
+ store.each {|guitar| puts guitar.color}
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+ Rake::TestTask.new { |t| t.pattern = 'test/*_test.rb' }
@@ -0,0 +1,14 @@
1
+ GEMSPEC =Gem::Specification.new do |s|
2
+ s.name = 'key_value_store'
3
+ s.version = '0.0.1'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.rubyforge_project = "key_value_store"
6
+ s.summary, s.description = 'Simple Tokyo Cabinet interface'
7
+ s.authors = 'Mike Jones, Andy Kent, George Malamidis'
8
+ s.email = 'george@nutrun.com'
9
+ s.homepage = 'http://nutrun.com'
10
+ s.has_rdoc = true
11
+ s.rdoc_options += ['--quiet', '--title', 'Datastore', '--main', 'README.rdoc', '--inline-source']
12
+ s.extra_rdoc_files = ['README.rdoc', 'COPYING']
13
+ s.files = ["COPYING","Rakefile","README.rdoc","key_value_store.gemspec","lib/key_value_store.rb"]
14
+ end
@@ -0,0 +1,251 @@
1
+ require 'tokyocabinet'
2
+ require 'fileutils'
3
+ require 'rubygems'
4
+ require 'json'
5
+
6
+ module KeyValueStore
7
+ def self.datadir=(datadir)
8
+ @datadir = datadir
9
+ end
10
+
11
+ def self.datadir
12
+ if @datadir
13
+ "#{@datadir}/#{environment}"
14
+ else
15
+ "#{ENV['HOME']}/.data/#{environment}"
16
+ end
17
+ end
18
+
19
+ def self.environment=(environment)
20
+ @environment = environment
21
+ end
22
+
23
+ def self.environment
24
+ @environment || :development
25
+ end
26
+
27
+ module API
28
+ def initialize(store_name, type, permission = :write)
29
+ @store_name, @type, @permission = store_name, type, permission
30
+ end
31
+
32
+ def delete(key)
33
+ connection.out(key)
34
+ end
35
+
36
+ def clear
37
+ connection.vanish
38
+ end
39
+
40
+ def count
41
+ connection.rnum
42
+ end
43
+
44
+ alias size count
45
+
46
+ def connection
47
+ return @connection if @connection && @connection.path
48
+ @connection = Connections.connection(@store_name, @permission, extension)
49
+ end
50
+
51
+ def close
52
+ connection.close
53
+ end
54
+
55
+ def sync
56
+ connection.sync
57
+ end
58
+
59
+ def rm_f
60
+ path = connection.path
61
+ if path
62
+ close
63
+ FileUtils.rm_f(path)
64
+ end
65
+ end
66
+
67
+ def rename(new_name)
68
+ path = connection.path
69
+ return nil unless path
70
+ close
71
+ FileUtils.cp(path, File.join(File.dirname(path), "#{new_name}.#{extension}"))
72
+ @store_name = new_name
73
+ connection
74
+ FileUtils.rm(path)
75
+ end
76
+ end
77
+
78
+ class Hash
79
+ include API
80
+
81
+ def put(obj)
82
+ connection[obj.storage_key.to_s] = JSON.dump(obj.to_storage_hash)
83
+ end
84
+
85
+ def get(key)
86
+ retval = connection[key.to_s]
87
+ if retval
88
+ @type.from_storage_hash(JSON.load(retval))
89
+ else
90
+ nil
91
+ end
92
+ end
93
+
94
+ def get_raw(key)
95
+ connection[key.to_s]
96
+ end
97
+
98
+ def each(&block)
99
+ connection.iterinit
100
+ while val = connection.iternext
101
+ yield get(val)
102
+ end
103
+ end
104
+
105
+ def each_raw(&block)
106
+ connection.iterinit
107
+ while val = connection.iternext
108
+ yield get_raw(val)
109
+ end
110
+ end
111
+
112
+ def extension
113
+ 'tch'
114
+ end
115
+ end
116
+
117
+ class BTree
118
+ include API
119
+
120
+ def get(key)
121
+ if results = connection.getlist(key)
122
+ results.map do |e|
123
+ instance = @type.from_storage_hash(JSON.load(e))
124
+ instance.instance_variable_set(:@storage_key, key)
125
+ instance
126
+ end
127
+ else
128
+ nil
129
+ end
130
+ end
131
+
132
+ def get_raw(key)
133
+ connection.getlist(key)
134
+ end
135
+
136
+ def put(obj)
137
+ connection.putdup(obj.storage_key, JSON.dump(obj.to_storage_hash))
138
+ end
139
+
140
+ def extension
141
+ 'tcb'
142
+ end
143
+ end
144
+
145
+ class MemoryHash
146
+ def initialize(type)
147
+ @type = type
148
+ end
149
+
150
+ def put(obj)
151
+ store[obj.storage_key] = JSON.dump(obj.to_storage_hash)
152
+ end
153
+
154
+ def get(key)
155
+ if found = store[key]
156
+ @type.from_storage_hash(JSON.load(found))
157
+ else
158
+ nil
159
+ end
160
+ end
161
+
162
+ def get_raw(key)
163
+ store[key]
164
+ end
165
+
166
+ def each(&block)
167
+ store.each { |k,v| yield @type.from_storage_hash(JSON.load(v)) }
168
+ end
169
+
170
+ def count
171
+ store.size
172
+ end
173
+
174
+ alias size count
175
+
176
+ def delete(key)
177
+ store.delete(key)
178
+ end
179
+
180
+ def clear
181
+ store.clear
182
+ end
183
+
184
+
185
+ def store
186
+ @store ||= {}
187
+ end
188
+ end
189
+
190
+ class Connections
191
+ include TokyoCabinet
192
+ include Enumerable
193
+
194
+ STORE_TYPES = {'tch' => TokyoCabinet::HDB, 'tcb' => TokyoCabinet::BDB}
195
+
196
+ def initialize
197
+ @connections = {}
198
+ trap('INT') do
199
+ close_all
200
+ exit
201
+ end
202
+ end
203
+
204
+ def connection(store, permission, extension)
205
+ FileUtils.mkdir_p(KeyValueStore.datadir)
206
+ file = File.join(KeyValueStore.datadir, "#{store}.#{extension}")
207
+ return @connections[file] if @connections[file] && @connections[file].path
208
+ @connections[file] = establish_connection(file, permission, STORE_TYPES[extension])
209
+ end
210
+
211
+ def close_all
212
+ each {|file, connection| connection.close }
213
+ end
214
+
215
+ def sync_all
216
+ each {|file, connection| connection.sync }
217
+ end
218
+
219
+ def clear_all
220
+ each {|file, connection| connection.vanish }
221
+ end
222
+
223
+ def each(&blk)
224
+ @connections.each(&blk)
225
+ end
226
+
227
+ def has_open_connections?
228
+ find {|file, connection| connection.path != nil }
229
+ end
230
+
231
+ private
232
+
233
+ def establish_connection(file, permission, type)
234
+ conn = type.new
235
+ conn.setxmsiz(0)
236
+ permissions = permission == :write ? (HDB::OWRITER | HDB::OCREAT) : HDB::OREADER
237
+ raise "#{conn.errmsg(conn.ecode)} [#{file}]" unless conn.open(file, permissions)
238
+ conn
239
+ end
240
+
241
+ class << self
242
+ def default
243
+ @connection_pool ||= Connections.new
244
+ end
245
+
246
+ def method_missing(meth, *args)
247
+ default.send(meth, *args)
248
+ end
249
+ end
250
+ end
251
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gmalamid-key_value_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mike Jones, Andy Kent, George Malamidis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: george@nutrun.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - COPYING
25
+ files:
26
+ - COPYING
27
+ - Rakefile
28
+ - README.rdoc
29
+ - key_value_store.gemspec
30
+ - lib/key_value_store.rb
31
+ has_rdoc: true
32
+ homepage: http://nutrun.com
33
+ post_install_message:
34
+ rdoc_options:
35
+ - --quiet
36
+ - --title
37
+ - Datastore
38
+ - --main
39
+ - README.rdoc
40
+ - --inline-source
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project: key_value_store
58
+ rubygems_version: 1.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: Simple Tokyo Cabinet interface
62
+ test_files: []
63
+