kyoto_record 0.0.1 → 0.0.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/kyoto_record.gemspec +1 -1
- data/kyoto_record.rb +62 -35
- data/kyoto_record_spec.rb +129 -2
- metadata +2 -2
data/kyoto_record.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "kyoto_record"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.2"
|
4
4
|
s.summary = "Persist your classes in Kyoto Cabinet."
|
5
5
|
s.description = "\"KyotoRecord\": Kyoto Cabinet wrapper for Ruby binding. Scannable. Indexable. A programming interface made for babies."
|
6
6
|
s.homepage = "http://github.com/thirdreplicator/kyoto_record"
|
data/kyoto_record.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# kyoto_record.rb
|
1
2
|
require 'kyotocabinet'
|
2
3
|
|
3
4
|
module KyotoRecord
|
@@ -9,6 +10,8 @@ module KyotoRecord
|
|
9
10
|
module Cabinet
|
10
11
|
include KyotoCabinet
|
11
12
|
|
13
|
+
DATA_DIR = File.dirname(__FILE__) + '/data'
|
14
|
+
|
12
15
|
def find(id)
|
13
16
|
value = @db.get(id)
|
14
17
|
if value
|
@@ -18,6 +21,16 @@ module KyotoRecord
|
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
24
|
+
def delete(id)
|
25
|
+
@db.remove(id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_all
|
29
|
+
scan(1, 1/0.0) do |obj|
|
30
|
+
obj.delete
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
21
34
|
def scan(start_key=nil, limit=1/0.0, &block)
|
22
35
|
cur = @db.cursor
|
23
36
|
i = 0
|
@@ -52,16 +65,16 @@ module KyotoRecord
|
|
52
65
|
end
|
53
66
|
|
54
67
|
# Utilities
|
55
|
-
def open_db
|
56
|
-
Dir.mkdir(
|
68
|
+
def open_db
|
69
|
+
Dir.mkdir(DATA_DIR) if !Dir.exists?(DATA_DIR)
|
57
70
|
db = DB::new
|
58
|
-
unless db.open(
|
71
|
+
unless db.open(data_file, DB::OWRITER | DB::OCREATE)
|
59
72
|
STDERR.printf("open error: %s\n", db.error)
|
60
73
|
end
|
61
74
|
db
|
62
75
|
end
|
63
76
|
|
64
|
-
def close_db
|
77
|
+
def close_db
|
65
78
|
unless @db.close
|
66
79
|
STDERR.printf("close error: %s\n", db.error)
|
67
80
|
end
|
@@ -70,9 +83,9 @@ module KyotoRecord
|
|
70
83
|
# Utilities
|
71
84
|
|
72
85
|
def set(k,v)
|
73
|
-
@db.set(k, Marshal.dump(v))
|
86
|
+
set_error(k,v) unless @db.set(k, Marshal.dump(v))
|
74
87
|
end
|
75
|
-
|
88
|
+
|
76
89
|
def get(k)
|
77
90
|
val = @db.get(k)
|
78
91
|
Marshal.load(val) if val
|
@@ -97,6 +110,16 @@ module KyotoRecord
|
|
97
110
|
def class_name
|
98
111
|
@class_name ||= self.to_s
|
99
112
|
end
|
113
|
+
|
114
|
+
def data_file
|
115
|
+
"#{DATA_DIR}/#{@base_name}.kch"
|
116
|
+
end
|
117
|
+
|
118
|
+
def set_error(k,v)
|
119
|
+
STDERR.printf("set error for (k,v)=(%s, %s): %s\n", k, v, @db.error)
|
120
|
+
raise @db.error
|
121
|
+
end
|
122
|
+
|
100
123
|
end # Cabinet
|
101
124
|
|
102
125
|
# Reuse the Cabinet module to make indexes of attributes.
|
@@ -112,7 +135,7 @@ module KyotoRecord
|
|
112
135
|
@klass = klass
|
113
136
|
@attr = attribute
|
114
137
|
@base_name = klass.to_s + "_" + attribute.to_s
|
115
|
-
@db = open_db
|
138
|
+
@db = open_db
|
116
139
|
end
|
117
140
|
end # class Index
|
118
141
|
|
@@ -121,35 +144,33 @@ module KyotoRecord
|
|
121
144
|
include Cabinet
|
122
145
|
|
123
146
|
def attr_kyoto( *attrs )
|
124
|
-
@
|
147
|
+
@base_name = class_name
|
148
|
+
@db = open_db
|
125
149
|
|
126
150
|
@attrs ||= {}
|
127
151
|
@indices ||= {}
|
128
152
|
|
129
|
-
#
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
153
|
+
# a general setter
|
154
|
+
define_method :set_attr do |k, v|
|
155
|
+
@values[k] = v
|
156
|
+
end
|
157
|
+
|
158
|
+
# a general getter
|
159
|
+
define_method :get_attr do |k|
|
160
|
+
@values[k]
|
161
|
+
end
|
162
|
+
|
163
|
+
def define_getter_and_setter(attr)
|
164
|
+
define_method :"#{attr}".to_s do
|
165
|
+
@values[attr]
|
134
166
|
end
|
135
167
|
|
136
|
-
#
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
def define_getter_and_setter(attr)
|
142
|
-
define_method :"#{attr}".to_s do
|
143
|
-
@values[attr]
|
144
|
-
end
|
145
|
-
|
146
|
-
define_method :"#{attr}=".to_s do |val|
|
147
|
-
set_attr(attr, val)
|
148
|
-
end
|
149
|
-
end
|
168
|
+
define_method :"#{attr}=".to_s do |val|
|
169
|
+
set_attr(attr, val)
|
170
|
+
end
|
171
|
+
end
|
150
172
|
|
151
|
-
|
152
|
-
end # if
|
173
|
+
define_getter_and_setter(:id)
|
153
174
|
|
154
175
|
# In case multiple attributes were passed in, let's loop over each one.
|
155
176
|
attrs.each do |attr|
|
@@ -189,14 +210,23 @@ EOM
|
|
189
210
|
def save
|
190
211
|
if !id
|
191
212
|
# TODO: use KyotoCabinet::DB#increment
|
192
|
-
id = self.class.last_id + 1
|
213
|
+
self.id = self.class.last_id + 1
|
193
214
|
self.class.last_id = id
|
194
215
|
end
|
195
|
-
|
216
|
+
|
196
217
|
write_indices(id)
|
197
218
|
write_to_kyoto(id, self)
|
198
219
|
end
|
199
220
|
|
221
|
+
def update(attr, value)
|
222
|
+
@values[attr] = value
|
223
|
+
save
|
224
|
+
end
|
225
|
+
|
226
|
+
def delete
|
227
|
+
self.class.delete(self.id)
|
228
|
+
end
|
229
|
+
|
200
230
|
def write_indices(id)
|
201
231
|
self.class.indices.each do |attr, index|
|
202
232
|
index.set(@values[attr], id)
|
@@ -204,14 +234,11 @@ EOM
|
|
204
234
|
end
|
205
235
|
|
206
236
|
def write_to_kyoto(k, v)
|
207
|
-
|
237
|
+
self.class.set(k, v)
|
208
238
|
end
|
209
239
|
|
210
240
|
private
|
211
241
|
|
212
|
-
def set_error(k,v)
|
213
|
-
STDERR.printf("set error for (k,v)=(%s, %s): %s\n", k, v, kc.error)
|
214
|
-
end
|
215
242
|
|
216
243
|
end
|
217
244
|
|
data/kyoto_record_spec.rb
CHANGED
@@ -19,13 +19,20 @@ describe 'KyotoRecord module' do
|
|
19
19
|
File.exist?('./data/A.kch').should be_true
|
20
20
|
end
|
21
21
|
|
22
|
-
it "should
|
22
|
+
it "find should return an instance of the enhanced user-defined class" do
|
23
23
|
@a = A.new
|
24
24
|
@a.x = 3
|
25
25
|
@a.save
|
26
26
|
A.find(1).should be_an_instance_of(A)
|
27
27
|
end
|
28
28
|
|
29
|
+
it "should have an id after being saved." do
|
30
|
+
@a = A.new
|
31
|
+
@a.x = 3
|
32
|
+
@a.save
|
33
|
+
@a.id.should == 1
|
34
|
+
end
|
35
|
+
|
29
36
|
it "should retrieve 3 if 3 was saved into a kyoto attribute" do
|
30
37
|
@a = A.new
|
31
38
|
@a.x = 3
|
@@ -47,6 +54,21 @@ describe 'KyotoRecord module' do
|
|
47
54
|
A.find(1).x.should be_an_instance_of( Time )
|
48
55
|
end
|
49
56
|
|
57
|
+
it "should be able to save 2 different objects using the same variable" do
|
58
|
+
a = A.new
|
59
|
+
a.x = "David"
|
60
|
+
a.save
|
61
|
+
|
62
|
+
a = A.new
|
63
|
+
a.x = "Bob"
|
64
|
+
a.save
|
65
|
+
|
66
|
+
A.find(1).x.should == "David"
|
67
|
+
A.find(1).id.should == 1
|
68
|
+
A.find(2).x.should == "Bob"
|
69
|
+
A.find(2).id.should == 2
|
70
|
+
end
|
71
|
+
|
50
72
|
it "should have a last_id of nil if nothing has been saved yet." do
|
51
73
|
A.last_id.should == 0
|
52
74
|
end
|
@@ -108,7 +130,7 @@ describe 'KyotoRecord module' do
|
|
108
130
|
|
109
131
|
describe "Iterating over the records." do
|
110
132
|
before(:each) do
|
111
|
-
# Insert
|
133
|
+
# Insert some records
|
112
134
|
10.times do |i|
|
113
135
|
a = A.new
|
114
136
|
a.x = i+1
|
@@ -148,6 +170,7 @@ describe 'KyotoRecord module' do
|
|
148
170
|
word.should == "56"
|
149
171
|
end
|
150
172
|
end
|
173
|
+
|
151
174
|
describe "Indexing of attributes." do
|
152
175
|
before(:each) do
|
153
176
|
class A
|
@@ -170,6 +193,110 @@ describe 'KyotoRecord module' do
|
|
170
193
|
A.find_by_username("David").username.should == "David"
|
171
194
|
end
|
172
195
|
end
|
196
|
+
describe "DELETE functionality" do
|
197
|
+
it "should be able to delete records by key (class utility function)" do
|
198
|
+
a = A.new
|
199
|
+
a.username = "David"
|
200
|
+
a.save
|
201
|
+
A.find(1).should_not be_nil
|
202
|
+
A.delete(1)
|
203
|
+
A.find(1).should be_nil
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should be able to delete records by key (instance method)" do
|
207
|
+
a = A.new
|
208
|
+
a.username = "David"
|
209
|
+
a.save
|
210
|
+
A.find(1).should_not be_nil
|
211
|
+
a.delete
|
212
|
+
A.find(1).should be_nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "UPDATE functionality" do
|
217
|
+
it "should be able to update just by saving over the existing one." do
|
218
|
+
a = A.new
|
219
|
+
a.username = "David"
|
220
|
+
a.save
|
221
|
+
|
222
|
+
# Loop him up.
|
223
|
+
a1 = A.find(1)
|
224
|
+
a1.username.should == "David"
|
225
|
+
|
226
|
+
# Modify him.
|
227
|
+
a1.username = "Bob"
|
228
|
+
a1.save
|
229
|
+
|
230
|
+
# Make sure he changed.
|
231
|
+
A.find(1).username.should == "Bob"
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should be able to update with a instance method." do
|
235
|
+
a = A.new
|
236
|
+
a.username = "David"
|
237
|
+
a.save
|
238
|
+
|
239
|
+
A.find(1).update(:username, "Bob")
|
240
|
+
A.find(1).username.should == "Bob"
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should be able to batch update by scanning over a range of ids." do
|
244
|
+
10.times do |i|
|
245
|
+
a = A.new
|
246
|
+
a.username = i+1
|
247
|
+
a.save
|
248
|
+
end
|
249
|
+
|
250
|
+
A.scan(1,5) do |a|
|
251
|
+
a.update(:username, "Anonymous")
|
252
|
+
end
|
253
|
+
|
254
|
+
A.find(1).username.should == "Anonymous"
|
255
|
+
A.find(3).username.should == "Anonymous"
|
256
|
+
A.find(5).username.should == "Anonymous"
|
257
|
+
A.find(6).username.should == 6
|
258
|
+
A.find(7).username.should == 7
|
259
|
+
A.find(10).username.should == 10
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe "DROP the whole database" do
|
264
|
+
it "should be able to drop the whole database." do
|
265
|
+
a = A.new
|
266
|
+
a.username = "David"
|
267
|
+
a.save
|
268
|
+
|
269
|
+
a = A.new
|
270
|
+
a.username = "Bob"
|
271
|
+
a.save
|
272
|
+
|
273
|
+
A.find(1).username.should == "David"
|
274
|
+
A.find(2).username.should == "Bob"
|
275
|
+
|
276
|
+
A.delete_all
|
277
|
+
|
278
|
+
A.find(1).should be_nil
|
279
|
+
A.find(2).should be_nil
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should be able to add more items after the database was dropped." do
|
283
|
+
a = A.new
|
284
|
+
a.username = "David"
|
285
|
+
a.save
|
286
|
+
|
287
|
+
A.find(1).username.should == "David"
|
288
|
+
|
289
|
+
A.delete_all
|
290
|
+
|
291
|
+
b = A.new
|
292
|
+
b.username = "Duck"
|
293
|
+
b.id.should be_nil
|
294
|
+
b.save
|
295
|
+
|
296
|
+
b.id.should == 2
|
297
|
+
A.find(2).should_not be_nil
|
298
|
+
end
|
299
|
+
end
|
173
300
|
|
174
301
|
describe "Auxilliary functions" do
|
175
302
|
it "should be able to know it's own name as a string" do
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: kyoto_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- David Beckwith
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-03-
|
13
|
+
date: 2011-03-05 00:00:00 -08:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|