rufus-tokyo 1.0.3 → 1.0.4

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