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