strokedb 0.0.2.1 → 0.0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/README +18 -20
  2. data/bench.html +4001 -0
  3. data/bin/strokedb +14 -0
  4. data/examples/movies.rb +105 -0
  5. data/examples/movies2.rb +97 -0
  6. data/examples/strokewiki/README +28 -0
  7. data/examples/strokewiki/view/edit.xhtml +27 -0
  8. data/examples/strokewiki/view/new.xhtml +26 -0
  9. data/examples/strokewiki/view/pages.xhtml +27 -0
  10. data/examples/strokewiki/view/show.xhtml +40 -0
  11. data/examples/strokewiki/view/versions.xhtml +25 -0
  12. data/examples/strokewiki/wiki.rb +106 -0
  13. data/examples/todo.rb +92 -0
  14. data/lib/strokedb.rb +85 -0
  15. data/lib/{config → strokedb}/config.rb +14 -9
  16. data/lib/strokedb/console.rb +87 -0
  17. data/lib/strokedb/core_ext.rb +10 -0
  18. data/lib/{util/ext → strokedb/core_ext}/blank.rb +1 -1
  19. data/lib/{util/ext → strokedb/core_ext}/enumerable.rb +0 -0
  20. data/lib/{util/ext → strokedb/core_ext}/fixnum.rb +0 -0
  21. data/lib/strokedb/core_ext/float.rb +4 -0
  22. data/lib/{util/ext → strokedb/core_ext}/hash.rb +0 -0
  23. data/lib/strokedb/core_ext/infinity.rb +33 -0
  24. data/lib/strokedb/core_ext/kernel.rb +41 -0
  25. data/lib/strokedb/core_ext/object.rb +16 -0
  26. data/lib/{util/ext → strokedb/core_ext}/string.rb +28 -1
  27. data/lib/strokedb/core_ext/symbol.rb +13 -0
  28. data/lib/strokedb/data_structures.rb +5 -0
  29. data/lib/strokedb/data_structures/chunked_skiplist.rb +123 -0
  30. data/lib/{data_structures → strokedb/data_structures}/inverted_list.rb +0 -0
  31. data/lib/{data_structures → strokedb/data_structures}/point_query.rb +0 -0
  32. data/lib/strokedb/data_structures/simple_skiplist.rb +350 -0
  33. data/lib/{data_structures → strokedb/data_structures}/skiplist.rb +1 -1
  34. data/lib/{document → strokedb}/document.rb +180 -71
  35. data/lib/{document → strokedb/document}/callback.rb +0 -0
  36. data/lib/{document → strokedb/document}/delete.rb +2 -2
  37. data/lib/strokedb/document/dsl.rb +4 -0
  38. data/lib/{document → strokedb/document/dsl}/associations.rb +0 -0
  39. data/lib/{document → strokedb/document/dsl}/coercions.rb +0 -0
  40. data/lib/strokedb/document/dsl/meta_dsl.rb +7 -0
  41. data/lib/{document → strokedb/document/dsl}/validations.rb +26 -21
  42. data/lib/{document → strokedb/document/dsl}/virtualize.rb +0 -0
  43. data/lib/{document → strokedb/document}/meta.rb +92 -29
  44. data/lib/{document → strokedb/document}/slot.rb +17 -5
  45. data/lib/{document → strokedb/document}/util.rb +0 -0
  46. data/lib/{document → strokedb/document}/versions.rb +2 -2
  47. data/lib/strokedb/index.rb +2 -0
  48. data/lib/strokedb/nsurl.rb +24 -0
  49. data/lib/strokedb/store.rb +149 -0
  50. data/lib/strokedb/stores.rb +6 -0
  51. data/lib/{stores → strokedb/stores}/chainable_storage.rb +20 -14
  52. data/lib/strokedb/stores/file_storage.rb +118 -0
  53. data/lib/{stores/inverted_list_index → strokedb/stores}/inverted_list_file_storage.rb +50 -0
  54. data/lib/strokedb/stores/memory_storage.rb +80 -0
  55. data/lib/{stores → strokedb/stores}/remote_store.rb +10 -4
  56. data/lib/strokedb/sync.rb +4 -0
  57. data/lib/{sync → strokedb/sync}/chain_sync.rb +0 -0
  58. data/lib/{sync → strokedb/sync}/diff.rb +12 -1
  59. data/lib/{sync/stroke_diff → strokedb/sync/diff}/array.rb +1 -1
  60. data/lib/{sync/stroke_diff → strokedb/sync/diff}/default.rb +0 -0
  61. data/lib/{sync/stroke_diff → strokedb/sync/diff}/hash.rb +1 -1
  62. data/lib/{sync/stroke_diff → strokedb/sync/diff}/string.rb +1 -1
  63. data/lib/{sync → strokedb/sync}/lamport_timestamp.rb +0 -0
  64. data/lib/{sync → strokedb/sync}/store_sync.rb +15 -7
  65. data/lib/strokedb/transaction.rb +78 -0
  66. data/lib/{util → strokedb}/util.rb +14 -7
  67. data/lib/strokedb/util/attach_dsl.rb +29 -0
  68. data/lib/{util → strokedb/util}/blankslate.rb +0 -0
  69. data/lib/strokedb/util/class_optimization.rb +93 -0
  70. data/lib/{util → strokedb/util}/inflect.rb +0 -0
  71. data/lib/strokedb/util/java_util.rb +13 -0
  72. data/lib/{util → strokedb/util}/lazy_array.rb +0 -0
  73. data/lib/{util → strokedb/util}/lazy_mapping_array.rb +4 -0
  74. data/lib/{util → strokedb/util}/lazy_mapping_hash.rb +0 -0
  75. data/lib/{util → strokedb/util}/serialization.rb +21 -0
  76. data/lib/strokedb/util/uuid.rb +159 -0
  77. data/lib/{util → strokedb/util}/xml.rb +0 -0
  78. data/lib/{view → strokedb}/view.rb +2 -2
  79. data/lib/strokedb/volumes.rb +5 -0
  80. data/lib/strokedb/volumes/archive_volume.rb +165 -0
  81. data/lib/strokedb/volumes/block_volume.rb +169 -0
  82. data/lib/strokedb/volumes/distributed_pointer.rb +43 -0
  83. data/lib/strokedb/volumes/fixed_length_skiplist_volume.rb +109 -0
  84. data/lib/strokedb/volumes/map_volume.rb +268 -0
  85. data/meta/MANIFEST +175 -0
  86. data/script/console +2 -70
  87. data/spec/integration/remote_store_spec.rb +70 -0
  88. data/spec/integration/search_spec.rb +76 -0
  89. data/spec/integration/spec_helper.rb +1 -0
  90. data/spec/lib/spec_helper.rb +1 -0
  91. data/spec/lib/strokedb/config_spec.rb +250 -0
  92. data/spec/lib/strokedb/core_ext/blank_spec.rb +20 -0
  93. data/spec/lib/strokedb/core_ext/extract_spec.rb +42 -0
  94. data/spec/lib/strokedb/core_ext/float_spec.rb +62 -0
  95. data/spec/lib/strokedb/core_ext/infinity_spec.rb +40 -0
  96. data/spec/lib/strokedb/core_ext/spec_helper.rb +1 -0
  97. data/spec/lib/strokedb/core_ext/string_spec.rb +25 -0
  98. data/spec/lib/strokedb/core_ext/symbol_spec.rb +8 -0
  99. data/spec/lib/strokedb/data_structures/chunked_skiplist_spec.rb +144 -0
  100. data/spec/lib/strokedb/data_structures/inverted_list_spec.rb +172 -0
  101. data/spec/lib/strokedb/data_structures/simple_skiplist_spec.rb +200 -0
  102. data/spec/lib/strokedb/data_structures/skiplist_spec.rb +253 -0
  103. data/spec/lib/strokedb/data_structures/spec_helper.rb +1 -0
  104. data/spec/lib/strokedb/document/associations_spec.rb +319 -0
  105. data/spec/lib/strokedb/document/callbacks_spec.rb +134 -0
  106. data/spec/lib/strokedb/document/coercions_spec.rb +110 -0
  107. data/spec/lib/strokedb/document/document_spec.rb +1063 -0
  108. data/spec/lib/strokedb/document/meta_meta_spec.rb +30 -0
  109. data/spec/lib/strokedb/document/meta_spec.rb +435 -0
  110. data/spec/lib/strokedb/document/metaslot_spec.rb +43 -0
  111. data/spec/lib/strokedb/document/slot_spec.rb +130 -0
  112. data/spec/lib/strokedb/document/spec_helper.rb +1 -0
  113. data/spec/lib/strokedb/document/validations_spec.rb +1081 -0
  114. data/spec/lib/strokedb/document/virtualize_spec.rb +80 -0
  115. data/spec/lib/strokedb/nsurl_spec.rb +73 -0
  116. data/spec/lib/strokedb/spec_helper.rb +1 -0
  117. data/spec/lib/strokedb/stores/chained_storages_spec.rb +116 -0
  118. data/spec/lib/strokedb/stores/spec_helper.rb +1 -0
  119. data/spec/lib/strokedb/stores/store_spec.rb +201 -0
  120. data/spec/lib/strokedb/stores/transaction_spec.rb +107 -0
  121. data/spec/lib/strokedb/sync/chain_sync_spec.rb +43 -0
  122. data/spec/lib/strokedb/sync/diff_spec.rb +111 -0
  123. data/spec/lib/strokedb/sync/lamport_timestamp_spec.rb +174 -0
  124. data/spec/lib/strokedb/sync/slot_diff_spec.rb +164 -0
  125. data/spec/lib/strokedb/sync/spec_helper.rb +1 -0
  126. data/spec/lib/strokedb/sync/store_sync_spec.rb +181 -0
  127. data/spec/lib/strokedb/sync/stroke_diff/array_spec.rb +97 -0
  128. data/spec/lib/strokedb/sync/stroke_diff/complex_spec.rb +58 -0
  129. data/spec/lib/strokedb/sync/stroke_diff/hash_spec.rb +144 -0
  130. data/spec/lib/strokedb/sync/stroke_diff/scalar_spec.rb +23 -0
  131. data/spec/lib/strokedb/sync/stroke_diff/spec_helper.rb +25 -0
  132. data/spec/lib/strokedb/sync/stroke_diff/string_spec.rb +61 -0
  133. data/spec/lib/strokedb/util/attach_dsl_spec.rb +45 -0
  134. data/spec/lib/strokedb/util/inflect_spec.rb +14 -0
  135. data/spec/lib/strokedb/util/lazy_array_spec.rb +157 -0
  136. data/spec/lib/strokedb/util/lazy_mapping_array_spec.rb +174 -0
  137. data/spec/lib/strokedb/util/lazy_mapping_hash_spec.rb +92 -0
  138. data/spec/lib/strokedb/util/spec_helper.rb +1 -0
  139. data/spec/lib/strokedb/util/uuid_spec.rb +46 -0
  140. data/spec/lib/strokedb/view_spec.rb +228 -0
  141. data/spec/lib/strokedb/volumes/archive_volume_spec.rb +105 -0
  142. data/spec/lib/strokedb/volumes/block_volume_spec.rb +100 -0
  143. data/spec/lib/strokedb/volumes/distributed_pointer_spec.rb +14 -0
  144. data/spec/lib/strokedb/volumes/fixed_length_skiplist_volume_spec.rb +177 -0
  145. data/spec/lib/strokedb/volumes/map_volume_spec.rb +172 -0
  146. data/spec/lib/strokedb/volumes/spec_helper.rb +1 -0
  147. data/spec/regression/docref_spec.rb +94 -0
  148. data/spec/regression/meta_spec.rb +23 -0
  149. data/spec/regression/spec_helper.rb +1 -0
  150. data/spec/regression/sync_spec.rb +36 -0
  151. data/spec/spec.opts +7 -0
  152. data/spec/spec_helper.rb +37 -0
  153. data/spec/temp/storages/TIMESTAMP +1 -0
  154. data/spec/temp/storages/UUID +1 -0
  155. data/spec/temp/storages/database-sync/TIMESTAMP +1 -0
  156. data/spec/temp/storages/database-sync/UUID +1 -0
  157. data/spec/temp/storages/database-sync/config +1 -0
  158. data/spec/temp/storages/database-sync/file/LAST +1 -0
  159. data/spec/temp/storages/database-sync/file/bd/f6/bdf675e5-8a7b-494e-97f2-f74a14ccd95d.av +0 -0
  160. data/spec/temp/storages/database-sync/file/uindex.wal +0 -0
  161. data/spec/temp/storages/database-sync/inverted_list_file/INVERTED_INDEX +1 -0
  162. data/spec/temp/storages/inverted_list_storage/INVERTED_INDEX +0 -0
  163. data/strokedb.gemspec +120 -0
  164. data/task/benchmark.task +9 -0
  165. data/task/ditz.task +30 -0
  166. data/task/echoe.rb +17 -0
  167. data/task/rcov.task +50 -0
  168. data/task/rdoc.task +10 -0
  169. data/task/rspec.task +0 -0
  170. data/vendor/java_inline.rb +106 -0
  171. data/vendor/rbmodexcl/mrimodexcl.rb +82 -0
  172. data/vendor/rbmodexcl/rbmodexcl.rb +5 -0
  173. data/vendor/rbmodexcl/rbxmodexcl.rb +48 -0
  174. data/vendor/rbmodexcl/spec/unextend_spec.rb +50 -0
  175. data/vendor/rbmodexcl/spec/uninclude_spec.rb +26 -0
  176. metadata +271 -79
  177. data/CONTRIBUTORS +0 -7
  178. data/CREDITS +0 -13
  179. data/bin/sdbc +0 -2
  180. data/lib/init.rb +0 -57
  181. data/lib/stores/inverted_list_index/inverted_list_index.rb +0 -49
  182. data/lib/stores/skiplist_store/chunk.rb +0 -119
  183. data/lib/stores/skiplist_store/chunk_storage.rb +0 -21
  184. data/lib/stores/skiplist_store/file_chunk_storage.rb +0 -44
  185. data/lib/stores/skiplist_store/memory_chunk_storage.rb +0 -37
  186. data/lib/stores/skiplist_store/skiplist_store.rb +0 -217
  187. data/lib/stores/store.rb +0 -5
  188. data/lib/sync/stroke_diff/stroke_diff.rb +0 -9
  189. data/lib/util/ext/object.rb +0 -8
  190. data/lib/util/java_util.rb +0 -9
  191. data/lib/util/trigger_partition.rb +0 -136
  192. data/strokedb.rb +0 -75
