yaanno-relaxdb 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Paul Carey
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,164 @@
1
+ h3. What's New?
2
+
3
+ * Pagination! CouchDB offers great support for retrieving a subset of data, but the housekeeping is tricky. RelaxDB takes care of it.
4
+ ** Note that if you invoke paginate_by on an already created view, the necessary reduce function won't be automatically created. Take a look at SortedByView and create the reduce func by hand.
5
+ * Support for multi key post
6
+ ** For example, @ Numbers.all.sorted_by(:val) { |q| q.keys([1,2,3,5]) } @
7
+ * Works with CouchDB 0.9 trunk as of 2008/10/08. Note that pagination won't work correctly on trunk until issue "COUCHDB-135":http://issues.apache.org/jira/browse/COUCHDB-135 is fixed.
8
+
9
+ *Note*: 0.2.1 requires CouchDB 0.9 trunk. 0.2.0 works with CouchDB 0.8 onwards.
10
+
11
+ h2. Overview
12
+
13
+ RelaxDB provides a Ruby interface to CouchDB. It offers a simple idiom for specifying object relationships. The underlying objects are persisted to the mighty CouchDB. Combined with the schema free nature of CouchDB, RelaxDB's current strength lies in quick prototyping of object models.
14
+
15
+ A few facilities are provided including pretty printing of GET requests and uploading of JavaScript views.
16
+
17
+ A basic merb plugin, "merb_relaxdb":http://github.com/paulcarey/merb_relaxdb/tree/master is also available.
18
+
19
+ For more complete documentation take a look at docs/spec_results.html and the corresponding specs.
20
+
21
+ h2. Details
22
+
23
+ h3. Getting started
24
+
25
+ <pre>
26
+ <code>
27
+ RelaxDB.configure :host => "localhost", :port => 5984
28
+ RelaxDB.use_db "scratch"
29
+ </code>
30
+ </pre>
31
+
32
+ h3. Defining models
33
+
34
+ <pre>
35
+ <code>
36
+ class Writer < RelaxDB::Document
37
+ property :name, :default => "anon"
38
+
39
+ has_many :posts, :class => "Post"
40
+ has_many :ratings, :class => "Post", :known_as => :critic
41
+ end
42
+
43
+ class Post < RelaxDB::Document
44
+ property :created_at
45
+ property :contents
46
+
47
+ belongs_to :writer
48
+ has_many :ratings, :class => "Rating"
49
+ end
50
+
51
+ class Rating < RelaxDB::Document
52
+ property :thumbs_up, :validator => lambda { |tu| tu >= 0 && tu < 3 }, :validation_msg => "No no"
53
+
54
+ belongs_to :post
55
+ belongs_to :critic
56
+ end
57
+ </code>
58
+ </pre>
59
+
60
+ h3. Exploring models
61
+
62
+ <pre>
63
+ <code>
64
+ paul = Writer.new(:name => "paul").save
65
+
66
+ post = Post.new(:contents => "foo")
67
+ paul.posts << post # post writer is set and post is saved
68
+ post.created_at # right now
69
+ paul.ratings << Rating.new(:thumbs_up => 3, :post => post) # returns false as rating fails validation
70
+ paul.ratings.size # 0
71
+
72
+ # Simple views are auto created
73
+ Rating.all.sorted_by(:thumbs_up) { |q| q.key(2).count(1) } # query params map directly to CouchDB
74
+ </code>
75
+ </pre>
76
+
77
+ h3. Paginating models
78
+
79
+ <pre>
80
+ <code>
81
+ # Controller (merb-action-args used for extracting view_params)
82
+
83
+ def action(page_params={})
84
+ u_id = @user._id
85
+
86
+ @posts = Post.paginate_by(page_params, :writer_id, :created_at) do |p|
87
+ p.startkey([u_id, {}]).endkey([u_id]).descending(true).count(5)
88
+ end
89
+ render
90
+ end
91
+
92
+ # In your view
93
+
94
+ <% @posts.each do |p| %>
95
+ <%= p.contents %>
96
+ <% end %>
97
+
98
+ <%= link_to "prev", "/posts/?#{@posts.prev_query}" if @posts.prev_query %>
99
+ <%= link_to "next", "/posts/?#{@posts.next_query}" if @posts.next_query %>
100
+ </code>
101
+ </pre>
102
+
103
+ h3. Paginating over your own views
104
+
105
+ <pre>
106
+ <code>
107
+
108
+ RelaxDB.paginate_view(page_params, "Letter", "by_letter_and_number", :letter, :number) do |p|
109
+ p.startkey(["b"]).endkey(["b", {}]).count(2)
110
+ end
111
+
112
+ </code>
113
+ </pre>
114
+
115
+ A more illustrative example is listed in the .paginate_view spec in spec/paginate_spec.rb
116
+
117
+ h3. Creating views by hand
118
+
119
+ <pre>
120
+ <code>
121
+ $ cat view.js
122
+ function Writer-allnames-map(doc) {
123
+ if(doc.class == "Writer")
124
+ emit(null, doc.name);
125
+ }
126
+
127
+ function Writer-allnames-reduce(keys, values) {
128
+ var allnames = "";
129
+ for(var i = 0; i < values.length; i++)
130
+ allnames += values[i];
131
+ return allnames;
132
+ }
133
+ $
134
+
135
+ RelaxDB::ViewUploader.upload("view.js")
136
+ RelaxDB.view("Writer", "allnames") # paul
137
+ </code>
138
+ </pre>
139
+
140
+ h3. Visualise
141
+
142
+ Create an object graph by simply running
143
+ <pre>
144
+ <code>
145
+ RelaxDB::GraphCreator.create
146
+ </code>
147
+ </pre>
148
+
149
+ Requires graphviz. Useful for visualising relationships between a limited number of document e.g. test fixtures. "Description and example":http://dev.strawberrydiva.com/visually_explore_couchdb/.
150
+
151
+ h3. Experimental Features
152
+
153
+ * Declarative denormalisation
154
+ ** Create a partial object graph in JSON with a single call
155
+ ** May be used to require fewer GET requests
156
+ ** View the denormalisation spec for examples
157
+
158
+ h2. Incomplete list of limitations
159
+
160
+ * Error handling is not robust
161
+ * Destroying an object results in non transactional nullification of child/peer references
162
+ * Objects can talk to only one database at a time
163
+ * No caching is used. Although adding an LRU cache would be fairly straightforward, this hasn't been done as it's not yet clear what caching strategies will be most effective.
164
+
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'spec/rake/spectask'
4
+
5
+ PLUGIN = "relaxdb"
6
+ NAME = "relaxdb"
7
+ GEM_VERSION = "0.2.2"
8
+ AUTHOR = "Paul Carey"
9
+ EMAIL = "paul.p.carey@gmail.com"
10
+ HOMEPAGE = "http://github.com/paulcarey/relaxdb/"
11
+ SUMMARY = "RelaxDB provides a simple interface to CouchDB"
12
+
13
+ spec = Gem::Specification.new do |s|
14
+ s.name = NAME
15
+ s.version = GEM_VERSION
16
+ s.platform = Gem::Platform::RUBY
17
+ s.has_rdoc = true
18
+ s.extra_rdoc_files = ["README.textile", "LICENSE"]
19
+ s.summary = SUMMARY
20
+ s.description = s.summary
21
+ s.author = AUTHOR
22
+ s.email = EMAIL
23
+ s.homepage = HOMEPAGE
24
+
25
+ s.add_dependency "extlib", ">=0.9.4"
26
+ s.add_dependency "json_pure"
27
+ s.add_dependency "uuid"
28
+
29
+ s.require_path = 'lib'
30
+ s.autorequire = PLUGIN
31
+ s.files = %w(LICENSE README.textile Rakefile) + Dir.glob("{docs,lib,spec}/**/*")
32
+ end
33
+
34
+ Rake::GemPackageTask.new(spec) do |pkg|
35
+ pkg.gem_spec = spec
36
+ end
37
+
38
+ desc "Install"
39
+ task :install => [:package] do
40
+ sh %{sudo gem install --local pkg/#{NAME}-#{GEM_VERSION} --no-update-sources}
41
+ end
42
+
43
+ desc "Run specs"
44
+ Spec::Rake::SpecTask.new('spec') do |t|
45
+ t.spec_files = FileList['spec/**/*.rb']
46
+ end
47
+
48
+ desc "Run specs and produce spec_results.html"
49
+ Spec::Rake::SpecTask.new('spec:html') do |t|
50
+ t.spec_files = FileList['spec/**/*.rb']
51
+ t.spec_opts = ["--format", "html:docs/spec_results.html"]
52
+ end
@@ -0,0 +1,604 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <title>RSpec results</title>
8
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
9
+ <meta http-equiv="Expires" content="-1" />
10
+ <meta http-equiv="Pragma" content="no-cache" />
11
+ <style type="text/css">
12
+ body {
13
+ margin: 0;
14
+ padding: 0;
15
+ background: #fff;
16
+ font-size: 80%;
17
+ }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <div class="rspec-report">
22
+ <script type="text/javascript">
23
+ // <![CDATA[
24
+ function moveProgressBar(percentDone) {
25
+ document.getElementById("rspec-header").style.width = percentDone +"%";
26
+ }
27
+ function makeRed(element_id) {
28
+ document.getElementById(element_id).style.background = '#C40D0D';
29
+ document.getElementById(element_id).style.color = '#FFFFFF';
30
+ }
31
+
32
+ function makeYellow(element_id) {
33
+ if (element_id == "rspec-header" && document.getElementById(element_id).style.background != '#C40D0D')
34
+ {
35
+ document.getElementById(element_id).style.background = '#FAF834';
36
+ document.getElementById(element_id).style.color = '#000000';
37
+ }
38
+ else
39
+ {
40
+ document.getElementById(element_id).style.background = '#FAF834';
41
+ document.getElementById(element_id).style.color = '#000000';
42
+ }
43
+ }
44
+
45
+ // ]]>
46
+ </script>
47
+ <style type="text/css">
48
+ #rspec-header {
49
+ background: #65C400; color: #fff;
50
+ }
51
+
52
+ .rspec-report h1 {
53
+ margin: 0px 10px 0px 10px;
54
+ padding: 10px;
55
+ font-family: "Lucida Grande", Helvetica, sans-serif;
56
+ font-size: 1.8em;
57
+ }
58
+
59
+ #summary {
60
+ margin: 0; padding: 5px 10px;
61
+ font-family: "Lucida Grande", Helvetica, sans-serif;
62
+ text-align: right;
63
+ position: absolute;
64
+ top: 0px;
65
+ right: 0px;
66
+ }
67
+
68
+ #summary p {
69
+ margin: 0 0 0 2px;
70
+ }
71
+
72
+ #summary #totals {
73
+ font-size: 1.2em;
74
+ }
75
+
76
+ .example_group {
77
+ margin: 0 10px 5px;
78
+ background: #fff;
79
+ }
80
+
81
+ dl {
82
+ margin: 0; padding: 0 0 5px;
83
+ font: normal 11px "Lucida Grande", Helvetica, sans-serif;
84
+ }
85
+
86
+ dt {
87
+ padding: 3px;
88
+ background: #65C400;
89
+ color: #fff;
90
+ font-weight: bold;
91
+ }
92
+
93
+ dd {
94
+ margin: 5px 0 5px 5px;
95
+ padding: 3px 3px 3px 18px;
96
+ }
97
+
98
+ dd.spec.passed {
99
+ border-left: 5px solid #65C400;
100
+ border-bottom: 1px solid #65C400;
101
+ background: #DBFFB4; color: #3D7700;
102
+ }
103
+
104
+ dd.spec.failed {
105
+ border-left: 5px solid #C20000;
106
+ border-bottom: 1px solid #C20000;
107
+ color: #C20000; background: #FFFBD3;
108
+ }
109
+
110
+ dd.spec.not_implemented {
111
+ border-left: 5px solid #FAF834;
112
+ border-bottom: 1px solid #FAF834;
113
+ background: #FCFB98; color: #131313;
114
+ }
115
+
116
+ dd.spec.pending_fixed {
117
+ border-left: 5px solid #0000C2;
118
+ border-bottom: 1px solid #0000C2;
119
+ color: #0000C2; background: #D3FBFF;
120
+ }
121
+
122
+ .backtrace {
123
+ color: #000;
124
+ font-size: 12px;
125
+ }
126
+
127
+ a {
128
+ color: #BE5C00;
129
+ }
130
+
131
+ /* Ruby code, style similar to vibrant ink */
132
+ .ruby {
133
+ font-size: 12px;
134
+ font-family: monospace;
135
+ color: white;
136
+ background-color: black;
137
+ padding: 0.1em 0 0.2em 0;
138
+ }
139
+
140
+ .ruby .keyword { color: #FF6600; }
141
+ .ruby .constant { color: #339999; }
142
+ .ruby .attribute { color: white; }
143
+ .ruby .global { color: white; }
144
+ .ruby .module { color: white; }
145
+ .ruby .class { color: white; }
146
+ .ruby .string { color: #66FF00; }
147
+ .ruby .ident { color: white; }
148
+ .ruby .method { color: #FFCC00; }
149
+ .ruby .number { color: white; }
150
+ .ruby .char { color: white; }
151
+ .ruby .comment { color: #9933CC; }
152
+ .ruby .symbol { color: white; }
153
+ .ruby .regex { color: #44B4CC; }
154
+ .ruby .punct { color: white; }
155
+ .ruby .escape { color: white; }
156
+ .ruby .interp { color: white; }
157
+ .ruby .expr { color: white; }
158
+
159
+ .ruby .offending { background-color: gray; }
160
+ .ruby .linenum {
161
+ width: 75px;
162
+ padding: 0.1em 1em 0.2em 0;
163
+ color: #000000;
164
+ background-color: #FFFBD3;
165
+ }
166
+
167
+ </style>
168
+
169
+ <div id="rspec-header">
170
+ <h1>RSpec Results</h1>
171
+
172
+ <div id="summary">
173
+ <p id="totals">&nbsp;</p>
174
+ <p id="duration">&nbsp;</p>
175
+ </div>
176
+ </div>
177
+
178
+ <div class="results">
179
+ <div class="example_group">
180
+ <dl>
181
+ <dt id="example_group_1">RelaxDB::BelongsToProxy</dt>
182
+ </dl>
183
+ </div>
184
+ <div class="example_group">
185
+ <dl>
186
+ <dt id="example_group_2">RelaxDB::BelongsToProxy belongs_to</dt>
187
+ <script type="text/javascript">moveProgressBar('1.0');</script>
188
+ <dd class="spec passed"><span class="passed_spec_name">should return nil when accessed before assignment</span></dd>
189
+ <script type="text/javascript">moveProgressBar('2.0');</script>
190
+ <dd class="spec passed"><span class="passed_spec_name">should be establishable via constructor attribute</span></dd>
191
+ <script type="text/javascript">moveProgressBar('3.0');</script>
192
+ <dd class="spec passed"><span class="passed_spec_name">should be establishable via constructor id</span></dd>
193
+ <script type="text/javascript">moveProgressBar('4.0');</script>
194
+ <dd class="spec passed"><span class="passed_spec_name">should establish the parent relationship when supplied a parent and saved</span></dd>
195
+ <script type="text/javascript">moveProgressBar('5.0');</script>
196
+ <dd class="spec passed"><span class="passed_spec_name">should establish the parent relationship when supplied a parent id and saved</span></dd>
197
+ <script type="text/javascript">moveProgressBar('6.0');</script>
198
+ <dd class="spec passed"><span class="passed_spec_name">should return the same object on repeated invocations</span></dd>
199
+ <script type="text/javascript">moveProgressBar('7.0');</script>
200
+ <dd class="spec passed"><span class="passed_spec_name">should be nullified when the parent is destroyed</span></dd>
201
+ <script type="text/javascript">moveProgressBar('8.0');</script>
202
+ <dd class="spec passed"><span class="passed_spec_name">should be preserved across save / load boundary</span></dd>
203
+ <script type="text/javascript">moveProgressBar('9.0');</script>
204
+ <dd class="spec passed"><span class="passed_spec_name">should be able to reference itself via its parent</span></dd>
205
+ </dl>
206
+ </div>
207
+ <div class="example_group">
208
+ <dl>
209
+ <dt id="example_group_3">RelaxDB::DesignDocument</dt>
210
+ </dl>
211
+ </div>
212
+ <div class="example_group">
213
+ <dl>
214
+ <dt id="example_group_4">RelaxDB::DesignDocument#save</dt>
215
+ <script type="text/javascript">moveProgressBar('10.0');</script>
216
+ <dd class="spec passed"><span class="passed_spec_name">should create a corresponding document in CouchDB</span></dd>
217
+ </dl>
218
+ </div>
219
+ <div class="example_group">
220
+ <dl>
221
+ <dt id="example_group_5">RelaxDB::DesignDocument#destroy</dt>
222
+ <script type="text/javascript">moveProgressBar('11.0');</script>
223
+ <dd class="spec passed"><span class="passed_spec_name">should delete the corresponding document from CouchDB</span></dd>
224
+ </dl>
225
+ </div>
226
+ <div class="example_group">
227
+ <dl>
228
+ <dt id="example_group_6">RelaxDB::Document</dt>
229
+ </dl>
230
+ </div>
231
+ <div class="example_group">
232
+ <dl>
233
+ <dt id="example_group_7">RelaxDB::Document.new</dt>
234
+ <script type="text/javascript">moveProgressBar('12.0');</script>
235
+ <dd class="spec passed"><span class="passed_spec_name">should create an object with an id</span></dd>
236
+ <script type="text/javascript">moveProgressBar('13.0');</script>
237
+ <dd class="spec passed"><span class="passed_spec_name">should create an object with a nil revision</span></dd>
238
+ <script type="text/javascript">moveProgressBar('14.0');</script>
239
+ <dd class="spec passed"><span class="passed_spec_name">should convert attributes that end in _at to dates</span></dd>
240
+ <script type="text/javascript">moveProgressBar('15.0');</script>
241
+ <dd class="spec passed"><span class="passed_spec_name">will silently ignore parameters that don't specify class attributes</span></dd>
242
+ </dl>
243
+ </div>
244
+ <div class="example_group">
245
+ <dl>
246
+ <dt id="example_group_8">RelaxDB::Document#to_json</dt>
247
+ <script type="text/javascript">moveProgressBar('16.0');</script>
248
+ <dd class="spec passed"><span class="passed_spec_name">should not output nil attributes</span></dd>
249
+ </dl>
250
+ </div>
251
+ <div class="example_group">
252
+ <dl>
253
+ <dt id="example_group_9">RelaxDB::Document#save</dt>
254
+ <script type="text/javascript">moveProgressBar('17.0');</script>
255
+ <dd class="spec passed"><span class="passed_spec_name">should set an object's revision</span></dd>
256
+ <script type="text/javascript">moveProgressBar('18.0');</script>
257
+ <dd class="spec passed"><span class="passed_spec_name">should result in an object considered saved</span></dd>
258
+ <script type="text/javascript">moveProgressBar('19.0');</script>
259
+ <dd class="spec passed"><span class="passed_spec_name">should be invokable multiple times</span></dd>
260
+ <script type="text/javascript">moveProgressBar('20.0');</script>
261
+ <dd class="spec passed"><span class="passed_spec_name">should set created_at when first saved</span></dd>
262
+ <script type="text/javascript">moveProgressBar('21.0');</script>
263
+ <dd class="spec passed"><span class="passed_spec_name">should set created_at when first saved unless supplied to the constructor</span></dd>
264
+ </dl>
265
+ </div>
266
+ <div class="example_group">
267
+ <dl>
268
+ <dt id="example_group_10">RelaxDB::Document loaded objects</dt>
269
+ <script type="text/javascript">moveProgressBar('22.0');</script>
270
+ <dd class="spec passed"><span class="passed_spec_name">should contain state as when saved</span></dd>
271
+ <script type="text/javascript">moveProgressBar('23.0');</script>
272
+ <dd class="spec passed"><span class="passed_spec_name">should be saveable</span></dd>
273
+ </dl>
274
+ </div>
275
+ <div class="example_group">
276
+ <dl>
277
+ <dt id="example_group_11">RelaxDB::Document#destroy</dt>
278
+ <script type="text/javascript">moveProgressBar('24.0');</script>
279
+ <dd class="spec passed"><span class="passed_spec_name">should delete the corresponding document from CouchDB</span></dd>
280
+ <script type="text/javascript">moveProgressBar('25.0');</script>
281
+ <dd class="spec passed"><span class="passed_spec_name">should prevent the object from being resaved</span></dd>
282
+ <script type="text/javascript">moveProgressBar('26.0');</script>
283
+ <dd class="spec passed"><span class="passed_spec_name">will result in undefined behaviour when invoked on unsaved objects</span></dd>
284
+ </dl>
285
+ </div>
286
+ <div class="example_group">
287
+ <dl>
288
+ <dt id="example_group_12">RelaxDB::Document#all.destroy!</dt>
289
+ <script type="text/javascript">moveProgressBar('27.0');</script>
290
+ <dd class="spec passed"><span class="passed_spec_name">should delete from CouchDB all documents of the corresponding class</span></dd>
291
+ </dl>
292
+ </div>
293
+ <div class="example_group">
294
+ <dl>
295
+ <dt id="example_group_13">RelaxDB::Document ==</dt>
296
+ <script type="text/javascript">moveProgressBar('28.0');</script>
297
+ <dd class="spec passed"><span class="passed_spec_name">should define equality based on CouchDB id</span></dd>
298
+ <script type="text/javascript">moveProgressBar('29.0');</script>
299
+ <dd class="spec passed"><span class="passed_spec_name">should return false when passed a nil object</span></dd>
300
+ </dl>
301
+ </div>
302
+ <div class="example_group">
303
+ <dl>
304
+ <dt id="example_group_14">RelaxDB::Document#all</dt>
305
+ <script type="text/javascript">moveProgressBar('30.0');</script>
306
+ <dd class="spec passed"><span class="passed_spec_name">should return all instances of that class</span></dd>
307
+ <script type="text/javascript">moveProgressBar('31.0');</script>
308
+ <dd class="spec passed"><span class="passed_spec_name">should return an empty array when no instances exist</span></dd>
309
+ </dl>
310
+ </div>
311
+ <div class="example_group">
312
+ <dl>
313
+ <dt id="example_group_15">RelaxDB::Document#all.sorted_by</dt>
314
+ <script type="text/javascript">moveProgressBar('32.0');</script>
315
+ <dd class="spec passed"><span class="passed_spec_name">should sort ascending by default</span></dd>
316
+ <script type="text/javascript">moveProgressBar('33.0');</script>
317
+ <dd class="spec passed"><span class="passed_spec_name">should sort desc when specified</span></dd>
318
+ <script type="text/javascript">moveProgressBar('34.0');</script>
319
+ <dd class="spec passed"><span class="passed_spec_name">should sort date attributes lexicographically</span></dd>
320
+ </dl>
321
+ </div>
322
+ <div class="example_group">
323
+ <dl>
324
+ <dt id="example_group_16">RelaxDB::Document#all.sorted_by results</dt>
325
+ <script type="text/javascript">moveProgressBar('35.0');</script>
326
+ <dd class="spec passed"><span class="passed_spec_name">should be retrievable by exact criteria</span></dd>
327
+ <script type="text/javascript">moveProgressBar('36.0');</script>
328
+ <dd class="spec passed"><span class="passed_spec_name">should be retrievable by relative criteria</span></dd>
329
+ <script type="text/javascript">moveProgressBar('37.0');</script>
330
+ <dd class="spec passed"><span class="passed_spec_name">should be retrievable by combined criteria</span></dd>
331
+ <script type="text/javascript">moveProgressBar('38.0');</script>
332
+ <dd class="spec passed"><span class="passed_spec_name">should be retrievable by combined criteria where not all docs contain all attributes</span></dd>
333
+ </dl>
334
+ </div>
335
+ <div class="example_group">
336
+ <dl>
337
+ <dt id="example_group_17">RelaxDB::Document defaults</dt>
338
+ <script type="text/javascript">moveProgressBar('39.0');</script>
339
+ <dd class="spec passed"><span class="passed_spec_name">should be set on initialisation</span></dd>
340
+ <script type="text/javascript">moveProgressBar('40.0');</script>
341
+ <dd class="spec passed"><span class="passed_spec_name">should be saved</span></dd>
342
+ <script type="text/javascript">moveProgressBar('41.0');</script>
343
+ <dd class="spec passed"><span class="passed_spec_name">should be ignored once overwritten</span></dd>
344
+ <script type="text/javascript">moveProgressBar('42.0');</script>
345
+ <dd class="spec passed"><span class="passed_spec_name">may be a simple value</span></dd>
346
+ <script type="text/javascript">moveProgressBar('43.0');</script>
347
+ <dd class="spec passed"><span class="passed_spec_name">may be a proc</span></dd>
348
+ </dl>
349
+ </div>
350
+ <div class="example_group">
351
+ <dl>
352
+ <dt id="example_group_18">RelaxDB::Document validator</dt>
353
+ <script type="text/javascript">moveProgressBar('44.0');</script>
354
+ <dd class="spec passed"><span class="passed_spec_name">should prevent an object from being saved if it evaluates to false</span></dd>
355
+ <script type="text/javascript">moveProgressBar('45.0');</script>
356
+ <dd class="spec passed"><span class="passed_spec_name">should prevent an object from being saved if it throws an exception</span></dd>
357
+ <script type="text/javascript">moveProgressBar('46.0');</script>
358
+ <dd class="spec passed"><span class="passed_spec_name">should pass the property value to the validator</span></dd>
359
+ <script type="text/javascript">moveProgressBar('47.0');</script>
360
+ <dd class="spec passed"><span class="passed_spec_name">should add the validation error message if supplied, on failure</span></dd>
361
+ <script type="text/javascript">moveProgressBar('48.0');</script>
362
+ <dd class="spec passed"><span class="passed_spec_name">should perform all validations</span></dd>
363
+ </dl>
364
+ </div>
365
+ <div class="example_group">
366
+ <dl>
367
+ <dt id="example_group_19">RelaxDB::HasManyProxy</dt>
368
+ </dl>
369
+ </div>
370
+ <div class="example_group">
371
+ <dl>
372
+ <dt id="example_group_20">RelaxDB::HasManyProxy has_many</dt>
373
+ <script type="text/javascript">moveProgressBar('49.0');</script>
374
+ <dd class="spec passed"><span class="passed_spec_name">should be considered enumerable</span></dd>
375
+ <script type="text/javascript">moveProgressBar('50.0');</script>
376
+ <dd class="spec passed"><span class="passed_spec_name">should actually be enumerable</span></dd>
377
+ <script type="text/javascript">moveProgressBar('51.0');</script>
378
+ <dd class="spec passed"><span class="passed_spec_name">should preserve the collection across the load / save boundary</span></dd>
379
+ </dl>
380
+ </div>
381
+ <div class="example_group">
382
+ <dl>
383
+ <dt id="example_group_21">RelaxDB::HasManyProxy has_many#&lt;&lt;</dt>
384
+ <script type="text/javascript">moveProgressBar('52.0');</script>
385
+ <dd class="spec passed"><span class="passed_spec_name">should link the added item to the parent</span></dd>
386
+ <script type="text/javascript">moveProgressBar('53.0');</script>
387
+ <dd class="spec passed"><span class="passed_spec_name">should return self</span></dd>
388
+ <script type="text/javascript">moveProgressBar('54.0');</script>
389
+ <dd class="spec passed"><span class="passed_spec_name">should not created duplicates when invoked with same object more than once</span></dd>
390
+ <script type="text/javascript">moveProgressBar('55.0');</script>
391
+ <dd class="spec passed"><span class="passed_spec_name">should return false when the child fails validation</span></dd>
392
+ </dl>
393
+ </div>
394
+ <div class="example_group">
395
+ <dl>
396
+ <dt id="example_group_22">RelaxDB::HasManyProxy has_many#=</dt>
397
+ <script type="text/javascript">moveProgressBar('56.0');</script>
398
+ <dd class="spec passed"><span class="passed_spec_name">should fail</span></dd>
399
+ </dl>
400
+ </div>
401
+ <div class="example_group">
402
+ <dl>
403
+ <dt id="example_group_23">RelaxDB::HasManyProxy has_many#delete</dt>
404
+ <script type="text/javascript">moveProgressBar('57.0');</script>
405
+ <dd class="spec passed"><span class="passed_spec_name">should nullify the belongs_to relationship</span></dd>
406
+ </dl>
407
+ </div>
408
+ <div class="example_group">
409
+ <dl>
410
+ <dt id="example_group_24">RelaxDB::HasManyProxy has_many#clear</dt>
411
+ <script type="text/javascript">moveProgressBar('58.0');</script>
412
+ <dd class="spec passed"><span class="passed_spec_name">should result in an empty collection</span></dd>
413
+ <script type="text/javascript">moveProgressBar('59.0');</script>
414
+ <dd class="spec passed"><span class="passed_spec_name">should nullify all child relationships</span></dd>
415
+ </dl>
416
+ </div>
417
+ <div class="example_group">
418
+ <dl>
419
+ <dt id="example_group_25">RelaxDB::HasManyProxy has_many owner</dt>
420
+ <script type="text/javascript">moveProgressBar('60.0');</script>
421
+ <dd class="spec passed"><span class="passed_spec_name">should be able to form multiple relationships with the same class of child</span></dd>
422
+ </dl>
423
+ </div>
424
+ <div class="example_group">
425
+ <dl>
426
+ <dt id="example_group_26">RelaxDB::HasManyProxy has_many owner#destroy</dt>
427
+ <script type="text/javascript">moveProgressBar('61.0');</script>
428
+ <dd class="spec passed"><span class="passed_spec_name">should nullify its child relationships</span></dd>
429
+ </dl>
430
+ </div>
431
+ <div class="example_group">
432
+ <dl>
433
+ <dt id="example_group_27">RelaxDB::HasOneProxy</dt>
434
+ </dl>
435
+ </div>
436
+ <div class="example_group">
437
+ <dl>
438
+ <dt id="example_group_28">RelaxDB::HasOneProxy has_one</dt>
439
+ <script type="text/javascript">moveProgressBar('62.0');</script>
440
+ <dd class="spec passed"><span class="passed_spec_name">should return nil when accessed before assignment</span></dd>
441
+ <script type="text/javascript">moveProgressBar('63.0');</script>
442
+ <dd class="spec passed"><span class="passed_spec_name">should be establishable via a constructor attribute</span></dd>
443
+ <script type="text/javascript">moveProgressBar('64.0');</script>
444
+ <dd class="spec passed"><span class="passed_spec_name">should be establishable via assignment</span></dd>
445
+ <script type="text/javascript">moveProgressBar('65.0');</script>
446
+ <dd class="spec passed"><span class="passed_spec_name">should return the same object on repeated invocations</span></dd>
447
+ <script type="text/javascript">moveProgressBar('66.0');</script>
448
+ <dd class="spec passed"><span class="passed_spec_name">should be preserved across load / save boundary</span></dd>
449
+ <script type="text/javascript">moveProgressBar('67.0');</script>
450
+ <dd class="spec passed"><span class="passed_spec_name">should be able reference itself via its child</span></dd>
451
+ </dl>
452
+ </div>
453
+ <div class="example_group">
454
+ <dl>
455
+ <dt id="example_group_29">RelaxDB::HasOneProxy has_one#=</dt>
456
+ <script type="text/javascript">moveProgressBar('68.0');</script>
457
+ <dd class="spec passed"><span class="passed_spec_name">should create a reference from the child to the parent</span></dd>
458
+ <script type="text/javascript">moveProgressBar('69.0');</script>
459
+ <dd class="spec passed"><span class="passed_spec_name">should save the assigned object</span></dd>
460
+ <script type="text/javascript">moveProgressBar('70.0');</script>
461
+ <dd class="spec passed"><span class="passed_spec_name">will not save the parent</span></dd>
462
+ <script type="text/javascript">moveProgressBar('71.0');</script>
463
+ <dd class="spec passed"><span class="passed_spec_name">should set the target to nil when nil is assigned</span></dd>
464
+ <script type="text/javascript">moveProgressBar('72.0');</script>
465
+ <dd class="spec passed"><span class="passed_spec_name">should nullify any existing relationship in the database</span></dd>
466
+ <script type="text/javascript">moveProgressBar('73.0');</script>
467
+ <dd class="spec passed"><span class="passed_spec_name">should nullify any existing relationship on a known in-memory object</span></dd>
468
+ <script type="text/javascript">moveProgressBar('74.0');</script>
469
+ <dd class="spec passed"><span class="passed_spec_name">will not nullify any existing relationship on unknown in-memory objects</span></dd>
470
+ <script type="text/javascript">moveProgressBar('75.0');</script>
471
+ <dd class="spec passed"><span class="passed_spec_name">will not throw an error when the rhs fails validation</span></dd>
472
+ </dl>
473
+ </div>
474
+ <div class="example_group">
475
+ <dl>
476
+ <dt id="example_group_30">RelaxDB::Query</dt>
477
+ </dl>
478
+ </div>
479
+ <div class="example_group">
480
+ <dl>
481
+ <dt id="example_group_31">RelaxDB::Query#view_name</dt>
482
+ <script type="text/javascript">moveProgressBar('76.0');</script>
483
+ <dd class="spec passed"><span class="passed_spec_name">should match a single key attribute</span></dd>
484
+ <script type="text/javascript">moveProgressBar('77.0');</script>
485
+ <dd class="spec passed"><span class="passed_spec_name">should match key attributes</span></dd>
486
+ </dl>
487
+ </div>
488
+ <div class="example_group">
489
+ <dl>
490
+ <dt id="example_group_32">RelaxDB::Query#view_path</dt>
491
+ <script type="text/javascript">moveProgressBar('78.0');</script>
492
+ <dd class="spec passed"><span class="passed_spec_name">should list design document and view name</span></dd>
493
+ <script type="text/javascript">moveProgressBar('79.0');</script>
494
+ <dd class="spec passed"><span class="passed_spec_name">should contain URL and JSON encoded key when the key has been set</span></dd>
495
+ <script type="text/javascript">moveProgressBar('80.0');</script>
496
+ <dd class="spec passed"><span class="passed_spec_name">should honour startkey, endkey and count</span></dd>
497
+ <script type="text/javascript">moveProgressBar('81.0');</script>
498
+ <dd class="spec passed"><span class="passed_spec_name">should specify the key as the empty string if key was set to nil</span></dd>
499
+ </dl>
500
+ </div>
501
+ <div class="example_group">
502
+ <dl>
503
+ <dt id="example_group_33">RelaxDB::ReferencesManyProxy</dt>
504
+ </dl>
505
+ </div>
506
+ <div class="example_group">
507
+ <dl>
508
+ <dt id="example_group_34">RelaxDB::ReferencesManyProxy references_many</dt>
509
+ <script type="text/javascript">moveProgressBar('82.0');</script>
510
+ <dd class="spec passed"><span class="passed_spec_name">is now deprecated and will be removed in the near future</span></dd>
511
+ <script type="text/javascript">moveProgressBar('83.0');</script>
512
+ <dd class="spec passed"><span class="passed_spec_name">should preserve the relationships across the save / load boundary</span></dd>
513
+ </dl>
514
+ </div>
515
+ <div class="example_group">
516
+ <dl>
517
+ <dt id="example_group_35">RelaxDB::ReferencesManyProxy references_many#=</dt>
518
+ <script type="text/javascript">moveProgressBar('84.0');</script>
519
+ <dd class="spec passed"><span class="passed_spec_name">should not be invoked</span></dd>
520
+ </dl>
521
+ </div>
522
+ <div class="example_group">
523
+ <dl>
524
+ <dt id="example_group_36">RelaxDB::ReferencesManyProxy references_many#&lt;&lt;</dt>
525
+ <script type="text/javascript">moveProgressBar('85.0');</script>
526
+ <dd class="spec passed"><span class="passed_spec_name">should set the relationship on both sides</span></dd>
527
+ <script type="text/javascript">moveProgressBar('86.0');</script>
528
+ <dd class="spec passed"><span class="passed_spec_name">should not create duplicates when the same object is added more than once</span></dd>
529
+ <script type="text/javascript">moveProgressBar('87.0');</script>
530
+ <dd class="spec passed"><span class="passed_spec_name">should not create duplicates when reciprocal objects are added from opposite sides</span></dd>
531
+ </dl>
532
+ </div>
533
+ <div class="example_group">
534
+ <dl>
535
+ <dt id="example_group_37">RelaxDB::ReferencesManyProxy references_many#delete</dt>
536
+ <script type="text/javascript">moveProgressBar('88.0');</script>
537
+ <dd class="spec passed"><span class="passed_spec_name">should nullify relationship on both sides</span></dd>
538
+ </dl>
539
+ </div>
540
+ <div class="example_group">
541
+ <dl>
542
+ <dt id="example_group_38">RelaxDB::ReferencesManyProxy references_many owner#destroy</dt>
543
+ <script type="text/javascript">moveProgressBar('89.0');</script>
544
+ <dd class="spec passed"><span class="passed_spec_name">will not remove its membership from its peers in memory</span></dd>
545
+ <script type="text/javascript">moveProgressBar('90.0');</script>
546
+ <dd class="spec passed"><span class="passed_spec_name">should remove its membership from its peers in CouchDB</span></dd>
547
+ </dl>
548
+ </div>
549
+ <div class="example_group">
550
+ <dl>
551
+ <dt id="example_group_39">RelaxDB</dt>
552
+ <script type="text/javascript">moveProgressBar('91.0');</script>
553
+ <dd class="spec passed"><span class="passed_spec_name">should offer an example where behaviour is different with caching enabled and caching disabled</span></dd>
554
+ </dl>
555
+ </div>
556
+ <div class="example_group">
557
+ <dl>
558
+ <dt id="example_group_40">RelaxDB.create_object</dt>
559
+ <script type="text/javascript">moveProgressBar('92.0');</script>
560
+ <dd class="spec passed"><span class="passed_spec_name">should return an instance of a known object if passed a hash with a class key</span></dd>
561
+ <script type="text/javascript">moveProgressBar('93.0');</script>
562
+ <dd class="spec passed"><span class="passed_spec_name">should return an instance of a dynamically created object if no class key is provided</span></dd>
563
+ </dl>
564
+ </div>
565
+ <div class="example_group">
566
+ <dl>
567
+ <dt id="example_group_41">RelaxDB.bulk_save</dt>
568
+ <script type="text/javascript">moveProgressBar('94.0');</script>
569
+ <dd class="spec passed"><span class="passed_spec_name">should be invokable multiple times</span></dd>
570
+ <script type="text/javascript">moveProgressBar('95.0');</script>
571
+ <dd class="spec passed"><span class="passed_spec_name">should succeed when passed no args</span></dd>
572
+ </dl>
573
+ </div>
574
+ <div class="example_group">
575
+ <dl>
576
+ <dt id="example_group_42">RelaxDB::ViewObject</dt>
577
+ </dl>
578
+ </div>
579
+ <div class="example_group">
580
+ <dl>
581
+ <dt id="example_group_43">RelaxDB::ViewObject.new</dt>
582
+ <script type="text/javascript">moveProgressBar('96.0');</script>
583
+ <dd class="spec passed"><span class="passed_spec_name">should provide readers for the object passed in the hash</span></dd>
584
+ <script type="text/javascript">moveProgressBar('97.0');</script>
585
+ <dd class="spec passed"><span class="passed_spec_name">should try to convert objects ending in _at to a time</span></dd>
586
+ </dl>
587
+ </div>
588
+ <div class="example_group">
589
+ <dl>
590
+ <dt id="example_group_44">RelaxDB::ViewObject.create</dt>
591
+ <script type="text/javascript">moveProgressBar('98.0');</script>
592
+ <dd class="spec passed"><span class="passed_spec_name">should return an array of view objects when passed an array</span></dd>
593
+ <script type="text/javascript">moveProgressBar('99.0');</script>
594
+ <dd class="spec passed"><span class="passed_spec_name">should return a view object when passed a hash</span></dd>
595
+ <script type="text/javascript">moveProgressBar('100.0');</script>
596
+ <dd class="spec passed"><span class="passed_spec_name">should return a simple value when passed a primitive</span></dd>
597
+ </dl>
598
+ </div>
599
+ <script type="text/javascript">document.getElementById('duration').innerHTML = "Finished in <strong>11.855995 seconds</strong>";</script>
600
+ <script type="text/javascript">document.getElementById('totals').innerHTML = "100 examples, 0 failures";</script>
601
+ </div>
602
+ </div>
603
+ </body>
604
+ </html>