xamplr 1.2.0 → 1.3.15

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