@@ -160,7 +160,7 @@ module StrokeDB
160
160
 
161
161
  def each
162
162
  n = @head.forward[0]
163
- until TailNode === n
163
+ until n.is_a?(TailNode)
164
164
  yield n
165
165
  n = n.forward[0]
166
166
  end
@@ -1,3 +1,11 @@
1
+ require 'document/dsl'
2
+ require 'document/util'
3
+ require 'document/meta'
4
+ require 'document/callback'
5
+ require 'document/delete'
6
+ require 'document/slot'
7
+ require 'document/versions'
8
+
1
9
  module StrokeDB
2
10
  # Slots which contain references to another documents are matched
3
11
  # with these regexps.
@@ -17,23 +25,23 @@ module StrokeDB
17
25
  def initialize(slotname)
18
26
  @slotname = slotname
19
27
  end
20
-
28
+
21
29
  def message
22
30
  "Can't find slot #{@slotname}"
23
31
  end
24
-
32
+
25
33
  def inspect
26
34
  "#<#{self.class.name}: #{message}>"
27
35
  end
28
36
  end
29
-
37
+
30
38
  #
31
- # Raised when Document#save! is called on an invalid document
39
+ # Raised when Document#save! is called on an invalid document
32
40
  # (for which doc.valid? returns false)
