cohitre-relaxdb 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.
@@ -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