rufus-tokyo 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG.txt +6 -0
  3. data/Rakefile +91 -0
  4. data/doc/decision_table.numbers +0 -0
  5. data/lib/rufus/edo/README.txt +101 -0
  6. data/lib/rufus/edo/tabcore.rb +1 -3
  7. data/lib/rufus/tokyo.rb +1 -2
  8. data/lib/rufus/tokyo/cabinet/lib.rb +4 -7
  9. data/lib/rufus/tokyo/cabinet/table.rb +10 -13
  10. data/lib/rufus/tokyo/cabinet/util.rb +4 -1
  11. data/lib/rufus/tokyo/hmethods.rb +4 -4
  12. data/lib/rufus/tokyo/outlen.rb +5 -1
  13. data/lib/rufus/tokyo/tyrant/abstract.rb +8 -0
  14. data/lib/rufus/tokyo/tyrant/lib.rb +6 -6
  15. data/lib/rufus/tokyo/tyrant/table.rb +9 -1
  16. data/lib/rufus/tokyo/version.rb +32 -0
  17. data/rufus-tokyo.gemspec +135 -0
  18. data/spec/cabinet_btree_spec.rb +92 -0
  19. data/spec/cabinet_fixed_spec.rb +33 -0
  20. data/spec/cabinet_spec.rb +291 -0
  21. data/spec/cabinetconfig_spec.rb +82 -0
  22. data/spec/dystopia_core_spec.rb +124 -0
  23. data/spec/edo_cabinet_btree_spec.rb +123 -0
  24. data/spec/edo_cabinet_fixed_spec.rb +42 -0
  25. data/spec/edo_cabinet_spec.rb +286 -0
  26. data/spec/edo_ntyrant_spec.rb +224 -0
  27. data/spec/edo_ntyrant_table_spec.rb +296 -0
  28. data/spec/edo_table_spec.rb +292 -0
  29. data/spec/hmethods_spec.rb +73 -0
  30. data/spec/incr.lua +23 -0
  31. data/spec/openable_spec.rb +51 -0
  32. data/spec/shared_abstract_spec.rb +426 -0
  33. data/spec/shared_table_spec.rb +675 -0
  34. data/spec/shared_tyrant_spec.rb +42 -0
  35. data/spec/spec_base.rb +23 -0
  36. data/spec/start_tyrants.sh +28 -0
  37. data/spec/stop_tyrants.sh +9 -0
  38. data/spec/table_spec.rb +267 -0
  39. data/spec/tyrant_spec.rb +218 -0
  40. data/spec/tyrant_table_spec.rb +298 -0
  41. data/spec/util_list_spec.rb +197 -0
  42. data/spec/util_map_spec.rb +130 -0
  43. data/tasks/dev.rb +70 -0
  44. data/test/bm0.rb +353 -0
  45. data/test/bm1_compression.rb +54 -0
  46. data/test/con0.rb +30 -0
  47. data/test/mem.rb +49 -0
  48. data/test/mem1.rb +44 -0
  49. data/test/readme0.rb +17 -0
  50. data/test/readme1.rb +21 -0
  51. data/test/readme2.rb +15 -0
  52. data/test/readme3.rb +24 -0
  53. data/test/readmes_test.sh +17 -0
  54. metadata +81 -21
  55. data/MIGRATED.txt +0 -1