33
41
  #
34
42
  class InvalidDocumentError < StandardError #:nodoc:
35
43
  attr_reader :document
36
-
44
+
37
45
  def initialize(document)
38
46
  @document = document
39
47
  end
@@ -41,7 +49,7 @@ module StrokeDB
41
49
  def message
42
50
  "Validation failed: #{@document.errors.messages.join(", ")}"
43
51
  end
44
-
52
+
45
53
  def inspect
46
54
  "#<#{self.class.name}: #{message}>"
47
55
  end
@@ -62,9 +70,17 @@ module StrokeDB
62
70
  # authors: ["Yurii Rashkovskii","Oleg Andreev"]
63
71
  #
64
72
  class Document
65
- include Validations::InstanceMethods
73
+ include StrokeDB::Validations::InstanceMethods
66
74
 
67
- attr_reader :store, :callbacks #:nodoc:
75
+ attr_reader :callbacks #:nodoc:
76
+
77
+ def store
78
+ if (txns = Thread.current[:strokedb_transactions]) && !txns.nil? && !txns.empty?
79
+ txns.last
80
+ else
81
+ @store
82
+ end
83
+ end
68
84
 
69
85
  def marshal_dump #:nodoc:
70
86
  (@new ? '1' : '0') + (@saved ? '1' : '0') + to_raw.to_json
