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.
@@ -76,34 +76,40 @@ module Xampl
76
76
  @@default_persister_options[:format] = format
77
77
  end
78
78
 
79
- def Xampl.enable_persister(name, kind=nil, format=nil)
80
- kind = kind || Xampl.default_persister_kind
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
- @@persister = @@known_persisters[name]
82
+ persister = @@known_persisters[name]
83
+ return persister if persister
84
84
 
85
- if @@persister then
86
- # @@persister.open # this won't work
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
- kind = @@persister.kind || kind
90
- format = @@persister.format || format
88
+ persister = persister_class.new(name, :xml_format, arg)
89
+ @@known_persisters[name] = persister
91
90
 
92
- if kind and kind != @@persister.kind then
93
- raise IncompatiblePersisterRequest.new(@@persister, "kind", kind, @@persister.kind)
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
- unless @@persister then
101
- @@persister = @@persister_kinds[kind].new(name, format)
102
- if (nil != name) then
103
- @@known_persisters[name] = @@persister
104
- end
105
- end
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
- @persister.close
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 :monto then
152
- Xampl::MongoPersister.add_lexical_indexs(indexes)
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 :mongo then
163
- Xampl::MongoPersister.add_numerical_indexs(indexes)
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, format)
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, format)
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
- raise exception if exception
365
- # ensure
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, format)
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, target_persister.format)
535
+ Xampl.enable_persister(name, target_persister.kind)
442
536
 
443
537
  rollback = true
444
538
  original_automatic = @@persister.automatic
@@ -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
- #puts "REPRESENT #{xampl} load needed: #{xampl.load_needed}"
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
- # Normally we'd expect to see the representation in the @format format, but
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
- if representation_fixed =~ /^</ then
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 'mongo' then
368
- require "xamplr/persisters/mongo"
351
+ if require 'redis' then
352
+ require 'weakref'
353
+ require "xamplr/persisters/redis"
369
354
  end
370
355
  rescue LoadError => e
371
- # Well. No MongoDB.
356
+ # Well. No redis.
372
357
  rescue
373
- # Well. No MongoDB.
358
+ # Well. No redis.
374
359
  end
375
360
 
376
361
  end
@@ -2,7 +2,7 @@ module Xampl
2
2
 
3
3
  class DumbPersister < Persister
4
4
 
5
- def initialize(name=nil, format=nil)
5
+ def initialize(name=nil, format=nil, ignore=nil)
6
6
  super(name, format)
7
7
 
8
8
  @module_map = {}
@@ -5,7 +5,8 @@ module Xampl
5
5
 
6
6
  class FilesystemPersister < AbstractCachingPersister
7
7
 
8
- def initialize(name=nil, format=nil, root=File.join(".", "repo"))
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
 
@@ -5,6 +5,7 @@ module Xampl
5
5
  class InMemoryPersister < Persister
6
6
 
7
7
  def initialize(name=nil, format=nil, capacity=20)
8
+ capacity ||= 20
8
9
  super(name, format)
9
10
 
10
11
  @module_map = {}
@@ -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
+