@@ -0,0 +1,292 @@
1
+
2
+ #
3
+ # Specifying rufus-tokyo
4
+ #
5
+ # Mon Feb 23 11:11:10 JST 2009
6
+ #
7
+
8
+ require File.join(File.dirname(__FILE__), 'spec_base')
9
+ require File.join(File.dirname(__FILE__), 'shared_table_spec')
10
+
11
+ begin
12
+ require 'rufus/edo'
13
+ rescue LoadError
14
+ end
15
+
16
+ if defined?(TokyoCabinet)
17
+
18
+ FileUtils.mkdir('tmp') rescue nil
19
+
20
+
21
+ describe Rufus::Edo::Table do
22
+
23
+ it 'should open in write/create mode by default' do
24
+
25
+ t = Rufus::Edo::Table.new('tmp/default.tct')
26
+ t.close
27
+ File.exist?('tmp/default.tct').should.equal(true)
28
+ FileUtils.rm('tmp/default.tct')
29
+ end
30
+
31
+ it 'should raise an error when file is missing' do
32
+
33
+ lambda {
34
+ Rufus::Edo::Table.new('tmp/missing.tct', :mode => 'r')
35
+ }.should.raise(
36
+ Rufus::Edo::EdoError).message.should.equal('(err 3) file not found')
37
+ end
38
+ end
39
+
40
+ describe Rufus::Edo::Table do
41
+
42
+ it 'should use open with a block will auto close the db correctly' do
43
+
44
+ res = Rufus::Edo::Table.open('tmp/spec_source.tct') do |table|
45
+ 10.times { |i| table["key #{i}"] = {:val => i} }
46
+ table.size.should.equal(10)
47
+ :result
48
+ end
49
+
50
+ res.should.equal(:result)
51
+
52
+ table = Rufus::Edo::Table.new('tmp/spec_source.tct')
53
+ 10.times do |i|
54
+ table["key #{i}"].should.equal({"val" => i.to_s})
55
+ end
56
+ table.close
57
+
58
+ FileUtils.rm('tmp/spec_source.tct')
59
+ end
60
+
61
+
62
+ it 'should use open without a block just like calling new correctly' do
63
+
64
+ table = Rufus::Edo::Table.open('tmp/spec_source.tct')
65
+ 10.times { |i| table["key #{i}"] = {:val => i} }
66
+ table.size.should.equal(10)
67
+ table.close
68
+
69
+ table = Rufus::Edo::Table.new('tmp/spec_source.tct')
70
+ 10.times do |i|
71
+ table["key #{i}"].should.equal({"val" => i.to_s})
72
+ end
73
+ table.close
74
+
75
+ FileUtils.rm('tmp/spec_source.tct')
76
+ end
77
+ end
78
+
79
+ describe Rufus::Edo::Table do
80
+
81
+ before do
82
+ @t = Rufus::Edo::Table.new('tmp/table.tct')
83
+ @t.clear
84
+ end
85
+ after do
86
+ @t.close
87
+ end
88
+
89
+ it 'should return its path' do
90
+
91
+ @t.path.should.equal('tmp/table.tct')
92
+ end
93
+
94
+ behaves_like 'table'
95
+ end
96
+
97
+ describe Rufus::Edo::Table do
98
+
99
+ before do
100
+ @t = Rufus::Edo::Table.new('tmp/table.tct')
101
+ @t.clear
102
+ end
103
+ after do
104
+ @t.close
105
+ end
106
+
107
+ behaves_like 'table with transactions'
108
+ end
109
+
110
+ # DONE
111
+
112
+ describe 'Rufus::Edo::Table #keys' do
113
+
114
+ before do
115
+ @n = 50
116
+ @t = Rufus::Edo::Table.new('tmp/test_new.tct')
117
+ @t.clear
118
+ @n.times { |i| @t["person#{i}"] = { 'name' => 'whoever' } }
119
+ @n.times { |i| @t["animal#{i}"] = { 'name' => 'whichever' } }
120
+ @t["toto#{0.chr}5"] = { 'name' => 'toto' }
121
+ end
122
+ after do
123
+ @t.close
124
+ end
125
+
126
+ behaves_like 'table #keys'
127
+ end
128
+
129
+
130
+ def prepare_table_with_data
131
+
132
+ t = Rufus::Edo::Table.new('tmp/test_new.tct')
133
+ t.clear
134
+ t['pk0'] = { 'name' => 'jim', 'age' => '25', 'lang' => 'ja,en' }
135
+ t['pk1'] = { 'name' => 'jeff', 'age' => '32', 'lang' => 'en,es' }
136
+ t['pk2'] = { 'name' => 'jack', 'age' => '44', 'lang' => 'en' }
137
+ t['pk3'] = { 'name' => 'jake', 'age' => '45', 'lang' => 'en,li' }
138
+ t
139
+ end
140
+
141
+ describe 'Rufus::Edo::Table' do
142
+
143
+ before do
144
+ @t = prepare_table_with_data
145
+ end
146
+ after do
147
+ @t.close
148
+ end
149
+
150
+ behaves_like 'table indexes'
151
+ end
152
+
153
+ describe 'Rufus::Edo::Table#lget' do
154
+
155
+ before do
156
+ @t = prepare_table_with_data
157
+ end
158
+ after do
159
+ @t.close
160
+ end
161
+
162
+ behaves_like 'table lget'
163
+ end
164
+
165
+ describe 'Rufus::Edo::Table, like a Ruby Hash,' do
166
+
167
+ before do
168
+ @t = prepare_table_with_data
169
+ end
170
+ after do
171
+ @t.close
172
+ end
173
+
174
+ behaves_like 'table like a hash'
175
+ end
176
+
177
+ describe 'queries on Rufus::Edo::Table' do
178
+
179
+ before do
180
+ @t = prepare_table_with_data
181
+ end
182
+ after do
183
+ @t.close
184
+ end
185
+
186
+ behaves_like 'table query'
187
+
188
+ #if TokyoCabinet::TDBQRY.public_instance_methods.collect { |e|
189
+ # e.to_s }.include?('setlimit')
190
+
191
+ # it 'can be limited and have an offset' do
192
+
193
+ # @t.query { |q|
194
+ # q.add 'lang', :includes, 'en'
195
+ # q.order_by 'name', :desc
196
+ # q.limit 2, 0
197
+ # }.collect { |e| e['name'] }.should.equal(%w{ jim jeff })
198
+ # @t.query { |q|
199
+ # q.add 'lang', :includes, 'en'
200
+ # q.order_by 'name', :desc
201
+ # q.limit 2, 2
202
+ # }.collect { |e| e['name'] }.should.equal(%w{ jake jack })
203
+ # end
204
+ #end
205
+ end
206
+
207
+ describe 'Queries on Tokyo Tyrant tables (via Rufus::Edo)' do
208
+
209
+ before do
210
+ @t = Rufus::Edo::Table.new('tmp/test_new.tct')
211
+ @t.clear
212
+ [
213
+ "consul readableness choleric hopperdozer juckies",
214
+ "fume overharshness besprinkler whirling erythrene",
215
+ "trumper defiable detractively cattiness superioress",
216
+ "vivificative consul agglomerated Peterloo way",
217
+ "unkilned bituminate antimatrimonial uran polyphony",
218
+ "kurumaya unannexed renownedly apetaloid consul",
219
+ "overdare nescience seronegative nagster overfatten",
220
+ ].each_with_index { |w, i|
221
+ @t["pk#{i}"] = { 'name' => "lambda#{i}", 'words' => w }
222
+ }
223
+ end
224
+ after do
225
+ @t.close
226
+ end
227
+
228
+ behaves_like 'table query (fts)'
229
+ end
230
+
231
+ describe 'Rufus::Tokyo::TableQuery#process' do
232
+
233
+ before do
234
+ @t = prepare_table_with_data
235
+ end
236
+ after do
237
+ @t.close
238
+ end
239
+
240
+ behaves_like 'table query #process'
241
+ end
242
+
243
+ describe 'results from Rufus::Edo::Table queries' do
244
+
245
+ before do
246
+ @t = prepare_table_with_data
247
+ end
248
+ after do
249
+ @t.close
250
+ end
251
+
252
+ behaves_like 'table query results'
253
+ end
254
+
255
+ describe Rufus::Edo::Table do
256
+
257
+ before do
258
+ @t = Rufus::Edo::Table.new('tmp/table.tct')
259
+ @t.clear
260
+ end
261
+ after do
262
+ @t.close
263
+ end
264
+
265
+ behaves_like 'a table structure flattening keys and values'
266
+ end
267
+
268
+ describe 'Rufus::Edo::Table\'s queries' do
269
+
270
+ before do
271
+ @t = prepare_table_with_data
272
+ end
273
+ after do
274
+ @t.close
275
+ end
276
+
277
+ behaves_like 'a table structure to_s-ing query stuff'
278
+ end
279
+
280
+ describe 'Rufus::Edo::Table and metasearch' do
281
+
282
+ before do
283
+ @t = prepare_table_with_data
284
+ end
285
+ after do
286
+ @t.close
287
+ end
288
+
289
+ behaves_like 'table query metasearch'
290
+ end
291
+ end
292
+
@@ -0,0 +1,73 @@
1
+
2
+ #
3
+ # Specifying rufus-tokyo
4
+ #
5
+ # Sun Feb 8 14:15:31 JST 2009
6
+ #
7
+
8
+ require File.dirname(__FILE__) + '/spec_base'
9
+
10
+ require 'rufus/tokyo/hmethods'
11
+
12
+
13
+ class MyHash
14
+ include Rufus::Tokyo::HashMethods
15
+
16
+ def initialize
17
+ super
18
+ @default_proc = nil
19
+ end
20
+
21
+ def get (k)
22
+ k.to_i % 2 == 0 ? k : nil
23
+ end
24
+ end
25
+
26
+ describe 'an instance that include HashMethods' do
27
+
28
+ before do
29
+ @h = MyHash.new
30
+ end
31
+
32
+ it 'should be ready for testing' do # :(
33
+ @h[1].should.be.nil
34
+ @h[2].should.equal(2)
35
+ end
36
+
37
+ it 'should accept a default value' do
38
+ @h.default = :default
39
+ @h.default.should.equal(:default)
40
+ end
41
+ end
42
+
43
+ describe 'an instance that has a default proc' do
44
+
45
+ before do
46
+ @h = MyHash.new
47
+ end
48
+
49
+ it 'should accept a default_proc' do
50
+ @h.default_proc = lambda { |h, k| k * 2 }
51
+ @h[1].should.equal(2)
52
+ @h[2].should.equal(2)
53
+ @h[3].should.equal(6)
54
+ end
55
+
56
+ it 'should respond nil to #default(k) when no default proc' do
57
+ @h.default.should.be.nil
58
+ @h.default(2).should.be.nil
59
+ end
60
+
61
+ it 'should respond to #default(k) with the correct default' do
62
+ @h.default = 'cat'
63
+ @h.default.should.equal('cat')
64
+ @h.default(2).should.equal('cat')
65
+ end
66
+
67
+ it 'should respond to #default(k) with the correct default (2)' do
68
+ @h.default_proc = lambda { |h, k| k * 3 }
69
+ @h.default.should.be.nil
70
+ @h.default(2).should.equal(6)
71
+ end
72
+ end
73
+
data/spec/incr.lua ADDED
@@ -0,0 +1,23 @@
1
+
2
+ --
3
+ -- Taken from http://tokyocabinet.sourceforge.net/tyrantdoc/#luaext
4
+ --
5
+
6
+ function incr (key, value)
7
+ value = tonumber(value)
8
+ if not value then
9
+ return nil
10
+ end
11
+ local old = tonumber(_get(key))
12
+ if old then
13
+ value = value + old
14
+ end
15
+ if not _put(key, value) then
16
+ return nil
17
+ end
18
+ return value
19
+ end
20
+
21
+ function hi()
22
+ return "Hi!"
23
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/spec_base'
2
+
3
+ require 'rufus/tokyo/openable'
4
+
5
+
6
+ class MyOpenable
7
+ extend Rufus::Tokyo::Openable
8
+
9
+ def initialize(*args)
10
+ @args = args
11
+ @closed = false
12
+ end
13
+
14
+ attr_reader :args
15
+
16
+ def close
17
+ @closed = true
18
+ end
19
+
20
+ def closed?
21
+ @closed
22
+ end
23
+ end
24
+ openable = lambda { |obj| obj.is_a? MyOpenable }
25
+
26
+ describe 'an instance that extends Openable' do
27
+ it 'should pass all arguments from open() down to new()' do
28
+ MyOpenable.open(1).args.should.equal([1])
29
+ MyOpenable.open(1, 2).args.should.equal([1, 2])
30
+ end
31
+
32
+ it 'should pass the constructed object into the block' do
33
+ MyOpenable.open { |o| o.should.be openable }
34
+ end
35
+
36
+ it 'should return the last value in the block' do
37
+ MyOpenable.open { :value }.should.equal(:value)
38
+ end
39
+
40
+ it 'should return the new object without a block' do
41
+ MyOpenable.open.should.be openable
42
+ end
43
+
44
+ it 'should call close after running the block' do
45
+ MyOpenable.open { |db| db.should.not.be.closed; db }.should.be.closed
46
+ end
47
+
48
+ it 'should not be closed when opened without a block' do
49
+ MyOpenable.open.should.not.be.closed
50
+ end
51
+ end
@@ -0,0 +1,426 @@
1
+
2
+ #
3
+ # Specifying rufus-tokyo
4
+ #
5
+ # Fri Jul 17 15:37:16 JST 2009 #rubykaigi2009
6
+ #
7
+
8
+ shared 'an abstract structure' do
9
+
10
+ it 'should be empty initially' do
11
+
12
+ @db.size.should.equal(0)
13
+ @db['pillow'].should.be.nil
14
+ end
15
+
16
+ it 'should accept values' do
17
+
18
+ @db['pillow'] = 'Shonagon'
19
+ @db.size.should.equal(1)
20
+ end
21
+
22
+ it 'should restitute values' do
23
+
24
+ @db['pillow'] = 'Shonagon'
25
+ @db['pillow'].should.equal('Shonagon')
26
+ end
27
+
28
+ it 'should delete values' do
29
+
30
+ @db['pillow'] = 'Shonagon'
31
+ @db.delete('pillow').should.equal('Shonagon')
32
+ @db.size.should.equal(0)
33
+ end
34
+
35
+ it 'should accept and restitute \\0 strings' do
36
+
37
+ s = "toto#{0.chr}nada"
38
+ @db[s] = s
39
+ @db[s].should.equal(s)
40
+ end
41
+
42
+ it 'should stringify keys and values' do
43
+ @db[123] = 456
44
+ @db["123".to_sym].should.equal("456")
45
+ end
46
+
47
+ it 'should reply to #keys and #values' do
48
+
49
+ keys = %w{ alpha bravo charly delta echo foxtrott }
50
+ keys.each_with_index { |k, i| @db[k] = i.to_s }
51
+ @db.keys.should.equal(keys)
52
+ @db.values.should.equal(%w{ 0 1 2 3 4 5 })
53
+ end
54
+
55
+ it 'should reply to #keys when there are keys containing \0' do
56
+
57
+ s = "toto#{0.chr}nada"
58
+ @db[s] = s
59
+ @db.keys.should.equal([ s ])
60
+ end
61
+
62
+ it 'should return a Ruby hash on merge' do
63
+
64
+ @db['a'] = 'A'
65
+
66
+ @db.merge({ 'b' => 'B', 'c' => 'C' }).should.equal(
67
+ { 'a' => 'A', 'b' => 'B', 'c' => 'C' })
68
+
69
+ @db['b'].should.be.nil
70
+
71
+ @db.size.should.equal(1)
72
+ end
73
+
74
+ it 'should have more values in case of merge!' do
75
+
76
+ @db['a'] = 'A'
77
+
78
+ @db.merge!({ 'b' => 'B', 'c' => 'C' })
79
+
80
+ @db.size.should.equal(3)
81
+ @db['b'].should.equal('B')
82
+ end
83
+ end
84
+
85
+ shared 'abstract structure #keys' do
86
+
87
+ it 'should return a Ruby Array by default' do
88
+
89
+ @db.keys.class.should.equal(::Array)
90
+ end
91
+
92
+ it 'should retrieve keys that contain \0' do
93
+
94
+ @db.keys.include?("toto#{0.chr}5").should.be.true
95
+ end
96
+
97
+ it 'should retrieve forward matching keys when key contains \0' do
98
+
99
+ @db.keys(:prefix => 'toto').should.equal([ "toto#{0.chr}5" ])
100
+ end
101
+
102
+ it 'should return a limited number of keys when :limit is set' do
103
+
104
+ @db.keys(:limit => 20).size.should.equal(20)
105
+ end
106
+
107
+ it 'should delete_keys_with_prefix' do
108
+
109
+ @db.delete_keys_with_prefix('animal')
110
+ @db.size.should.equal(@n + 1)
111
+ @db.keys(:prefix => 'animal').size.should.equal(0)
112
+ end
113
+
114
+ unless @db.class.name.match(/^Rufus::Edo/)
115
+
116
+ it 'should return a Cabinet List when :native => true' do
117
+
118
+ l = @db.keys(:native => true)
119
+ l.class.should.equal(Rufus::Tokyo::List)
120
+ l.size.should.equal(2 * @n + 1)
121
+ l.free
122
+ end
123
+
124
+ it 'should retrieve forward matching keys when :prefix => "prefix-"' do
125
+
126
+ @db.keys(:prefix => 'person').size.should.equal(@n)
127
+
128
+ l = @db.keys(:prefix => 'animal', :native => true)
129
+ l.size.should.equal(@n)
130
+ l.free
131
+ end
132
+ end
133
+ end
134
+
135
+ shared 'abstract structure with transactions' do
136
+
137
+ #before do
138
+ # @db = Rufus::Tokyo::Cabinet.new('tmp/cabinet_spec.tch')
139
+ # @db.clear
140
+ #end
141
+ #after do
142
+ # @db.close
143
+ #end
144
+
145
+ it 'should correctly abort transactions' do
146
+
147
+ @db.transaction {
148
+ @db['pk0'] = 'value0'
149
+ @db.abort
150
+ }
151
+ @db.size.should.be.zero
152
+ end
153
+
154
+ it 'should rollback transactions with errors, and bubble exceptions' do
155
+
156
+ begin
157
+ @db.transaction {
158
+ @db['pk0'] = 'value0'
159
+ raise 'something goes wrong'
160
+ }
161
+ rescue RuntimeError
162
+ end
163
+ @db.size.should.be.zero
164
+ end
165
+
166
+ it 'should rollback transactions with Abort exceptions, and consume exceptions' do
167
+
168
+ @db.transaction {
169
+ @db['pk0'] = 'value0'
170
+ raise Rufus::Tokyo::Transactions::Abort
171
+ }
172
+ @db.size.should.be.zero
173
+ end
174
+
175
+ it 'should commit successful transactions' do
176
+
177
+ @db.transaction do
178
+ @db['pk0'] = 'showanojidaietaminamishima'
179
+ end
180
+ @db['pk0'].should.equal('showanojidaietaminamishima')
181
+ end
182
+
183
+ it 'should abort low level transactions' do
184
+
185
+ @db.tranbegin
186
+ @db['pk0'] = 'shikataganai'
187
+ @db.tranabort
188
+ @db.size.should.be.zero
189
+ end
190
+
191
+ it 'should commit low level transactions' do
192
+
193
+ @db.tranbegin
194
+ @db['pk0'] = 'shikataganai'
195
+ @db.trancommit
196
+ @db['pk0'].should.equal('shikataganai')
197
+ end
198
+ end
199
+
200
+ shared 'abstract structure #lget/lput/ldelete' do
201
+
202
+ it 'should get multiple values' do
203
+
204
+ @db.lget(%w{ 0 1 2 }).should.equal({"0"=>"val0", "1"=>"val1", "2"=>"val2"})
205
+ @db.mget(%w{ 0 1 2 }).should.equal({"0"=>"val0", "1"=>"val1", "2"=>"val2"})
206
+ @db.mget(*%w{ 0 1 2 }).should.equal({"0"=>"val0", "1"=>"val1", "2"=>"val2"})
207
+ end
208
+
209
+ it 'should put multiple values' do
210
+
211
+ @db.lput('3' => 'val3', '4' => 'val4')
212
+ @db.lget(%w{ 2 3 }).should.equal({"2"=>"val2", "3"=>"val3"})
213
+ end
214
+
215
+ it 'should delete multiple values' do
216
+
217
+ @db.ldelete(%w{ 2 3 })
218
+ @db.lget(%w{ 0 1 2 }).should.equal({"0"=>"val0", "1"=>"val1"})
219
+ end
220
+
221
+ it 'should delete multiple values' do
222
+
223
+ @db.ldelete(*%w{ 2 3 })
224
+ @db.lget(%w{ 0 1 2 }).should.equal({"0"=>"val0", "1"=>"val1"})
225
+ end
226
+ end
227
+
228
+ shared 'abstract structure #add{int|double}' do
229
+
230
+ it 'should increment (int)' do
231
+
232
+ @db.addint('counter', 1).should.equal(1)
233
+ @db.incr('counter', 1).should.equal(2)
234
+ @db.addint('counter', 2).should.equal(4)
235
+ @db.incr('counter').should.equal(5)
236
+
237
+ @db.counter_value('counter').should.equal(5)
238
+ end
239
+
240
+ it 'should fail gracefully if counter has already a [string] value (int)' do
241
+
242
+ @db['counter'] = 'a'
243
+
244
+ lambda { @db.addint('counter', 1) }.should.raise(
245
+ @db.class.name.match(/^Rufus::Edo/) ?
246
+ Rufus::Edo::EdoError : Rufus::Tokyo::TokyoError)
247
+
248
+ @db['counter'].should.equal('a')
249
+ end
250
+
251
+ it 'should increment (double)' do
252
+
253
+ @db.adddouble('counter', 1.0).should.equal(1.0)
254
+ @db.incr('counter', 1.5).should.equal(2.5)
255
+ @db.adddouble('counter', 2.2).should.equal(4.7)
256
+
257
+ @db.counter_value('counter').should.equal(4.7)
258
+ end
259
+
260
+ it 'should fail gracefully if counter has already a [string] value (double)' do
261
+
262
+ @db['counter'] = 'a'
263
+
264
+ lambda {
265
+ @db.adddouble('counter', 1.0)
266
+ }.should.raise(
267
+ @db.class.name.match(/^Rufus::Edo/) ?
268
+ Rufus::Edo::EdoError : Rufus::Tokyo::TokyoError)
269
+
270
+ @db['counter'].should.equal('a')
271
+ end
272
+ end
273
+
274
+ shared 'abstract structure #putkeep' do
275
+
276
+ it 'should accept values' do
277
+
278
+ @db.putkeep('pillow', 'Shonagon')
279
+ @db.size.should.equal(1)
280
+ end
281
+
282
+ it 'should restitute values' do
283
+
284
+ @db.putkeep('pillow', 'Shonagon')
285
+ @db['pillow'].should.equal('Shonagon')
286
+ end
287
+
288
+ it 'should not overwrite values if already set' do
289
+
290
+ @db['pillow'] = 'Shonagon'
291
+ @db['pillow'].should.equal('Shonagon')
292
+
293
+ @db.putkeep('pillow', 'Ruby')
294
+ @db['pillow'].should.equal('Shonagon')
295
+ end
296
+
297
+ it 'should return true if not yet set' do
298
+
299
+ @db.putkeep('pillow', 'Shonagon').should.equal(true)
300
+ end
301
+
302
+ it 'should return false if already set' do
303
+
304
+ @db['pillow'] = 'Shonagon'
305
+ @db.putkeep('pillow', 'Ruby').should.equal(false)
306
+ end
307
+
308
+ it 'should accept binary data \0' do
309
+
310
+ s = "Sei#{0.chr}Shonagon"
311
+
312
+ @db.putkeep(s, s).should.be.true
313
+ @db[s].should.equal(s)
314
+ end
315
+ end
316
+
317
+ shared 'abstract structure #putcat' do
318
+
319
+ it 'should append' do
320
+
321
+ @db['heian'] = 'Shonagon'
322
+ @db.putcat('heian', ', Shikibu')
323
+ @db.putcat('heian', ', Michitsuna')
324
+
325
+ @db['heian'].should.equal('Shonagon, Shikibu, Michitsuna')
326
+ end
327
+
328
+ it 'should create when not present' do
329
+
330
+ @db.putcat('regent', 'Fujiwara no Michinaga')
331
+
332
+ @db['regent'].should.equal('Fujiwara no Michinaga')
333
+ end
334
+ end
335
+
336
+ shared 'tyrant with embedded lua' do
337
+
338
+ it 'should call lua extensions' do
339
+
340
+ @db['toto'] = '0'
341
+ 3.times { @db.ext(:incr, 'toto', '1') }
342
+ @db.ext('incr', 'toto', 2) # lax
343
+
344
+ @db['toto'].should.equal('5')
345
+ end
346
+
347
+ it 'should return nil when function is missing' do
348
+
349
+ @db.ext(:missing, 'nada', 'forever').should.equal(nil)
350
+ end
351
+ end
352
+
353
+ shared 'an abstract structure flattening keys and values' do
354
+
355
+ it 'should to_s keys and values when #[]=' do
356
+
357
+ @db[:hello] = :world
358
+ @db['hello'].should.equal('world')
359
+ @db[:hello].should.equal('world')
360
+ @db.size.should.equal(1)
361
+ end
362
+
363
+ it 'should to_s keys and values when #putkeep' do
364
+
365
+ @db.putkeep(:pillow, :shonagon)
366
+ @db[:pillow].should.equal('shonagon')
367
+ @db.size.should.equal(1)
368
+ end
369
+
370
+ it 'should to_s keys when #delete' do
371
+
372
+ @db['hello'] = 'world'
373
+ @db.delete(:hello).should.equal('world')
374
+ @db.size.should.equal(0)
375
+ end
376
+
377
+ it 'should to_s keys when #lget' do
378
+
379
+ writers = %w[ shikibu shonagon ]
380
+ writers.each { |s| @db[s] = s }
381
+ @db.lget(writers.collect { |w| w.to_sym }).keys.sort.should.equal(writers)
382
+ end
383
+
384
+ it 'should to_s keys when #ldelete' do
385
+
386
+ writers = %w[ shonagon shikibu ]
387
+ writers.each { |s| @db[s] = s }
388
+
389
+ @db.ldelete(writers.collect { |w| w.to_sym })
390
+
391
+ @db.size.should.equal(0)
392
+ end
393
+
394
+ it 'should to_s keys when #incr' do
395
+
396
+ @db.incr(:genji_count, 1).should.equal(1)
397
+ @db.incr(:genji_count, 1).should.equal(2)
398
+ end
399
+
400
+ it 'should to_s keys and values when #merge!' do
401
+
402
+ @db.merge!(:genji => 1, :to_no_chujo => 2)
403
+
404
+ @db['genji'].should.equal('1')
405
+ @db['to_no_chujo'].should.equal('2')
406
+ end
407
+ end
408
+
409
+ shared 'an abstract structure with a default value' do
410
+
411
+ it 'should restitute the default value' do
412
+
413
+ @db['known'].should.equal('Ulysse')
414
+ @db['unknown'].should.equal('Nemo')
415
+ end
416
+ end
417
+
418
+ shared 'an abstract structure with a default_proc' do
419
+
420
+ it 'should restitute the value computed by the default_proc' do
421
+
422
+ @db['known'].should.equal('Ulysse')
423
+ @db['unknown'].should.equal('default:unknown')
424
+ end
425
+ end
426
+