paulcarey-relaxdb 0.1.0

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,301 @@
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
+ p = Atom.new
19
+ p._id.should_not be_nil
20
+ end
21
+
22
+ it "should create an object with a nil revision" do
23
+ Atom.new._rev.should be_nil
24
+ end
25
+
26
+ it "should convert attributes that end in _at to dates" do
27
+ now = Time.now
28
+ p = Post.new(:viewed_at => now).save
29
+ p = RelaxDB.load(p._id)
30
+ p.viewed_at.class.should == Time
31
+ p.viewed_at.should be_close(now, 1)
32
+ end
33
+
34
+ it "will silently ignore parameters that don't specify class attributes" do
35
+ # Consider this a feature or bug. It allows an object containing both request params
36
+ # and superflous data to be passed directly to a constructor.
37
+ Post.new(:foo => "").save
38
+ end
39
+
40
+ end
41
+
42
+ describe "#to_json" do
43
+
44
+ it "should not output nil attributes" do
45
+ Atom.new.to_json.should_not include("rev")
46
+ end
47
+
48
+ end
49
+
50
+ describe "#save" do
51
+
52
+ it "should set an object's revision" do
53
+ p = Atom.new.save
54
+ p._rev.should_not be_nil
55
+ end
56
+
57
+ it "should result in an object considered saved" do
58
+ Atom.new.save.should_not be_unsaved
59
+ end
60
+
61
+ it "should be invokable multiple times" do
62
+ p = Atom.new
63
+ p.save
64
+ p.save
65
+ end
66
+
67
+ it "should set created_at when first saved" do
68
+ now = Time.now
69
+ created_at = Post.new.save.created_at
70
+ now.should be_close(created_at, 1)
71
+ end
72
+
73
+ it "should set created_at when first saved unless supplied to the constructor" do
74
+ back_then = Time.now - 1000
75
+ p = Post.new(:created_at => back_then).save
76
+ p.created_at.should be_close(back_then, 1)
77
+ end
78
+
79
+ end
80
+
81
+ describe "loaded objects" do
82
+
83
+ it "should contain state as when saved" do
84
+ now = Time.now
85
+ p = Primitives.new(:str => "foo", :num => 19.30, :true_bool => true, :false_bool => false, :created_at => now).save
86
+ p = RelaxDB.load(p._id)
87
+ p.str.should == "foo"
88
+ p.num.should == 19.30
89
+ p.true_bool.should be_true
90
+ p.false_bool.should_not be_true
91
+ p.created_at.should be_close(now, 1)
92
+ p.empty.should be_nil
93
+ end
94
+
95
+ it "should be saveable" do
96
+ a = Atom.new.save
97
+ a = RelaxDB.load(a._id)
98
+ a.save
99
+ end
100
+
101
+ end
102
+
103
+ describe "#destroy" do
104
+
105
+ it "should delete the corresponding document from CouchDB" do
106
+ p = Atom.new.save.destroy!
107
+ lambda { RelaxDB.load(p._id) }.should raise_error
108
+ end
109
+
110
+ it "should prevent the object from being resaved" do
111
+ p = Atom.new.save.destroy!
112
+ lambda { p.save }.should raise_error
113
+ end
114
+
115
+ it "will result in undefined behaviour when invoked on unsaved objects" do
116
+ Photo.new.destroy!
117
+ lambda { Atom.new.destroy! }.should raise_error
118
+ end
119
+
120
+ end
121
+
122
+ describe "#all.destroy!" do
123
+
124
+ it "should delete from CouchDB all documents of the corresponding class" do
125
+ Atom.new.save
126
+ Post.new.save
127
+ Post.new.save
128
+ Post.all.destroy!
129
+ Post.all.should be_empty
130
+ Atom.all.size.should == 1
131
+ end
132
+
133
+ end
134
+
135
+ describe "==" do
136
+
137
+ it "should define equality based on CouchDB id" do
138
+ i1 = Atom.new.save
139
+ i2 = Atom.new.save
140
+ i3 = RelaxDB.load(i1._id)
141
+ i1.should_not == i2
142
+ i1.should == i3
143
+ end
144
+
145
+ it "should return false when passed a nil object" do
146
+ (Atom.new == nil).should_not be_true
147
+ end
148
+
149
+ end
150
+
151
+ describe "#all" do
152
+
153
+ it "should return all instances of that class" do
154
+ Photo.new.save
155
+ Rating.new.save
156
+ Rating.new.save
157
+ Rating.all.size.should == 2
158
+ end
159
+
160
+ it "should return an empty array when no instances exist" do
161
+ Atom.all.should be_an_instance_of(Array)
162
+ Atom.all.should be_empty
163
+ end
164
+
165
+ end
166
+
167
+ describe "#all.sorted_by" do
168
+
169
+ it "should sort ascending by default" do
170
+ Post.new(:content => "b").save
171
+ Post.new(:content => "a").save
172
+ posts = Post.all.sorted_by(:content)
173
+ posts[0].content.should == "a"
174
+ posts[1].content.should == "b"
175
+ end
176
+
177
+ it "should sort desc when specified" do
178
+ Post.new(:content => "a").save
179
+ Post.new(:content => "b").save
180
+ posts = Post.all.sorted_by(:content) { |q| q.descending(true) }
181
+ posts[0].content.should == "b"
182
+ posts[1].content.should == "a"
183
+ end
184
+
185
+ it "should sort date attributes lexicographically" do
186
+ t = Time.new
187
+ Post.new(:viewed_at => t+1000, :content => "late").save
188
+ Post.new(:viewed_at => t, :content => "early").save
189
+ posts = Post.all.sorted_by(:viewed_at)
190
+ posts[0].content.should == "early"
191
+ posts[1].content.should == "late"
192
+ end
193
+
194
+ describe "results" do
195
+
196
+ it "should be retrievable by exact criteria" do
197
+ Post.new(:subject => "foo").save
198
+ Post.new(:subject => "foo").save
199
+ Post.new(:subject => "bar").save
200
+ Post.all.sorted_by(:subject) { |q| q.key("foo") }.size.should == 2
201
+ end
202
+
203
+ it "should be retrievable by relative criteria" do
204
+ Rating.new(:stars => 1).save
205
+ Rating.new(:stars => 5).save
206
+ Rating.all.sorted_by(:stars) { |q| q.endkey(3) }.size.should == 1
207
+ end
208
+
209
+ it "should be retrievable by combined criteria" do
210
+ User.new(:name => "paul", :age => 28).save
211
+ User.new(:name => "paul", :age => 72).save
212
+ User.new(:name => "atlas", :age => 99).save
213
+ User.all.sorted_by(:name, :age) { |q| q.startkey(["paul",0 ]).endkey(["paul", 50]) }.size.should == 1
214
+ end
215
+
216
+ it "should be retrievable by combined criteria where not all docs contain all attributes" do
217
+ User.new(:name => "paul", :age => 28).save
218
+ User.new(:name => "paul", :age => 72).save
219
+ User.new(:name => "atlas").save
220
+ User.all.sorted_by(:name, :age) { |q| q.startkey(["paul",0 ]).endkey(["paul", 50]) }.size.should == 1
221
+ end
222
+
223
+ end
224
+
225
+ end
226
+
227
+ describe "defaults" do
228
+
229
+ it "should be set on initialisation" do
230
+ r = Rating.new
231
+ r.stars.should == 5
232
+ end
233
+
234
+ it "should be saved" do
235
+ r = Rating.new.save
236
+ RelaxDB.load(r._id).stars.should == 5
237
+ end
238
+
239
+ it "should be ignored once overwritten" do
240
+ r = Rating.new
241
+ r.stars = nil
242
+ r.save
243
+ RelaxDB.load(r._id).stars.should be_nil
244
+ end
245
+
246
+ it "may be a simple value" do
247
+ simple = Class.new(RelaxDB::Document) do
248
+ property :foo, :default => :bar
249
+ end
250
+ simple.new.foo.should == :bar
251
+ end
252
+
253
+ it "may be a proc" do
254
+ simple = Class.new(RelaxDB::Document) do
255
+ property :foo, :default => lambda { :bar }
256
+ end
257
+ simple.new.foo.should == :bar
258
+ end
259
+
260
+ end
261
+
262
+ describe "validator" do
263
+
264
+ it "should prevent an object from being saved if it evaluates to false" do
265
+ r = Class.new(RelaxDB::Document) do
266
+ property :thumbs_up, :validator => lambda { false }
267
+ end
268
+ r.new.save.should be_false
269
+ end
270
+
271
+ it "should pass the property value to the validator" do
272
+ r = Class.new(RelaxDB::Document) do
273
+ property :thumbs_up, :validator => lambda { |tu| tu >=0 && tu < 3 }
274
+ end
275
+ r.new(:thumbs_up => 2).save.should_not be_false
276
+ r.new(:thumbs_up => 3).save.should be_false
277
+ end
278
+
279
+ it "should add the validation error message if supplied, on failure" do
280
+ r = Class.new(RelaxDB::Document) do
281
+ property :thumbs_up, :validator => lambda { false }, :validation_msg => "Too many thumbs"
282
+ end
283
+ x = r.new
284
+ x.save
285
+ x.errors[:thumbs_up].should == "Too many thumbs"
286
+ end
287
+
288
+ it "should perform all validations" do
289
+ r = Class.new(RelaxDB::Document) do
290
+ property :foo, :validator => lambda { false }, :validation_msg => "oof"
291
+ property :bar, :validator => lambda { false }, :validation_msg => "rab"
292
+ end
293
+ x = r.new
294
+ x.save
295
+ x.errors[:foo].should == "oof"
296
+ x.errors[:bar].should == "rab"
297
+ end
298
+
299
+ end
300
+
301
+ end
@@ -0,0 +1,139 @@
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
+ describe "#<<" do
38
+
39
+ it "should link the added item to the parent" do
40
+ u = User.new
41
+ u.items << Item.new
42
+ u.items[0].user.should == u
43
+ end
44
+
45
+ it "should return self" do
46
+ u = User.new.save
47
+ u.items << Item.new << Item.new
48
+ u.items[0].user.should == u
49
+ u.items[1].user.should == u
50
+ end
51
+
52
+ it "should not created duplicates when invoked with same object more than once" do
53
+ u = User.new.save
54
+ i = Item.new
55
+ u.items << i << i
56
+ u.items.size.should == 1
57
+ end
58
+
59
+ it "should return false when the child fails validation" do
60
+ d = Dysfunctional.new
61
+ r = (d.failures << Failure.new)
62
+ r.should be_false
63
+ d.failures.should be_empty
64
+ end
65
+
66
+ end
67
+
68
+ describe "#=" do
69
+
70
+ it "should fail" do
71
+ # This may be implemented in future
72
+ lambda { User.new.items = [] }.should raise_error
73
+ end
74
+
75
+ end
76
+
77
+ describe "#delete" do
78
+
79
+ it "should nullify the belongs_to relationship" do
80
+ u = User.new.save
81
+ i = Item.new
82
+ u.items << i
83
+ u.items.delete i
84
+ i.user.should be_nil
85
+ RelaxDB.load(i._id).user.should be_nil
86
+ end
87
+
88
+ end
89
+
90
+ describe "#clear" do
91
+
92
+ it "should result in an empty collection" do
93
+ u = User.new.save
94
+ u.items << Item.new << Item.new
95
+ u.items.clear
96
+ u.items.should be_empty
97
+ end
98
+
99
+ it "should nullify all child relationships" do
100
+ u = User.new.save
101
+ i1, i2 = Item.new, Item.new
102
+ u.items << i1
103
+ u.items << i2
104
+ u.items.clear
105
+
106
+ i1.user.should be_nil
107
+ i2.user.should be_nil
108
+ RelaxDB.load(i1._id).user.should be_nil
109
+ RelaxDB.load(i2._id).user.should be_nil
110
+ end
111
+
112
+ end
113
+
114
+ describe "owner" do
115
+
116
+ it "should be able to form multiple relationships with the same class of child" do
117
+ u1, u2 = User.new.save, User.new.save
118
+ i = Invite.new(:recipient => u2)
119
+ u1.invites_sent << Invite.new
120
+ RelaxDB.load(u1._id).invites_sent[0] == i
121
+ RelaxDB.load(u2._id).invites_received[0] == i
122
+ end
123
+
124
+ describe "#destroy" do
125
+
126
+ it "should nullify its child relationships" do
127
+ u = User.new.save
128
+ u.items << Item.new << Item.new
129
+ u.destroy!
130
+ Item.all.sorted_by(:user_id) { |q| q.key(u._id) }.should be_empty
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+
139
+ end
@@ -0,0 +1,121 @@
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(: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_one" do
16
+
17
+ it "should return nil when accessed before assignment" do
18
+ p = Photo.new
19
+ p.rating.should == nil
20
+ end
21
+
22
+ it "should be establishable via a constructor attribute" do
23
+ r = Rating.new
24
+ p = Photo.new :rating => r
25
+ p.rating.should == r
26
+ end
27
+
28
+ it "should be establishable via assignment" do
29
+ p = Photo.new
30
+ r = Rating.new
31
+ p.rating = r
32
+ p.rating.should == r
33
+ end
34
+
35
+ it "should return the same object on repeated invocations" do
36
+ p = Photo.new.save
37
+ p.rating = Rating.new
38
+ p = RelaxDB.load(p._id)
39
+ p.rating.object_id.should == p.rating.object_id
40
+ end
41
+
42
+ it "should be preserved across load / save boundary" do
43
+ r = Rating.new
44
+ p = Photo.new(:rating => r).save
45
+ p = RelaxDB.load p._id
46
+ p.rating.should == r
47
+ end
48
+
49
+ it "should be able reference itself via its child" do
50
+ r = Rating.new
51
+ p = Photo.new(:rating => r).save
52
+ p = RelaxDB.load p._id
53
+ p.rating.photo.should == p
54
+ end
55
+
56
+ describe "#=" do
57
+
58
+ it "should create a reference from the child to the parent" do
59
+ p = Photo.new
60
+ r = Rating.new
61
+ p.rating = r
62
+ r.photo.should == p
63
+ end
64
+
65
+ it "should save the assigned object" do
66
+ p = Photo.new
67
+ r = Rating.new
68
+ p.rating = r
69
+ r.should_not be_unsaved
70
+ end
71
+
72
+ it "will not save the parent" do
73
+ p = Photo.new
74
+ r = Rating.new
75
+ p.rating = r
76
+ p.should be_unsaved
77
+ end
78
+
79
+ it "should set the target to nil when nil is assigned" do
80
+ p = Photo.new
81
+ p.rating = nil
82
+ p.rating.should be_nil
83
+ end
84
+
85
+ it "should nullify any existing relationship in the database" do
86
+ p = Photo.new
87
+ r = Rating.new
88
+ p.rating = r
89
+ p.rating = nil
90
+ RelaxDB.load(r._id).photo.should be_nil
91
+ end
92
+
93
+ it "should nullify any existing relationship on a known in-memory object" do
94
+ p = Photo.new
95
+ r = Rating.new
96
+ p.rating = r
97
+ p.rating = nil
98
+ r.photo.should be_nil
99
+ end
100
+
101
+ it "will not nullify any existing relationship on unknown in-memory objects" do
102
+ p = Photo.new.save
103
+ r = Rating.new
104
+ p.rating = r
105
+ r_copy = RelaxDB.load(r._id)
106
+ p.rating = nil
107
+ r_copy.photo.should_not be_nil
108
+ end
109
+
110
+ it "will not throw an error when the rhs fails validation" do
111
+ d = Dysfunctional.new.save
112
+ f = Failure.new
113
+ d.failure = f
114
+ d.failure.should == f
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/spec_models.rb'
3
+
4
+ describe RelaxDB::Query do
5
+
6
+ describe "#view_name" do
7
+
8
+ it "should match a single key attribute" do
9
+ q = RelaxDB::SortedByView.new("", :foo)
10
+ q.view_name.should == "all_sorted_by_foo"
11
+ end
12
+
13
+ it "should match key attributes" do
14
+ q = RelaxDB::SortedByView.new("", :foo, :bar)
15
+ q.view_name.should == "all_sorted_by_foo_and_bar"
16
+ end
17
+ end
18
+
19
+ describe "#view_path" do
20
+
21
+ it "should list design document and view name" do
22
+ q = RelaxDB::Query.new("Zenith", "mount")
23
+ q.view_path.should == "_view/Zenith/mount"
24
+ end
25
+
26
+ it "should contain URL and JSON encoded key when the key has been set" do
27
+ q = RelaxDB::Query.new("Zenith", "mount")
28
+ q.key("olympus")
29
+ q.view_path.should == "_view/Zenith/mount?key=%22olympus%22"
30
+ end
31
+
32
+ it "should honour startkey, endkey and count" do
33
+ q = RelaxDB::Query.new("Zenith", "all_sorted_by_name_and_height")
34
+ q.startkey(["olympus"]).endkey(["vesuvius", 3600]).count(100)
35
+ q.view_path.should == "_view/Zenith/all_sorted_by_name_and_height?startkey=%5B%22olympus%22%5D&endkey=%5B%22vesuvius%22%2C3600%5D&count=100"
36
+ end
37
+
38
+ it "should specify the key as the empty string if key was set to nil" do
39
+ q = RelaxDB::Query.new("", "")
40
+ q.key(nil)
41
+ q.view_path.should == "_view//?key=%22%22"
42
+ end
43
+
44
+ end
45
+
46
+ end