cohitre-relaxdb 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ # Experimental only for now
4
+
5
+ class Tree < RelaxDB::Document
6
+ property :name
7
+ property :climate
8
+ has_one :leaf
9
+ end
10
+
11
+ class Leaf < RelaxDB::Document
12
+ belongs_to :tree, :denormalise => [:name]
13
+ end
14
+
15
+ describe RelaxDB::Document, "denormalisation" do
16
+
17
+ before(:all) do
18
+ RelaxDB.configure(:host => "localhost", :port => 5984)
19
+ end
20
+
21
+ before(:each) do
22
+ RelaxDB.delete_db "relaxdb_spec_db" rescue "ok"
23
+ RelaxDB.use_db "relaxdb_spec_db"
24
+ end
25
+
26
+ describe "belongs_to" do
27
+
28
+ it "should store denormalised options in its json representation" do
29
+ tree = Tree.new(:name => "sapling").save
30
+ leaf = Leaf.new(:tree => tree)
31
+ obj = JSON.parse(leaf.to_json)
32
+ obj["tree_name"].should == "sapling"
33
+ end
34
+
35
+ it "should ignore denormalised options for nil properties" do
36
+ Leaf.new.to_json
37
+ end
38
+
39
+ it "should not interfere with normal belongs_to behaviour" do
40
+ tree = Tree.new(:name => "sapling", :climate => "tropical").save
41
+ leaf = Leaf.new(:tree => tree).save
42
+ leaf = RelaxDB.load(leaf._id)
43
+ leaf.tree.name.should == "sapling"
44
+ leaf.tree.climate.should == "tropical"
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/spec_models.rb'
3
+
4
+ describe RelaxDB::DesignDocument do
5
+
6
+ before(:all) do
7
+ RelaxDB.configure(:host => "localhost", :port => 5984)
8
+ end
9
+
10
+ before(:each) do
11
+ RelaxDB.delete_db "relaxdb_spec_db" rescue "ok"
12
+ RelaxDB.use_db "relaxdb_spec_db"
13
+ end
14
+
15
+ describe "#save" do
16
+
17
+ it "should create a corresponding document in CouchDB" do
18
+ RelaxDB::DesignDocument.get("foo").save
19
+ RelaxDB.load("_design%2Ffoo").should_not be_nil
20
+ end
21
+
22
+ end
23
+
24
+ describe "#destroy" do
25
+
26
+ it "should delete the corresponding document from CouchDB" do
27
+ dd = RelaxDB::DesignDocument.get("foo").save
28
+ dd.destroy!
29
+ lambda { RelaxDB.load("_design%2Ffoo") }.should raise_error
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,364 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/spec_models.rb'
3
+
4
+ describe RelaxDB::Document do
5
+
6
+ before(:all) do
7
+ RelaxDB.configure(:host => "localhost", :port => 5984)
8
+ end
9
+
10
+ before(:each) do
11
+ RelaxDB.delete_db "relaxdb_spec_db" rescue "ok"
12
+ RelaxDB.use_db "relaxdb_spec_db"
13
+ end
14
+
15
+ describe ".new" do
16
+
17
+ it "should create an object with an id" do
18
+ Atom.new._id.should_not be_nil
19
+ end
20
+
21
+ it "should create an object with a nil revision" do
22
+ Atom.new._rev.should be_nil
23
+ end
24
+
25
+ it "should convert attributes that end in _at to dates" do
26
+ now = Time.now
27
+ p = Post.new(:viewed_at => now).save
28
+ p = RelaxDB.load(p._id)
29
+ p.viewed_at.class.should == Time
30
+ p.viewed_at.should be_close(now, 1)
31
+ end
32
+
33
+ it "will silently ignore parameters that don't specify class attributes" do
34
+ # Consider this a feature or bug. It allows an object containing both request params
35
+ # and superflous data to be passed directly to a constructor.
36
+ Post.new(:foo => "").save
37
+ end
38
+
39
+ end
40
+
41
+ describe "#to_json" do
42
+
43
+ it "should not output nil attributes" do
44
+ Atom.new.to_json.should_not include("rev")
45
+ end
46
+
47
+ end
48
+
49
+ describe "#save" do
50
+
51
+ it "should set an object's revision" do
52
+ p = Atom.new.save
53
+ p._rev.should_not be_nil
54
+ end
55
+
56
+ it "should result in an object considered saved" do
57
+ Atom.new.save.should_not be_unsaved
58
+ end
59
+
60
+ it "should be invokable multiple times" do
61
+ p = Atom.new
62
+ p.save
63
+ p.save
64
+ end
65
+
66
+ it "should set created_at when first saved" do
67
+ now = Time.now
68
+ created_at = Post.new.save.created_at
69
+ now.should be_close(created_at, 1)
70
+ end
71
+
72
+ it "should set created_at when first saved unless supplied to the constructor" do
73
+ back_then = Time.now - 1000
74
+ p = Post.new(:created_at => back_then).save
75
+ p.created_at.should be_close(back_then, 1)
76
+ end
77
+
78
+ end
79
+
80
+ describe "user defined property reader" do
81
+
82
+ it "should not effect normal operation" do
83
+ o = BespokeReader.new(:val => 101).save
84
+ o = RelaxDB.load o._id
85
+ o.val.should == 106
86
+ end
87
+
88
+ it "should not modify internal state" do
89
+ o = BespokeReader.new(:val => 101).save
90
+ o = RelaxDB.load o._id
91
+ o.instance_variable_get(:@val).should == 101
92
+ end
93
+
94
+ end
95
+
96
+ describe "user defined property writer" do
97
+
98
+ it "should not be used" do
99
+ o = BespokeWriter.new(:val => 101).save
100
+ o = RelaxDB.load o._id
101
+ o.val.should == 81
102
+ end
103
+
104
+ end
105
+
106
+ describe "loaded objects" do
107
+
108
+ it "should contain state as when saved" do
109
+ now = Time.now
110
+ p = Primitives.new(:str => "foo", :num => 19.30, :true_bool => true, :false_bool => false, :created_at => now).save
111
+ p = RelaxDB.load(p._id)
112
+ p.str.should == "foo"
113
+ p.num.should == 19.30
114
+ p.true_bool.should be_true
115
+ p.false_bool.should_not be_true
116
+ p.created_at.should be_close(now, 1)
117
+ p.empty.should be_nil
118
+ end
119
+
120
+ it "should be saveable" do
121
+ a = Atom.new.save
122
+ a = RelaxDB.load(a._id)
123
+ a.save
124
+ end
125
+
126
+ end
127
+
128
+ describe "#destroy" do
129
+
130
+ it "should delete the corresponding document from CouchDB" do
131
+ p = Atom.new.save.destroy!
132
+ lambda { RelaxDB.load(p._id) }.should raise_error
133
+ end
134
+
135
+ it "should prevent the object from being resaved" do
136
+ p = Atom.new.save.destroy!
137
+ lambda { p.save }.should raise_error
138
+ end
139
+
140
+ it "will result in undefined behaviour when invoked on unsaved objects" do
141
+ lambda { Atom.new.destroy! }.should raise_error
142
+ end
143
+
144
+ end
145
+
146
+ describe "#all.destroy!" do
147
+
148
+ it "should delete from CouchDB all documents of the corresponding class" do
149
+ Atom.new.save
150
+ Post.new.save
151
+ Post.new.save
152
+ Post.all.destroy!
153
+ Post.all.should be_empty
154
+ Atom.all.size.should == 1
155
+ end
156
+
157
+ end
158
+
159
+ describe "==" do
160
+
161
+ it "should define equality based on CouchDB id" do
162
+ i1 = Atom.new.save
163
+ i2 = Atom.new.save
164
+ i3 = RelaxDB.load(i1._id)
165
+ i1.should_not == i2
166
+ i1.should == i3
167
+ end
168
+
169
+ it "should return false when passed a nil object" do
170
+ (Atom.new == nil).should_not be_true
171
+ end
172
+
173
+ end
174
+
175
+ describe ".all" do
176
+
177
+ it "should return all instances of that class" do
178
+ Photo.new.save
179
+ Rating.new.save
180
+ Rating.new.save
181
+ Rating.all.size.should == 2
182
+ end
183
+
184
+ it "should return an empty array when no instances exist" do
185
+ Atom.all.should be_an_instance_of(Array)
186
+ Atom.all.should be_empty
187
+ end
188
+
189
+ end
190
+
191
+ describe ".all.sorted_by" do
192
+
193
+ it "should sort ascending by default" do
194
+ Post.new(:content => "b").save
195
+ Post.new(:content => "a").save
196
+ posts = Post.all.sorted_by(:content)
197
+ posts[0].content.should == "a"
198
+ posts[1].content.should == "b"
199
+ end
200
+
201
+ it "should sort desc when specified" do
202
+ Post.new(:content => "a").save
203
+ Post.new(:content => "b").save
204
+ posts = Post.all.sorted_by(:content) { |q| q.descending(true) }
205
+ posts[0].content.should == "b"
206
+ posts[1].content.should == "a"
207
+ end
208
+
209
+ it "should sort date attributes lexicographically" do
210
+ t = Time.new
211
+ Post.new(:viewed_at => t+1000, :content => "late").save
212
+ Post.new(:viewed_at => t, :content => "early").save
213
+ posts = Post.all.sorted_by(:viewed_at)
214
+ posts[0].content.should == "early"
215
+ posts[1].content.should == "late"
216
+ end
217
+
218
+ describe "results" do
219
+
220
+ it "should be retrievable by exact criteria" do
221
+ Post.new(:subject => "foo").save
222
+ Post.new(:subject => "foo").save
223
+ Post.new(:subject => "bar").save
224
+ Post.all.sorted_by(:subject) { |q| q.key("foo") }.size.should == 2
225
+ end
226
+
227
+ it "should be retrievable by relative criteria" do
228
+ Rating.new(:stars => 1).save
229
+ Rating.new(:stars => 5).save
230
+ Rating.all.sorted_by(:stars) { |q| q.endkey(3) }.size.should == 1
231
+ end
232
+
233
+ it "should be retrievable by combined criteria" do
234
+ User.new(:name => "paul", :age => 28).save
235
+ User.new(:name => "paul", :age => 72).save
236
+ User.new(:name => "atlas", :age => 99).save
237
+ User.all.sorted_by(:name, :age) { |q| q.startkey(["paul",0 ]).endkey(["paul", 50]) }.size.should == 1
238
+ end
239
+
240
+ it "should be retrievable by combined criteria where not all docs contain all attributes" do
241
+ User.new(:name => "paul", :age => 28).save
242
+ User.new(:name => "paul", :age => 72).save
243
+ User.new(:name => "atlas").save
244
+ User.all.sorted_by(:name, :age) { |q| q.startkey(["paul",0 ]).endkey(["paul", 50]) }.size.should == 1
245
+ end
246
+
247
+ it "should be retrievable by a multi key post" do
248
+ 5.times { |i| Primitives.new(:num => i).save }
249
+ ps = Primitives.all.sorted_by(:num) { |q| q.keys([0, 4]) }
250
+ ps.map { |p| p.num }.should == [0, 4]
251
+ end
252
+
253
+ end
254
+
255
+ end
256
+
257
+ describe "defaults" do
258
+
259
+ it "should be set on initialisation" do
260
+ r = Rating.new
261
+ r.stars.should == 5
262
+ end
263
+
264
+ it "should be saved" do
265
+ r = Rating.new.save
266
+ RelaxDB.load(r._id).stars.should == 5
267
+ end
268
+
269
+ it "should be ignored once overwritten" do
270
+ r = Rating.new
271
+ r.stars = nil
272
+ r.save
273
+ RelaxDB.load(r._id).stars.should be_nil
274
+ end
275
+
276
+ it "may be a simple value" do
277
+ simple = Class.new(RelaxDB::Document) do
278
+ property :foo, :default => :bar
279
+ end
280
+ simple.new.foo.should == :bar
281
+ end
282
+
283
+ it "may be a proc" do
284
+ simple = Class.new(RelaxDB::Document) do
285
+ property :foo, :default => lambda { :bar }
286
+ end
287
+ simple.new.foo.should == :bar
288
+ end
289
+
290
+ end
291
+
292
+ describe "validator" do
293
+
294
+ it "should prevent an object from being saved if it evaluates to false" do
295
+ r = Class.new(RelaxDB::Document) do
296
+ property :thumbs_up, :validator => lambda { false }
297
+ end
298
+ r.new.save.should be_false
299
+ end
300
+
301
+ it "should prevent an object from being saved if it throws an exception" do
302
+ r = Class.new(RelaxDB::Document) do
303
+ property :thumbs_up, :validator => lambda { raise "foo" }
304
+ end
305
+ r.new.save.should be_false
306
+ end
307
+
308
+ it "should pass the property value to the validator" do
309
+ r = Class.new(RelaxDB::Document) do
310
+ property :thumbs_up, :validator => lambda { |tu| tu >=0 && tu < 3 }
311
+ end
312
+ r.new(:thumbs_up => 2).save.should be
313
+ r.new(:thumbs_up => 3).save.should be_false
314
+ end
315
+
316
+ it "should add the validation error message if supplied, on failure" do
317
+ r = Class.new(RelaxDB::Document) do
318
+ property :thumbs_up, :validator => lambda { false }, :validation_msg => "Too many thumbs"
319
+ end
320
+ x = r.new
321
+ x.save
322
+ x.errors[:thumbs_up].should == "Too many thumbs"
323
+ end
324
+
325
+ it "should perform all validations" do
326
+ r = Class.new(RelaxDB::Document) do
327
+ property :foo, :validator => lambda { false }, :validation_msg => "oof"
328
+ property :bar, :validator => lambda { false }, :validation_msg => "rab"
329
+ end
330
+ x = r.new
331
+ x.save
332
+ x.errors[:foo].should == "oof"
333
+ x.errors[:bar].should == "rab"
334
+ end
335
+
336
+ it "should prevent saving unless all validations pass" do
337
+ r = Class.new(RelaxDB::Document) do
338
+ property :foo, :validator => lambda { false }
339
+ property :bar, :validator => lambda { true }
340
+ end
341
+ x = r.new
342
+ x.save.should == false
343
+ end
344
+
345
+ it "may be a proc" do
346
+ r = Class.new(RelaxDB::Document) do
347
+ property :thumbs_up, :validator => lambda { false }
348
+ end
349
+ r.new.save.should be_false
350
+ end
351
+
352
+ it "may be a method" do
353
+ r = Class.new(RelaxDB::Document) do
354
+ property :thumbs_up, :validator => :count_em
355
+ def count_em
356
+ false
357
+ end
358
+ end
359
+ r.new.save.should be_false
360
+ end
361
+
362
+ end
363
+
364
+ end
@@ -0,0 +1,147 @@
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
+ RelaxDB.configure(:host => "localhost", :port => 5984)
8
+ end
9
+
10
+ before(:each) do
11
+ RelaxDB.delete_db "relaxdb_spec_db" rescue "ok"
12
+ RelaxDB.use_db "relaxdb_spec_db"
13
+ end
14
+
15
+ describe "has_many" do
16
+
17
+ it "should be considered enumerable" do
18
+ u = User.new.save
19
+ u.items.should be_a_kind_of( Enumerable)
20
+ end
21
+
22
+ it "should actually be enumerable" do
23
+ u = User.new.save
24
+ u.items << Item.new(:name => "a")
25
+ u.items << Item.new(:name => "b")
26
+ names = u.items.inject("") { |memo, i| memo << i.name }
27
+ names.should == "ab"
28
+ end
29
+
30
+ it "should preserve the collection across the load / save boundary" do
31
+ u = User.new.save
32
+ u.items << Item.new
33
+ u = RelaxDB.load u._id
34
+ u.items.size.should == 1
35
+ end
36
+
37
+ it "should work with MultiWordClassNames" do
38
+ c = MultiWordChild.new
39
+ m = MultiWordClass.new.save
40
+ m.multi_word_children << c
41
+ m = RelaxDB.load m._id
42
+ m.multi_word_children[0].should == c
43
+ end
44
+
45
+ describe "#<<" do
46
+
47
+ it "should link the added item to the parent" do
48
+ u = User.new
49
+ u.items << Item.new
50
+ u.items[0].user.should == u
51
+ end
52
+
53
+ it "should return self" do
54
+ u = User.new.save
55
+ u.items << Item.new << Item.new
56
+ u.items[0].user.should == u
57
+ u.items[1].user.should == u
58
+ end
59
+
60
+ it "should not created duplicates when invoked with same object more than once" do
61
+ u = User.new.save
62
+ i = Item.new
63
+ u.items << i << i
64
+ u.items.size.should == 1
65
+ end
66
+
67
+ it "should return false when the child fails validation" do
68
+ d = Dysfunctional.new
69
+ r = (d.failures << Failure.new)
70
+ r.should be_false
71
+ d.failures.should be_empty
72
+ end
73
+
74
+ end
75
+
76
+ describe "#=" do
77
+
78
+ it "should fail" do
79
+ # This may be implemented in future
80
+ lambda { User.new.items = [] }.should raise_error
81
+ end
82
+
83
+ end
84
+
85
+ describe "#delete" do
86
+
87
+ it "should nullify the belongs_to relationship" do
88
+ u = User.new.save
89
+ i = Item.new
90
+ u.items << i
91
+ u.items.delete i
92
+ i.user.should be_nil
93
+ RelaxDB.load(i._id).user.should be_nil
94
+ end
95
+
96
+ end
97
+
98
+ describe "#clear" do
99
+
100
+ it "should result in an empty collection" do
101
+ u = User.new.save
102
+ u.items << Item.new << Item.new
103
+ u.items.clear
104
+ u.items.should be_empty
105
+ end
106
+
107
+ it "should nullify all child relationships" do
108
+ u = User.new.save
109
+ i1, i2 = Item.new, Item.new
110
+ u.items << i1
111
+ u.items << i2
112
+ u.items.clear
113
+
114
+ i1.user.should be_nil
115
+ i2.user.should be_nil
116
+ RelaxDB.load(i1._id).user.should be_nil
117
+ RelaxDB.load(i2._id).user.should be_nil
118
+ end
119
+
120
+ end
121
+
122
+ describe "owner" do
123
+
124
+ it "should be able to form multiple relationships with the same class of child" do
125
+ u1, u2 = User.new.save, User.new.save
126
+ i = Invite.new(:recipient => u2)
127
+ u1.invites_sent << Invite.new
128
+ RelaxDB.load(u1._id).invites_sent[0] == i
129
+ RelaxDB.load(u2._id).invites_received[0] == i
130
+ end
131
+
132
+ describe "#destroy" do
133
+
134
+ it "should nullify its child relationships" do
135
+ u = User.new.save
136
+ u.items << Item.new << Item.new
137
+ u.destroy!
138
+ Item.all.sorted_by(:user_id) { |q| q.key(u._id) }.should be_empty
139
+ end
140
+
141
+ end
142
+
143
+ end
144
+
145
+ end
146
+
147
+ end