strokedb 0.0.2.1 → 0.0.2.2

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.
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