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.
- data/README +18 -20
- data/bench.html +4001 -0
- data/bin/strokedb +14 -0
- data/examples/movies.rb +105 -0
- data/examples/movies2.rb +97 -0
- data/examples/strokewiki/README +28 -0
- data/examples/strokewiki/view/edit.xhtml +27 -0
- data/examples/strokewiki/view/new.xhtml +26 -0
- data/examples/strokewiki/view/pages.xhtml +27 -0
- data/examples/strokewiki/view/show.xhtml +40 -0
- data/examples/strokewiki/view/versions.xhtml +25 -0
- data/examples/strokewiki/wiki.rb +106 -0
- data/examples/todo.rb +92 -0
- data/lib/strokedb.rb +85 -0
- data/lib/{config → strokedb}/config.rb +14 -9
- data/lib/strokedb/console.rb +87 -0
- data/lib/strokedb/core_ext.rb +10 -0
- data/lib/{util/ext → strokedb/core_ext}/blank.rb +1 -1
- data/lib/{util/ext → strokedb/core_ext}/enumerable.rb +0 -0
- data/lib/{util/ext → strokedb/core_ext}/fixnum.rb +0 -0
- data/lib/strokedb/core_ext/float.rb +4 -0
- data/lib/{util/ext → strokedb/core_ext}/hash.rb +0 -0
- data/lib/strokedb/core_ext/infinity.rb +33 -0
- data/lib/strokedb/core_ext/kernel.rb +41 -0
- data/lib/strokedb/core_ext/object.rb +16 -0
- data/lib/{util/ext → strokedb/core_ext}/string.rb +28 -1
- data/lib/strokedb/core_ext/symbol.rb +13 -0
- data/lib/strokedb/data_structures.rb +5 -0
- data/lib/strokedb/data_structures/chunked_skiplist.rb +123 -0
- data/lib/{data_structures → strokedb/data_structures}/inverted_list.rb +0 -0
- data/lib/{data_structures → strokedb/data_structures}/point_query.rb +0 -0
- data/lib/strokedb/data_structures/simple_skiplist.rb +350 -0
- data/lib/{data_structures → strokedb/data_structures}/skiplist.rb +1 -1
- data/lib/{document → strokedb}/document.rb +180 -71
- data/lib/{document → strokedb/document}/callback.rb +0 -0
- data/lib/{document → strokedb/document}/delete.rb +2 -2
- data/lib/strokedb/document/dsl.rb +4 -0
- data/lib/{document → strokedb/document/dsl}/associations.rb +0 -0
- data/lib/{document → strokedb/document/dsl}/coercions.rb +0 -0
- data/lib/strokedb/document/dsl/meta_dsl.rb +7 -0
- data/lib/{document → strokedb/document/dsl}/validations.rb +26 -21
- data/lib/{document → strokedb/document/dsl}/virtualize.rb +0 -0
- data/lib/{document → strokedb/document}/meta.rb +92 -29
- data/lib/{document → strokedb/document}/slot.rb +17 -5
- data/lib/{document → strokedb/document}/util.rb +0 -0
- data/lib/{document → strokedb/document}/versions.rb +2 -2
- data/lib/strokedb/index.rb +2 -0
- data/lib/strokedb/nsurl.rb +24 -0
- data/lib/strokedb/store.rb +149 -0
- data/lib/strokedb/stores.rb +6 -0
- data/lib/{stores → strokedb/stores}/chainable_storage.rb +20 -14
- data/lib/strokedb/stores/file_storage.rb +118 -0
- data/lib/{stores/inverted_list_index → strokedb/stores}/inverted_list_file_storage.rb +50 -0
- data/lib/strokedb/stores/memory_storage.rb +80 -0
- data/lib/{stores → strokedb/stores}/remote_store.rb +10 -4
- data/lib/strokedb/sync.rb +4 -0
- data/lib/{sync → strokedb/sync}/chain_sync.rb +0 -0
- data/lib/{sync → strokedb/sync}/diff.rb +12 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/array.rb +1 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/default.rb +0 -0
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/hash.rb +1 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/string.rb +1 -1
- data/lib/{sync → strokedb/sync}/lamport_timestamp.rb +0 -0
- data/lib/{sync → strokedb/sync}/store_sync.rb +15 -7
- data/lib/strokedb/transaction.rb +78 -0
- data/lib/{util → strokedb}/util.rb +14 -7
- data/lib/strokedb/util/attach_dsl.rb +29 -0
- data/lib/{util → strokedb/util}/blankslate.rb +0 -0
- data/lib/strokedb/util/class_optimization.rb +93 -0
- data/lib/{util → strokedb/util}/inflect.rb +0 -0
- data/lib/strokedb/util/java_util.rb +13 -0
- data/lib/{util → strokedb/util}/lazy_array.rb +0 -0
- data/lib/{util → strokedb/util}/lazy_mapping_array.rb +4 -0
- data/lib/{util → strokedb/util}/lazy_mapping_hash.rb +0 -0
- data/lib/{util → strokedb/util}/serialization.rb +21 -0
- data/lib/strokedb/util/uuid.rb +159 -0
- data/lib/{util → strokedb/util}/xml.rb +0 -0
- data/lib/{view → strokedb}/view.rb +2 -2
- data/lib/strokedb/volumes.rb +5 -0
- data/lib/strokedb/volumes/archive_volume.rb +165 -0
- data/lib/strokedb/volumes/block_volume.rb +169 -0
- data/lib/strokedb/volumes/distributed_pointer.rb +43 -0
- data/lib/strokedb/volumes/fixed_length_skiplist_volume.rb +109 -0
- data/lib/strokedb/volumes/map_volume.rb +268 -0
- data/meta/MANIFEST +175 -0
- data/script/console +2 -70
- data/spec/integration/remote_store_spec.rb +70 -0
- data/spec/integration/search_spec.rb +76 -0
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/lib/spec_helper.rb +1 -0
- data/spec/lib/strokedb/config_spec.rb +250 -0
- data/spec/lib/strokedb/core_ext/blank_spec.rb +20 -0
- data/spec/lib/strokedb/core_ext/extract_spec.rb +42 -0
- data/spec/lib/strokedb/core_ext/float_spec.rb +62 -0
- data/spec/lib/strokedb/core_ext/infinity_spec.rb +40 -0
- data/spec/lib/strokedb/core_ext/spec_helper.rb +1 -0
- data/spec/lib/strokedb/core_ext/string_spec.rb +25 -0
- data/spec/lib/strokedb/core_ext/symbol_spec.rb +8 -0
- data/spec/lib/strokedb/data_structures/chunked_skiplist_spec.rb +144 -0
- data/spec/lib/strokedb/data_structures/inverted_list_spec.rb +172 -0
- data/spec/lib/strokedb/data_structures/simple_skiplist_spec.rb +200 -0
- data/spec/lib/strokedb/data_structures/skiplist_spec.rb +253 -0
- data/spec/lib/strokedb/data_structures/spec_helper.rb +1 -0
- data/spec/lib/strokedb/document/associations_spec.rb +319 -0
- data/spec/lib/strokedb/document/callbacks_spec.rb +134 -0
- data/spec/lib/strokedb/document/coercions_spec.rb +110 -0
- data/spec/lib/strokedb/document/document_spec.rb +1063 -0
- data/spec/lib/strokedb/document/meta_meta_spec.rb +30 -0
- data/spec/lib/strokedb/document/meta_spec.rb +435 -0
- data/spec/lib/strokedb/document/metaslot_spec.rb +43 -0
- data/spec/lib/strokedb/document/slot_spec.rb +130 -0
- data/spec/lib/strokedb/document/spec_helper.rb +1 -0
- data/spec/lib/strokedb/document/validations_spec.rb +1081 -0
- data/spec/lib/strokedb/document/virtualize_spec.rb +80 -0
- data/spec/lib/strokedb/nsurl_spec.rb +73 -0
- data/spec/lib/strokedb/spec_helper.rb +1 -0
- data/spec/lib/strokedb/stores/chained_storages_spec.rb +116 -0
- data/spec/lib/strokedb/stores/spec_helper.rb +1 -0
- data/spec/lib/strokedb/stores/store_spec.rb +201 -0
- data/spec/lib/strokedb/stores/transaction_spec.rb +107 -0
- data/spec/lib/strokedb/sync/chain_sync_spec.rb +43 -0
- data/spec/lib/strokedb/sync/diff_spec.rb +111 -0
- data/spec/lib/strokedb/sync/lamport_timestamp_spec.rb +174 -0
- data/spec/lib/strokedb/sync/slot_diff_spec.rb +164 -0
- data/spec/lib/strokedb/sync/spec_helper.rb +1 -0
- data/spec/lib/strokedb/sync/store_sync_spec.rb +181 -0
- data/spec/lib/strokedb/sync/stroke_diff/array_spec.rb +97 -0
- data/spec/lib/strokedb/sync/stroke_diff/complex_spec.rb +58 -0
- data/spec/lib/strokedb/sync/stroke_diff/hash_spec.rb +144 -0
- data/spec/lib/strokedb/sync/stroke_diff/scalar_spec.rb +23 -0
- data/spec/lib/strokedb/sync/stroke_diff/spec_helper.rb +25 -0
- data/spec/lib/strokedb/sync/stroke_diff/string_spec.rb +61 -0
- data/spec/lib/strokedb/util/attach_dsl_spec.rb +45 -0
- data/spec/lib/strokedb/util/inflect_spec.rb +14 -0
- data/spec/lib/strokedb/util/lazy_array_spec.rb +157 -0
- data/spec/lib/strokedb/util/lazy_mapping_array_spec.rb +174 -0
- data/spec/lib/strokedb/util/lazy_mapping_hash_spec.rb +92 -0
- data/spec/lib/strokedb/util/spec_helper.rb +1 -0
- data/spec/lib/strokedb/util/uuid_spec.rb +46 -0
- data/spec/lib/strokedb/view_spec.rb +228 -0
- data/spec/lib/strokedb/volumes/archive_volume_spec.rb +105 -0
- data/spec/lib/strokedb/volumes/block_volume_spec.rb +100 -0
- data/spec/lib/strokedb/volumes/distributed_pointer_spec.rb +14 -0
- data/spec/lib/strokedb/volumes/fixed_length_skiplist_volume_spec.rb +177 -0
- data/spec/lib/strokedb/volumes/map_volume_spec.rb +172 -0
- data/spec/lib/strokedb/volumes/spec_helper.rb +1 -0
- data/spec/regression/docref_spec.rb +94 -0
- data/spec/regression/meta_spec.rb +23 -0
- data/spec/regression/spec_helper.rb +1 -0
- data/spec/regression/sync_spec.rb +36 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/temp/storages/TIMESTAMP +1 -0
- data/spec/temp/storages/UUID +1 -0
- data/spec/temp/storages/database-sync/TIMESTAMP +1 -0
- data/spec/temp/storages/database-sync/UUID +1 -0
- data/spec/temp/storages/database-sync/config +1 -0
- data/spec/temp/storages/database-sync/file/LAST +1 -0
- data/spec/temp/storages/database-sync/file/bd/f6/bdf675e5-8a7b-494e-97f2-f74a14ccd95d.av +0 -0
- data/spec/temp/storages/database-sync/file/uindex.wal +0 -0
- data/spec/temp/storages/database-sync/inverted_list_file/INVERTED_INDEX +1 -0
- data/spec/temp/storages/inverted_list_storage/INVERTED_INDEX +0 -0
- data/strokedb.gemspec +120 -0
- data/task/benchmark.task +9 -0
- data/task/ditz.task +30 -0
- data/task/echoe.rb +17 -0
- data/task/rcov.task +50 -0
- data/task/rdoc.task +10 -0
- data/task/rspec.task +0 -0
- data/vendor/java_inline.rb +106 -0
- data/vendor/rbmodexcl/mrimodexcl.rb +82 -0
- data/vendor/rbmodexcl/rbmodexcl.rb +5 -0
- data/vendor/rbmodexcl/rbxmodexcl.rb +48 -0
- data/vendor/rbmodexcl/spec/unextend_spec.rb +50 -0
- data/vendor/rbmodexcl/spec/uninclude_spec.rb +26 -0
- metadata +271 -79
- data/CONTRIBUTORS +0 -7
- data/CREDITS +0 -13
- data/bin/sdbc +0 -2
- data/lib/init.rb +0 -57
- data/lib/stores/inverted_list_index/inverted_list_index.rb +0 -49
- data/lib/stores/skiplist_store/chunk.rb +0 -119
- data/lib/stores/skiplist_store/chunk_storage.rb +0 -21
- data/lib/stores/skiplist_store/file_chunk_storage.rb +0 -44
- data/lib/stores/skiplist_store/memory_chunk_storage.rb +0 -37
- data/lib/stores/skiplist_store/skiplist_store.rb +0 -217
- data/lib/stores/store.rb +0 -5
- data/lib/sync/stroke_diff/stroke_diff.rb +0 -9
- data/lib/util/ext/object.rb +0 -8
- data/lib/util/java_util.rb +0 -9
- data/lib/util/trigger_partition.rb +0 -136
- 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
|