xamplr 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/CHANGES.txt +13 -0
  2. data/LICENSE +3 -0
  3. data/README.rdoc +26 -0
  4. data/README.rdoc.orig +118 -0
  5. data/Rakefile +85 -0
  6. data/VERSION.yml +4 -0
  7. data/examples/random-people-shared-addresses/Makefile +16 -0
  8. data/examples/random-people-shared-addresses/batch-load-users.rb +83 -0
  9. data/examples/random-people-shared-addresses/find-mentions.rb +47 -0
  10. data/examples/random-people-shared-addresses/find-people-by-address.rb +104 -0
  11. data/examples/random-people-shared-addresses/optimise.rb +16 -0
  12. data/examples/random-people-shared-addresses/people.rb +35 -0
  13. data/examples/random-people-shared-addresses/query.rb +75 -0
  14. data/examples/random-people-shared-addresses/query2.rb +73 -0
  15. data/examples/random-people-shared-addresses/random-names.csv +10000 -0
  16. data/examples/random-people-shared-addresses/settings.rb +3 -0
  17. data/examples/random-people-shared-addresses/what-to-query-on.rb +82 -0
  18. data/examples/random-people-shared-addresses/xampl-gen.rb +36 -0
  19. data/examples/random-people-shared-addresses/xml/people.xml +14 -0
  20. data/examples/random-people/Makefile +16 -0
  21. data/examples/random-people/batch-load-users.rb +61 -0
  22. data/examples/random-people/optimise.rb +16 -0
  23. data/examples/random-people/people.rb +22 -0
  24. data/examples/random-people/query.rb +73 -0
  25. data/examples/random-people/query2.rb +73 -0
  26. data/examples/random-people/random-names.csv +10000 -0
  27. data/examples/random-people/rawtc.rb +91 -0
  28. data/examples/random-people/settings.rb +3 -0
  29. data/examples/random-people/what-to-query-on.rb +80 -0
  30. data/examples/random-people/xampl-gen.rb +36 -0
  31. data/examples/random-people/xml/people.xml +11 -0
  32. data/examples/read-testing/Makefile +10 -0
  33. data/examples/read-testing/load.rb +65 -0
  34. data/examples/read-testing/read.rb +51 -0
  35. data/examples/read-testing/rrr.rb +87 -0
  36. data/examples/read-testing/settings.rb +2 -0
  37. data/examples/read-testing/xampl-gen.rb +36 -0
  38. data/examples/read-testing/xml/text.xml +8 -0
  39. data/examples/tokyo-cabinet-experimental/expt-query.rb +42 -0
  40. data/examples/tokyo-cabinet-experimental/expt-query2.rb +42 -0
  41. data/examples/tokyo-cabinet-experimental/expt-query3.rb +41 -0
  42. data/examples/tokyo-cabinet-experimental/expt-reader.rb +32 -0
  43. data/examples/tokyo-cabinet-experimental/expt.rb +61 -0
  44. data/examples/tokyo-cabinet-experimental/xampl-gen.rb +36 -0
  45. data/examples/tokyo-cabinet-experimental/xml/tcx.xml +6 -0
  46. data/lib/xampl-generator.rb +3 -0
  47. data/lib/xampl.rb +3 -0
  48. data/lib/xamplr-generator.rb +10 -0
  49. data/lib/xamplr.rb +37 -0
  50. data/lib/xamplr/README-POSSIBLE-PROBLEMS +5 -0
  51. data/lib/xamplr/TODO +1 -0
  52. data/lib/xamplr/exceptions.rb +97 -0
  53. data/lib/xamplr/from-xml-orig.rb +350 -0
  54. data/lib/xamplr/from-xml.rb +439 -0
  55. data/lib/xamplr/gen-elements.xml +6230 -0
  56. data/lib/xamplr/gen.elements.xml +108 -0
  57. data/lib/xamplr/generate-elements.rb +15 -0
  58. data/lib/xamplr/generator.rb +5 -0
  59. data/lib/xamplr/graphml-out.rb +470 -0
  60. data/lib/xamplr/handwritten/example.rb +698 -0
  61. data/lib/xamplr/handwritten/hand-example.rb +533 -0
  62. data/lib/xamplr/handwritten/test-handwritten.rb +873 -0
  63. data/lib/xamplr/indexed-array.rb +115 -0
  64. data/lib/xamplr/mixins.rb +397 -0
  65. data/lib/xamplr/my.gen.elements.xml +461 -0
  66. data/lib/xamplr/notifications.rb +57 -0
  67. data/lib/xamplr/obsolete/fsdb.rb +62 -0
  68. data/lib/xamplr/persist-to-xml.rb +249 -0
  69. data/lib/xamplr/persistence.rb +522 -0
  70. data/lib/xamplr/persistence.rb.more_thread_safe +771 -0
  71. data/lib/xamplr/persistence.rb.partially_thread_safe +763 -0
  72. data/lib/xamplr/persister.rb +310 -0
  73. data/lib/xamplr/persisters/caches.rb +186 -0
  74. data/lib/xamplr/persisters/caching.rb +172 -0
  75. data/lib/xamplr/persisters/filesystem.rb +60 -0
  76. data/lib/xamplr/persisters/in-memory.rb +180 -0
  77. data/lib/xamplr/persisters/simple.rb +59 -0
  78. data/lib/xamplr/persisters/tokyo-cabinet.rb +641 -0
  79. data/lib/xamplr/simpleTemplate/danger.rx +4 -0
  80. data/lib/xamplr/simpleTemplate/obsolete/input-c.r4 +35 -0
  81. data/lib/xamplr/simpleTemplate/obsolete/play.r6.txt +12 -0
  82. data/lib/xamplr/simpleTemplate/obsolete/play_more.r6.txt +20 -0
  83. data/lib/xamplr/simpleTemplate/obsolete/test001.r5 +8 -0
  84. data/lib/xamplr/simpleTemplate/obsolete/test002.r5 +13 -0
  85. data/lib/xamplr/simpleTemplate/obsolete/test003.r5 +37 -0
  86. data/lib/xamplr/simpleTemplate/old/r6.000.rb +122 -0
  87. data/lib/xamplr/simpleTemplate/old/r6.001.rb +145 -0
  88. data/lib/xamplr/simpleTemplate/play.r6 +12 -0
  89. data/lib/xamplr/simpleTemplate/play_more.r6 +20 -0
  90. data/lib/xamplr/simpleTemplate/play_noblanks.r6 +21 -0
  91. data/lib/xamplr/simpleTemplate/playq.r6 +16 -0
  92. data/lib/xamplr/simpleTemplate/r6.rb +87 -0
  93. data/lib/xamplr/simpleTemplate/simple-template.rb +75 -0
  94. data/lib/xamplr/templates/child.template +47 -0
  95. data/lib/xamplr/templates/child_indexed.template +89 -0
  96. data/lib/xamplr/templates/child_modules.template +5 -0
  97. data/lib/xamplr/templates/element_classes.template +11 -0
  98. data/lib/xamplr/templates/element_data.template +282 -0
  99. data/lib/xamplr/templates/element_empty.template +285 -0
  100. data/lib/xamplr/templates/element_mixed.template +277 -0
  101. data/lib/xamplr/templates/element_simple.template +276 -0
  102. data/lib/xamplr/templates/package.template +26 -0
  103. data/lib/xamplr/test-support/Makefile +47 -0
  104. data/lib/xamplr/test-support/bench-cache.rb +80 -0
  105. data/lib/xamplr/test-support/bench-script.rb +21 -0
  106. data/lib/xamplr/test-support/bench.rb +116 -0
  107. data/lib/xamplr/test-support/bench2.rb +132 -0
  108. data/lib/xamplr/test-support/test-cache.rb +147 -0
  109. data/lib/xamplr/test-support/test-data/binding.xml +7 -0
  110. data/lib/xamplr/test-support/test-data/example.xml +14 -0
  111. data/lib/xamplr/test-support/test-data/internationalization-utf8.txt +1 -0
  112. data/lib/xamplr/test-support/test-data/labels.xml +37 -0
  113. data/lib/xamplr/test-support/test-data/labels001.xml +38 -0
  114. data/lib/xamplr/test-support/test-deep-change.rb +135 -0
  115. data/lib/xamplr/test-support/test-elements.rb +109 -0
  116. data/lib/xamplr/test-support/test-indexed-array.rb +169 -0
  117. data/lib/xamplr/test-support/test-misc.rb +73 -0
  118. data/lib/xamplr/test-support/test-names.rb +67 -0
  119. data/lib/xamplr/test-support/test-rollback.rb +106 -0
  120. data/lib/xamplr/test-support/test.rb +1504 -0
  121. data/lib/xamplr/to-ruby.rb +220 -0
  122. data/lib/xamplr/to-xml.rb +158 -0
  123. data/lib/xamplr/version.rb +67 -0
  124. data/lib/xamplr/visitor.rb +140 -0
  125. data/lib/xamplr/visitors.rb +573 -0
  126. data/lib/xamplr/xampl-generator.rb +533 -0
  127. data/lib/xamplr/xampl-hand-generated.rb +1535 -0
  128. data/lib/xamplr/xampl-module.rb +36 -0
  129. data/lib/xamplr/xampl-object-internals.rb +6 -0
  130. data/lib/xamplr/xampl-object.rb +202 -0
  131. data/lib/xamplr/xampl-persisted-object.rb +122 -0
  132. data/lib/xamplr/xml-text.rb +117 -0
  133. data/lib/xamplr/xml/document.xml +7 -0
  134. data/lib/xamplr/xml/elements.xml +101 -0
  135. data/lib/xamplr/xml/elements000.xml +73 -0
  136. data/lib/xamplr/xml/example.xml +23 -0
  137. data/lib/xamplr/xml/options.xml +12 -0
  138. data/lib/xamplr/xml/uche.xml +38 -0
  139. data/lib/xamplr/yEd-sample.graphml +300 -0
  140. data/test/test_helper.rb +10 -0
  141. data/test/xamplr_test.rb +7 -0
  142. metadata +245 -0
