xamplr 1.9.13 → 1.9.14
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/VERSION.yml +1 -1
- data/lib/xamplr/TODO +1 -1
- data/lib/xamplr/exceptions.rb +47 -1
- data/lib/xamplr/from-xml.rb +128 -191
- data/lib/xamplr/from-xml.rb.libxml +463 -0
- data/lib/xamplr/from-xml.rb.nokogiri +437 -0
- data/lib/xamplr/persist-to-xml.rb +3 -21
- data/lib/xamplr/persistence.rb +151 -57
- data/lib/xamplr/persister.rb +13 -28
- data/lib/xamplr/persisters/dumb.rb +1 -1
- data/lib/xamplr/persisters/filesystem.rb +2 -1
- data/lib/xamplr/persisters/in-memory.rb +1 -0
- data/lib/xamplr/persisters/{mongo.rb → mongo.rb.cannot-use} +0 -0
- data/lib/xamplr/persisters/redis.rb +449 -0
- data/lib/xamplr/persisters/simple.rb +1 -1
- data/lib/xamplr/persisters/tokyo-cabinet.rb +4 -2
- data/lib/xamplr/tests/.gitignore +1 -0
- data/lib/xamplr/tests/redis/Makefile +3 -0
- data/lib/xamplr/tests/redis/author.rb +19 -0
- data/lib/xamplr/tests/redis/project-generator.rb +31 -0
- data/lib/xamplr/tests/redis/redis_spec.rb +727 -0
- data/lib/xamplr/tests/redis/spec.opts +1 -0
- data/lib/xamplr/tests/redis/spec_helper.rb +48 -0
- data/lib/xamplr/tests/redis/testing-db/.gitignore +3 -0
- data/lib/xamplr/tests/redis/testing-db/Makefile +5 -0
- data/lib/xamplr/tests/redis/testing-db/unit-testing.redis.conf +332 -0
- data/lib/xamplr/tests/redis/xml/redis-test.xml +8 -0
- data/lib/xamplr/visitors.rb +51 -51
- data/lib/xamplr/xampl-object.rb +20 -5
- data/lib/xamplr/xampl-persisted-object.rb +15 -58
- data/xamplr.gemspec +20 -6
- metadata +23 -9
data/lib/xamplr/persistence.rb
CHANGED
@@ -76,34 +76,40 @@ module Xampl
|
|
76
76
|
@@default_persister_options[:format] = format
|
77
77
|
end
|
78
78
|
|
79
|
-
def Xampl.
|
80
|
-
|
81
|
-
format = format || Xampl.default_persister_format
|
79
|
+
def Xampl.create_named_persister(name, kind, arg=nil)
|
80
|
+
raise NoAnonymousPersisters.new unless name # there is no such thing as an anonymous persister, maybe later
|
82
81
|
|
83
|
-
|
82
|
+
persister = @@known_persisters[name]
|
83
|
+
return persister if persister
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
# TODO -- if we know the persister, why are we being so anal about kind and format???
|
85
|
+
persister_class = @@persister_kinds[kind]
|
86
|
+
return nil unless persister_class
|
88
87
|
|
89
|
-
|
90
|
-
|
88
|
+
persister = persister_class.new(name, :xml_format, arg)
|
89
|
+
@@known_persisters[name] = persister
|
91
90
|
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
if format and format != @@persister.format then
|
96
|
-
raise IncompatiblePersisterRequest.new(@@persister, "format", format, @@persister.format)
|
97
|
-
end
|
98
|
-
end
|
91
|
+
return persister
|
92
|
+
end
|
99
93
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
94
|
+
def Xampl.find_named_persister(name)
|
95
|
+
persister = @@known_persisters[name]
|
96
|
+
end
|
97
|
+
|
98
|
+
def Xampl.enable_named_persister(name)
|
99
|
+
persister = @@known_persisters[name]
|
100
|
+
raise NoPersisterNamed.new(name) unless persister
|
101
|
+
|
102
|
+
@@persister = persister
|
103
|
+
end
|
104
|
+
|
105
|
+
def Xampl.enable_persister(name, preferred_kind=nil)
|
106
|
+
# you'd better know what you are doing if you call this
|
107
|
+
|
108
|
+
raise NoPersisterNamed.new unless name
|
106
109
|
|
110
|
+
preferred_kind = preferred_kind || Xampl.default_persister_kind
|
111
|
+
|
112
|
+
@@persister = @@known_persisters[name] || Xampl.create_named_persister(name, preferred_kind, nil)
|
107
113
|
@@persister
|
108
114
|
end
|
109
115
|
|
@@ -111,11 +117,12 @@ module Xampl
|
|
111
117
|
puts "Known Persisters:: --------------------------"
|
112
118
|
@@known_persisters.each { |n, k| puts " #{n} #{k}" }
|
113
119
|
puts "---------------------------------------------"
|
120
|
+
puts caller(0)
|
114
121
|
end
|
115
122
|
|
116
123
|
def Xampl.flush_persister_caches
|
117
124
|
Xampl.print_known_persisters
|
118
|
-
|
125
|
+
@@persister.close
|
119
126
|
@@known_persisters.delete(@@persister.name)
|
120
127
|
Xampl.print_known_persisters
|
121
128
|
end
|
@@ -137,19 +144,21 @@ module Xampl
|
|
137
144
|
end
|
138
145
|
|
139
146
|
def Xampl.drop_persister(name)
|
140
|
-
Xampl.print_known_persisters
|
147
|
+
# Xampl.print_known_persisters
|
141
148
|
p = @@known_persisters[name]
|
142
149
|
p.close if p
|
143
150
|
@@known_persisters.delete(name)
|
144
|
-
Xampl.print_known_persisters
|
151
|
+
# Xampl.print_known_persisters
|
145
152
|
end
|
146
153
|
|
147
154
|
def Xampl.add_lexical_indexs(indexes)
|
148
155
|
case Xampl.default_persister_kind
|
149
156
|
when :tokyo_cabinet then
|
150
157
|
Xampl::TokyoCabinetPersister.add_lexical_indexs(indexes)
|
151
|
-
when :
|
152
|
-
Xampl::
|
158
|
+
when :redis then
|
159
|
+
Xampl::RedisPersister.add_lexical_indexs(indexes)
|
160
|
+
#when :mongo then
|
161
|
+
# Xampl::MongoPersister.add_lexical_indexs(indexes)
|
153
162
|
else
|
154
163
|
raise IncompatiblePersisterConfiguration.new(Xampl.default_persister_kind, "lexical_indexes")
|
155
164
|
end
|
@@ -159,8 +168,10 @@ module Xampl
|
|
159
168
|
case Xampl.default_persister_kind
|
160
169
|
when :tokyo_cabinet then
|
161
170
|
Xampl::TokyoCabinetPersister.add_numerical_indexs(indexes)
|
162
|
-
when :
|
163
|
-
Xampl::
|
171
|
+
when :redis then
|
172
|
+
Xampl::RedisPersister.add_numerical_indexs(indexes)
|
173
|
+
#when :mongo then
|
174
|
+
# Xampl::MongoPersister.add_numerical_indexs(indexes)
|
164
175
|
else
|
165
176
|
raise IncompatiblePersisterConfiguration.new(Xampl.default_persister_kind, "numerical_indexs")
|
166
177
|
end
|
@@ -213,7 +224,7 @@ module Xampl
|
|
213
224
|
@@xampl_lock.synchronize(:EX) do
|
214
225
|
begin
|
215
226
|
initial_persister = @@persister
|
216
|
-
Xampl.enable_persister(name, kind
|
227
|
+
Xampl.enable_persister(name, kind)
|
217
228
|
|
218
229
|
original_automatic = @@persister.automatic
|
219
230
|
|
@@ -278,7 +289,6 @@ module Xampl
|
|
278
289
|
end
|
279
290
|
end
|
280
291
|
|
281
|
-
#def Xampl.transaction_using_proc(thing, kind=nil, automatic=true, format=nil, & block)
|
282
292
|
def Xampl.transaction(thing, kind=nil, automatic=true, format=nil, & block)
|
283
293
|
# this method cannot account for returns in transactions (proc vs lambda/method issues)
|
284
294
|
if String === thing then
|
@@ -289,13 +299,89 @@ module Xampl
|
|
289
299
|
raise XamplException.new("can't base a transaction on a #{thing.class.name} (#{thing})")
|
290
300
|
end
|
291
301
|
|
302
|
+
if block_given? then
|
303
|
+
@@xampl_lock.synchronize(:EX) do
|
304
|
+
rollback = true
|
305
|
+
exception = nil
|
306
|
+
begin
|
307
|
+
initial_persister = @@persister
|
308
|
+
Xampl.enable_persister(name, kind)
|
309
|
+
|
310
|
+
original_automatic = @@persister.automatic
|
311
|
+
|
312
|
+
begin
|
313
|
+
#TODO -- impose some rules on nested transactions/enable_persisters??
|
314
|
+
|
315
|
+
Xampl.auto_persistence(automatic)
|
316
|
+
|
317
|
+
result = yield
|
318
|
+
|
319
|
+
rollback = false
|
320
|
+
Xampl.block_future_changes(true)
|
321
|
+
Xampl.sync
|
322
|
+
return result
|
323
|
+
rescue => e
|
324
|
+
# puts e.backtrace
|
325
|
+
exception = e
|
326
|
+
ensure
|
327
|
+
Xampl.block_future_changes(false)
|
328
|
+
Xampl.auto_persistence(original_automatic)
|
329
|
+
|
330
|
+
if rollback then
|
331
|
+
# we get here if the transaction block finishes early, for one of three reasons:
|
332
|
+
# 1) exception
|
333
|
+
# 2) throw
|
334
|
+
# 3) explicit return in the block
|
335
|
+
# it is arguable that throw and returns are 'okay', or normal exits from the block... I don't know???
|
336
|
+
|
337
|
+
if exception then
|
338
|
+
# the early finish was caused by an exception
|
339
|
+
|
340
|
+
Xampl.rollback
|
341
|
+
else
|
342
|
+
# this is the throw/explicit-return
|
343
|
+
|
344
|
+
#TODO -- is this a good idea??
|
345
|
+
Xampl.block_future_changes(true)
|
346
|
+
Xampl.sync
|
347
|
+
|
348
|
+
|
349
|
+
#TODO -- uncomment this, it's handy
|
350
|
+
# STDERR.puts "---------"
|
351
|
+
# STDERR.puts "Either a return or a throw from a transaction. The DB is synced, but this is not a good thing to be doing."
|
352
|
+
# STDERR.puts caller(0)
|
353
|
+
# STDERR.puts "---------"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
ensure
|
358
|
+
@@persister = initial_persister
|
359
|
+
|
360
|
+
if exception then
|
361
|
+
raise RuntimeError, "ROLLBACK(#{__LINE__}):: #{exception}", exception.backtrace
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def Xampl.transaction_not_so_good(thing, kind=nil, automatic=true, format=nil, & block)
|
369
|
+
# this method cannot account for returns in transactions (proc vs lambda/method issues)
|
370
|
+
if String === thing then
|
371
|
+
name = thing
|
372
|
+
elsif XamplObject === thing then
|
373
|
+
name = thing.persister.name
|
374
|
+
else
|
375
|
+
raise XamplException.new("can't base a transaction on a #{thing.class.name} (#{thing})")
|
376
|
+
end
|
377
|
+
|
292
378
|
if block_given? then
|
293
379
|
@@xampl_lock.synchronize(:EX) do
|
294
380
|
# if true then
|
295
381
|
begin
|
296
382
|
# @@xampl_lock.sync_lock(:EX)
|
297
383
|
initial_persister = @@persister
|
298
|
-
Xampl.enable_persister(name, kind
|
384
|
+
Xampl.enable_persister(name, kind)
|
299
385
|
|
300
386
|
rollback = true
|
301
387
|
exception = nil
|
@@ -305,22 +391,32 @@ module Xampl
|
|
305
391
|
#TODO -- impose some rules on nested transactions/enable_persisters??
|
306
392
|
|
307
393
|
Xampl.auto_persistence(automatic)
|
394
|
+
|
308
395
|
result = yield
|
396
|
+
|
397
|
+
rollback = false
|
309
398
|
Xampl.block_future_changes(true)
|
310
399
|
Xampl.sync
|
311
|
-
rollback = false
|
312
400
|
return result
|
313
401
|
rescue => e
|
402
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
403
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] rollback: #{ rollback }"
|
314
404
|
exception = e
|
315
405
|
ensure
|
316
406
|
Xampl.block_future_changes(false)
|
317
407
|
Xampl.auto_persistence(original_automatic)
|
408
|
+
|
409
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] rollback: #{ rollback }"
|
318
410
|
if rollback then
|
411
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
412
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] rollback: #{ rollback }"
|
319
413
|
# we get here if the transaction block finishes early
|
320
414
|
if exception then
|
415
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] rollback: #{ rollback }"
|
321
416
|
# the early finish was caused by an exception
|
322
417
|
raise RuntimeError, "ROLLBACK(#{__LINE__}):: #{exception}", exception.backtrace
|
323
418
|
else
|
419
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
324
420
|
# How could we have arrived at this point???
|
325
421
|
# Well, I don't know all the reasons, but the ones I do know are:
|
326
422
|
# - return was used in the block passed into the transaction
|
@@ -337,32 +433,30 @@ module Xampl
|
|
337
433
|
STDERR.puts(trace)
|
338
434
|
end
|
339
435
|
STDERR.puts "---------"
|
340
|
-
|
341
|
-
=begin
|
342
|
-
|
343
|
-
begin
|
344
|
-
puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] "
|
345
|
-
Xampl.block_future_changes(true)
|
346
|
-
puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] "
|
347
|
-
Xampl.sync
|
348
|
-
rollback = false
|
349
|
-
rescue => e
|
350
|
-
puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] "
|
351
|
-
# so we know the persister had a problem
|
352
|
-
puts "PERSISTER ERROR(#{__LINE__}) #{ e }"
|
353
|
-
ensure
|
354
|
-
puts "#{ __FILE__ }:#{ __LINE__ } [#{__method__}] "
|
355
|
-
Xampl.block_future_changes(false)
|
356
|
-
end
|
357
|
-
|
358
|
-
=end
|
359
436
|
end
|
437
|
+
else
|
438
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
439
|
+
end
|
440
|
+
|
441
|
+
if rollback then
|
442
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
443
|
+
Xampl.rollback
|
444
|
+
rollback = false
|
445
|
+
else
|
446
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
360
447
|
end
|
361
|
-
Xampl.rollback if rollback
|
362
448
|
@@persister = initial_persister
|
363
449
|
end
|
364
|
-
|
365
|
-
|
450
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] rollback: #{ rollback }"
|
451
|
+
if exception
|
452
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] rollback: #{ rollback }"
|
453
|
+
raise exception
|
454
|
+
else
|
455
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] rollback: #{ rollback }"
|
456
|
+
end
|
457
|
+
ensure
|
458
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME" if rollback
|
459
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] rollback: #{ rollback } ????????????" if rollback
|
366
460
|
# @@xampl_lock.sync_unlock
|
367
461
|
end
|
368
462
|
end
|
@@ -382,7 +476,7 @@ module Xampl
|
|
382
476
|
if block_given? then
|
383
477
|
@@xampl_lock.synchronize(:EX) do
|
384
478
|
initial_persister = @@persister
|
385
|
-
Xampl.enable_persister(name, kind
|
479
|
+
Xampl.enable_persister(name, kind)
|
386
480
|
target_persister = @@persister
|
387
481
|
|
388
482
|
rollback = true
|
@@ -438,7 +532,7 @@ module Xampl
|
|
438
532
|
|
439
533
|
if block_given? then
|
440
534
|
initial_persister = @@persister
|
441
|
-
Xampl.enable_persister(name, target_persister.kind
|
535
|
+
Xampl.enable_persister(name, target_persister.kind)
|
442
536
|
|
443
537
|
rollback = true
|
444
538
|
original_automatic = @@persister.automatic
|
data/lib/xamplr/persister.rb
CHANGED
@@ -72,17 +72,9 @@ module Xampl
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def has_changed(xampl)
|
75
|
-
#raise XamplException.new(:live_across_rollback) if @rolled_back
|
76
|
-
# puts "!!!! has_changed #{xampl} #{xampl.get_the_index} -- persist required: #{xampl.persist_required}"
|
77
75
|
if xampl.persist_required && xampl.is_changed then
|
78
|
-
unless self == xampl.persister
|
79
|
-
raise MixedPersisters.new(xampl.persister, self)
|
80
|
-
end
|
76
|
+
raise MixedPersisters.new(xampl.persister, self) unless self == xampl.persister
|
81
77
|
@changed[xampl] = xampl
|
82
|
-
# puts "!!!! change recorded ==> #{@changed.size}/#{count_changed} #{@changed.object_id} !!!!"
|
83
|
-
# @changed.each{ | thing, ignore |
|
84
|
-
# puts " changed: #{thing}, index: #{thing.get_the_index}, changed: #{thing.is_changed}"
|
85
|
-
# }
|
86
78
|
end
|
87
79
|
end
|
88
80
|
|
@@ -132,15 +124,7 @@ module Xampl
|
|
132
124
|
end
|
133
125
|
|
134
126
|
def represent(xampl, mentions=[])
|
135
|
-
|
136
|
-
# return nil if xampl.load_needed
|
137
|
-
rep = nil
|
138
|
-
case xampl.default_persister_format || @format
|
139
|
-
when nil, :xml_format then
|
140
|
-
rep = xampl.persist("", mentions)
|
141
|
-
when :ruby_format then
|
142
|
-
rep = xampl.to_ruby(mentions)
|
143
|
-
end
|
127
|
+
rep = xampl.persist("", mentions)
|
144
128
|
return rep
|
145
129
|
rescue => e
|
146
130
|
msg = "Failed to represent #{ xampl } due to: #{ e }"
|
@@ -150,15 +134,15 @@ module Xampl
|
|
150
134
|
end
|
151
135
|
|
152
136
|
def realise(representation, target=nil)
|
153
|
-
#
|
154
|
-
# that isn't necessarily the case. Try to work out what the format might be...
|
155
|
-
|
156
|
-
#TODO -- this is a bit brutal, but it should work (it is the rule is that this is supposed to be UTF-8)
|
137
|
+
# This is a bit brutal, but it works (and, anyway, it *is* the rule is that this is supposed to be UTF-8)
|
157
138
|
representation_fixed = representation.encode('UTF-8', :invalid => :replace, :undef => :replace)
|
158
|
-
# puts "#{ ::File.basename __FILE__ }:#{ __LINE__ } [#{__method__}] ENCODING: #{ representation.encoding } -> #{ representation_fixed.encoding }"
|
159
139
|
|
160
140
|
xampl = nil
|
161
|
-
|
141
|
+
|
142
|
+
# These days 'new' representations can only be XML. Historically it could have been Ruby. Check the
|
143
|
+
# representations (quickly) and choose appropriately.
|
144
|
+
if '<' == representation_fixed[0] then
|
145
|
+
# well it isn't ruby, so it must be XML. Of course this means no leading whitespace, which happens to be true.
|
162
146
|
xampl = XamplObject.realise_from_xml_string(representation_fixed, target)
|
163
147
|
else
|
164
148
|
xampl = XamplObject.from_ruby(representation_fixed, target)
|
@@ -364,13 +348,14 @@ module Xampl
|
|
364
348
|
end
|
365
349
|
|
366
350
|
begin
|
367
|
-
if require '
|
368
|
-
require
|
351
|
+
if require 'redis' then
|
352
|
+
require 'weakref'
|
353
|
+
require "xamplr/persisters/redis"
|
369
354
|
end
|
370
355
|
rescue LoadError => e
|
371
|
-
# Well. No
|
356
|
+
# Well. No redis.
|
372
357
|
rescue
|
373
|
-
# Well. No
|
358
|
+
# Well. No redis.
|
374
359
|
end
|
375
360
|
|
376
361
|
end
|
@@ -5,7 +5,8 @@ module Xampl
|
|
5
5
|
|
6
6
|
class FilesystemPersister < AbstractCachingPersister
|
7
7
|
|
8
|
-
def initialize(name=nil, format=nil, root=
|
8
|
+
def initialize(name=nil, format=nil, root=nil)
|
9
|
+
root = File.join(".", "repo") if root.nil?
|
9
10
|
super(root, name, format)
|
10
11
|
end
|
11
12
|
|
File without changes
|
@@ -0,0 +1,449 @@
|
|
1
|
+
module Xampl
|
2
|
+
|
3
|
+
class RedisPersister < Persister
|
4
|
+
|
5
|
+
attr_reader :repo_name,
|
6
|
+
:instance_options,
|
7
|
+
:client, # currently it's an instance of ::Redis from the redis-rb library
|
8
|
+
:suggested_repo_properties,
|
9
|
+
:repo_properties
|
10
|
+
|
11
|
+
@@default_redis_options = {
|
12
|
+
:repo_properties => {
|
13
|
+
# DB Properties, maybe only set when the DB is created for the first time.
|
14
|
+
:mentions => true,
|
15
|
+
},
|
16
|
+
|
17
|
+
# Connect Properties
|
18
|
+
:thread_safe => true, # redis connections will be thread safe
|
19
|
+
:redis_server => "redis://127.0.0.1:6379/0", #This is the format expected by redis-rb, just use it
|
20
|
+
:clobbering_allowed => false,
|
21
|
+
:allow_connections => true,
|
22
|
+
:connect_to_known => true, # will connect to repos already in the redis db
|
23
|
+
:connect_to_unknown => true, # will connect to repose not in the redis db
|
24
|
+
:testing => false
|
25
|
+
}
|
26
|
+
|
27
|
+
REPOSITORIES_KEY = "XAMPL::REPOSITORIES"
|
28
|
+
|
29
|
+
def initialize(name=nil, format=nil, options={})
|
30
|
+
super(name, format)
|
31
|
+
|
32
|
+
@repo_name = name
|
33
|
+
# puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] @@default_redis_options: #{ @@default_redis_options.inspect }"
|
34
|
+
# puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] Xampl.raw_persister_options: #{ Xampl.raw_persister_options.inspect }"
|
35
|
+
# puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] options: #{ options.inspect }"
|
36
|
+
|
37
|
+
@instance_options = {}
|
38
|
+
@instance_options = @instance_options.merge(@@default_redis_options)
|
39
|
+
@instance_options = @instance_options.merge(Xampl.raw_persister_options)
|
40
|
+
@instance_options = @instance_options.merge(options) if options
|
41
|
+
|
42
|
+
@suggested_repo_properties = @instance_options.delete(:repo_properties)
|
43
|
+
|
44
|
+
clear_cache
|
45
|
+
ensure_connected
|
46
|
+
end
|
47
|
+
|
48
|
+
def RedisPersister.kind
|
49
|
+
:redis
|
50
|
+
end
|
51
|
+
|
52
|
+
def kind
|
53
|
+
RedisPersister.kind
|
54
|
+
end
|
55
|
+
|
56
|
+
def ensure_connected
|
57
|
+
return if @client
|
58
|
+
return unless instance_options[:allow_connections]
|
59
|
+
|
60
|
+
server = @instance_options[:redis_server]
|
61
|
+
thread_safe = @instance_options[:thread_safe]
|
62
|
+
@client = Redis.connect(:url => server, :thread_safe => thread_safe)
|
63
|
+
|
64
|
+
# This check is necessary to make sure that the connection is made, otherwise the exception will be sometime in the future
|
65
|
+
# the redis exceptions are good enough, so don't bother trapping right here
|
66
|
+
@client.ping
|
67
|
+
|
68
|
+
# check to see if the repository with this name is known (in redis) already
|
69
|
+
load_repo_properties
|
70
|
+
|
71
|
+
if @repo_properties && !@instance_options[:connect_to_known] then
|
72
|
+
ensure_disconnected
|
73
|
+
raise IncompatiblePersisterConfiguration.new('redis', "prevent connections to existing repos named: '#{ repo_name }' in #{ server }")
|
74
|
+
end
|
75
|
+
if @repo_properties.nil? && !@instance_options[:connect_to_unknown] then
|
76
|
+
ensure_disconnected
|
77
|
+
raise IncompatiblePersisterConfiguration.new('redis', "prevent connections to unknown repos named: '#{ repo_name }' in #{ server }")
|
78
|
+
end
|
79
|
+
|
80
|
+
return if @repo_properties
|
81
|
+
|
82
|
+
# if it is unknown, then set it up
|
83
|
+
|
84
|
+
@repo_properties = {}
|
85
|
+
suggested_repo_properties.each do |k, v|
|
86
|
+
@repo_properties[k] = v
|
87
|
+
end
|
88
|
+
@repo_properties['name'] = repo_name
|
89
|
+
@repo_properties['created_at'] = DateTime.now.to_s
|
90
|
+
|
91
|
+
@client.multi do
|
92
|
+
@client.mapped_hmset(repo_properties_key, @repo_properties)
|
93
|
+
@client.sadd(REPOSITORIES_KEY, repo_name)
|
94
|
+
end
|
95
|
+
|
96
|
+
load_repo_properties
|
97
|
+
end
|
98
|
+
|
99
|
+
def ensure_disconnected
|
100
|
+
return unless client
|
101
|
+
return unless instance_options[:allow_connections]
|
102
|
+
|
103
|
+
@client.quit
|
104
|
+
ensure
|
105
|
+
@repo_properties = nil
|
106
|
+
@client = nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def clobber
|
110
|
+
# TODO -- this is a BAD idea if there are any other connections to this repo (i.e. in different processes)
|
111
|
+
|
112
|
+
unless @instance_options[:clobbering_allowed] then
|
113
|
+
raise IncompatiblePersisterConfiguration.new('redis', "clobbering is not enabled for this connection to repo: '#{ repo_name }' in #{ @instance_options[:redis_server] }")
|
114
|
+
end
|
115
|
+
|
116
|
+
#TODO -- getting the keys outside the multi might allow some new key to sneak in there
|
117
|
+
keys = @client.keys("#{ common_key_prefix }*")
|
118
|
+
@client.multi do
|
119
|
+
@client.del(repo_properties_key)
|
120
|
+
@client.srem(REPOSITORIES_KEY, repo_name)
|
121
|
+
|
122
|
+
keys.each do |key|
|
123
|
+
@client.del(key)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
ensure_disconnected
|
128
|
+
end
|
129
|
+
|
130
|
+
def repo_properties_key
|
131
|
+
key = "XAMPL::PROPERTIES::#{ @repo_name }"
|
132
|
+
return key
|
133
|
+
end
|
134
|
+
|
135
|
+
def load_repo_properties
|
136
|
+
key = repo_properties_key()
|
137
|
+
@repo_properties = @client.hgetall(key)
|
138
|
+
@repo_properties = nil if @repo_properties.empty?
|
139
|
+
end
|
140
|
+
|
141
|
+
def clear_cache
|
142
|
+
@cache_hits = 0
|
143
|
+
@cache = {}
|
144
|
+
@new_cache = {}
|
145
|
+
end
|
146
|
+
|
147
|
+
alias fresh_cache clear_cache
|
148
|
+
|
149
|
+
def close
|
150
|
+
ensure_disconnected
|
151
|
+
clear_cache
|
152
|
+
end
|
153
|
+
|
154
|
+
def common_key_prefix
|
155
|
+
"XAMPL::#{ @repo_name }::"
|
156
|
+
end
|
157
|
+
|
158
|
+
def key_for_class(klass, index)
|
159
|
+
#NOTE -- the XAMPL::#{ @repo_name }:: is a prefix common to all keys specific to this repository
|
160
|
+
"#{ common_key_prefix }#{ klass.name }[#{ index }]"
|
161
|
+
end
|
162
|
+
|
163
|
+
def key_for_xampl(xampl)
|
164
|
+
key_for_class(xampl.class, xampl.get_the_index)
|
165
|
+
end
|
166
|
+
|
167
|
+
def known_repos
|
168
|
+
return @client.smembers(REPOSITORIES_KEY)
|
169
|
+
end
|
170
|
+
|
171
|
+
def perm_cache(xampl)
|
172
|
+
raise NotXamplPersistedObject.new(xampl) unless xampl.kind_of?(XamplPersistedObject)
|
173
|
+
|
174
|
+
key = key_for_xampl(xampl)
|
175
|
+
existing = @cache[key]
|
176
|
+
|
177
|
+
begin
|
178
|
+
# raise DuplicateXamplInPersister.new(existing, xampl, self) if existing && (existing.weakref_alive?) && (existing.__getobj__ != xampl)
|
179
|
+
raise DuplicateXamplInPersister.new(existing, xampl, self) if existing && (existing.__getobj__ != xampl)
|
180
|
+
rescue WeakRef::RefError => e
|
181
|
+
# key is there but the original object isn't...
|
182
|
+
end
|
183
|
+
|
184
|
+
@cache[key] = WeakRef.new(xampl)
|
185
|
+
end
|
186
|
+
|
187
|
+
def perm_uncache(xampl)
|
188
|
+
raise NotXamplPersistedObject.new(xampl) unless xampl.kind_of?(XamplPersistedObject)
|
189
|
+
|
190
|
+
key = key_for_xampl(xampl)
|
191
|
+
@cache.delete(key).__getobj__
|
192
|
+
end
|
193
|
+
|
194
|
+
def cache(xampl)
|
195
|
+
# this is called by xampl for the temporary new_cache
|
196
|
+
raise NotXamplPersistedObject.new(xampl) unless xampl.kind_of?(XamplPersistedObject)
|
197
|
+
|
198
|
+
key = key_for_xampl(xampl)
|
199
|
+
|
200
|
+
existing = @new_cache[key]
|
201
|
+
raise DuplicateXamplInPersister.new(existing, xampl, self) if existing && (existing != xampl)
|
202
|
+
|
203
|
+
existing = @cache[key]
|
204
|
+
begin
|
205
|
+
# raise DuplicateXamplInPersister.new(existing, xampl, self) if existing && (existing.weakref_alive?) && (existing.__getobj__ != xampl)
|
206
|
+
raise DuplicateXamplInPersister.new(existing, xampl, self) if existing && (existing.__getobj__ != xampl)
|
207
|
+
rescue WeakRef::RefError => e
|
208
|
+
# key is there but the original object isn't...
|
209
|
+
end
|
210
|
+
|
211
|
+
@new_cache[key] = xampl
|
212
|
+
end
|
213
|
+
|
214
|
+
def uncache(xampl)
|
215
|
+
# this is called by xampl for the temporary new_cache
|
216
|
+
raise NotXamplPersistedObject.new(xampl) unless xampl.kind_of?(XamplPersistedObject)
|
217
|
+
|
218
|
+
key = key_for_xampl(xampl)
|
219
|
+
@new_cache.delete(key)
|
220
|
+
end
|
221
|
+
|
222
|
+
def in_perm_cache?(klass, index)
|
223
|
+
key = key_for_class(klass, index)
|
224
|
+
xampl = @cache[key]
|
225
|
+
|
226
|
+
(xampl && xampl.weakref_alive?) ? true : false
|
227
|
+
end
|
228
|
+
|
229
|
+
alias in_cache? in_perm_cache?
|
230
|
+
|
231
|
+
def in_new_cache?(klass, index)
|
232
|
+
key = key_for_class(klass, index)
|
233
|
+
@new_cache.include?(key)
|
234
|
+
end
|
235
|
+
|
236
|
+
def in_any_cache?(klass, index)
|
237
|
+
in_new_cache?(klass, index) || in_cache?(klass, index)
|
238
|
+
end
|
239
|
+
|
240
|
+
def read_from_cache(klass, index, target=nil)
|
241
|
+
|
242
|
+
key = key_for_class(klass, index)
|
243
|
+
|
244
|
+
xampl = @cache[key]
|
245
|
+
begin
|
246
|
+
xampl = xampl.__getobj__ if xampl
|
247
|
+
rescue WeakRef::RefError => e
|
248
|
+
#not there
|
249
|
+
xampl = nil
|
250
|
+
end
|
251
|
+
# xampl = (xampl && xampl.weakref_alive?) ? xampl.__getobj__ : nil
|
252
|
+
|
253
|
+
unless xampl then
|
254
|
+
xampl = @new_cache[key]
|
255
|
+
end
|
256
|
+
|
257
|
+
return nil, target unless xampl
|
258
|
+
|
259
|
+
if target and target != xampl then
|
260
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
261
|
+
target.invalidate
|
262
|
+
raise XamplException.new(:cache_conflict)
|
263
|
+
end
|
264
|
+
unless xampl.load_needed then
|
265
|
+
@cache_hits = @cache_hits + 1
|
266
|
+
return xampl, target
|
267
|
+
end
|
268
|
+
return xampl, xampl
|
269
|
+
end
|
270
|
+
|
271
|
+
def sync_done
|
272
|
+
# simply moves the new_cache to the permanent cache
|
273
|
+
(@new_cache || {}).each do |key, xampl|
|
274
|
+
@cache[key] = WeakRef.new(xampl)
|
275
|
+
end
|
276
|
+
@new_cache = {}
|
277
|
+
end
|
278
|
+
|
279
|
+
def write(xampl)
|
280
|
+
unless xampl.get_the_index
|
281
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
282
|
+
raise NotXamplPersistedObject.new(xampl)
|
283
|
+
end
|
284
|
+
|
285
|
+
#TODO -- honour the mentions config information (FROM THE DB not the configuration!!)
|
286
|
+
mentions = []
|
287
|
+
xml = represent(xampl, mentions)
|
288
|
+
key = key_for_xampl(xampl)
|
289
|
+
|
290
|
+
#TODO save the modified-time-like value to support multi processing
|
291
|
+
|
292
|
+
# puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] redis #{ self }"
|
293
|
+
# puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] write: #{ xampl } --> #{ xml }"
|
294
|
+
# puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] key: #{ key }"
|
295
|
+
|
296
|
+
client.set(key, xml)
|
297
|
+
@write_count = @write_count + 1
|
298
|
+
xampl.changes_accepted
|
299
|
+
return true
|
300
|
+
|
301
|
+
rescue NotXamplPersistedObject => nxpo
|
302
|
+
raise nxpo
|
303
|
+
rescue => e
|
304
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] #{ e }"
|
305
|
+
puts e.backtrace
|
306
|
+
return false
|
307
|
+
end
|
308
|
+
|
309
|
+
def read(klass, pid, target=nil)
|
310
|
+
xampl, target = read_from_cache(klass, pid, target)
|
311
|
+
return xampl if xampl and !target
|
312
|
+
|
313
|
+
key = key_for_class(klass, pid)
|
314
|
+
xml = client.get(key)
|
315
|
+
unless xml
|
316
|
+
puts "#{File.basename(__FILE__)}:#{__LINE__} [#{ __method__ }] TEST ME"
|
317
|
+
return nil
|
318
|
+
end
|
319
|
+
|
320
|
+
xampl = realise(xml, target)
|
321
|
+
Xampl.store_in_cache(@cache, xampl, self) { xampl }
|
322
|
+
xampl.introduce_persister(self)
|
323
|
+
|
324
|
+
@read_count = @read_count + 1
|
325
|
+
xampl.changes_accepted
|
326
|
+
@changed.delete(xampl)
|
327
|
+
return xampl
|
328
|
+
end
|
329
|
+
|
330
|
+
def rollback_cleanup
|
331
|
+
|
332
|
+
@new_cache.each { |name, xampl|
|
333
|
+
if xampl then
|
334
|
+
@changed.delete(xampl)
|
335
|
+
xampl.invalidate
|
336
|
+
|
337
|
+
# map.each { |name2, map2|
|
338
|
+
# if map2 then
|
339
|
+
# map2.each { |pid, xampl|
|
340
|
+
# @changed.delete(xampl)
|
341
|
+
# xampl.invalidate
|
342
|
+
# }
|
343
|
+
# end
|
344
|
+
# }
|
345
|
+
end
|
346
|
+
}
|
347
|
+
@changed.each { |xampl, ignore|
|
348
|
+
xampl.force_load
|
349
|
+
}
|
350
|
+
@new_cache = {}
|
351
|
+
|
352
|
+
super
|
353
|
+
end
|
354
|
+
|
355
|
+
=begin
|
356
|
+
|
357
|
+
|
358
|
+
|
359
|
+
|
360
|
+
|
361
|
+
|
362
|
+
def backup(base_path)
|
363
|
+
#TODO
|
364
|
+
end
|
365
|
+
|
366
|
+
def do_sync_write
|
367
|
+
#TODO
|
368
|
+
end
|
369
|
+
|
370
|
+
def done_sync_write
|
371
|
+
#TODO
|
372
|
+
end
|
373
|
+
|
374
|
+
def expunge(xampl)
|
375
|
+
#TODO
|
376
|
+
end
|
377
|
+
|
378
|
+
def find_mentions_of(xampl)
|
379
|
+
#TODO
|
380
|
+
end
|
381
|
+
|
382
|
+
def find_meta(hint=false)
|
383
|
+
#TODO
|
384
|
+
end
|
385
|
+
|
386
|
+
def find_pids(hint=false)
|
387
|
+
#TODO
|
388
|
+
end
|
389
|
+
|
390
|
+
def find_xampl(hint=false)
|
391
|
+
#TODO
|
392
|
+
end
|
393
|
+
|
394
|
+
def how_indexed(xampl)
|
395
|
+
#TODO
|
396
|
+
end
|
397
|
+
|
398
|
+
def inspect
|
399
|
+
#TODO
|
400
|
+
end
|
401
|
+
|
402
|
+
def note_errors(msg="TokyoCabinet Error:: %s\n")
|
403
|
+
#TODO
|
404
|
+
end
|
405
|
+
|
406
|
+
def open_tc_db
|
407
|
+
#TODO
|
408
|
+
end
|
409
|
+
|
410
|
+
def optimise(opts={})
|
411
|
+
#TODO
|
412
|
+
end
|
413
|
+
|
414
|
+
def query(hint=false)
|
415
|
+
#TODO
|
416
|
+
end
|
417
|
+
|
418
|
+
def query_implemented
|
419
|
+
#TODO
|
420
|
+
end
|
421
|
+
|
422
|
+
def read_representation(klass, pid)
|
423
|
+
#TODO
|
424
|
+
end
|
425
|
+
|
426
|
+
def remove_all_mention(root, xampl)
|
427
|
+
#TODO
|
428
|
+
end
|
429
|
+
|
430
|
+
def setup_db
|
431
|
+
#TODO
|
432
|
+
end
|
433
|
+
|
434
|
+
def start_sync_write
|
435
|
+
#TODO
|
436
|
+
end
|
437
|
+
|
438
|
+
def to_s
|
439
|
+
#TODO
|
440
|
+
end
|
441
|
+
|
442
|
+
=end
|
443
|
+
|
444
|
+
end
|
445
|
+
|
446
|
+
|
447
|
+
Xampl.register_persister_kind(RedisPersister)
|
448
|
+
end
|
449
|
+
|