@@ -82,13 +98,34 @@ module StrokeDB
82
98
  def initialize(document)
83
99
  @document = document
84
100
  _meta = document[:meta]
85
- concat [_meta].flatten.compact.map{|v| v.is_a?(DocumentReferenceValue) ? v.load : v}
101
+ concat _meta.to_a
86
102
  end
87
103
 
88
104
  def <<(meta)
89
105
  add_meta(meta, :call_initialization_callbacks => true)
90
106
  end
91
107
 
108
+ alias :_delete :delete
109
+ def delete(meta)
110
+ case meta
111
+ when Document
112
+ _delete meta
113
+ _module = StrokeDB::Document.collect_meta_modules(@document.store, meta).first
114
+ when Meta
115
+ _delete meta.document(@document.store)
116
+ _module = meta
117
+ else
118
+ raise ArgumentError, "Meta should be either document or meta module"
119
+ end
120
+
121
+ @document[:meta] = self
122
+
123
+ if _module
124
+ @document.unextend(_module)
125
+ end
126
+
127
+ end
128
+
92
129
  def add_meta(meta, opts = {})
93
130
  opts = opts.stringify_keys
94
131
  _module = nil
@@ -110,10 +147,8 @@ module StrokeDB
110
147
 
111
148
  if _module
112
149
  @document.extend(_module)