@@ -0,0 +1,641 @@
1
+ module Xampl
2
+
3
+ require 'fileutils'
4
+ require 'tokyocabinet'
5
+ require 'xamplr/persisters/caching'
6
+ require 'set'
7
+
8
+ # require 'ruby-prof'
9
+
10
+ class TokyoCabinetPersister < AbstractCachingPersister
11
+ include TokyoCabinet
12
+
13
+ def note_errors(msg="TokyoCabinet Error:: %s\n")
14
+ result = yield
15
+
16
+ rmsg = nil
17
+ unless result then
18
+ rmsg = sprintf(msg, @tc_db.errmsg(@tc_db.ecode))
19
+ STDERR.printf(rmsg)
20
+ STDERR.puts "---------"
21
+ caller(0).each do |trace|
22
+ STDERR.puts(trace)
23
+ end
24
+ STDERR.puts "---------"
25
+ end
26
+ return rmsg
27
+ end
28
+
29
+ $lexical_indexes = Set.new(%w{ class pid time-stamp xampl_from xampl_to }) unless defined?($lexical_indexes)
30
+ $numeric_indexes = Set.new unless defined?($numeric_indexes)
31
+
32
+ def TokyoCabinetPersister.add_lexical_indexs(indexes)
33
+ $lexical_indexes.merge(indexes)
34
+ end
35
+
36
+ def TokyoCabinetPersister.add_numeric_indexs(indexes)
37
+ $numeric_indexes.merge(indexes)
38
+ end
39
+
40
+ def initialize(name=nil, format=nil, root=File.join(".", "repo"))
41
+ super(root, name, format)
42
+
43
+ FileUtils.mkdir_p(@root_dir) unless File.exist?(@root_dir)
44
+ @filename = "#{@root_dir}/repo.tct"
45
+
46
+ open_tc_db()
47
+
48
+ # note_errors("TC[[#{ @filename }]]:: optimisation error: %s\n") do
49
+ # @tc_db.optimize(-1, -1, -1, TDB::TDEFLATE)
50
+ # end
51
+ # note_errors("TC[[#{ @filename }]]:: close error: %s\n") do
52
+ # @tc_db.close
53
+ # end
54
+ end
55
+
56
+ def open_tc_db
57
+ return if @tc_db
58
+ # puts "#{File.basename(__FILE__)}:#{__LINE__} open tc db: #{ @filename }"
59
+ #puts "#{File.basename(__FILE__)}:#{__LINE__} callers..."
60
+ #caller(0).each { | trace | puts " #{trace}"}
61
+ @tc_db = TDB.new
62
+ note_errors("TC[[#{ @filename }]]:: tuning error: %s\n") do
63
+ @tc_db.tune(-1, -1, -1, TDB::TDEFLATE)
64
+ end
65
+
66
+ note_errors("TC[[#{ @filename }]]:: open [#{ @filename }] error: %s\n") do
67
+ @tc_db.open(@filename, TDB::OWRITER | TDB::OCREAT | TDB::OLCKNB ) #TDB::OTSYNC slows it down by almost 50 times
68
+ end
69
+
70
+ # Don't care if there are errors (in fact, if the index exists a failure is the expected thing)
71
+
72
+ $lexical_indexes.each do | index_name |
73
+ r = @tc_db.setindex(index_name, TDB::ITLEXICAL | TDB::ITKEEP)
74
+ end
75
+ $numeric_indexes.each do | index_name |
76
+ @tc_db.setindex(index_name, TDB::ITDECIMAL | TDB::ITKEEP)
77
+ end
78
+ end
79
+
80
+ def optimise(opts)
81
+ return unless @tc_db
82
+
83
+ if opts[:indexes_only] then
84
+ # Don't care if there are errors (in fact, if the index exists a failure is the expected thing)
85
+ $lexical_indexes.each do | index_name |
86
+ @tc_db.setindex(index_name, 9998)
87
+ end
88
+ $numeric_indexes.each do | index_name |
89
+ @tc_db.setindex(index_name, 9998)
90
+ end
91
+ else
92
+ note_errors("TC[[#{ @filename }]]:: optimisation error: %s\n") do
93
+ @tc_db.optimize(-1, -1, -1, 0xff)
94
+ end
95
+ end
96
+ end
97
+
98
+ def close
99
+ if @tc_db then
100
+ self.sync
101
+ note_errors("TC[[#{ @filename }]]:: close error: %s\n") do
102
+ @tc_db.close
103
+ end
104
+ @tc_db = nil
105
+ # puts "#{File.basename(__FILE__)}:#{__LINE__} close tc db: #{ @filename }"
106
+ end
107
+ end
108
+
109
+ def TokyoCabinetPersister.kind
110
+ :tokyo_cabinet
111
+ end
112
+
113
+ def kind
114
+ TokyoCabinetPersister.kind
115
+ end
116
+
117
+ def query_implemented
118
+ true
119
+ end
120
+
121
+ def query(hint=false)
122
+ open_tc_db
123
+ query = TableQuery.new(@tc_db)
124
+
125
+ yield query
126
+
127
+ result_keys = nil
128
+ the_hint = nil
129
+ if hint then
130
+ result_keys, the_hint = query.search(true)
131
+ else
132
+ result_keys = query.search
133
+ end
134
+ results = result_keys.collect { | key | @tc_db[ key ] }
135
+
136
+ class_cache = {}
137
+ results.each do | result |
138
+ next unless result
139
+
140
+ class_name = result['class']
141
+ result_class = class_cache[class_name]
142
+ unless result_class then
143
+ class_name.split("::").each do | chunk |
144
+ if result_class then
145
+ result_class = result_class.const_get( chunk )
146
+ else
147
+ result_class = Kernel.const_get( chunk )
148
+ end
149
+ end
150
+
151
+ class_cache[class_name] = result_class
152
+ end
153
+
154
+ result['xampl'] = self.lookup(result_class, result['pid'])
155
+ end
156
+
157
+ if hint then
158
+ return results, the_hint
159
+ else
160
+ return results
161
+ end
162
+ end
163
+
164
+ def find_xampl(hint=false)
165
+ open_tc_db
166
+ query = TableQuery.new(@tc_db)
167
+
168
+ yield query
169
+
170
+ class_cache = {}
171
+
172
+ result_keys = nil
173
+ the_hint = nil
174
+ if hint then
175
+ result_keys, the_hint = query.search(true)
176
+ else
177
+ result_keys = query.search
178
+ end
179
+
180
+ results = result_keys.collect do | key |
181
+ result = @tc_db[ key ]
182
+ next unless result
183
+
184
+ class_name = result['class']
185
+ result_class = class_cache[class_name]
186
+ unless result_class then
187
+ class_name.split("::").each do | chunk |
188
+ if result_class then
189
+ result_class = result_class.const_get( chunk )
190
+ else
191
+ result_class = Kernel.const_get( chunk )
192
+ end
193
+ end
194
+
195
+ class_cache[class_name] = result_class
196
+ end
197
+
198
+ self.lookup(result_class, result['pid'])
199
+ end
200
+
201
+ if hint then
202
+ return results, the_hint
203
+ else
204
+ return results
205
+ end
206
+ end
207
+
208
+ def find_pids(hint=false)
209
+ open_tc_db
210
+ query = TableQuery.new(@tc_db)
211
+
212
+ yield query
213
+
214
+ result_keys = nil
215
+ the_hint = nil
216
+ if hint then
217
+ result_keys, the_hint = query.search(true)
218
+ else
219
+ result_keys = query.search
220
+ end
221
+
222
+ if hint then
223
+ return result_keys, the_hint
224
+ else
225
+ return result_keys
226
+ end
227
+ end
228
+
229
+ def find_meta(hint=false)
230
+ open_tc_db
231
+ query = TableQuery.new(@tc_db)
232
+
233
+ yield query
234
+
235
+ result_keys = nil
236
+ the_hint = nil
237
+ if hint then
238
+ result_keys, the_hint = query.search(true)
239
+ else
240
+ result_keys = query.search
241
+ end
242
+
243
+ results = result_keys.collect { | key | @tc_db[ key ] }
244
+
245
+ if hint then
246
+ return results, the_hint
247
+ else
248
+ return results
249
+ end
250
+ end
251
+
252
+ def find_mentions_of(xampl)
253
+ open_tc_db
254
+
255
+ place = File.join(xampl.class.name.split("::"), xampl.get_the_index)
256
+
257
+ query = TableQuery.new(@tc_db)
258
+ query.add_condition('xampl_to', :equals, place)
259
+ result_keys = query.search
260
+
261
+ class_cache = {}
262
+ results = result_keys.collect do | key |
263
+ result = @tc_db[ key ]
264
+ next unless result
265
+
266
+ mentioner = result['xampl_from']
267
+ class_name = result['class']
268
+ result_class = class_cache[class_name]
269
+ unless result_class then
270
+ class_name.split("::").each do | chunk |
271
+ if result_class then
272
+ result_class = result_class.const_get( chunk )
273
+ else
274
+ result_class = Kernel.const_get( chunk )
275
+ end
276
+ end
277
+
278
+ class_cache[class_name] = result_class
279
+ end
280
+
281
+ self.lookup(result_class, result['pid'])
282
+ end
283
+ return results
284
+ end
285
+
286
+ def do_sync_write
287
+ # RubyProf.start
288
+ #
289
+ # do_sync_write_work
290
+ #
291
+ # result = RubyProf.stop
292
+ # printer = RubyProf::FlatPrinter.new(result)
293
+ # printer.print(STDOUT, 0)
294
+ # puts "#{File.basename(__FILE__)}:#{__LINE__} stop this profiler"
295
+ # end
296
+ #
297
+ # def do_sync_write_work
298
+ open_tc_db
299
+ @time_stamp = Time.now.to_f.to_s
300
+
301
+ # puts "DO SYNC WRITE: #{ @changed.size } to be written (#{ @filename })"
302
+ # note_errors("TC:: open error: %s\n") do
303
+ # @tc_db.open(@filename, TDB::OWRITER | TDB::OCREAT | TDB::OLCKNB ) #TDB::OTSYNC slows it down by almost 50 times
304
+ # end
305
+
306
+ begin
307
+ note_errors("TC[[#{ @filename }]]:: tranbegin error: %s\n") do
308
+ @tc_db.tranbegin
309
+ end
310
+
311
+ @changed.each do |xampl, ignore|
312
+ write(xampl)
313
+ end
314
+ rescue => e
315
+ msg = "no TC.abort attempted"
316
+ msg = note_errors("TC[[#{ @filename }]]:: trancommit error: %s\n") do
317
+ @tc_db.tranabort
318
+ end
319
+ raise "TokyoCabinetPersister Error:: #{ msg }/#{ e }"
320
+ else
321
+ note_errors("TC[[#{ @filename }]]:: trancommit error: %s\n") do
322
+ @tc_db.trancommit
323
+ end
324
+ ensure
325
+ # note_errors("TC[[#{ @filename }]]:: close error: %s\n") do
326
+ # @tc_db.close()
327
+ # end
328
+ end
329
+ # puts " num records: #{ @tc_db.rnum() }"
330
+ end
331
+
332
+ def write(xampl)
333
+ raise XamplException.new(:no_index_so_no_persist) unless xampl.get_the_index
334
+
335
+ place = File.join(xampl.class.name.split("::"), xampl.get_the_index)
336
+ mentions = Set.new
337
+ data = represent(xampl, mentions)
338
+
339
+ query = TableQuery.new(@tc_db)
340
+ query.add_condition('xampl_from', :equals, place)
341
+ note_errors("TC[[#{ @filename }]]:: failed to remove from mentions, error: %s\n") do
342
+ query.searchout
343
+ end
344
+
345
+ mentions.each do | mention |
346
+ mention_place = File.join(mention.class.name.split("::"), mention.get_the_index)
347
+ #TODO -- will repeadedly changing a persisted xampl object fragment the TC db?
348
+
349
+ pk = @tc_db.genuid
350
+ mention_hash = {
351
+ 'xampl_from' => place,
352
+ 'class' => xampl.class.name,
353
+ 'pid' => xampl.get_the_index,
354
+ 'xampl_to' => mention_place
355
+ }
356
+
357
+ note_errors("TC[[#{ @filename }]]:: write error: %s\n") do
358
+ @tc_db.put(pk, mention_hash)
359
+ end
360
+ end
361
+
362
+ xampl_hash = {
363
+ 'class' => xampl.class.name,
364
+ 'pid' => xampl.get_the_index,
365
+ 'time-stamp' => @time_stamp,
366
+ 'xampl' => data
367
+ }
368
+
369
+ hash = xampl.describe_yourself
370
+ if hash then
371
+ xampl_hash = hash.merge(xampl_hash)
372
+ end
373
+
374
+ note_errors("TC[[#{ @filename }]]:: write error: %s\n") do
375
+ @tc_db.put(place, xampl_hash)
376
+ end
377
+
378
+ @write_count = @write_count + 1
379
+ xampl.changes_accepted
380
+ return true
381
+ end
382
+
383
+ $TC_COUNT = 0
384
+ $FS_COUNT = 0
385
+ $NF_COUNT = 0
386
+
387
+ def read_representation(klass, pid)
388
+ #TODO -- is this being called too often, e.g. by new_xxx???
389
+ # puts "#{File.basename(__FILE__)}:#{__LINE__} READ #{ klass }/#{ pid }"
390
+ # caller(0).each { | trace | puts " #{trace}"}
391
+
392
+ open_tc_db
393
+ place = File.join(klass.name.split("::"), pid)
394
+ representation = nil
395
+
396
+ meta = @tc_db[place]
397
+ representation = meta['xampl'] if meta
398
+
399
+ # puts "#{File.basename(__FILE__)}:#{__LINE__} TC: #{ klass }/#{ pid }" if representation
400
+ $TC_COUNT += 1 if representation
401
+
402
+ # puts "read: #{ place }, size: #{ representation.size }"
403
+ # puts representation[0..100]
404
+
405
+ unless representation then
406
+ # try the filesystem if it is not in the TC repository
407
+ place = File.join(@root_dir, klass.name.split("::"), pid)
408
+ representation = File.read(place) if File.exist?(place)
409
+ $FS_COUNT += 1 if representation
410
+ # puts "#{File.basename(__FILE__)}:#{__LINE__} FS: #{ klass }/#{ pid } (FS: #{ $FS_COUNT}, TC: #{ $TC_COUNT }, NF: #{ $NF_COUNT }" if representation
411
+ end
412
+ # puts "#{File.basename(__FILE__)}:#{__LINE__} ??: #{ klass }/#{ pid }" unless representation
413
+ $NF_COUNT += 1
414
+
415
+ return representation
416
+ end
417
+ end
418
+
419
+ #
420
+ # Derrived from rufus-tyrant, but simplified significantly, and using the
421
+ # TokyoCabinet named constants rather than numbers
422
+ #
423
+
424
+ class TableQuery
425
+ include TokyoCabinet
426
+
427
+ OPERATORS = {
428
+ # strings...
429
+
430
+ :streq => TDBQRY::QCSTREQ, # string equality
431
+ :eq => TDBQRY::QCSTREQ,
432
+ :eql => TDBQRY::QCSTREQ,
433
+ :equals => TDBQRY::QCSTREQ,
434
+
435
+ :strinc => TDBQRY::QCSTRINC, # string include
436
+ :inc => TDBQRY::QCSTRINC, # string include
437
+ :includes => TDBQRY::QCSTRINC, # string include
438
+
439
+ :strbw => TDBQRY::QCSTRBW, # string begins with
440
+ :bw => TDBQRY::QCSTRBW,
441
+ :starts_with => TDBQRY::QCSTRBW,
442
+ :strew => TDBQRY::QCSTREW, # string ends with
443
+ :ew => TDBQRY::QCSTREW,
444
+ :ends_with => TDBQRY::QCSTREW,
445
+
446
+ :strand => TDBQRY::QCSTRAND, # string which include all the tokens in the given exp
447
+ :and => TDBQRY::QCSTRAND,
448
+
449
+ :stror => TDBQRY::QCSTROR, # string which include at least one of the tokens
450
+ :or => TDBQRY::QCSTROR,
451
+
452
+ :stroreq => TDBQRY::QCSTROREQ, # string which is equal to at least one token
453
+
454
+ :strorrx => TDBQRY::QCSTRRX, # string which matches the given regex
455
+ :regex => TDBQRY::QCSTRRX,
456
+ :matches => TDBQRY::QCSTRRX,
457
+
458
+ # numbers...
459
+
460
+ :numeq => TDBQRY::QCNUMEQ, # equal
461
+ :numequals => TDBQRY::QCNUMEQ,
462
+ :numgt => TDBQRY::QCNUMGT, # greater than
463
+ :gt => TDBQRY::QCNUMGT,
464
+ :numge => TDBQRY::QCNUMGE, # greater or equal
465
+ :ge => TDBQRY::QCNUMGE,
466
+ :gte => TDBQRY::QCNUMGE,
467
+ :numlt => TDBQRY::QCNUMLT, # greater or equal
468
+ :lt => TDBQRY::QCNUMLT,
469
+ :numle => TDBQRY::QCNUMLE, # greater or equal
470
+ :le => TDBQRY::QCNUMLE,
471
+ :lte => TDBQRY::QCNUMLE,
472
+ :numbt => TDBQRY::QCNUMBT, # a number between two tokens in the given exp
473
+ :bt => TDBQRY::QCNUMBT,
474
+ :between => TDBQRY::QCNUMBT,
475
+
476
+ :numoreq => TDBQRY::QCNUMOREQ # number which is equal to at least one token
477
+ }
478
+
479
+ TDBQCNEGATE = TDBQRY::QCNEGATE
480
+ TDBQCNOIDX = TDBQRY::QCNOIDX
481
+
482
+ DIRECTIONS = {
483
+ :strasc => TDBQRY::QOSTRASC,
484
+ :strdesc => TDBQRY::QOSTRDESC,
485
+ :asc => TDBQRY::QOSTRASC,
486
+ :desc => TDBQRY::QOSTRDESC,
487
+ :numasc => TDBQRY::QONUMASC,
488
+ :numdesc => TDBQRY::QONUMDESC
489
+ }
490
+
491
+ #
492
+ # Creates a query for a given Rufus::Tokyo::Table
493
+ #
494
+ # Queries are usually created via the #query (#prepare_query #do_query)
495
+ # of the Table instance.
496
+ #
497
+ # Methods of interest here are :
498
+ #
499
+ # * #add (or #add_condition)
500
+ # * #order_by
501
+ # * #limit
502
+ #
503
+ # also
504
+ #
505
+ # * #pk_only
506
+ # * #no_pk
507
+ #
508
+
509
+ def initialize (table)
510
+ @query = TDBQRY::new(table)
511
+ @opts = {}
512
+ end
513
+
514
+ #
515
+ # Performs the search
516
+ #
517
+
518
+ def search(hint=false)
519
+ r = @query.search
520
+ if hint then
521
+ return r, @query.hint
522
+ else
523
+ return r
524
+ end
525
+ end
526
+
527
+ #
528
+ # Performs the search and removes whatever's found
529
+ #
530
+
531
+ def searchout
532
+ r = @query.searchout
533
+ end
534
+
535
+ # limits the search
536
+
537
+ def setlimit(max=nil, skip=nil)
538
+ @query.setlimit(max, skip)
539
+ end
540
+
541
+ #
542
+ # Adds a condition
543
+ #
544
+ # table.query { |q|
545
+ # q.add 'name', :equals, 'Oppenheimer'
546
+ # q.add 'age', :numgt, 35
547
+ # }
548
+ #
549
+ # Understood 'operators' :
550
+ #
551
+ # :streq # string equality
552
+ # :eq
553
+ # :eql
554
+ # :equals
555
+ #
556
+ # :strinc # string include
557
+ # :inc # string include
558
+ # :includes # string include
559
+ #
560
+ # :strbw # string begins with
561
+ # :bw
562
+ # :starts_with
563
+ # :strew # string ends with
564
+ # :ew
565
+ # :ends_with
566
+ #
567
+ # :strand # string which include all the tokens in the given exp
568
+ # :and
569
+ #
570
+ # :stror # string which include at least one of the tokens
571
+ # :or
572
+ #
573
+ # :stroreq # string which is equal to at least one token
574
+ #
575
+ # :strorrx # string which matches the given regex
576
+ # :regex
577
+ # :matches
578
+ #
579
+ # # numbers...
580
+ #
581
+ # :numeq # equal
582
+ # :numequals
583
+ # :numgt # greater than
584
+ # :gt
585
+ # :numge # greater or equal
586
+ # :ge
587
+ # :gte
588
+ # :numlt # greater or equal
589
+ # :lt
590
+ # :numle # greater or equal
591
+ # :le
592
+ # :lte
593
+ # :numbt # a number between two tokens in the given exp
594
+ # :bt
595
+ # :between
596
+ #
597
+ # :numoreq # number which is equal to at least one token
598
+ #
599
+
600
+ def add (colname, operator, val, affirmative=true, no_index=false)
601
+ op = operator.is_a?(Fixnum) ? operator : OPERATORS[operator]
602
+ op = op | TDBQRY::QCNEGATE unless affirmative
603
+ op = op | TDBQRY::QCNOIDX if no_index
604
+
605
+ @query.addcond(colname, op, val)
606
+ end
607
+
608
+ alias :add_condition :add
609
+
610
+ #
611
+ # Sets the max number of records to return for this query.
612
+ #
613
+ # (If you're using TC >= 1.4.10 the optional 'offset' (skip) parameter
614
+ # is accepted)
615
+ #
616
+
617
+ def limit (i, offset=-1)
618
+ @query.setlimit(i, offset)
619
+ end
620
+
621
+ #
622
+ # Sets the sort order for the result of the query
623
+ #
624
+ # The 'direction' may be :
625
+ #
626
+ # :strasc # string ascending
627
+ # :strdesc
628
+ # :asc # string ascending
629
+ # :desc
630
+ # :numasc # number ascending
631
+ # :numdesc
632
+ #
633
+
634
+ def order_by (colname, direction=:strasc)
635
+ @query.setorder(colname, DIRECTIONS[direction])
636
+ end
637
+ end
638
+
639
+ Xampl.register_persister_kind(TokyoCabinetPersister)
640
+ end
641
+