oklahoma_mixer 0.3.0 → 0.4.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/CHANGELOG.rdoc +10 -0
- data/TODO.rdoc +4 -4
- data/lib/oklahoma_mixer.rb +1 -1
- data/lib/oklahoma_mixer/array_list.rb +4 -9
- data/lib/oklahoma_mixer/array_list/c.rb +7 -3
- data/lib/oklahoma_mixer/hash_database.rb +7 -3
- data/lib/oklahoma_mixer/table_database.rb +78 -73
- data/lib/oklahoma_mixer/table_database/c.rb +3 -0
- data/test/shared/tuning_tests.rb +9 -0
- data/test/table_database/query_test.rb +348 -0
- data/test/table_database/read_only_query_test.rb +155 -0
- metadata +27 -13
data/CHANGELOG.rdoc
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
Below is a complete listing of changes for each revision of Oklahoma Mixer.
|
4
4
|
|
5
|
+
== 0.4.0
|
6
|
+
|
7
|
+
* Added the read_only?() method
|
8
|
+
* Added error handling for Table Databases queries
|
9
|
+
* Added support for Table Databases queries on read only databases
|
10
|
+
* Added support for iteration blocks to Table Databases searches
|
11
|
+
* Modified Table Databases blocks to yield tuples consistent with the iterators
|
12
|
+
* Improved count() performance by removing unneeded document conversion
|
13
|
+
* Improved paginate() performance by avoiding double type conversion
|
14
|
+
|
5
15
|
== 0.3.0
|
6
16
|
|
7
17
|
* Added the empty?() method
|
data/TODO.rdoc
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
The following is a list of planned expansions for Oklahoma Mixer in the order I
|
4
4
|
intend to address them.
|
5
5
|
|
6
|
-
1.
|
7
|
-
2.
|
8
|
-
3.
|
9
|
-
4.
|
6
|
+
1. Include some higher level abstractions like mixed tables, queues, and shards
|
7
|
+
2. Add support for Tokyo Tyrant
|
8
|
+
3. Ensure Ruby 1.9 compatibility
|
9
|
+
4. Write API documentation
|
10
10
|
5. Add support for Tokyo Dystopia
|
data/lib/oklahoma_mixer.rb
CHANGED
@@ -11,17 +11,12 @@ module OklahomaMixer
|
|
11
11
|
|
12
12
|
attr_reader :pointer
|
13
13
|
|
14
|
-
|
15
|
-
value = C.read_from_func(:shift, @pointer)
|
16
|
-
block_given? ? yield(value) : value
|
17
|
-
end
|
14
|
+
include Enumerable
|
18
15
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
values << yield(value)
|
16
|
+
def each
|
17
|
+
(0...C.num(pointer)).each do |i|
|
18
|
+
yield C.read_from_func(:val, :no_free, @pointer, i)
|
23
19
|
end
|
24
|
-
values
|
25
20
|
end
|
26
21
|
|
27
22
|
def push(*values)
|
@@ -12,10 +12,14 @@ module OklahomaMixer
|
|
12
12
|
:returns => :pointer
|
13
13
|
func :name => :del,
|
14
14
|
:args => :pointer
|
15
|
-
|
16
|
-
func :name => :
|
17
|
-
:args =>
|
15
|
+
|
16
|
+
func :name => :num,
|
17
|
+
:args => :pointer,
|
18
|
+
:returns => :int
|
19
|
+
func :name => :val,
|
20
|
+
:args => [:pointer, :int, :pointer],
|
18
21
|
:returns => :pointer
|
22
|
+
|
19
23
|
func :name => :push,
|
20
24
|
:args => [:pointer, :pointer, :int]
|
21
25
|
end
|
@@ -39,9 +39,9 @@ module OklahomaMixer
|
|
39
39
|
tune(options)
|
40
40
|
|
41
41
|
warn "mode option supersedes mode argument" if mode and options[:mode]
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
mode_enum = cast_to_enum_int(options.fetch(:mode, mode || "wc"), :mode)
|
43
|
+
@read_only = (mode_enum & cast_to_enum_int("r", :mode)).nonzero?
|
44
|
+
try(:open, path, mode_enum)
|
45
45
|
rescue Exception
|
46
46
|
close if defined?(@db) and @db
|
47
47
|
raise
|
@@ -57,6 +57,10 @@ module OklahomaMixer
|
|
57
57
|
|
58
58
|
attr_reader :path
|
59
59
|
|
60
|
+
def read_only?
|
61
|
+
@read_only
|
62
|
+
end
|
63
|
+
|
60
64
|
def file_size
|
61
65
|
lib.fsiz(@db)
|
62
66
|
end
|
@@ -114,7 +114,7 @@ module OklahomaMixer
|
|
114
114
|
def all(options = { }, &iterator)
|
115
115
|
query(options) do |q|
|
116
116
|
mode = results_mode(options)
|
117
|
-
if
|
117
|
+
if not iterator.nil? and not read_only?
|
118
118
|
results = self
|
119
119
|
callback = lambda { |key_pointer, key_size, doc_map, _|
|
120
120
|
if mode != :docs
|
@@ -125,9 +125,10 @@ module OklahomaMixer
|
|
125
125
|
doc = map.to_hash { |string| cast_to_encoded_string(string) }
|
126
126
|
end
|
127
127
|
flags = case mode
|
128
|
-
when :keys then
|
129
|
-
when :docs then
|
130
|
-
|
128
|
+
when :keys then iterator[key]
|
129
|
+
when :docs then iterator[doc]
|
130
|
+
when :aoh then iterator[doc.merge!(:primary_key => key)]
|
131
|
+
else iterator[[key, doc]]
|
131
132
|
end
|
132
133
|
Array(flags).inject(0) { |returned_flags, flag|
|
133
134
|
returned_flags | case flag.to_s
|
@@ -144,30 +145,16 @@ module OklahomaMixer
|
|
144
145
|
end
|
145
146
|
}
|
146
147
|
}
|
148
|
+
unless lib.qryproc(q.pointer, callback, nil)
|
149
|
+
error_code = lib.ecode(@db)
|
150
|
+
error_message = lib.errmsg(error_code)
|
151
|
+
fail Error::QueryError,
|
152
|
+
"#{error_message} (error code #{error_code})"
|
153
|
+
end
|
154
|
+
results
|
147
155
|
else
|
148
|
-
|
149
|
-
callback = lambda { |key_pointer, key_size, doc_map, _|
|
150
|
-
if mode == :docs
|
151
|
-
results << cast_value_out(doc_map, :no_free)
|
152
|
-
else
|
153
|
-
key = cast_key_out(key_pointer.get_bytes(0, key_size))
|
154
|
-
case mode
|
155
|
-
when :keys
|
156
|
-
results << key
|
157
|
-
when :hoh
|
158
|
-
results[key] = cast_value_out(doc_map, :no_free)
|
159
|
-
when :aoh
|
160
|
-
results << cast_value_out(doc_map, :no_free).
|
161
|
-
merge(:primary_key => key)
|
162
|
-
else
|
163
|
-
results << [key, cast_value_out(doc_map, :no_free)]
|
164
|
-
end
|
165
|
-
end
|
166
|
-
0
|
167
|
-
}
|
156
|
+
query_results(lib.qrysearch(q.pointer), mode, &iterator)
|
168
157
|
end
|
169
|
-
lib.qryproc(q.pointer, callback, nil)
|
170
|
-
results
|
171
158
|
end
|
172
159
|
end
|
173
160
|
|
@@ -177,7 +164,7 @@ module OklahomaMixer
|
|
177
164
|
|
178
165
|
def count(options = { })
|
179
166
|
count = 0
|
180
|
-
all(options) { count += 1 }
|
167
|
+
all(options.merge(:select => :keys)) { count += 1 }
|
181
168
|
count
|
182
169
|
end
|
183
170
|
|
@@ -191,39 +178,31 @@ module OklahomaMixer
|
|
191
178
|
results.per_page = (options[:per_page] || 30).to_i
|
192
179
|
fail Error::QueryError, ":per_page must be >= 1" if results.per_page < 1
|
193
180
|
results.total_entries = 0
|
194
|
-
all(
|
195
|
-
:limit => nil ) ) { |key, value|
|
181
|
+
all(options.merge(:limit => nil)) do |kv|
|
196
182
|
if results.total_entries >= results.offset and
|
197
183
|
results.size < results.per_page
|
198
|
-
|
199
|
-
|
200
|
-
results << key
|
201
|
-
when :docs
|
202
|
-
results << value
|
203
|
-
when :hoh
|
204
|
-
results[key] = value
|
205
|
-
when :aoh
|
206
|
-
results << value.merge(:primary_key => key)
|
184
|
+
if mode == :hoh
|
185
|
+
results[kv.first] = kv.last
|
207
186
|
else
|
208
|
-
results <<
|
187
|
+
results << kv
|
209
188
|
end
|
210
189
|
end
|
211
190
|
results.total_entries += 1
|
212
|
-
|
191
|
+
end
|
213
192
|
results
|
214
193
|
end
|
215
194
|
|
216
|
-
def union(q, *queries)
|
217
|
-
search([q] + queries, lib::SEARCHES[:TDBMSUNION])
|
195
|
+
def union(q, *queries, &iterator)
|
196
|
+
search([q] + queries, lib::SEARCHES[:TDBMSUNION], &iterator)
|
218
197
|
end
|
219
198
|
|
220
|
-
def intersection(q, *queries)
|
221
|
-
search([q] + queries, lib::SEARCHES[:TDBMSISECT])
|
199
|
+
def intersection(q, *queries, &iterator)
|
200
|
+
search([q] + queries, lib::SEARCHES[:TDBMSISECT], &iterator)
|
222
201
|
end
|
223
202
|
alias_method :isect, :intersection
|
224
203
|
|
225
|
-
def difference(q, *queries)
|
226
|
-
search([q] + queries, lib::SEARCHES[:TDBMSDIFF])
|
204
|
+
def difference(q, *queries, &iterator)
|
205
|
+
search([q] + queries, lib::SEARCHES[:TDBMSDIFF], &iterator)
|
227
206
|
end
|
228
207
|
alias_method :diff, :difference
|
229
208
|
|
@@ -361,34 +340,61 @@ module OklahomaMixer
|
|
361
340
|
end
|
362
341
|
end
|
363
342
|
|
364
|
-
def
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
when :docs
|
375
|
-
keys.map { |key| self[cast_key_out(key)] }
|
376
|
-
when :hoh
|
377
|
-
results = { }
|
378
|
-
while key = keys.shift { |k| cast_key_out(k) }
|
379
|
-
results[key] = self[key]
|
343
|
+
def query_results(results, mode, &iterator)
|
344
|
+
keys = ArrayList.new(results)
|
345
|
+
if iterator.nil?
|
346
|
+
results = mode == :hoh ? { } : [ ]
|
347
|
+
iterator = lambda do |key_and_value|
|
348
|
+
if mode == :hoh
|
349
|
+
results[key_and_value.first] = key_and_value.last
|
350
|
+
else
|
351
|
+
results << key_and_value
|
352
|
+
end
|
380
353
|
end
|
381
|
-
results
|
382
|
-
when :aoh
|
383
|
-
keys.map { |key|
|
384
|
-
key = cast_key_out(key)
|
385
|
-
self[key].merge(:primary_key => key)
|
386
|
-
}
|
387
354
|
else
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
355
|
+
results = self
|
356
|
+
end
|
357
|
+
keys.each do |key|
|
358
|
+
flags = Array( case mode
|
359
|
+
when :keys
|
360
|
+
iterator[cast_key_out(key)]
|
361
|
+
when :docs
|
362
|
+
iterator[self[cast_key_out(key)]]
|
363
|
+
when :aoh
|
364
|
+
k = cast_key_out(key)
|
365
|
+
iterator[self[k].merge!(:primary_key => k)]
|
366
|
+
else
|
367
|
+
k = cast_key_out(key)
|
368
|
+
v = self[k]
|
369
|
+
iterator[[k, v]]
|
370
|
+
end ).map { |flag| flag.to_s }
|
371
|
+
if flags.include? "delete"
|
372
|
+
if read_only?
|
373
|
+
warn "attempted delete from a read only query"
|
374
|
+
else
|
375
|
+
delete(key)
|
376
|
+
end
|
377
|
+
elsif v and flags.include? "update"
|
378
|
+
if read_only?
|
379
|
+
warn "attempted update from a read only query"
|
380
|
+
else
|
381
|
+
self[k] = v
|
382
|
+
end
|
383
|
+
end
|
384
|
+
break if flags.include? "break"
|
385
|
+
end
|
386
|
+
results
|
387
|
+
ensure
|
388
|
+
keys.free if keys
|
389
|
+
end
|
390
|
+
|
391
|
+
def search(queries, operation, &iterator)
|
392
|
+
qs = queries.map { |q| query(q) }
|
393
|
+
Utilities.temp_pointer(qs.size) do |pointer|
|
394
|
+
pointer.write_array_of_pointer(qs.map { |q| q.pointer })
|
395
|
+
query_results( lib.metasearch(pointer, qs.size, operation),
|
396
|
+
results_mode(queries.first),
|
397
|
+
&iterator)
|
392
398
|
end
|
393
399
|
ensure
|
394
400
|
if qs
|
@@ -396,7 +402,6 @@ module OklahomaMixer
|
|
396
402
|
q.free
|
397
403
|
end
|
398
404
|
end
|
399
|
-
keys.free if keys
|
400
405
|
end
|
401
406
|
end
|
402
407
|
end
|
@@ -90,6 +90,9 @@ module OklahomaMixer
|
|
90
90
|
:args => [:pointer, :string, INDEXES],
|
91
91
|
:returns => :bool
|
92
92
|
|
93
|
+
func :name => :qrysearch,
|
94
|
+
:args => :pointer,
|
95
|
+
:returns => :pointer
|
93
96
|
call :name => :TDBQRYPROC,
|
94
97
|
:args => [:pointer, :int, :pointer, :pointer],
|
95
98
|
:returns => :int
|
data/test/shared/tuning_tests.rb
CHANGED
@@ -66,6 +66,15 @@ module TuningTests
|
|
66
66
|
"A warning was not issued for an option mode with a mode argument" )
|
67
67
|
end
|
68
68
|
|
69
|
+
def test_r_mode_marks_a_database_as_read_only
|
70
|
+
db do |wc| # default mode: wc
|
71
|
+
assert(!wc.read_only?, "A write/create database was marked read only")
|
72
|
+
end
|
73
|
+
db("r") do |r|
|
74
|
+
assert(r.read_only?, "A read only was not marked as such")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
69
78
|
def test_an_unknown_mode_triggers_a_warning
|
70
79
|
warning = capture_stderr do
|
71
80
|
db("wcu") do
|
@@ -114,6 +114,16 @@ class TestQuery < Test::Unit::TestCase
|
|
114
114
|
results.sort_by { |doc| doc.size } )
|
115
115
|
end
|
116
116
|
|
117
|
+
def test_all_can_pass_key_in_document_to_a_passed_block_and_return_self
|
118
|
+
load_simple_data
|
119
|
+
results = [ ]
|
120
|
+
assert_equal(@db, @db.all(:return => :aoh) { |kv| results << kv })
|
121
|
+
assert_equal( [ {:primary_key => "pk2"},
|
122
|
+
{ :primary_key => "pk1",
|
123
|
+
"a" => "1", "b" => "2", "c" => "3" } ],
|
124
|
+
results.sort_by { |doc| doc.size } )
|
125
|
+
end
|
126
|
+
|
117
127
|
def test_all_with_a_block_does_not_modify_records_by_default
|
118
128
|
load_simple_data
|
119
129
|
assert_equal(@db, @db.all { })
|
@@ -159,6 +169,49 @@ class TestQuery < Test::Unit::TestCase
|
|
159
169
|
assert_match((%w[pk1 pk2] - results).first, @db.keys.first)
|
160
170
|
end
|
161
171
|
|
172
|
+
def test_all_methods_can_control_what_is_passed_to_the_block
|
173
|
+
load_simple_data
|
174
|
+
[ [{:select => :keys}, "pk1"],
|
175
|
+
[{:select => :docs}, {"a" => "1", "b" => "2", "c" => "3"}],
|
176
|
+
[ {:return => :aoa}, [ "pk1",
|
177
|
+
{"a" => "1", "b" => "2", "c" => "3"} ] ],
|
178
|
+
[ {:return => :hoh}, [ "pk1",
|
179
|
+
{"a" => "1", "b" => "2", "c" => "3"} ] ],
|
180
|
+
[ {:return => :aoh}, { :primary_key => "pk1",
|
181
|
+
"a" => "1",
|
182
|
+
"b" => "2",
|
183
|
+
"c" => "3" } ] ].each do |query, results|
|
184
|
+
args = [ ]
|
185
|
+
@db.all(query.merge(:conditions => [:a, :==, 1])) do |kv|
|
186
|
+
args << kv
|
187
|
+
end
|
188
|
+
assert_equal([results], args)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_all_yields_key_value_tuples
|
193
|
+
load_simple_data
|
194
|
+
[ [ {:return => :aoa}, [ "pk1",
|
195
|
+
{"a" => "1", "b" => "2", "c" => "3"} ] ],
|
196
|
+
[ {:return => :hoh}, [ "pk1",
|
197
|
+
{ "a" => "1",
|
198
|
+
"b" => "2",
|
199
|
+
"c" => "3" } ] ] ].each do |query, tuple|
|
200
|
+
yielded = nil
|
201
|
+
@db.all(query.merge(:conditions => [:a, :==, 1])) do |kv|
|
202
|
+
yielded = kv
|
203
|
+
end
|
204
|
+
assert_equal(tuple, yielded)
|
205
|
+
key, value = nil, nil
|
206
|
+
@db.all(query.merge(:conditions => [:a, :==, 1])) do |k, v|
|
207
|
+
key = k
|
208
|
+
value = v
|
209
|
+
end
|
210
|
+
assert_equal(tuple.first, key)
|
211
|
+
assert_equal(tuple.last, value)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
162
215
|
def test_all_fails_with_an_error_for_malformed_conditions
|
163
216
|
assert_raise(OKMixer::Error::QueryError) do
|
164
217
|
@db.all(:conditions => :first) # not column, operator, and expression
|
@@ -757,6 +810,82 @@ class TestQuery < Test::Unit::TestCase
|
|
757
810
|
:order => :first },
|
758
811
|
{:conditions => [:age, :==, 34]} ) )
|
759
812
|
end
|
813
|
+
|
814
|
+
def test_union_can_be_passed_a_block_to_iterate_over_the_results
|
815
|
+
load_condition_data
|
816
|
+
results = [ ]
|
817
|
+
assert_equal( @db,
|
818
|
+
@db.union( { :select => :keys,
|
819
|
+
:conditions => [:first, :ends_with?, "es"],
|
820
|
+
:order => :first },
|
821
|
+
{:conditions => [:age, :==, 34]} ) { |k|
|
822
|
+
results << k
|
823
|
+
} )
|
824
|
+
assert_equal(%w[dana james], results)
|
825
|
+
end
|
826
|
+
|
827
|
+
def test_union_with_a_block_can_update_records
|
828
|
+
load_condition_data
|
829
|
+
assert_equal( @db,
|
830
|
+
@db.union( { :conditions => [:first, :ends_with?, "es"],
|
831
|
+
:order => :first },
|
832
|
+
{:conditions => [:age, :==, 34]} ) { |k, v|
|
833
|
+
if k == "dana"
|
834
|
+
v["salutation"] = "Mrs." # add
|
835
|
+
v["middle"] = "AL" # update
|
836
|
+
v.delete("age") # delete
|
837
|
+
:update
|
838
|
+
end
|
839
|
+
} )
|
840
|
+
assert_equal( { "salutation" => "Mrs.",
|
841
|
+
"first" => "Dana",
|
842
|
+
"middle" => "AL",
|
843
|
+
"last" => "Gray" }, @db["dana"] )
|
844
|
+
end
|
845
|
+
|
846
|
+
def test_union_with_a_block_can_delete_records
|
847
|
+
load_condition_data
|
848
|
+
results = [ ]
|
849
|
+
assert_equal( @db,
|
850
|
+
@db.union( { :select => :keys,
|
851
|
+
:conditions => [:first, :ends_with?, "es"],
|
852
|
+
:order => :first },
|
853
|
+
{:conditions => [:age, :==, 34]} ) { |k|
|
854
|
+
:delete
|
855
|
+
} )
|
856
|
+
assert_equal(1, @db.size)
|
857
|
+
assert_equal(%w[jim], @db.keys)
|
858
|
+
end
|
859
|
+
|
860
|
+
def test_union_with_a_block_can_end_the_query
|
861
|
+
load_condition_data
|
862
|
+
results = [ ]
|
863
|
+
assert_equal( @db,
|
864
|
+
@db.union( { :select => :keys,
|
865
|
+
:conditions => [:first, :ends_with?, "es"],
|
866
|
+
:order => :first },
|
867
|
+
{:conditions => [:age, :==, 34]} ) { |k|
|
868
|
+
results << k
|
869
|
+
:break
|
870
|
+
} )
|
871
|
+
assert_equal(%w[dana], results)
|
872
|
+
end
|
873
|
+
|
874
|
+
def test_union_with_a_block_can_combine_flags
|
875
|
+
load_condition_data
|
876
|
+
results = [ ]
|
877
|
+
assert_equal( @db,
|
878
|
+
@db.union( { :select => :keys,
|
879
|
+
:conditions => [:first, :ends_with?, "es"],
|
880
|
+
:order => :first },
|
881
|
+
{:conditions => [:age, :==, 34]} ) { |k|
|
882
|
+
results << k
|
883
|
+
%w[delete break]
|
884
|
+
} )
|
885
|
+
assert_equal(%w[dana], results)
|
886
|
+
assert_nil(@db["dana"])
|
887
|
+
assert_equal(2, @db.size)
|
888
|
+
end
|
760
889
|
|
761
890
|
def test_intersection_returns_the_set_intersection_of_multiple_queries
|
762
891
|
load_condition_data
|
@@ -798,6 +927,86 @@ class TestQuery < Test::Unit::TestCase
|
|
798
927
|
:order => :first },
|
799
928
|
{:conditions => [:last, :==, "Gray"]} ) )
|
800
929
|
end
|
930
|
+
|
931
|
+
def test_intersection_can_be_passed_a_block_to_iterate_over_the_results
|
932
|
+
load_condition_data
|
933
|
+
results = [ ]
|
934
|
+
assert_equal( @db,
|
935
|
+
@db.isect( { :select => :keys,
|
936
|
+
:return => :hoh,
|
937
|
+
:conditions => [:first, :include?, "a"],
|
938
|
+
:order => :first },
|
939
|
+
{:conditions => [:last, :==, "Gray"]} ) { |k|
|
940
|
+
results << k
|
941
|
+
} )
|
942
|
+
assert_equal(%w[dana james], results)
|
943
|
+
end
|
944
|
+
|
945
|
+
def test_intersection_with_a_block_can_update_records
|
946
|
+
load_condition_data
|
947
|
+
assert_equal( @db,
|
948
|
+
@db.isect( { :return => :hoh,
|
949
|
+
:conditions => [:first, :include?, "a"],
|
950
|
+
:order => :first },
|
951
|
+
{:conditions => [:last, :==, "Gray"]} ) { |k, v|
|
952
|
+
if k == "dana"
|
953
|
+
v["salutation"] = "Mrs." # add
|
954
|
+
v["middle"] = "AL" # update
|
955
|
+
v.delete("age") # delete
|
956
|
+
:update
|
957
|
+
end
|
958
|
+
} )
|
959
|
+
assert_equal( { "salutation" => "Mrs.",
|
960
|
+
"first" => "Dana",
|
961
|
+
"middle" => "AL",
|
962
|
+
"last" => "Gray" }, @db["dana"] )
|
963
|
+
end
|
964
|
+
|
965
|
+
def test_intersection_with_a_block_can_delete_records
|
966
|
+
load_condition_data
|
967
|
+
assert_equal( @db,
|
968
|
+
@db.isect( { :select => :keys,
|
969
|
+
:return => :hoh,
|
970
|
+
:conditions => [:first, :include?, "a"],
|
971
|
+
:order => :first },
|
972
|
+
{:conditions => [:last, :==, "Gray"]} ) { |k|
|
973
|
+
:delete
|
974
|
+
} )
|
975
|
+
assert_equal(1, @db.size)
|
976
|
+
assert_equal(%w[jim], @db.keys)
|
977
|
+
end
|
978
|
+
|
979
|
+
def test_intersection_with_a_block_can_end_the_query
|
980
|
+
load_condition_data
|
981
|
+
results = [ ]
|
982
|
+
assert_equal( @db,
|
983
|
+
@db.isect( { :select => :keys,
|
984
|
+
:return => :hoh,
|
985
|
+
:conditions => [:first, :include?, "a"],
|
986
|
+
:order => :first },
|
987
|
+
{:conditions => [:last, :==, "Gray"]} ) { |k|
|
988
|
+
results << k
|
989
|
+
:break
|
990
|
+
} )
|
991
|
+
assert_equal(%w[dana], results)
|
992
|
+
end
|
993
|
+
|
994
|
+
def test_intersection_with_a_block_can_combine_flags
|
995
|
+
load_condition_data
|
996
|
+
results = [ ]
|
997
|
+
assert_equal( @db,
|
998
|
+
@db.isect( { :select => :keys,
|
999
|
+
:return => :hoh,
|
1000
|
+
:conditions => [:first, :include?, "a"],
|
1001
|
+
:order => :first },
|
1002
|
+
{:conditions => [:last, :==, "Gray"]} ) { |k|
|
1003
|
+
results << k
|
1004
|
+
%w[delete break]
|
1005
|
+
} )
|
1006
|
+
assert_equal(%w[dana], results)
|
1007
|
+
assert_nil(@db["dana"])
|
1008
|
+
assert_equal(2, @db.size)
|
1009
|
+
end
|
801
1010
|
|
802
1011
|
def test_difference_returns_the_set_difference_of_multiple_queries
|
803
1012
|
load_condition_data
|
@@ -836,6 +1045,145 @@ class TestQuery < Test::Unit::TestCase
|
|
836
1045
|
{:conditions => [:first, :==, "Jim"]} ) )
|
837
1046
|
end
|
838
1047
|
|
1048
|
+
def test_difference_can_be_passed_a_block_to_iterate_over_the_results
|
1049
|
+
load_condition_data
|
1050
|
+
results = [ ]
|
1051
|
+
assert_equal( @db,
|
1052
|
+
@db.diff( { :select => :keys,
|
1053
|
+
:conditions => [:last, :==, "Gray"],
|
1054
|
+
:order => :first },
|
1055
|
+
{:conditions => [:first, :==, "Jim"]} ) { |k|
|
1056
|
+
results << k
|
1057
|
+
} )
|
1058
|
+
assert_equal(%w[dana james], results)
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
def test_difference_with_a_block_can_update_records
|
1062
|
+
load_condition_data
|
1063
|
+
assert_equal( @db,
|
1064
|
+
@db.diff( { :conditions => [:last, :==, "Gray"],
|
1065
|
+
:order => :first },
|
1066
|
+
{:conditions => [:first, :==, "Jim"]} ) { |k, v|
|
1067
|
+
if k == "dana"
|
1068
|
+
v["salutation"] = "Mrs." # add
|
1069
|
+
v["middle"] = "AL" # update
|
1070
|
+
v.delete("age") # delete
|
1071
|
+
:update
|
1072
|
+
end
|
1073
|
+
} )
|
1074
|
+
assert_equal( { "salutation" => "Mrs.",
|
1075
|
+
"first" => "Dana",
|
1076
|
+
"middle" => "AL",
|
1077
|
+
"last" => "Gray" }, @db["dana"] )
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
def test_difference_with_a_block_can_delete_records
|
1081
|
+
load_condition_data
|
1082
|
+
assert_equal( @db,
|
1083
|
+
@db.diff( { :select => :keys,
|
1084
|
+
:conditions => [:last, :==, "Gray"],
|
1085
|
+
:order => :first },
|
1086
|
+
{:conditions => [:first, :==, "Jim"]} ) { |k|
|
1087
|
+
:delete
|
1088
|
+
} )
|
1089
|
+
assert_equal(1, @db.size)
|
1090
|
+
assert_equal(%w[jim], @db.keys)
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
def test_difference_with_a_block_can_end_the_query
|
1094
|
+
load_condition_data
|
1095
|
+
results = [ ]
|
1096
|
+
assert_equal( @db,
|
1097
|
+
@db.diff( { :select => :keys,
|
1098
|
+
:conditions => [:last, :==, "Gray"],
|
1099
|
+
:order => :first },
|
1100
|
+
{:conditions => [:first, :==, "Jim"]} ) { |k|
|
1101
|
+
results << k
|
1102
|
+
:break
|
1103
|
+
} )
|
1104
|
+
assert_equal(%w[dana], results)
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
def test_difference_with_a_block_can_combine_flags
|
1108
|
+
load_condition_data
|
1109
|
+
results = [ ]
|
1110
|
+
assert_equal( @db,
|
1111
|
+
@db.diff( { :select => :keys,
|
1112
|
+
:conditions => [:last, :==, "Gray"],
|
1113
|
+
:order => :first },
|
1114
|
+
{:conditions => [:first, :==, "Jim"]} ) { |k|
|
1115
|
+
results << k
|
1116
|
+
%w[delete break]
|
1117
|
+
} )
|
1118
|
+
assert_equal(%w[dana], results)
|
1119
|
+
assert_nil(@db["dana"])
|
1120
|
+
assert_equal(2, @db.size)
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
def test_search_methods_can_control_what_is_passed_to_the_block
|
1124
|
+
load_condition_data
|
1125
|
+
[ [{:select => :keys}, "dana"],
|
1126
|
+
[ {:select => :docs}, { "first" => "Dana",
|
1127
|
+
"middle" => "Ann Leslie",
|
1128
|
+
"last" => "Gray",
|
1129
|
+
"age" => "34" } ],
|
1130
|
+
[ {:return => :aoa}, [ "dana",
|
1131
|
+
{ "first" => "Dana",
|
1132
|
+
"middle" => "Ann Leslie",
|
1133
|
+
"last" => "Gray",
|
1134
|
+
"age" => "34" } ] ],
|
1135
|
+
[ {:return => :hoh}, [ "dana",
|
1136
|
+
{ "first" => "Dana",
|
1137
|
+
"middle" => "Ann Leslie",
|
1138
|
+
"last" => "Gray",
|
1139
|
+
"age" => "34" } ] ],
|
1140
|
+
[ {:return => :aoh}, { :primary_key => "dana",
|
1141
|
+
"first" => "Dana",
|
1142
|
+
"middle" => "Ann Leslie",
|
1143
|
+
"last" => "Gray",
|
1144
|
+
"age" => "34" } ] ].each do |query, results|
|
1145
|
+
%w[union intersection difference].each do |search|
|
1146
|
+
args = [ ]
|
1147
|
+
@db.send( search,
|
1148
|
+
query.merge(:conditions => [:first, :==, "Dana"]) ) do |kv|
|
1149
|
+
args << kv
|
1150
|
+
end
|
1151
|
+
assert_equal([results], args)
|
1152
|
+
end
|
1153
|
+
end
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
def test_search_methods_yields_key_value_tuples
|
1157
|
+
load_condition_data
|
1158
|
+
[ [ {:return => :aoa}, [ "dana",
|
1159
|
+
{ "first" => "Dana",
|
1160
|
+
"middle" => "Ann Leslie",
|
1161
|
+
"last" => "Gray",
|
1162
|
+
"age" => "34" } ] ],
|
1163
|
+
[ {:return => :hoh}, [ "dana",
|
1164
|
+
{ "first" => "Dana",
|
1165
|
+
"middle" => "Ann Leslie",
|
1166
|
+
"last" => "Gray",
|
1167
|
+
"age" => "34" } ] ] ].each do |query, tuple|
|
1168
|
+
%w[union intersection difference].each do |search|
|
1169
|
+
yielded = nil
|
1170
|
+
@db.send( search,
|
1171
|
+
query.merge(:conditions => [:first, :==, "Dana"]) ) do |kv|
|
1172
|
+
yielded = kv
|
1173
|
+
end
|
1174
|
+
assert_equal(tuple, yielded)
|
1175
|
+
key, value = nil, nil
|
1176
|
+
@db.send( search,
|
1177
|
+
query.merge(:conditions => [:first, :==, "Dana"]) ) do |k, v|
|
1178
|
+
key = k
|
1179
|
+
value = v
|
1180
|
+
end
|
1181
|
+
assert_equal(tuple.first, key)
|
1182
|
+
assert_equal(tuple.last, value)
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
|
839
1187
|
private
|
840
1188
|
|
841
1189
|
def load_simple_data
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class TestReadOnlyQuery < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
# create the database and load some data
|
6
|
+
tdb do |db|
|
7
|
+
db[:pk1] = {:a => 1, :b => 2, :c => 3}
|
8
|
+
db[:pk2] = { }
|
9
|
+
end
|
10
|
+
@db = tdb("r")
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@db.close
|
15
|
+
remove_db_files
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_all_with_a_block_can_end_the_query
|
19
|
+
results = [ ]
|
20
|
+
assert_equal(@db, @db.all { |k, _| results << k; :break })
|
21
|
+
assert_equal(1, results.size)
|
22
|
+
assert_match(/\Apk[12]\z/, results.first)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_all_with_block_delete_is_ignored_and_triggers_a_warning
|
26
|
+
warning = capture_stderr do
|
27
|
+
assert_equal(@db, @db.all { :delete })
|
28
|
+
end
|
29
|
+
assert_equal(2, @db.size)
|
30
|
+
assert( !warning.empty?,
|
31
|
+
"A warning was not issued for :delete in a read only query" )
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_all_with_block_update_is_ignored_and_triggers_a_warning
|
35
|
+
warning = capture_stderr do
|
36
|
+
assert_equal( @db, @db.all { |k, v|
|
37
|
+
if k == "pk1"
|
38
|
+
v["a"] = "1.1" # change
|
39
|
+
v.delete("c") # remove
|
40
|
+
v[:d] = 4 # add
|
41
|
+
:update
|
42
|
+
end
|
43
|
+
} )
|
44
|
+
end
|
45
|
+
assert_equal({"a" => "1", "b" => "2", "c" => "3"}, @db[:pk1])
|
46
|
+
assert( !warning.empty?,
|
47
|
+
"A warning was not issued for :update in a read only query" )
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_all_methods_can_control_what_is_passed_to_the_block
|
51
|
+
[ [{:select => :keys}, "pk1"],
|
52
|
+
[{:select => :docs}, {"a" => "1", "b" => "2", "c" => "3"}],
|
53
|
+
[ {:return => :aoa}, [ "pk1",
|
54
|
+
{"a" => "1", "b" => "2", "c" => "3"} ] ],
|
55
|
+
[ {:return => :hoh}, [ "pk1",
|
56
|
+
{"a" => "1", "b" => "2", "c" => "3"} ] ],
|
57
|
+
[ {:return => :aoh}, { :primary_key => "pk1",
|
58
|
+
"a" => "1",
|
59
|
+
"b" => "2",
|
60
|
+
"c" => "3" } ] ].each do |query, results|
|
61
|
+
args = [ ]
|
62
|
+
@db.all(query.merge(:conditions => [:a, :==, 1])) do |kv|
|
63
|
+
args << kv
|
64
|
+
end
|
65
|
+
assert_equal([results], args)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_all_yields_key_value_tuples
|
70
|
+
[ [ {:return => :aoa}, [ "pk1",
|
71
|
+
{"a" => "1", "b" => "2", "c" => "3"} ] ],
|
72
|
+
[ {:return => :hoh}, [ "pk1",
|
73
|
+
{ "a" => "1",
|
74
|
+
"b" => "2",
|
75
|
+
"c" => "3" } ] ] ].each do |query, tuple|
|
76
|
+
yielded = nil
|
77
|
+
@db.all(query.merge(:conditions => [:a, :==, 1])) do |kv|
|
78
|
+
yielded = kv
|
79
|
+
end
|
80
|
+
assert_equal(tuple, yielded)
|
81
|
+
key, value = nil, nil
|
82
|
+
@db.all(query.merge(:conditions => [:a, :==, 1])) do |k, v|
|
83
|
+
key = k
|
84
|
+
value = v
|
85
|
+
end
|
86
|
+
assert_equal(tuple.first, key)
|
87
|
+
assert_equal(tuple.last, value)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_count_works_on_a_read_only_database
|
92
|
+
assert_equal(@db.size, @db.count)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_paginate_works_on_a_read_only_database
|
96
|
+
assert_equal( %w[pk1], @db.paginate( :select => :keys,
|
97
|
+
:order => :primary_key,
|
98
|
+
:per_page => 1,
|
99
|
+
:page => 1 ) )
|
100
|
+
assert_equal( %w[pk2], @db.paginate( :select => :keys,
|
101
|
+
:order => :primary_key,
|
102
|
+
:per_page => 1,
|
103
|
+
:page => 2 ) )
|
104
|
+
assert_equal( [ ], @db.paginate( :select => :keys,
|
105
|
+
:order => :primary_key,
|
106
|
+
:per_page => 1,
|
107
|
+
:page => 3 ) )
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_search_with_a_block_can_end_the_query
|
111
|
+
each_search do |search|
|
112
|
+
results = [ ]
|
113
|
+
assert_equal(@db, @db.send(search, :order => :primary_key) { |k, _|
|
114
|
+
results << k; :break
|
115
|
+
})
|
116
|
+
assert_equal(1, results.size)
|
117
|
+
assert_equal("pk1", results.first)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_search_with_block_delete_is_ignored_and_triggers_a_warning
|
122
|
+
each_search do |search|
|
123
|
+
warning = capture_stderr do
|
124
|
+
assert_equal(@db, @db.send(search, :order => :primary_key) { :delete })
|
125
|
+
end
|
126
|
+
assert_equal(2, @db.size)
|
127
|
+
assert( !warning.empty?,
|
128
|
+
"A warning was not issued for :delete in a read only search" )
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_search_with_block_update_is_ignored_and_triggers_a_warning
|
133
|
+
each_search do |search|
|
134
|
+
warning = capture_stderr do
|
135
|
+
assert_equal( @db, @db.send(search, :order => :primary_key) { |k, v|
|
136
|
+
if k == "pk1"
|
137
|
+
v["a"] = "1.1" # change
|
138
|
+
v.delete("c") # remove
|
139
|
+
v[:d] = 4 # add
|
140
|
+
:update
|
141
|
+
end
|
142
|
+
} )
|
143
|
+
end
|
144
|
+
assert_equal({"a" => "1", "b" => "2", "c" => "3"}, @db[:pk1])
|
145
|
+
assert( !warning.empty?,
|
146
|
+
"A warning was not issued for :update in a read only search" )
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def each_search(&test)
|
153
|
+
%w[union intersection difference].each(&test)
|
154
|
+
end
|
155
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oklahoma_mixer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 0.4.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- James Edward Gray II
|
@@ -9,29 +14,35 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-02-
|
17
|
+
date: 2010-02-20 00:00:00 -06:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: ffi
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
- 5
|
30
|
+
- 4
|
23
31
|
version: 0.5.4
|
24
|
-
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
25
34
|
- !ruby/object:Gem::Dependency
|
26
35
|
name: rake
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
38
|
requirements:
|
31
39
|
- - ">="
|
32
40
|
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
33
43
|
version: "0"
|
34
|
-
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
35
46
|
description: |
|
36
47
|
Oklahoma Mixer is a intended to be an all inclusive wrapper for Tokyo Cabinet.
|
37
48
|
It provides Rubyish interfaces for all database types and supports the full
|
@@ -95,6 +106,7 @@ files:
|
|
95
106
|
- test/table_database/document_storage_test.rb
|
96
107
|
- test/table_database/index_test.rb
|
97
108
|
- test/table_database/query_test.rb
|
109
|
+
- test/table_database/read_only_query_test.rb
|
98
110
|
- test/table_database/table_tuning_test.rb
|
99
111
|
- test/test_helper.rb
|
100
112
|
- AUTHORS.rdoc
|
@@ -120,18 +132,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
132
|
requirements:
|
121
133
|
- - ">="
|
122
134
|
- !ruby/object:Gem::Version
|
135
|
+
segments:
|
136
|
+
- 0
|
123
137
|
version: "0"
|
124
|
-
version:
|
125
138
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
139
|
requirements:
|
127
140
|
- - ">="
|
128
141
|
- !ruby/object:Gem::Version
|
142
|
+
segments:
|
143
|
+
- 0
|
129
144
|
version: "0"
|
130
|
-
version:
|
131
145
|
requirements: []
|
132
146
|
|
133
147
|
rubyforge_project:
|
134
|
-
rubygems_version: 1.3.
|
148
|
+
rubygems_version: 1.3.6
|
135
149
|
signing_key:
|
136
150
|
specification_version: 3
|
137
151
|
summary: An full featured and robust FFI interface to Tokyo Cabinet.
|