xamplr 1.9.13 → 1.9.14
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/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
|
+
|