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.
- data/CHANGES.txt +13 -0
- data/LICENSE +3 -0
- data/README.rdoc +26 -0
- data/README.rdoc.orig +118 -0
- data/Rakefile +85 -0
- data/VERSION.yml +4 -0
- data/examples/random-people-shared-addresses/Makefile +16 -0
- data/examples/random-people-shared-addresses/batch-load-users.rb +83 -0
- data/examples/random-people-shared-addresses/find-mentions.rb +47 -0
- data/examples/random-people-shared-addresses/find-people-by-address.rb +104 -0
- data/examples/random-people-shared-addresses/optimise.rb +16 -0
- data/examples/random-people-shared-addresses/people.rb +35 -0
- data/examples/random-people-shared-addresses/query.rb +75 -0
- data/examples/random-people-shared-addresses/query2.rb +73 -0
- data/examples/random-people-shared-addresses/random-names.csv +10000 -0
- data/examples/random-people-shared-addresses/settings.rb +3 -0
- data/examples/random-people-shared-addresses/what-to-query-on.rb +82 -0
- data/examples/random-people-shared-addresses/xampl-gen.rb +36 -0
- data/examples/random-people-shared-addresses/xml/people.xml +14 -0
- data/examples/random-people/Makefile +16 -0
- data/examples/random-people/batch-load-users.rb +61 -0
- data/examples/random-people/optimise.rb +16 -0
- data/examples/random-people/people.rb +22 -0
- data/examples/random-people/query.rb +73 -0
- data/examples/random-people/query2.rb +73 -0
- data/examples/random-people/random-names.csv +10000 -0
- data/examples/random-people/rawtc.rb +91 -0
- data/examples/random-people/settings.rb +3 -0
- data/examples/random-people/what-to-query-on.rb +80 -0
- data/examples/random-people/xampl-gen.rb +36 -0
- data/examples/random-people/xml/people.xml +11 -0
- data/examples/read-testing/Makefile +10 -0
- data/examples/read-testing/load.rb +65 -0
- data/examples/read-testing/read.rb +51 -0
- data/examples/read-testing/rrr.rb +87 -0
- data/examples/read-testing/settings.rb +2 -0
- data/examples/read-testing/xampl-gen.rb +36 -0
- data/examples/read-testing/xml/text.xml +8 -0
- data/examples/tokyo-cabinet-experimental/expt-query.rb +42 -0
- data/examples/tokyo-cabinet-experimental/expt-query2.rb +42 -0
- data/examples/tokyo-cabinet-experimental/expt-query3.rb +41 -0
- data/examples/tokyo-cabinet-experimental/expt-reader.rb +32 -0
- data/examples/tokyo-cabinet-experimental/expt.rb +61 -0
- data/examples/tokyo-cabinet-experimental/xampl-gen.rb +36 -0
- data/examples/tokyo-cabinet-experimental/xml/tcx.xml +6 -0
- data/lib/xampl-generator.rb +3 -0
- data/lib/xampl.rb +3 -0
- data/lib/xamplr-generator.rb +10 -0
- data/lib/xamplr.rb +37 -0
- data/lib/xamplr/README-POSSIBLE-PROBLEMS +5 -0
- data/lib/xamplr/TODO +1 -0
- data/lib/xamplr/exceptions.rb +97 -0
- data/lib/xamplr/from-xml-orig.rb +350 -0
- data/lib/xamplr/from-xml.rb +439 -0
- data/lib/xamplr/gen-elements.xml +6230 -0
- data/lib/xamplr/gen.elements.xml +108 -0
- data/lib/xamplr/generate-elements.rb +15 -0
- data/lib/xamplr/generator.rb +5 -0
- data/lib/xamplr/graphml-out.rb +470 -0
- data/lib/xamplr/handwritten/example.rb +698 -0
- data/lib/xamplr/handwritten/hand-example.rb +533 -0
- data/lib/xamplr/handwritten/test-handwritten.rb +873 -0
- data/lib/xamplr/indexed-array.rb +115 -0
- data/lib/xamplr/mixins.rb +397 -0
- data/lib/xamplr/my.gen.elements.xml +461 -0
- data/lib/xamplr/notifications.rb +57 -0
- data/lib/xamplr/obsolete/fsdb.rb +62 -0
- data/lib/xamplr/persist-to-xml.rb +249 -0
- data/lib/xamplr/persistence.rb +522 -0
- data/lib/xamplr/persistence.rb.more_thread_safe +771 -0
- data/lib/xamplr/persistence.rb.partially_thread_safe +763 -0
- data/lib/xamplr/persister.rb +310 -0
- data/lib/xamplr/persisters/caches.rb +186 -0
- data/lib/xamplr/persisters/caching.rb +172 -0
- data/lib/xamplr/persisters/filesystem.rb +60 -0
- data/lib/xamplr/persisters/in-memory.rb +180 -0
- data/lib/xamplr/persisters/simple.rb +59 -0
- data/lib/xamplr/persisters/tokyo-cabinet.rb +641 -0
- data/lib/xamplr/simpleTemplate/danger.rx +4 -0
- data/lib/xamplr/simpleTemplate/obsolete/input-c.r4 +35 -0
- data/lib/xamplr/simpleTemplate/obsolete/play.r6.txt +12 -0
- data/lib/xamplr/simpleTemplate/obsolete/play_more.r6.txt +20 -0
- data/lib/xamplr/simpleTemplate/obsolete/test001.r5 +8 -0
- data/lib/xamplr/simpleTemplate/obsolete/test002.r5 +13 -0
- data/lib/xamplr/simpleTemplate/obsolete/test003.r5 +37 -0
- data/lib/xamplr/simpleTemplate/old/r6.000.rb +122 -0
- data/lib/xamplr/simpleTemplate/old/r6.001.rb +145 -0
- data/lib/xamplr/simpleTemplate/play.r6 +12 -0
- data/lib/xamplr/simpleTemplate/play_more.r6 +20 -0
- data/lib/xamplr/simpleTemplate/play_noblanks.r6 +21 -0
- data/lib/xamplr/simpleTemplate/playq.r6 +16 -0
- data/lib/xamplr/simpleTemplate/r6.rb +87 -0
- data/lib/xamplr/simpleTemplate/simple-template.rb +75 -0
- data/lib/xamplr/templates/child.template +47 -0
- data/lib/xamplr/templates/child_indexed.template +89 -0
- data/lib/xamplr/templates/child_modules.template +5 -0
- data/lib/xamplr/templates/element_classes.template +11 -0
- data/lib/xamplr/templates/element_data.template +282 -0
- data/lib/xamplr/templates/element_empty.template +285 -0
- data/lib/xamplr/templates/element_mixed.template +277 -0
- data/lib/xamplr/templates/element_simple.template +276 -0
- data/lib/xamplr/templates/package.template +26 -0
- data/lib/xamplr/test-support/Makefile +47 -0
- data/lib/xamplr/test-support/bench-cache.rb +80 -0
- data/lib/xamplr/test-support/bench-script.rb +21 -0
- data/lib/xamplr/test-support/bench.rb +116 -0
- data/lib/xamplr/test-support/bench2.rb +132 -0
- data/lib/xamplr/test-support/test-cache.rb +147 -0
- data/lib/xamplr/test-support/test-data/binding.xml +7 -0
- data/lib/xamplr/test-support/test-data/example.xml +14 -0
- data/lib/xamplr/test-support/test-data/internationalization-utf8.txt +1 -0
- data/lib/xamplr/test-support/test-data/labels.xml +37 -0
- data/lib/xamplr/test-support/test-data/labels001.xml +38 -0
- data/lib/xamplr/test-support/test-deep-change.rb +135 -0
- data/lib/xamplr/test-support/test-elements.rb +109 -0
- data/lib/xamplr/test-support/test-indexed-array.rb +169 -0
- data/lib/xamplr/test-support/test-misc.rb +73 -0
- data/lib/xamplr/test-support/test-names.rb +67 -0
- data/lib/xamplr/test-support/test-rollback.rb +106 -0
- data/lib/xamplr/test-support/test.rb +1504 -0
- data/lib/xamplr/to-ruby.rb +220 -0
- data/lib/xamplr/to-xml.rb +158 -0
- data/lib/xamplr/version.rb +67 -0
- data/lib/xamplr/visitor.rb +140 -0
- data/lib/xamplr/visitors.rb +573 -0
- data/lib/xamplr/xampl-generator.rb +533 -0
- data/lib/xamplr/xampl-hand-generated.rb +1535 -0
- data/lib/xamplr/xampl-module.rb +36 -0
- data/lib/xamplr/xampl-object-internals.rb +6 -0
- data/lib/xamplr/xampl-object.rb +202 -0
- data/lib/xamplr/xampl-persisted-object.rb +122 -0
- data/lib/xamplr/xml-text.rb +117 -0
- data/lib/xamplr/xml/document.xml +7 -0
- data/lib/xamplr/xml/elements.xml +101 -0
- data/lib/xamplr/xml/elements000.xml +73 -0
- data/lib/xamplr/xml/example.xml +23 -0
- data/lib/xamplr/xml/options.xml +12 -0
- data/lib/xamplr/xml/uche.xml +38 -0
- data/lib/xamplr/yEd-sample.graphml +300 -0
- data/test/test_helper.rb +10 -0
- data/test/xamplr_test.rb +7 -0
- 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
|
+
|