relaxdb 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.textile +200 -0
- data/Rakefile +63 -0
- data/docs/spec_results.html +1059 -0
- data/lib/more/atomic_bulk_save_support.rb +18 -0
- data/lib/more/grapher.rb +48 -0
- data/lib/relaxdb.rb +50 -0
- data/lib/relaxdb/all_delegator.rb +44 -0
- data/lib/relaxdb/belongs_to_proxy.rb +29 -0
- data/lib/relaxdb/design_doc.rb +57 -0
- data/lib/relaxdb/document.rb +600 -0
- data/lib/relaxdb/extlib.rb +24 -0
- data/lib/relaxdb/has_many_proxy.rb +101 -0
- data/lib/relaxdb/has_one_proxy.rb +42 -0
- data/lib/relaxdb/migration.rb +40 -0
- data/lib/relaxdb/migration_version.rb +21 -0
- data/lib/relaxdb/net_http_server.rb +61 -0
- data/lib/relaxdb/paginate_params.rb +53 -0
- data/lib/relaxdb/paginator.rb +88 -0
- data/lib/relaxdb/query.rb +76 -0
- data/lib/relaxdb/references_many_proxy.rb +97 -0
- data/lib/relaxdb/relaxdb.rb +250 -0
- data/lib/relaxdb/server.rb +109 -0
- data/lib/relaxdb/taf2_curb_server.rb +63 -0
- data/lib/relaxdb/uuid_generator.rb +21 -0
- data/lib/relaxdb/validators.rb +11 -0
- data/lib/relaxdb/view_object.rb +34 -0
- data/lib/relaxdb/view_result.rb +18 -0
- data/lib/relaxdb/view_uploader.rb +49 -0
- data/lib/relaxdb/views.rb +114 -0
- data/readme.rb +80 -0
- data/spec/belongs_to_spec.rb +124 -0
- data/spec/callbacks_spec.rb +80 -0
- data/spec/derived_properties_spec.rb +112 -0
- data/spec/design_doc_spec.rb +34 -0
- data/spec/doc_inheritable_spec.rb +100 -0
- data/spec/document_spec.rb +545 -0
- data/spec/has_many_spec.rb +202 -0
- data/spec/has_one_spec.rb +123 -0
- data/spec/migration_spec.rb +97 -0
- data/spec/migration_version_spec.rb +28 -0
- data/spec/paginate_params_spec.rb +15 -0
- data/spec/paginate_spec.rb +360 -0
- data/spec/query_spec.rb +90 -0
- data/spec/references_many_spec.rb +173 -0
- data/spec/relaxdb_spec.rb +364 -0
- data/spec/server_spec.rb +32 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +65 -0
- data/spec/spec_models.rb +199 -0
- data/spec/view_by_spec.rb +76 -0
- data/spec/view_object_spec.rb +47 -0
- data/spec/view_spec.rb +23 -0
- metadata +137 -0
@@ -0,0 +1,202 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe RelaxDB::HasManyProxy do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
setup_test_db
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "has_many" do
|
11
|
+
|
12
|
+
|
13
|
+
describe "target_class in the generated view" do
|
14
|
+
it "should infer the class name from the relationship if not supplied" do
|
15
|
+
view = mock(:view).as_null_object
|
16
|
+
RelaxDB::ViewCreator.should_receive(:has_n).with(
|
17
|
+
"", # client_class
|
18
|
+
:foo_bars, # relationship
|
19
|
+
"FooBar", # target_class
|
20
|
+
"" # relationship_to_client
|
21
|
+
).and_return view
|
22
|
+
klass = Class.new(RelaxDB::Document) do
|
23
|
+
has_many :foo_bars
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should use the class name if supplied" do
|
28
|
+
view = mock(:view).as_null_object
|
29
|
+
RelaxDB::ViewCreator.should_receive(:has_n).with(
|
30
|
+
"", # client_class
|
31
|
+
:foo_bars, # relationship
|
32
|
+
"Bar", # target_class
|
33
|
+
"" # relationship_to_client
|
34
|
+
).and_return view
|
35
|
+
klass = Class.new(RelaxDB::Document) do
|
36
|
+
has_many :foo_bars, :class => "Bar"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be considered enumerable" do
|
42
|
+
u = User.new.save
|
43
|
+
u.items.should be_a_kind_of(Enumerable)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should actually be enumerable" do
|
47
|
+
u = User.new.save
|
48
|
+
u.items << Item.new(:name => "a")
|
49
|
+
u.items << Item.new(:name => "b")
|
50
|
+
names = u.items.inject("") { |memo, i| memo << i.name }
|
51
|
+
names.should == "ab"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should preserve the collection across the load / save boundary" do
|
55
|
+
u = User.new.save
|
56
|
+
u.items << Item.new
|
57
|
+
u = RelaxDB.load u._id
|
58
|
+
u.items.size.should == 1
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should work with MultiWordClassNames" do
|
62
|
+
c = MultiWordChild.new
|
63
|
+
m = MultiWordClass.new.save
|
64
|
+
m.multi_word_children << c
|
65
|
+
m = RelaxDB.load m._id
|
66
|
+
m.multi_word_children[0].should == c
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#<<" do
|
70
|
+
|
71
|
+
it "should link the added item to the parent" do
|
72
|
+
u = User.new
|
73
|
+
u.items << Item.new
|
74
|
+
u.items[0].user.should == u
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should return self" do
|
78
|
+
u = User.new.save
|
79
|
+
u.items << Item.new << Item.new
|
80
|
+
u.items[0].user.should == u
|
81
|
+
u.items[1].user.should == u
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should not created duplicates when invoked with same object more than once" do
|
85
|
+
u = User.new.save
|
86
|
+
i = Item.new
|
87
|
+
u.items << i << i
|
88
|
+
u.items.size.should == 1
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should return false when the child fails validation" do
|
92
|
+
d = Dysfunctional.new
|
93
|
+
r = (d.failures << Failure.new)
|
94
|
+
r.should be_false
|
95
|
+
d.failures.should be_empty
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#=" do
|
101
|
+
|
102
|
+
before(:each) do
|
103
|
+
# Create the underlying views
|
104
|
+
User.new(:items => [], :invites_received => [], :invites_sent => [])
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should not attempt to save the child objects when the relationship is established" do
|
108
|
+
RelaxDB.db.put_count = 0
|
109
|
+
i1, i2 = Item.new(:name => "i1"), Item.new(:name => "i2")
|
110
|
+
User.new(:items => [i1, i2])
|
111
|
+
RelaxDB.db.put_count.should == 0
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should preserve given relationships across save/load boundary" do
|
115
|
+
i1, i2 = Item.new(:name => "i1"), Item.new(:name => "i2")
|
116
|
+
u = User.new(:items => [i1, i2])
|
117
|
+
RelaxDB.bulk_save u, *u.items
|
118
|
+
u = RelaxDB.load u._id
|
119
|
+
u.items.map { |i| i.name }.sort.join.should == "i1i2"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should invoke the derived properties writer" do
|
123
|
+
class HmsdParent < RelaxDB::Document
|
124
|
+
property :foo, :derived => [:zongs, lambda {|f, o| o.zongs.first.z / 2 }]
|
125
|
+
has_many :zongs, :class => "HmsdChild"
|
126
|
+
end
|
127
|
+
class HmsdChild < RelaxDB::Document
|
128
|
+
property :z
|
129
|
+
belongs_to :hmsd_parent
|
130
|
+
end
|
131
|
+
oz = HmsdChild.new(:z => 10)
|
132
|
+
op = HmsdParent.new(:zongs => [oz])
|
133
|
+
op.foo.should == 5
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
describe "#delete" do
|
139
|
+
|
140
|
+
it "should nullify the belongs_to relationship" do
|
141
|
+
u = User.new.save
|
142
|
+
i = Item.new
|
143
|
+
u.items << i
|
144
|
+
u.items.delete i
|
145
|
+
i.user.should be_nil
|
146
|
+
RelaxDB.load(i._id).user.should be_nil
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#clear" do
|
152
|
+
|
153
|
+
it "should result in an empty collection" do
|
154
|
+
u = User.new.save
|
155
|
+
u.items << Item.new << Item.new
|
156
|
+
u.items.clear
|
157
|
+
u.items.should be_empty
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should nullify all child relationships" do
|
161
|
+
u = User.new.save
|
162
|
+
i1, i2 = Item.new, Item.new
|
163
|
+
u.items << i1
|
164
|
+
u.items << i2
|
165
|
+
u.items.clear
|
166
|
+
|
167
|
+
i1.user.should be_nil
|
168
|
+
i2.user.should be_nil
|
169
|
+
RelaxDB.load(i1._id).user.should be_nil
|
170
|
+
RelaxDB.load(i2._id).user.should be_nil
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "owner" do
|
176
|
+
|
177
|
+
it "should be able to form multiple relationships with the same class of child" do
|
178
|
+
u1, u2 = User.new.save, User.new.save
|
179
|
+
i = Invite.new(:recipient => u2)
|
180
|
+
u1.invites_sent << Invite.new
|
181
|
+
RelaxDB.load(u1._id).invites_sent[0] == i
|
182
|
+
RelaxDB.load(u2._id).invites_received[0] == i
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "#destroy" do
|
186
|
+
|
187
|
+
it "should nullify its child relationships" do
|
188
|
+
Item.view_by :user_id
|
189
|
+
|
190
|
+
u = User.new.save
|
191
|
+
u.items << Item.new << Item.new
|
192
|
+
u.destroy!
|
193
|
+
Item.by_user_id(:key => u._id).should be_empty
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe RelaxDB::HasOneProxy do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
setup_test_db
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "has_one" do
|
11
|
+
|
12
|
+
it "should return nil when accessed before assignment" do
|
13
|
+
p = Photo.new
|
14
|
+
p.rating.should == nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be establishable via a constructor attribute" do
|
18
|
+
r = Rating.new
|
19
|
+
p = Photo.new :rating => r
|
20
|
+
p.rating.should == r
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be establishable via assignment" do
|
24
|
+
p = Photo.new
|
25
|
+
r = Rating.new
|
26
|
+
p.rating = r
|
27
|
+
p.rating.should == r
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return the same object on repeated invocations" do
|
31
|
+
p = Photo.new.save
|
32
|
+
p.rating = Rating.new
|
33
|
+
p = RelaxDB.load(p._id)
|
34
|
+
p.rating.object_id.should == p.rating.object_id
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should be preserved across load / save boundary" do
|
38
|
+
r = Rating.new
|
39
|
+
p = Photo.new(:rating => r).save
|
40
|
+
p = RelaxDB.load p._id
|
41
|
+
p.rating.should == r
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should be able reference itself via its child" do
|
45
|
+
r = Rating.new
|
46
|
+
p = Photo.new(:rating => r).save
|
47
|
+
p = RelaxDB.load p._id
|
48
|
+
p.rating.photo.should == p
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should work with MultiWordClassNames" do
|
52
|
+
c = MultiWordChild.new
|
53
|
+
m = MultiWordClass.new(:multi_word_child => c).save
|
54
|
+
m = RelaxDB.load m._id
|
55
|
+
m.multi_word_child.should == c
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#=" do
|
59
|
+
|
60
|
+
it "should create a reference from the child to the parent" do
|
61
|
+
p = Photo.new
|
62
|
+
r = Rating.new
|
63
|
+
p.rating = r
|
64
|
+
r.photo.should == p
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should save the assigned object" do
|
68
|
+
p = Photo.new
|
69
|
+
r = Rating.new
|
70
|
+
p.rating = r
|
71
|
+
r.should_not be_unsaved
|
72
|
+
end
|
73
|
+
|
74
|
+
it "will not save the parent" do
|
75
|
+
p = Photo.new
|
76
|
+
r = Rating.new
|
77
|
+
p.rating = r
|
78
|
+
p.should be_unsaved
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should set the target to nil when nil is assigned" do
|
82
|
+
p = Photo.new
|
83
|
+
p.rating = nil
|
84
|
+
p.rating.should be_nil
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should nullify any existing relationship in the database" do
|
88
|
+
p = Photo.new
|
89
|
+
r = Rating.new
|
90
|
+
p.rating = r
|
91
|
+
p.rating = nil
|
92
|
+
RelaxDB.load(r._id).photo.should be_nil
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should nullify any existing relationship on a known in-memory object" do
|
96
|
+
p = Photo.new
|
97
|
+
r = Rating.new
|
98
|
+
p.rating = r
|
99
|
+
p.rating = nil
|
100
|
+
r.photo.should be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it "will not nullify any existing relationship on unknown in-memory objects" do
|
104
|
+
p = Photo.new.save
|
105
|
+
r = Rating.new
|
106
|
+
p.rating = r
|
107
|
+
r_copy = RelaxDB.load(r._id)
|
108
|
+
p.rating = nil
|
109
|
+
r_copy.photo.should_not be_nil
|
110
|
+
end
|
111
|
+
|
112
|
+
it "will not throw an error when the rhs fails validation" do
|
113
|
+
d = Dysfunctional.new.save
|
114
|
+
f = Failure.new
|
115
|
+
d.failure = f
|
116
|
+
d.failure.should == f
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
require File.join(File.dirname(__FILE__), "spec_models")
|
3
|
+
|
4
|
+
describe RelaxDB::Migration do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@mig = RelaxDB::Migration
|
8
|
+
setup_test_db
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should yield each obj to the block and save the result" do
|
12
|
+
Primitives.new(:num => 5).save!
|
13
|
+
r = @mig.run Primitives do |p|
|
14
|
+
pn = Primitives.new(:num => p.num * 2)
|
15
|
+
[p, pn]
|
16
|
+
end
|
17
|
+
Primitives.by_num.map { |p| p.num}.should == [5, 10]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise an exception if a save results in a conflict" do
|
21
|
+
op = Primitives.new.save!
|
22
|
+
lambda do
|
23
|
+
@mig.run Primitives do |p|
|
24
|
+
op.save!
|
25
|
+
p
|
26
|
+
end
|
27
|
+
end.should raise_error(RelaxDB::UpdateConflict)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not save docs for blocks that return nil" do
|
31
|
+
Primitives.new.save!
|
32
|
+
@mig.run Primitives do |p|
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "multiple docs" do
|
38
|
+
|
39
|
+
before(:each) do
|
40
|
+
ps = (1..5).map { |i| Primitives.new :num => i }
|
41
|
+
RelaxDB.bulk_save *ps
|
42
|
+
RelaxDB.db.reset_req_count
|
43
|
+
end
|
44
|
+
|
45
|
+
# Note: two extra requests per paginate_view request are required
|
46
|
+
it "should operate on a doc set of the given size aka limit" do
|
47
|
+
@mig.run(Primitives, 1) { |p| p.num *= p.num; p }
|
48
|
+
RelaxDB.db.req_count.should == 10 + 5 * 2
|
49
|
+
Primitives.by_num.map { |p| p.num }.should == [1, 4, 9, 16, 25]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should operate on a doc set of default size" do
|
53
|
+
@mig.run(Primitives) { |p| p.num *= p.num; p }
|
54
|
+
RelaxDB.db.req_count.should == 2 + 1 * 2
|
55
|
+
Primitives.by_num.map { |p| p.num }.should == [1, 4, 9, 16, 25]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#fv" do
|
61
|
+
it "should return valid numbers" do
|
62
|
+
@mig.fv("foo/bar/001_foo.rb").should == 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".run_all" do
|
67
|
+
|
68
|
+
it "should save the version after each successful migration" do
|
69
|
+
@mig.run_all "a/b/1_", lambda {}
|
70
|
+
RelaxDB::MigrationVersion.version.should == 1
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not run those migrations whose version is less than the current version" do
|
74
|
+
v = RelaxDB::MigrationVersion.retrieve
|
75
|
+
v.version = 2
|
76
|
+
v.save!
|
77
|
+
@mig.run_all "3_", lambda {}
|
78
|
+
RelaxDB::MigrationVersion.version.should == 3
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should run those migrations whose version is greater than the current version" do
|
82
|
+
v = RelaxDB::MigrationVersion.retrieve
|
83
|
+
v.version = 2
|
84
|
+
v.save!
|
85
|
+
@mig.run_all "1_foo", lambda {}
|
86
|
+
RelaxDB::MigrationVersion.version.should == 2
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should raise an exception on failure" do
|
90
|
+
lambda do
|
91
|
+
@mig.run_all "1_foo", lambda { raise "Expected" }
|
92
|
+
end.should raise_error(RuntimeError, "Expected")
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
require File.join(File.dirname(__FILE__), "spec_models")
|
3
|
+
|
4
|
+
describe RelaxDB::MigrationVersion do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
setup_test_db
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should not exist in a clean db" do
|
11
|
+
RelaxDB.load(RelaxDB::MigrationVersion::DOC_ID).should be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return zero when it doesnt exist" do
|
15
|
+
RelaxDB::MigrationVersion.version.should == 0
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should autosave on retrieval when when it doesnt exist" do
|
19
|
+
RelaxDB::MigrationVersion.version
|
20
|
+
RelaxDB.load(RelaxDB::MigrationVersion::DOC_ID).should be
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return the saved version" do
|
24
|
+
RelaxDB::MigrationVersion.update 10
|
25
|
+
RelaxDB::MigrationVersion.version.should == 10
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|