113
-
114
- _module.send!(:setup_callbacks, @document) rescue nil
115
-
116
- if opts['call_initialization_callbacks']
150
+
151
+ if opts['call_initialization_callbacks']
117
152
  @document.send!(:execute_callbacks_for, _module, :on_initialization)
118
153
  @document.send!(:execute_callbacks_for, _module, :on_new_document) if @document.new?
119
154
  end
@@ -168,6 +203,7 @@ module StrokeDB
168
203
  # If slot was not found, it will return <tt>nil</tt>
169
204
  #
170
205
  def [](slotname)
206
+ slotname = slotname.document.uuid if (slotname.is_a?(Meta) && slotname.is_a?(Module)) || (slotname == Meta)
171
207
  @slots[slotname.to_s].value rescue nil
172
208
  end
173
209
 
@@ -177,11 +213,12 @@ module StrokeDB
177
213
  # document[:slot_1] = "some value"
178
214
  #
179
215
  def []=(slotname, value)
216
+ slotname = slotname.document.uuid if (slotname.is_a?(Meta) && slotname.is_a?(Module)) || (slotname == Meta)
180
217
  slotname = slotname.to_s
181
218
 
182
219
  (@slots[slotname] ||= Slot.new(self, slotname)).value = value
183
220
  update_version!(slotname)
184
-
221
+
185
222
  value
186
223
  end
187
224
 
@@ -206,7 +243,7 @@ module StrokeDB
206
243
  #
207
244
  def remove_slot!(slotname)
208
245
  slotname = slotname.to_s
209
-
246
+
210
247
  @slots.delete slotname
211
248
  update_version! slotname
212
249
 
@@ -233,9 +270,9 @@ module StrokeDB
233
270
 
234
271
  def pretty_print #:nodoc:
235
272
  slots = to_raw.except('meta')
236
-
237
- s = is_a?(ImmutableDocument) ? "#<(imm)" : "#<"
238
-
273
+
274
+ s = is_a?(ImmutableDocument) ? "#<^" : "#<"
275
+
239
276
  Util.catch_circular_reference(self) do
240
277
  if self[:meta] && name = meta[:name]
241
278
  s << "#{name} "
@@ -245,9 +282,13 @@ module StrokeDB
245
282
 
246
283
  slots.keys.sort.each do |k|
247
284
  if %w(version previous_version).member?(k) && v = self[k]
248
- s << "#{k}: #{v.gsub(/^(0)+/,'')[0,4]}..., "
285
+ s << "#{k}: #{v[0,4]}..., "
249
286
  else
