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
@@ -0,0 +1,110 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ def setup
4
+ setup_default_store
5
+ setup_index
6
+ Object.send!(:remove_const, 'Foo') if defined?(Foo)
7
+ Object.send!(:remove_const, 'Bar') if defined?(Bar)
8
+ Object.send!(:remove_const, 'User') if defined?(User)
9
+ Object.send!(:remove_const, 'Email') if defined?(Email)
10
+ Object.send!(:remove_const, 'Item') if defined?(Item)
11
+ end
12
+
13
+ describe "Document slot coercion" do
14
+ before :each do
15
+ setup
16
+ end
17
+
18
+ it "should coerce initialization slot specified to a number" do
19
+ Foo = Meta.new do
20
+ coerces :some_slot, :to => :number
21
+ end
22
+ foo = Foo.new(:some_slot => "1")
23
+ foo.some_slot.should == 1
24
+ end
25
+
26
+ it "should coerce slot specified to a number" do
27
+ Foo = Meta.new do
28
+ coerces :some_slot, :to => :number
29
+ end
30
+ foo = Foo.new
31
+ foo.some_slot = "1"
32
+ foo.some_slot.should == 1
33
+ end
34
+
35
+ it "should coerce initialization slot specified to a string" do
36
+ Foo = Meta.new do
37
+ coerces :some_slot, :to => :string
38
+ end
39
+ foo = Foo.new(:some_slot => 1)
40
+ foo.some_slot.should == "1"
41
+ end
42
+
43
+ it "should coerce slot specified to a string" do
44
+ Foo = Meta.new do
45
+ coerces :some_slot, :to => :string
46
+ end
47
+ foo = Foo.new
48
+ foo.some_slot = 1
49
+ foo.some_slot.should == "1"
50
+ end
51
+
52
+ it "should coerce initialization slots specified" do
53
+ Foo = Meta.new do
54
+ coerces [:some_slot, :another_slot], :to => :string
55
+ end
56
+ foo = Foo.new(:some_slot => 1, :another_slot => 2)
57
+ foo.some_slot.should == "1"
58
+ foo.another_slot.should == "2"
59
+ end
60
+
61
+ it "should coerce slots specified" do
62
+ Foo = Meta.new do
63
+ coerces [:some_slot, :another_slot], :to => :string
64
+ end
65
+ foo = Foo.new
66
+ foo.some_slot = 1
67
+ foo.some_slot.should == "1"
68
+ foo.another_slot = 2
69
+ foo.another_slot.should == "2"
70
+ end
71
+
72
+
73
+ it "should not coerce initialization non-numeric slot specified to a number" do
74
+ Foo = Meta.new do
75
+ coerces :some_slot, :to => :number
76
+ end
77
+ foo = Foo.new(:some_slot => "bad1")
78
+ foo.some_slot.should == "bad1"
79
+ end
80
+
81
+ it "should respect :if" do
82
+ Foo = Meta.new do
83
+ coerces :some_slot, :to => :number, :if => 'if_slot'
84
+ end
85
+ foo = Foo.new
86
+ foo.if_slot = true
87
+ foo.some_slot = "1"
88
+ foo.some_slot.should == 1
89
+ foo = Foo.new
90
+ foo.if_slot = false
91
+ foo.some_slot = "1"
92
+ foo.some_slot.should == "1"
93
+ end
94
+
95
+ it "should respect :unless" do
96
+ Foo = Meta.new do
97
+ coerces :some_slot, :to => :number, :unless => 'if_slot'
98
+ end
99
+ foo = Foo.new
100
+ foo.if_slot = true
101
+ foo.some_slot = "1"
102
+ foo.some_slot.should == "1"
103
+ foo = Foo.new
104
+ foo.if_slot = false
105
+ foo.some_slot = "1"
106
+ foo.some_slot.should == 1
107
+ end
108
+
109
+ end
110
+
@@ -0,0 +1,1063 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Document class" do
4
+
5
+ before(:each) do
6
+ @store = setup_default_store
7
+ setup_index
8
+ end
9
+
10
+ it "should be able to find document by UUID" do
11
+ @document = Document.create!
12
+ Document.find(@document.uuid).should == @document
13
+ Document.find(@store,@document.uuid).should == @document
14
+ end
15
+
16
+ it "should be able to find document by query" do
17
+ @document = Document.create!
18
+ Document.find(:uuid => @document.uuid).should == [@document]
19
+ Document.find(@store, :uuid => @document.uuid).should == [@document]
20
+ end
21
+
22
+ it "should raise ArgumentError when invoking #find with wrong argument" do
23
+ @document = Document.create!
24
+ [ [], nil, 1 ].each do |arg|
25
+ lambda { Document.find(arg) }.should raise_error(ArgumentError)
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+
32
+ describe "Document", :shared => true do
33
+
34
+ it "should create new slot" do
35
+ lambda do
36
+ @document[:new_slot] = "someval"
37
+ @document[:new_slot].should == "someval"
38
+ end.should change(@document,:slotnames)
39
+ end
40
+
41
+ it "should be able to remove slot" do
42
+ original_slotnames = @document.slotnames
43
+ lambda do
44
+ @document[:new_slot] = "someval"
45
+ @document[:new_slot].should == "someval"
46
+ end.should change(@document,:slotnames)
47
+ lambda do
48
+ @document.remove_slot!(:new_slot)
49
+ end.should change(@document,:slotnames)
50
+ (@document.slotnames - ['previous_version']).should == original_slotnames # TODO: check this exclusion
51
+ end
52
+
53
+
54
+ it "should call when_slot_not_found callback on missing slot" do
55
+ @document.callbacks['when_slot_not_found'] = [mock("callback")]
56
+ @document.should_receive(:execute_callbacks).with(:when_slot_not_found,'slot_that_surely_does_not_exist').and_return("Yes!")
57
+ @document.slot_that_surely_does_not_exist.should == "Yes!"
58
+ end
59
+
60
+ it "should raise an exception if slot not found when trying to read it" do
61
+ lambda { @document.slot_that_never_can_exist }.should raise_error(SlotNotFoundError)
62
+ end
63
+
64
+ it "should allow to write slot by writer method" do
65
+ @document.slot1 = 2
66
+ @document[:slot1].should == 2
67
+ end
68
+
69
+ it "should allow to read slot by reader method" do
70
+ @document[:slot1] = 1
71
+ @document.slot1.should == 1
72
+ end
73
+
74
+ it "should allow to read slot by reader? method" do
75
+ @document[:slot1] = 1
76
+ @document[:slot2] = 0
77
+ @document[:slot3] = nil
78
+ @document[:slot4] = false
79
+ @document.slot1?.should be_true
80
+ @document.slot2?.should be_true
81
+ @document.slot3?.should be_false
82
+ @document.slot4?.should be_false
83
+ end
84
+
85
+ # update_slots
86
+
87
+ it "should batch update slots" do
88
+ @document.update_slots(:aaa => "aaa", :bbb => true)
89
+ @document.aaa.should == "aaa"
90
+ @document.bbb.should == true
91
+ end
92
+
93
+ it "should pass batch update slots to matching slot= methods if any" do
94
+ @document.should_receive(:aaa=).with("aaa").once
95
+ @document.should_receive(:bbb=).with(true).once
96
+ @document.update_slots(:aaa => "aaa", :bbb => true)
97
+ end
98
+
99
+ it "should batch update slots but should not touch version/previous_version if update haven't changed document" do
100
+ @document = @document.update_slots!(:aaa => "aaa", :bbb => true).reload
101
+ lambda do
102
+ lambda do
103
+ @document.update_slots(:aaa => "aaa", :bbb => true)
104
+ end.should_not change(@document, :version)
105
+ end.should_not change(@document, :previous_version)
106
+ end
107
+
108
+ it "should not save batch update slots" do
109
+ @document.save! # ensure it is not new
110
+ doc = @document.reload
111
+ @document.update_slots(:aaa1 => "aaa", :bbb1 => true)
112
+ doc = @document.reload
113
+ doc[:aaa1].should be_nil
114
+ doc[:bbb1].should be_nil
115
+ end
116
+
117
+ it "should support batch update slots with saving" do
118
+ doc = @document.update_slots!(:aaa => "aaa", :bbb => true)
119
+ doc.aaa.should == "aaa"
120
+ doc.bbb.should == true
121
+ doc = doc.reload
122
+ doc.aaa.should == "aaa"
123
+ doc.bbb.should == true
124
+ end
125
+
126
+ # reverse_update_slots
127
+
128
+ it "should batch update slots in reverse (||=)" do
129
+ @document.aaa = "before"
130
+ @document.reverse_update_slots(:aaa => "after", :bbb => false)
131
+ @document.aaa.should == "before"
132
+ @document.bbb.should == false
133
+ end
134
+
135
+ it "should support batch reverse_update_slots with saving" do
136
+ @document.aaa = "before"
137
+ doc = @document.reverse_update_slots!(:aaa => "after", :bbb => false)
138
+ doc.aaa.should == "before"
139
+ doc.bbb.should == false
140
+ doc = doc.reload
141
+ doc.aaa.should == "before"
142
+ doc.bbb.should == false
143
+ end
144
+
145
+ # callbacks
146
+
147
+ it "should add callbacks" do
148
+ cb1 = Callback.new(nil,:callback_name) {}
149
+ cb2 = Callback.new(nil,:another_callback_name) {}
150
+ @document.add_callback(cb1)
151
+ @document.add_callback(cb2)
152
+ @document.callbacks[:callback_name].should include(cb1)
153
+ @document.callbacks[:another_callback_name].should include(cb2)
154
+ end
155
+
156
+ it "should replace uniquely identified callbacks" do
157
+ cb1 = Callback.new(nil,:callback_name, :special) {}
158
+ cb2 = Callback.new(nil,:callback_name, :special) {}
159
+ @document.add_callback(cb1)
160
+ @document.add_callback(cb2)
161
+ @document.callbacks[:callback_name].should have(1).item
162
+ @document.callbacks[:callback_name].should include(cb2)
163
+ end
164
+
165
+ it "should report existing slot as existing" do
166
+ @document[:existing_slot] = 1
167
+ @document.should have_slot(:existing_slot)
168
+ end
169
+
170
+ it "should report existing slot with nil value as existing" do
171
+ @document[:existing_slot] = nil
172
+ @document.should have_slot(:existing_slot)
173
+ end
174
+
175
+ it "should report non-existing slot as non-existing" do
176
+ @document.should_not have_slot(:existing_slot)
177
+ end
178
+
179
+ it "should report existing 'virtual' slot as existing" do
180
+ @document.should_receive(:method_missing).with(:existing_slot).and_return 1
181
+ @document.should have_slot(:existing_slot)
182
+ end
183
+
184
+ it "should report non-existing 'virtual' slot as non-existing" do
185
+ @document.should_receive(:method_missing).with(:existing_slot).and_return { raise SlotNotFoundError.new(:existing_slot)}
186
+ @document.should_not have_slot(:existing_slot)
187
+ end
188
+
189
+ it "should convert Symbol values to String instantly (including Symbol usage in structures)" do
190
+ @document.symbol_slot = :a
191
+ @document.symbol_slot.should == "a"
192
+ @document.symbol_slot = [[:a]]
193
+ @document.symbol_slot.should == [["a"]]
194
+ @document.symbol_slot = {:a => :b}
195
+ @document.symbol_slot.should == {"a" => "b"}
196
+ @document.symbol_slot = [{:a => :b}]
197
+ @document.symbol_slot.should == [{"a" => "b"}]
198
+ end
199
+
200
+ it "should convert Symbol values to String (including Symbol usage in structures)" do
201
+ @document.symbol_slot = :a
202
+ @document = @document.save!.reload
203
+ @document.symbol_slot.should == "a"
204
+ @document.symbol_slot = [[:a]]
205
+ @document = @document.save!.reload
206
+ @document.symbol_slot.should == [["a"]]
207
+ @document.symbol_slot = {:a => :b}
208
+ @document = @document.save!.reload
209
+ @document.symbol_slot.should == {"a" => "b"}
210
+ @document.symbol_slot = [{:a => :b}]
211
+ @document = @document.save!.reload
212
+ @document.symbol_slot.should == [{"a" => "b"}]
213
+ end
214
+
215
+ it "should convert Meta values to Documents instantly" do
216
+ @document.meta_slot = Meta
217
+ @document.meta_slot.should == Meta.document(@document.store)
218
+ @document.metas_slot = [Meta]
219
+ @document.metas_slot.should == [Meta.document(@document.store)]
220
+ end
221
+
222
+ it "should convert Meta values to Documents" do
223
+ @document.meta_slot = Meta
224
+ @document.metas_slot = [Meta]
225
+ @document = @document.save!.reload
226
+ @document.meta_slot.should == Meta.document(@document.store)
227
+ @document.metas_slot.should == [Meta.document(@document.store)]
228
+ end
229
+
230
+ it "should not save itself once declared immutable" do
231
+ @document.make_immutable!
232
+ @document.store.should_not_receive(:save!)
233
+ @document.save!
234
+ end
235
+
236
+ it "should be able to return current version" do
237
+ @document.should_not be_a_kind_of(VersionedDocument)
238
+ @document.versions.current.should == @document
239
+ @document.versions.current.should be_a_kind_of(VersionedDocument)
240
+ end
241
+
242
+ it "should have #hash calculated from uuid" do
243
+ hash = @document.hash
244
+ another_doc = Document.from_raw(@document.store,@document.to_raw)
245
+ another_doc_with_different_uuid = Document.from_raw(@document.store,@document.to_raw.merge('uuid' => Util.random_uuid))
246
+ another_doc.hash.should == hash
247
+ another_doc_with_different_uuid.hash.should_not == hash
248
+ end
249
+
250
+
251
+ end
252
+
253
+ describe "New Document" do
254
+
255
+ before(:each) do
256
+ setup_default_store
257
+ @document = Document.new
258
+ end
259
+
260
+ it "should have UUID" do
261
+ @document.uuid.should match(UUID_RE)
262
+ end
263
+
264
+ it "should be new" do
265
+ @document.should be_new
266
+ end
267
+
268
+ it "should not be head" do
269
+ @document.should_not be_head
270
+ end
271
+
272
+ it "should have version" do
273
+ @document.version.should_not be_nil
274
+ end
275
+
276
+ it "should have NIL UUID version" do
277
+ @document.version.should == NIL_UUID
278
+ end
279
+
280
+ it "should have no previous version" do
281
+ @document.previous_version.should be_nil
282
+ @document.versions.previous.should be_nil
283
+ end
284
+
285
+ it "should have only version slotname" do
286
+ @document.slotnames.to_set.should == ['version','uuid'].to_set
287
+ end
288
+
289
+ it "should have no versions" do
290
+ @document.versions.should be_empty
291
+ end
292
+
293
+
294
+ it "should be reloadable to itself" do
295
+ reloaded_doc = @document.reload
296
+ reloaded_doc.object_id.should == @document.object_id
297
+ reloaded_doc.should be_new
298
+ end
299
+
300
+ it "should be both first and head version" do
301
+ @document.versions.first.should == @document
302
+ @document.versions.head.should == @document
303
+ end
304
+
305
+ it "should return string with Document's JSON representation" do
306
+ @document.to_json.should == "{\"uuid\":\"#{@document.uuid}\",\"version\":\"#{@document.version}\"}"
307
+ end
308
+
309
+ it "should return string with Document's XML representation" do
310
+ pending('bug') do
311
+ @document.to_xml.should == "FIXME"
312
+ end
313
+ end
314
+
315
+ it_should_behave_like "Document"
316
+
317
+ end
318
+
319
+ describe "New Document with slots supplied" do
320
+
321
+ before(:each) do
322
+ setup_default_store
323
+ @document = Document.new(:slot1 => "val1", :slot2 => "val2")
324
+ end
325
+
326
+ it "should have corresponding slotnames" do
327
+ @document.slotnames.to_set.should == ['slot1','slot2','version','uuid'].to_set
328
+ end
329
+
330
+ it "should update slot value" do
331
+ @document[:slot1] = "someval"
332
+ @document[:slot1].should == "someval"
333
+ end
334
+
335
+ it "should be saveable" do
336
+ @document.save!
337
+ @document.should_not be_new
338
+ end
339
+
340
+ it_should_behave_like "Document"
341
+
342
+ describe "with #slot= method(s)" do
343
+ it "should pass matching slots to methods" do
344
+ Kernel.should_receive(:called_slot1=).with("val1")
345
+ my_document_class = Class.new(Document) do
346
+ def slot1=(v)
347
+ Kernel.send(:called_slot1=,v)
348
+ end
349
+ end
350
+ @document = my_document_class.new(:slot1 => "val1", :slot2 => "val2")
351
+
352
+ end
353
+ end
354
+
355
+ end
356
+
357
+ describe "Forked documents" do
358
+ before(:each) do
359
+ setup_default_store
360
+ end
361
+ it "should have the same previous_version " do
362
+ @doc1 = Document.create!(:a => 11)
363
+ @doc1.save!
364
+ @first_version = @doc1.version.dup
365
+ @doc1.a = 12
366
+ @doc1.save!
367
+
368
+ # clone
369
+ @doc2 = @doc1.versions[@first_version]
370
+ @doc2.a = 21
371
+ @doc2.save!
372
+
373
+ @doc1.previous_version.should == @first_version
374
+ @doc2.previous_version.should == @first_version
375
+ end
376
+ end
377
+
378
+ describe "Saved Document" do
379
+
380
+ before(:each) do
381
+ @store = setup_default_store
382
+ @document = Document.create!(:some_data => 1)
383
+ end
384
+
385
+ it "should have version" do
386
+ @document.version.should match(/#{VERSION_RE}/)
387
+ end
388
+
389
+ it "should not be new" do
390
+ @document.should_not be_new
391
+ end
392
+
393
+ it "should be head" do
394
+ @document.should be_head
395
+ end
396
+
397
+ it "should be reloadable" do
398
+ reloaded_doc = @document.reload
399
+ reloaded_doc.should == @document
400
+ reloaded_doc.object_id.should_not == @document.object_id
401
+ end
402
+
403
+ it "should not change version and previous_version once not modified and saved" do
404
+ old_version = @document.version
405
+ old_previos_version = @document.previous_version
406
+ @document.save!
407
+ @document.version.should == old_version
408
+ @document.previous_version.should == old_previos_version
409
+ end
410
+
411
+ it "should change version once modified; previous version should be set to original version" do
412
+ old_version = @document.version
413
+ @document[:a] = 1
414
+ @document.version.should_not == old_version
415
+ @document.previous_version.should == old_version
416
+ end
417
+
418
+ it "should not change version once Array slot is accessed" do
419
+ @document[:a] = [1]
420
+ @document.save!
421
+ old_version = @document.version
422
+ @document[:a].index(0)
423
+ @document.version.should == old_version
424
+ end
425
+
426
+ it "should change version once Array slot is modified; previous version should be set to original version" do
427
+ @document[:a] = []
428
+ @document.save!
429
+ old_version = @document.version
430
+ @document = @document.reload
431
+ @document[:a] << 1
432
+ @document.version.should_not == old_version
433
+ @document.previous_version.should == old_version
434
+ @document = @document.reload
435
+ @document[:a].unshift 1
436
+ @document.version.should_not == old_version
437
+ @document.previous_version.should == old_version
438
+ @document = @document.reload
439
+ @document[:a][0] = 1
440
+ @document.version.should_not == old_version
441
+ @document.previous_version.should == old_version
442
+ end
443
+
444
+ it "should not change version once Hash slot is accessed" do
445
+ @document[:a] = {}
446
+ @document.save!
447
+ old_version = @document.version
448
+ val = @document[:a][:b]
449
+ @document.version.should == old_version
450
+ end
451
+
452
+ it "should change version once Hash slot is modified; previous version should be set to original version" do
453
+ @document[:a] = {}
454
+ @document.save!
455
+ old_version = @document.version
456
+ @document[:a][:b] = 1
457
+ @document.version.should_not == old_version
458
+ @document.previous_version.should == old_version
459
+ end
460
+
461
+ it "should change version once some slot is removed; previous version should be set to original version" do
462
+ old_version = @document.version
463
+ @document.remove_slot!(:some_data)
464
+ @document.version.should_not == old_version
465
+ @document.previous_version.should == old_version
466
+ end
467
+
468
+ it "should be deleteable" do
469
+ old_version = @document.version
470
+ @document.delete!
471
+ @document.should be_a_kind_of(DeletedDocument)
472
+ @document.should_not be_mutable
473
+ @document.version.should_not == old_version
474
+ @document.previous_version.should == old_version
475
+ end
476
+
477
+ it_should_behave_like "Document"
478
+
479
+ end
480
+
481
+ describe "Deleted document" do
482
+
483
+ before(:each) do
484
+ @store = setup_default_store
485
+ @document = Document.create!(:some_data => 1)
486
+ @old_version = @document.version
487
+ @document.delete!
488
+ end
489
+
490
+ it "once reloaded shouldn't be mutable" do
491
+ @document = @document.reload
492
+ @document.should_not be_mutable
493
+ end
494
+
495
+ it "should be undeletable" do
496
+ undeleted_document = @document.undelete!
497
+ @document.should_not be_mutable
498
+ undeleted_document.should_not be_a_kind_of(DeletedDocument)
499
+ end
500
+
501
+
502
+ it "should have old version after undeletion" do
503
+ @document = @document.undelete!
504
+ @document.version.should == @old_version
505
+ end
506
+
507
+ end
508
+
509
+ describe "Head Document with references" do
510
+
511
+ before(:each) do
512
+ setup_default_store
513
+ @doc1 = Document.create!(:one => 1)
514
+ @doc2 = Document.create!(:two => 2)
515
+ @doc3 = Document.new(:three => 3)
516
+ @document = Document.create!(:some_link => @doc1, :some_indirect_link => [@doc2], :some_other_link => @doc3)
517
+ @document.test = :yes
518
+ @document.save!
519
+ @doc3.save!
520
+ end
521
+
522
+ it "should not link to specific versions" do
523
+ @document.should be_head
524
+ @document.some_link.should_not be_a_kind_of(VersionedDocument)
525
+ @document.some_other_link.should_not be_a_kind_of(VersionedDocument)
526
+ @document.some_indirect_link.first.should_not be_a_kind_of(VersionedDocument)
527
+ end
528
+
529
+ it "should not link to specific versions when reloaded" do
530
+ @document = @document.reload
531
+ @document.should be_head
532
+ @document.some_link.should_not be_a_kind_of(VersionedDocument)
533
+ @document.some_other_link.should_not be_a_kind_of(VersionedDocument)
534
+ @document.some_indirect_link.first.should_not be_a_kind_of(VersionedDocument)
535
+ end
536
+
537
+ end
538
+
539
+ describe "Head Document with meta" do
540
+
541
+ before(:each) do
542
+ setup_default_store
543
+ Object.send!(:remove_const,'SomeMeta') if defined?(SomeMeta)
544
+ SomeMeta = Meta.new
545
+ @document = SomeMeta.create!
546
+ @document = @document.reload
547
+ @document.should be_head
548
+ end
549
+
550
+ it "should link to head meta" do
551
+ Object.send!(:remove_const,'SomeMeta') if defined?(SomeMeta)
552
+ SomeMeta = Meta.new(:some_slot => 1)
553
+ SomeMeta.document # ensure new metadoc version is saved
554
+ @document.meta.should be_head
555
+ @document.meta.should_not be_a_kind_of(VersionedDocument)
556
+ @document.meta.some_slot.should == 1
557
+ end
558
+
559
+ end
560
+
561
+ describe "Non-head Document with meta" do
562
+
563
+ before(:each) do
564
+ setup_default_store
565
+ Object.send!(:remove_const,'SomeMeta') if defined?(SomeMeta)
566
+ SomeMeta = Meta.new
567
+ @document = SomeMeta.create!
568
+ @document.update_slots! :updated => true
569
+ @document = @document.versions.previous
570
+ @document.should_not be_head
571
+ end
572
+
573
+ it "should link to exact meta version" do
574
+ Object.send!(:remove_const,'SomeMeta') if defined?(SomeMeta)
575
+ SomeMeta = Meta.new(:some_slot => 1)
576
+
577
+ @document.meta.should_not be_head
578
+ @document.meta.should be_a_kind_of(VersionedDocument)
579
+ @document.meta.should_not have_slot(:some_slot)
580
+ end
581
+
582
+ end
583
+ describe "Saved VersionedDocument" do
584
+
585
+ before(:each) do
586
+ setup_default_store
587
+ @document = Document.create!(:some_data => 1)
588
+ @versioned_document = @document.versions[@document.version]
589
+ end
590
+
591
+ it "should not be head" do
592
+ @versioned_document.should_not be_head
593
+ end
594
+
595
+ it "should be reloadable" do
596
+ StrokeDB.default_store.should_receive(:find).with(@document.uuid,@document.version)
597
+ @versioned_document.reload
598
+ end
599
+
600
+ end
601
+
602
+
603
+ describe "VersionedDocument with references" do
604
+
605
+ before(:each) do
606
+ setup_default_store
607
+ @doc1 = Document.create!(:one => 1)
608
+ @doc2 = Document.create!(:two => 2)
609
+ @doc3 = Document.new(:three => 3)
610
+ @document = Document.create!(:some_link => @doc1, :some_indirect_link => [@doc2], :some_other_link => @doc3)
611
+ @doc3.save!
612
+ @versioned_document = @document.versions[@document.version]
613
+ @versioned_document.should be_a_kind_of(VersionedDocument)
614
+ @versioned_document.should_not be_head
615
+ end
616
+
617
+ it "should link to specific versions" do
618
+ @versioned_document.some_link.should be_a_kind_of(VersionedDocument)
619
+ @versioned_document.some_other_link.should be_a_kind_of(VersionedDocument)
620
+ @versioned_document.some_indirect_link.first.should be_a_kind_of(VersionedDocument)
621
+ end
622
+
623
+ end
624
+
625
+
626
+ describe "Document with previous version" do
627
+
628
+ before(:each) do
629
+ @store = setup_default_store
630
+ @document = Document.create!
631
+ @first_version = Document.find(@document.uuid)
632
+ @document.new_slot = 1
633
+ @document.save!
634
+ @second_version = Document.find(@document.uuid)
635
+ end
636
+
637
+ it "should have versions" do
638
+ @document.version.should_not be_empty
639
+ end
640
+
641
+
642
+ it "should be able to access previous version" do
643
+ prev_version = @store.find(@document.uuid,@document.previous_version)
644
+ @document.versions[@document.previous_version].should == prev_version
645
+ @document.versions.previous.should == prev_version
646
+ end
647
+
648
+ it "should be able to access first version" do
649
+ @document.versions.first.should == @document.versions.previous
650
+ end
651
+
652
+ it "should return all previous versions of document" do
653
+ @document.new_slot2 = 'foo'
654
+ @document.save!
655
+ @document.versions.all_preceding.length.should == 2
656
+ @document.versions.all_preceding.class.should == Array
657
+ @document.versions.all_preceding.should include(@first_version)
658
+ @document.versions.all_preceding.should include(@second_version)
659
+ @document.versions.all_preceding.should_not include(@document)
660
+ end
661
+
662
+ end
663
+
664
+ describe "Non-head version of document" do
665
+ before(:each) do
666
+ @store = setup_default_store
667
+ @document = Document.create!
668
+ @document.new_slot = 1
669
+ @document.save!
670
+ @non_head_document = @document.versions.previous
671
+ end
672
+
673
+ it "should be able to access head version" do
674
+ @non_head_document.versions.head.should == @document
675
+ end
676
+
677
+ it "should not be deletable" do
678
+ lambda { @non_head_document.delete! }.should raise_error(DocumentDeletionError)
679
+ end
680
+
681
+ end
682
+
683
+
684
+
685
+
686
+ describe "Document with single meta" do
687
+
688
+ before(:each) do
689
+ @store = setup_default_store
690
+ setup_default_store
691
+ setup_index
692
+ @meta = Document.create!(@store)
693
+ @document = Document.create!(@store, :meta => @meta)
694
+ end
695
+
696
+ it "but specified within array should return single meta which should be mutable" do
697
+ @document = Document.create!(@store, :meta => [@meta])
698
+ @document.meta.should == @meta
699
+ @document.meta.should be_mutable
700
+ end
701
+
702
+ it "should return single meta which should be mutable" do
703
+ @document.meta.should == @meta
704
+ @document.meta.should be_mutable
705
+ end
706
+
707
+ end
708
+
709
+
710
+
711
+ describe "Document with multiple metas" do
712
+
713
+ before(:each) do
714
+ @store = setup_default_store
715
+ setup_index
716
+ @metas = []
717
+ 3.times do |i|
718
+ @metas << Document.create!(:a => i, i => i, :name => i.to_s)
719
+ end
720
+ Object.send!(:remove_const,'SomeMeta') if defined?(SomeMeta)
721
+ SomeMeta = Meta.new do
722
+ on_initialization do |doc|
723
+ doc.hello = 'world'
724
+ end
725
+ end
726
+
727
+ @document = Document.new(:meta => @metas)
728
+ end
729
+
730
+ it "should return single merged meta" do
731
+ meta = @document.meta
732
+ meta.should be_a_kind_of(Document)
733
+ meta[:a].should == 2
734
+ meta[0].should == 0
735
+ meta[1].should == 1
736
+ meta[2].should == 2
737
+ meta.name.should == "0,1,2"
738
+ @document[:meta].should be_a_kind_of(Array)
739
+ end
740
+
741
+ it "should make single merged meta immutable" do
742
+ meta = @document.meta
743
+ meta.should_not be_mutable
744
+ end
745
+
746
+ it "should be able to return metas collection" do
747
+ @document.metas.should be_a_kind_of(Array)
748
+ end
749
+
750
+ it "should be able to add meta by pushing its document to metas" do
751
+ @document.metas << SomeMeta.document
752
+ @document.hello.should == 'world'
753
+ @document.metas.should include(SomeMeta.document)
754
+ @document.should be_a_kind_of(SomeMeta)
755
+ end
756
+
757
+ it "should be able to add meta by pushing its module to metas" do
758
+ @document.metas << SomeMeta
759
+ @document.hello.should == 'world'
760
+ @document.metas.should include(SomeMeta.document)
761
+ @document.should be_a_kind_of(SomeMeta)
762
+ end
763
+
764
+ it "should be able to remove meta by removing its document from metas" do
765
+ @document.metas << SomeMeta.document
766
+ @document.metas.delete SomeMeta.document
767
+ @document.metas.should_not include(SomeMeta.document)
768
+ @document.should_not be_a_kind_of(SomeMeta)
769
+ end
770
+
771
+ it "should be able to remove meta by removing its module from metas" do
772
+ @document.metas << SomeMeta
773
+ @document.metas.delete SomeMeta
774
+ @document.metas.should_not include(SomeMeta.document)
775
+ @document.should_not be_a_kind_of(SomeMeta)
776
+ end
777
+
778
+
779
+ it "should raise ArgumentError when pushing neither document nor module" do
780
+ lambda { @document.metas << 1 }.should raise_error(ArgumentError)
781
+ end
782
+ end
783
+
784
+
785
+ describe "Document initialization with store omitted", :shared => true do
786
+
787
+ it "should raise an exception if no default store available" do
788
+ StrokeDB.stub!(:default_store).and_return(nil)
789
+ lambda { Document.new(*@args) }.should raise_error(NoDefaultStoreError)
790
+ end
791
+
792
+ it "should use default store if available" do
793
+ StrokeDB.stub!(:default_store).and_return(mock("Store"))
794
+ doc = Document.new(*@args)
795
+ doc.store.should == StrokeDB.default_store
796
+ end
797
+
798
+ end
799
+
800
+ describe "Document initialization with store omitted but with some slots specified" do
801
+
802
+ before(:each) do
803
+ @args = [{:slot1 => 1}]
804
+ end
805
+
806
+ it_should_behave_like "Document initialization with store omitted"
807
+
808
+ end
809
+
810
+ describe "Document initialization with store omitted but with no slots specified" do
811
+
812
+ before(:each) do
813
+ @args = []
814
+ end
815
+
816
+ it_should_behave_like "Document initialization with store omitted"
817
+
818
+ end
819
+
820
+ describe "Document with version" do
821
+
822
+ before(:each) do
823
+ setup_default_store
824
+ @document = Document.new(:some_data => 1)
825
+ end
826
+
827
+ it "should be equal to another document with the same version and uuid" do
828
+ @another_document = Document.new(:some_data => 1, :uuid => @document.uuid)
829
+ @another_document.version = @document.version
830
+ @document.should == @another_document
831
+ @document.should be_eql(@another_document)
832
+ end
833
+
834
+ it "should not be equal to another document with the same version but another uuid" do
835
+ @another_document = Document.new(:some_data => 1)
836
+ @another_document.version = @document.version
837
+ @document.should_not == @another_document
838
+ end
839
+
840
+ end
841
+
842
+ describe "Immutable Document" do
843
+
844
+ before(:each) do
845
+ setup_default_store
846
+ @document = Document.new(:some_data => 1).make_immutable!
847
+ @document.should_not be_mutable
848
+ end
849
+
850
+ it "should be able to be mutable again" do
851
+ @document.make_mutable!
852
+ @document.should be_mutable
853
+ end
854
+
855
+
856
+
857
+ end
858
+
859
+ describe "Valid Document's JSON" do
860
+
861
+ before(:each) do
862
+ @store = mock("Store")
863
+ @document = Document.new(@store,:slot1 => "val1", :slot2 => "val2")
864
+ @json = @document.to_raw.to_json
865
+ @decoded_json = JSON.parse(@json)
866
+ end
867
+
868
+ it "should be loadable into Document" do
869
+ doc = Document.from_raw(@store,@decoded_json)
870
+ doc.uuid.should == @document.uuid
871
+ doc.slotnames.to_set.should == ['slot1','slot2','version','uuid'].to_set
872
+ end
873
+
874
+ it "should reuse cached previous version at first modification" do
875
+ doc = Document.from_raw(@store,@decoded_json)
876
+ doc[:hello] = 'world'
877
+ doc[:hello] = 'world!'
878
+ doc[:previous_version].should == @document.version
879
+ end
880
+
881
+
882
+ end
883
+
884
+ describe "Valid Document's JSON with meta name specified" do
885
+
886
+ before(:each) do
887
+ @store = setup_default_store
888
+ Object.send!(:remove_const,'SomeDocument') if defined?(SomeDocument)
889
+ SomeDocument = Meta.new
890
+ @document = SomeDocument.new(@store,:slot1 => "val1", :slot2 => "val2")
891
+ @json = @document.to_raw.to_json
892
+ @decoded_json = JSON.parse(@json)
893
+ end
894
+
895
+ it "should load meta's module if it is available" do
896
+ doc = Document.from_raw(@store,@decoded_json)
897
+ doc.should be_a_kind_of(SomeDocument)
898
+ end
899
+
900
+ it "should not load meta's module if it is not available" do
901
+ lambda do
902
+ doc = Document.from_raw(@store,@decoded_json)
903
+ end.should_not raise_error
904
+ end
905
+
906
+
907
+ end
908
+
909
+ describe "Valid Document's JSON with multiple meta names specified" do
910
+
911
+ before(:each) do
912
+ @store = setup_default_store
913
+ @metas = []
914
+ 3.times do |i|
915
+ @metas << Meta.new(:name => "SomeDocument#{i}")
916
+ end
917
+ @document = @metas.inject{|a,b| a+=b}.new(@store,:slot1 => "val1", :slot2 => "val2", :meta => @metas)
918
+ @json = @document.to_raw.to_json
919
+ @decoded_json = JSON.parse(@json)
920
+ end
921
+
922
+ it "should load all available meta modules" do
923
+ Object.send!(:remove_const,'SomeDocument0') if defined?(SomeDocument0)
924
+ SomeDocument0 = Meta.new
925
+ Object.send!(:remove_const,'SomeDocument2') if defined?(SomeDocument2)
926
+ SomeDocument2 = Meta.new
927
+ doc = Document.from_raw(@store,@decoded_json)
928
+ doc.should be_a_kind_of(SomeDocument0)
929
+ doc.should be_a_kind_of(SomeDocument2)
930
+ end
931
+
932
+ it "should call all on_initialization callbacks for all available meta modules" do
933
+ Object.send!(:remove_const,'SomeDocument0') if defined?(SomeDocument0)
934
+ SomeDocument0 = Meta.new do
935
+ on_initialization do |doc|
936
+ Kernel.send(:callback_0_called)
937
+ end
938
+ end
939
+ Object.send!(:remove_const,'SomeDocument2') if defined?(SomeDocument2)
940
+ SomeDocument2 = Meta.new do
941
+ on_initialization do |doc|
942
+ Kernel.send(:callback_2_called)
943
+ end
944
+ end
945
+ Kernel.should_receive(:callback_0_called)
946
+ Kernel.should_receive(:callback_2_called)
947
+ doc = Document.from_raw(@store,@decoded_json)
948
+ end
949
+ end
950
+
951
+ describe "Composite document ( result of Document#+(document) )" do
952
+
953
+ before(:each) do
954
+ setup_default_store
955
+ @document1 = Document.new :slot1 => 1, :x => 1
956
+ @document2 = Document.new :slot1 => 2, :slot2 => 2
957
+ @composite = @document1+@document2
958
+ end
959
+
960
+ it "should be a Document" do
961
+ @composite.should be_a_kind_of(Document)
962
+ end
963
+
964
+ it "should have new UUID" do
965
+ @composite.uuid.should match(UUID_RE)
966
+ @composite.uuid.should_not == @document1.uuid
967
+ @composite.uuid.should_not == @document2.uuid
968
+ end
969
+
970
+ it "should have new version" do
971
+ @composite.version.should_not == @document1.version
972
+ @composite.version.should_not == @document2.version
973
+ end
974
+
975
+ it "should update identical slots" do
976
+ @composite.slot1.should == 2
977
+ end
978
+
979
+ it "should add different slots" do
980
+ @composite.slot2.should == 2
981
+ end
982
+
983
+ it "should not remove missing slots" do
984
+ @composite.x.should == 1
985
+ end
986
+
987
+ end
988
+
989
+ describe "Saved document with validations" do
990
+
991
+ before(:each) do
992
+ Object.send!(:remove_const,'Foo') if defined?(Foo)
993
+ setup_default_store
994
+ end
995
+
996
+ it "should be deletable with validates_presence_of" do
997
+ Foo = Meta.new { validates_presence_of :name }
998
+ doc = Foo.create! :name => 'foo'
999
+ doc.delete!
1000
+ end
1001
+
1002
+ it "should be deletable with validates_type_of" do
1003
+ Foo = Meta.new { validates_type_of :name, :as => 'String' }
1004
+ doc = Foo.create! :name => 'foo'
1005
+ doc.delete!
1006
+ end
1007
+
1008
+ it "should be deletable with validates_uniqueness_of" do
1009
+ Foo = Meta.new { validates_uniqueness_of :name }
1010
+ doc = Foo.create! :name => 'foo'
1011
+ doc.delete!
1012
+ end
1013
+
1014
+ it "should be deletable with validates_inclusion_of" do
1015
+ Foo = Meta.new { validates_inclusion_of :name, :in => ['foo','bar'] }
1016
+ doc = Foo.create! :name => 'foo'
1017
+ doc.delete!
1018
+ end
1019
+
1020
+ it "should be deletable with validates_exclusion_of" do
1021
+ Foo = Meta.new { validates_exclusion_of :name, :in => ['foo','bar'] }
1022
+ doc = Foo.create! :name => 'ueep'
1023
+ doc.delete!
1024
+ end
1025
+
1026
+ it "should be deletable with validates_numericality_of" do
1027
+ Foo = Meta.new { validates_numericality_of :number }
1028
+ doc = Foo.create! :number => '1'
1029
+ doc.delete!
1030
+ end
1031
+
1032
+ it "should be deletable with validates_format_of" do
1033
+ Foo = Meta.new { validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }
1034
+ doc = Foo.create! :email => 'foo@bar.org'
1035
+ doc.delete!
1036
+ end
1037
+
1038
+ it "should be deletable with validates_confirmation_of" do
1039
+ Foo = Meta.new { validates_confirmation_of :password }
1040
+ doc = Foo.create! :password => "sekret", :password_confirmation => "sekret"
1041
+ doc.delete!
1042
+ end
1043
+
1044
+ it "should be deletable with validates_acceptance_of" do
1045
+ Foo = Meta.new { validates_acceptance_of :eula, :accept => "yep" }
1046
+ doc = Foo.create! :eula => "yep"
1047
+ doc.delete!
1048
+ end
1049
+
1050
+ it "should be deletable with validates_length_of" do
1051
+ Foo = Meta.new { validates_length_of :name, :within => 10..50 }
1052
+ doc = Foo.create! :name => "supercalifragilistico"
1053
+ doc.delete!
1054
+ end
1055
+
1056
+ it "should be deletable with validates_associated" do
1057
+ Foo = Meta.new { has_many :bars; validates_associated :bars }
1058
+ Bar = Meta.new
1059
+ doc = Foo.create!
1060
+ doc.delete!
1061
+ end
1062
+
1063
+ end