250
- s << "#{k}: #{self[k].inspect}, "
287
+ if k.match(/^#{UUID_RE}$/)
288
+ s << "[#{store.find(k).name}]: #{self[k].inspect}, " rescue s << "#{k}: #{self[k].inspect}, "
289
+ else
290
+ s << "#{k}: #{self[k].inspect}, "
291
+ end
251
292
  end
252
293
  end
253
294
 
@@ -255,7 +296,7 @@ module StrokeDB
255
296
  s.chomp!(' ')
256
297
  s << ">"
257
298
  end
258
-
299
+
259
300
  s
260
301
  rescue Util::CircularReferenceCondition
261
302
  "#(#{(self[:meta] ? "#{meta}" : "Doc")} #{('@#'+uuid)[0,5]}...)"
@@ -287,8 +328,8 @@ module StrokeDB
287
328
  @slots.each_pair do |k,v|
288
329
  raw_slots[k.to_s] = v.to_raw
289
330
  end
290
-
291
- raw_slots
331
+
332
+ raw_slots.to_raw
292
333
  end
293
334
 
294
335
  def to_optimized_raw #:nodoc:
@@ -298,14 +339,12 @@ module StrokeDB
298
339
  #
299
340
  # Creates a document from a serialized representation
300
341
  #
301
- def self.from_raw(store, raw_slots, opts = {}) #:nodoc:
302
- doc = new(store, raw_slots, true)
342
+ def self.from_raw(store, raw_slots, opts = {}, &block) #:nodoc:
343
+ doc = new(store, raw_slots, true, &block)
303
344
 
304
345
  collect_meta_modules(store, raw_slots['meta']).each do |meta_module|
305
346
  unless doc.is_a? meta_module
306
347
  doc.extend(meta_module)
307
-
308
- meta_module.send!(:setup_callbacks, doc) rescue nil
309
348
  end
310
349
  end
311
350
 
@@ -330,10 +369,14 @@ module StrokeDB
330
369
  # If first argument is Store, that particular store will be used; otherwise default store will be assumed.
331
370
  def self.find(*args)
332
371
  store = nil
333
- if args.empty? || args.first.is_a?(String) || args.first.is_a?(Hash)
334
- store = StrokeDB.default_store
372
+ if (txns = Thread.current[:strokedb_transactions]) && !txns.nil? && !txns.empty?
373
+ store = txns.last
335
374
  else
336
- store = args.shift
375
+ if args.empty? || args.first.is_a?(String) || args.first.is_a?(Hash) || args.first.nil?
376
+ store = StrokeDB.default_store
377
+ else
378
+ store = args.shift
379
+ end
337
380
  end
338
381
  raise NoDefaultStoreError.new unless store
339
382
  query = args.first
@@ -343,7 +386,7 @@ module StrokeDB
343
386
  when Hash
344
387
  store.search(query)
345
388
  else
346
- raise TypeError
389
+ raise ArgumentError, "use UUID or query to find document(s)"
347
390
  end
348
391
  end
349
392
 
@@ -386,30 +429,42 @@ module StrokeDB
386
429
  store.save!(self)
387
430
  @new = false
388
431
  @saved = true
389
-
432
+
390
433
  execute_callbacks :after_save
391
-
434
+
392
435
  self
393
436
  end
394
437
 
395
- #
396
- # Updates slots with specified <tt>hash</tt> and returns itself.
397
- #
438
+ # Updates slots with a specified <tt>hash</tt> and returns itself.
398
439
  def update_slots(hash)
399
440
  hash.each do |k, v|
400
- self[k] = v
441
+ send("#{k}=", v) unless self[k] == v
401
442
  end
402
-
403
443
  self
404
444
  end
405
445
 
406
- #
407
446
  # Same as update_slots, but also saves the document.
408
- #
409
447
  def update_slots!(hash)
410
448
  update_slots(hash).save!
411
449
  end
412
450
 
451
+
452
+ # Updates nil/false slots with a specified <tt>hash</tt> and returns itself.
453
+ # Already set slots are not modified (<tt>||=</tt> is used).
454
+ # Acts like <tt>hash1.reverse_merge(hash2)</tt> (<tt>hash2.merge(hash1)</tt>).
455
+ #
456
+ def reverse_update_slots(hash)
457
+ hash.each do |k, v|
458
+ self[k] ||= v
459
+ end
460
+ self
461
+ end
462
+
463
+ # Same as reverse_update_slots, but also saves the document.
464
+ def reverse_update_slots!(hash)
465
+ reverse_update_slots(hash).save!
466
+ end
467
+
413
468
  #
414
469
  # Returns document's metadocument (if any). In case if document has more than one metadocument,
415
470
  # it will combine all metadocuments into one 'virtual' metadocument
@@ -419,14 +474,14 @@ module StrokeDB
419
474
  # simple case
420
475
  return m || Document.new(@store)
421
476
  end
422
-
477
+
423
478
  return m.first if m.size == 1
424
479
 
425
480
  mm = m.clone
426
481
  collected_meta = mm.shift.clone
427
482
 
428
483
  names = collected_meta[:name].split(',') rescue []
429
-
484
+
430
485
  mm.each do |next_meta|
431
486
  next_meta = next_meta.clone
432
487
  collected_meta += next_meta
@@ -455,7 +510,7 @@ module StrokeDB
455
510
  # Please not that it accept both meta modules and their documents, there is no difference
456
511
  #
457
512
  def metas
458
- Metas.new(self)
513
+ @metas ||= Metas.new(self)
459
514
  end
460
515
 
461
516
  #
@@ -472,6 +527,10 @@ module StrokeDB
472
527
  @uuid ||= self[:uuid]
473
528
  end
474
529
 
530
+ def raw_uuid #:nodoc:
531
+ @raw_uuid ||= uuid.to_raw_uuid
532
+ end
533
+
475
534
  #
476
535
  # Returns document's previous version (which is stored in <tt>previous_version</tt> slot)
477
536
  #
@@ -498,7 +557,7 @@ module StrokeDB
498
557
  case doc
499
558
  when Document, DocumentReferenceValue
500
559
  doc = doc.load if doc.kind_of? DocumentReferenceValue
501
-
560
+
502
561
  # we make a quick UUID check here to skip two heavy to_raw calls
503
562
  doc.uuid == uuid && doc.to_raw == to_raw
504
563
  else
@@ -526,16 +585,16 @@ module StrokeDB
526
585
 
527
586
  def method_missing(sym, *args) #:nodoc:
528
587
  sym = sym.to_s
529
-
588
+
530
589
  return send(:[]=, sym.chomp('='), *args) if sym.ends_with? '='
531
590
  return self[sym] if slotnames.include? sym
532
591
  return !!send(sym.chomp('?'), *args) if sym.ends_with? '?'
533
-
592
+
534
593
  raise SlotNotFoundError.new(sym) if (callbacks['when_slot_not_found'] || []).empty?
535
594
 
536
595
  r = execute_callbacks(:when_slot_not_found, sym)
537
596
  raise r if r.is_a? SlotNotFoundError # TODO: spec this behavior
538
-
597
+
539
598
  r
540
599
  end
541
600
 
@@ -568,21 +627,21 @@ module StrokeDB
568
627
  end
569
628
  end
570
629
 
571
- # initialize the document. initialize_raw is true when
630
+ # initialize the document. initialize_raw is true when
572
631
  # document is initialized from a raw serialized form
573
632
  def do_initialize(store, slots={}, initialize_raw = false) #:nodoc:
574
633
  @callbacks = {}
575
634
  @store = store
576
635
 
577
636
  if initialize_raw
578
- initialize_raw_slots slots
637
+ initialize_raw_slots slots
579
638
  @saved = true
580
639
  else
581
640
  @new = true
582
641
  initialize_slots slots
583
642
 
584
643
  self[:uuid] = Util.random_uuid unless self[:uuid]
585
- generate_new_version! unless self[:version]
644
+ self[:version] ||= NIL_UUID
586
645
  end
587
646
  end
588
647
 
@@ -591,24 +650,23 @@ module StrokeDB
591
650
  @slots = {}
592
651
  slots = slots.stringify_keys
593
652
 
594
- # there is a reason for meta slot is initialized separately —
653
+ # there is a reason for meta slot is initialized separately —
595
654
  # we need to setup coercions before initializing actual slots
596
655
  if meta = slots['meta']
597
656
  meta = [meta] unless meta.is_a?(Array)
598
657
  meta.each {|m| metas.add_meta(m) }
599
658
  end
600
-
601
- slots.except('meta').each {|name,value| self[name] = value }
659
+
660
+ slots.except('meta').each {|name,value| send("#{name}=", value) }
602
661
 
603
662
  # now, when we have all slots initialized, we can run initialization callbacks
604
663
  execute_callbacks :on_initialization
605
664
  execute_callbacks :on_new_document
606
665
  end
607
-
666
+
608
667
  # initialize slots from a raw representation
609
668
  def initialize_raw_slots(slots) #:nodoc:
610
669
  @slots = {}
611
-
612
670
  slots.each do |name,value|
613
671
  s = Slot.new(self, name)
614
672
  s.raw_value = value
@@ -617,26 +675,73 @@ module StrokeDB
617
675
  end
618
676
  end
619
677
 
620
- # returns an array of meta modules (as constants) for a given something
621
- # (a document reference, a document itself, or an array of the former)
622
- def self.collect_meta_modules(store, meta) #:nodoc:
623
- meta_names = []
624
678
 
625
- case meta
626
- when VERSIONREF
627
- if m = store.find($1, $2)
628
- meta_names << m[:name]
679
+ class MetaModulesCollector
680
+ def initialize(store, subject)
681
+ @store = store
682
+ @subject = subject
683
+ end
684
+
685
+ def resolve_module_name(uuid)
686
+ if metadoc = @store.find(uuid, self.lookup_version_for_meta(@subject))
687
+ mod = Module.find_by_nsurl(metadoc[:nsurl])
688
+
689
+ if self.has_defined_constant_for_meta?(mod, metadoc)
690
+ at_top_level?(mod) ? "::#{metadoc[:name]}" : "#{mod.name}::#{metadoc[:name]}"
691
+ else
692
+ Meta.resolve_uuid_name(metadoc[:nsurl], metadoc[:name])
693
+ end
629
694
  end
630
- when DOCREF
631
- if m = store.find($1)
632
- meta_names << m[:name]
695
+ end
696
+
697
+ def at_top_level?(mod)
698
+ mod == Module || mod.nil?
699
+ end
700
+
701
+ def collect!
702
+ meta_names = []
703
+
704
+ case @subject
705
+ when VERSIONREF, DOCREF
706
+ meta_names << resolve_module_name($1)
707
+ when Array
708
+ meta_names = @subject.map { |subj| subj = MetaModulesCollector.new(@store, subj).collect! }.flatten
709
+ when Document
710
+ meta_names << @subject[:name]
633
711
  end
634
- when Array
635
- meta_names = meta.map { |m| collect_meta_modules(store, m) }.flatten
636
- when Document
637
- meta_names << meta[:name]
712
+
713
+ meta_names
714
+ end
715
+
716
+ def lookup_version_for_meta(meta)
717
+ version = case meta
718
+ when VERSIONREF then $2
719
+ else nil
720
+ end
721
+ version
722
+ end
723
+
724
+ def has_defined_constant_for_meta?(mod, metadoc)
725
+ top_level_meta?(mod, metadoc) || has_meta_definition?(mod, metadoc)
638
726
  end
639
727
 
728
+ def top_level_meta?(mod, doc)
729
+ (mod == Module && Object.constants.include?(doc[:name]))
730
+ end
731
+
732
+ def has_meta_definition?(mod, metadoc)
733
+ (mod && mod.constants.include?(metadoc[:name]))
734
+ end
735
+ end
736
+
737
+
738
+
739
+ # returns an array of meta modules (as constants) for a given something
740
+ # (a document reference, a document itself, or an array of the former)
741
+ def self.collect_meta_modules(store, meta) #:nodoc:
742
+ @collector = MetaModulesCollector.new(store, meta)
743
+ meta_names = @collector.collect!
744
+
640
745
  meta_names.map { |m| m.is_a?(String) ? (m.constantize rescue nil) : m }.compact
641
746
  end
642
747
 
@@ -649,7 +754,7 @@ module StrokeDB
649
754
  self[:previous_version] = version unless version.nil?
650
755
 
651
756
  generate_new_version!
652
-
757
+
653
758
  @saved = nil
654
759
  end
655
760
  end
@@ -680,5 +785,9 @@ module StrokeDB
680
785
  def save!
681
786
  self
682
787
  end
788
+
789
+ def make_mutable!
790
+ unextend(ImmutableDocument)
791
+ end
683
792
  end
684
793
  end