paulcarey-relaxdb 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +48 -0
- data/Rakefile +1 -1
- data/lib/more/grapher.rb +6 -0
- data/lib/relaxdb.rb +3 -0
- data/lib/relaxdb/design_doc.rb +3 -3
- data/lib/relaxdb/document.rb +25 -1
- data/lib/relaxdb/paginate_params.rb +52 -0
- data/lib/relaxdb/paginator.rb +79 -0
- data/lib/relaxdb/query.rb +19 -6
- data/lib/relaxdb/relaxdb.rb +5 -1
- data/lib/relaxdb/server.rb +6 -1
- data/lib/relaxdb/sorted_by_view.rb +24 -10
- data/spec/design_doc_spec.rb +2 -2
- data/spec/document_spec.rb +26 -1
- data/spec/query_spec.rb +30 -6
- data/spec/spec_models.rb +17 -0
- metadata +3 -1
data/README.textile
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
h3. What's New?
|
2
|
+
* Pagination! CouchDB offers great support for retrieving a subset of data, but the housekeeping is tricky. RelaxDB takes care of it.
|
3
|
+
* Works with CouchDB 0.9 trunk as of 2008/10/08. Note that pagination won't work on trunk unil https://issues.apache.org/jira/browse/COUCHDB-135 is fixed.
|
4
|
+
|
1
5
|
h2. Overview
|
2
6
|
|
3
7
|
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.
|
@@ -64,6 +68,32 @@ h3. Exploring models
|
|
64
68
|
</code>
|
65
69
|
</pre>
|
66
70
|
|
71
|
+
h3. Paginating
|
72
|
+
|
73
|
+
<pre>
|
74
|
+
<code>
|
75
|
+
# Controller (merb-action-args used for extracting view_params)
|
76
|
+
|
77
|
+
def action(view_params={})
|
78
|
+
u_id = @user._id
|
79
|
+
|
80
|
+
@posts = Post.paginate_by(view_params, :writer_id, :created_at) do |p|
|
81
|
+
p.startkey([u_id, {}]).endkey([u_id]).descending(true).count(5)
|
82
|
+
end
|
83
|
+
render
|
84
|
+
end
|
85
|
+
|
86
|
+
# In your view
|
87
|
+
|
88
|
+
<% @posts.each do |p| %>
|
89
|
+
<%= p.contents %>
|
90
|
+
<% end %>
|
91
|
+
|
92
|
+
<%= link_to "prev", "/posts/?#{@posts.prev_query}" if @posts.prev_query %>
|
93
|
+
<%= link_to "next", "/posts/?#{@posts.next_query}" if @posts.next_query %>
|
94
|
+
</code>
|
95
|
+
</pre>
|
96
|
+
|
67
97
|
h3. Creating views by hand
|
68
98
|
|
69
99
|
<pre>
|
@@ -87,6 +117,24 @@ h3. Creating views by hand
|
|
87
117
|
</code>
|
88
118
|
</pre>
|
89
119
|
|
120
|
+
h3. Visualise
|
121
|
+
|
122
|
+
Create an object graph by simply running
|
123
|
+
<pre>
|
124
|
+
<code>
|
125
|
+
RelaxDB::GraphCreator.create
|
126
|
+
</code>
|
127
|
+
</pre>
|
128
|
+
|
129
|
+
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/.
|
130
|
+
|
131
|
+
h3. Experimental Features
|
132
|
+
|
133
|
+
* Declarative denormalisation
|
134
|
+
** Create a partial object graph in JSON with a single call
|
135
|
+
** May be used to require fewer GET requests
|
136
|
+
** View the denormalisation spec for examples
|
137
|
+
|
90
138
|
h2. Incomplete list of limitations
|
91
139
|
|
92
140
|
* Error handling is not robust
|
data/Rakefile
CHANGED
data/lib/more/grapher.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
module RelaxDB
|
2
2
|
|
3
|
+
#
|
4
|
+
# The GraphCreator uses dot to create a graphical model of an entire CouchDB database
|
5
|
+
# It probably only makes sense to run it on a database of a limited size
|
6
|
+
# The created graphs can be very useful for exploring relationships
|
7
|
+
# Run ruby scratch/grapher_demo.rb for an example
|
8
|
+
#
|
3
9
|
class GraphCreator
|
4
10
|
|
5
11
|
def self.create
|
data/lib/relaxdb.rb
CHANGED
@@ -20,6 +20,8 @@ require 'relaxdb/document'
|
|
20
20
|
require 'relaxdb/extlib'
|
21
21
|
require 'relaxdb/has_many_proxy'
|
22
22
|
require 'relaxdb/has_one_proxy'
|
23
|
+
require 'relaxdb/paginate_params'
|
24
|
+
require 'relaxdb/paginator'
|
23
25
|
require 'relaxdb/query'
|
24
26
|
require 'relaxdb/references_many_proxy'
|
25
27
|
require 'relaxdb/relaxdb'
|
@@ -27,6 +29,7 @@ require 'relaxdb/server'
|
|
27
29
|
require 'relaxdb/sorted_by_view'
|
28
30
|
require 'relaxdb/uuid_generator'
|
29
31
|
require 'relaxdb/view_object'
|
32
|
+
require 'relaxdb/view_result'
|
30
33
|
require 'relaxdb/view_uploader'
|
31
34
|
require 'relaxdb/views'
|
32
35
|
require 'more/grapher.rb'
|
data/lib/relaxdb/design_doc.rb
CHANGED
@@ -24,7 +24,7 @@ module RelaxDB
|
|
24
24
|
|
25
25
|
def save
|
26
26
|
database = RelaxDB.db
|
27
|
-
resp = database.put(
|
27
|
+
resp = database.put(::CGI::escape(@data["_id"]), @data.to_json)
|
28
28
|
@data["_rev"] = JSON.parse(resp.body)["rev"]
|
29
29
|
self
|
30
30
|
end
|
@@ -32,7 +32,7 @@ module RelaxDB
|
|
32
32
|
def self.get(client_class)
|
33
33
|
begin
|
34
34
|
database = RelaxDB.db
|
35
|
-
resp = database.get("_design/#{client_class}")
|
35
|
+
resp = database.get(::CGI::escape("_design/#{client_class}"))
|
36
36
|
DesignDocument.new(client_class, JSON.parse(resp.body))
|
37
37
|
rescue => e
|
38
38
|
DesignDocument.new(client_class, {"_id" => "_design/#{client_class}"} )
|
@@ -41,7 +41,7 @@ module RelaxDB
|
|
41
41
|
|
42
42
|
def destroy!
|
43
43
|
# Implicitly prevent the object from being resaved by failing to update its revision
|
44
|
-
RelaxDB.db.delete("#{@data["_id"]}?rev=#{@data["_rev"]}")
|
44
|
+
RelaxDB.db.delete("#{::CGI::escape(@data["_id"])}?rev=#{@data["_rev"]}")
|
45
45
|
self
|
46
46
|
end
|
47
47
|
|
data/lib/relaxdb/document.rb
CHANGED
@@ -278,6 +278,11 @@ module RelaxDB
|
|
278
278
|
define_method("#{relationship}_id=") do |id|
|
279
279
|
instance_variable_set("@#{relationship}_id".to_sym, id)
|
280
280
|
end
|
281
|
+
|
282
|
+
# Allows belongs_to relationships to be used by the paginator
|
283
|
+
define_method("#{relationship}_id") do
|
284
|
+
instance_variable_get("@#{relationship}_id")
|
285
|
+
end
|
281
286
|
|
282
287
|
end
|
283
288
|
|
@@ -346,7 +351,26 @@ module RelaxDB
|
|
346
351
|
callback.is_a?(Proc) ? callback.call(self) : send(callback)
|
347
352
|
end
|
348
353
|
end
|
349
|
-
|
354
|
+
|
355
|
+
def self.paginate_by(page_params, *atts)
|
356
|
+
paginate_params = PaginateParams.new
|
357
|
+
yield paginate_params
|
358
|
+
raise paginate_params.error_msg if paginate_params.invalid?
|
359
|
+
|
360
|
+
paginator = Paginator.new(paginate_params, page_params)
|
361
|
+
|
362
|
+
doc_view = SortedByView.new(self.name, *atts)
|
363
|
+
doc_query = Query.new(self.name, doc_view.view_name)
|
364
|
+
doc_query.merge(paginator.paginate_params)
|
365
|
+
|
366
|
+
@docs = RelaxDB.retrieve(doc_query.view_path, self, doc_view.view_name, doc_view.map_function)
|
367
|
+
@docs.reverse! if paginate_params.order_inverted?
|
368
|
+
|
369
|
+
paginator.add_next_and_prev(@docs, self.name, doc_view, atts)
|
370
|
+
|
371
|
+
@docs
|
372
|
+
end
|
373
|
+
|
350
374
|
end
|
351
375
|
|
352
376
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class PaginateParams
|
4
|
+
|
5
|
+
@@params = %w(key startkey startkey_docid endkey endkey_docid count update descending group)
|
6
|
+
|
7
|
+
@@params.each do |param|
|
8
|
+
define_method(param.to_sym) do |*val|
|
9
|
+
if val.empty?
|
10
|
+
instance_variable_get("@#{param}")
|
11
|
+
else
|
12
|
+
instance_variable_set("@#{param}", val[0])
|
13
|
+
# null is meaningful to CouchDB. _set allows us to know that a param has been set, even to nil
|
14
|
+
instance_variable_set("@#{param}_set", true)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
# If a client hasn't explicitly set descending, set it to the CouchDB default
|
22
|
+
@descending = false if @descending.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def update(params)
|
26
|
+
@order_inverted = params[:descending].nil? ? false : @descending ^ params[:descending]
|
27
|
+
@descending = !@descending if @order_inverted
|
28
|
+
|
29
|
+
@endkey = @startkey if @order_inverted
|
30
|
+
|
31
|
+
@startkey = params[:startkey] || @startkey
|
32
|
+
|
33
|
+
@skip = 1 if params[:startkey]
|
34
|
+
|
35
|
+
@startkey_docid = params[:startkey_docid] if params[:startkey_docid]
|
36
|
+
@endkey_docid = params[:endkey_docid] if params[:endkey_docid]
|
37
|
+
end
|
38
|
+
|
39
|
+
def order_inverted?
|
40
|
+
@order_inverted
|
41
|
+
end
|
42
|
+
|
43
|
+
def invalid?
|
44
|
+
# Simply because allowing either to be omitted increases the complexity of the paginator
|
45
|
+
# This constraint may be removed in future, but don't hold your breath
|
46
|
+
@startkey_set && @endkey_set ? nil : "Both startkey and endkey must be set"
|
47
|
+
end
|
48
|
+
alias error_msg invalid?
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class Paginator
|
4
|
+
|
5
|
+
attr_reader :paginate_params
|
6
|
+
|
7
|
+
def initialize(paginate_params, page_params)
|
8
|
+
@paginate_params = paginate_params
|
9
|
+
@orig_paginate_params = @paginate_params.clone
|
10
|
+
|
11
|
+
page_params = page_params.is_a?(String) ? JSON.parse(page_params).to_mash : page_params
|
12
|
+
@paginate_params.update(page_params)
|
13
|
+
end
|
14
|
+
|
15
|
+
def total_doc_count(design_doc, view)
|
16
|
+
query = lambda do
|
17
|
+
RelaxDB.view(design_doc, view.reduce_view_name) do |q|
|
18
|
+
q.group(true).group_level(0)
|
19
|
+
q.startkey(@orig_paginate_params.startkey).endkey(@orig_paginate_params.endkey).descending(@orig_paginate_params.descending)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
result = query.call
|
25
|
+
rescue
|
26
|
+
# add the map reduce func if it doesn't exist
|
27
|
+
DesignDocument.get(design_doc).add_map_view(view.reduce_view_name, view.map_function).
|
28
|
+
add_reduce_view(view.reduce_view_name, view.reduce_function).save
|
29
|
+
result = query.call
|
30
|
+
end
|
31
|
+
|
32
|
+
total_docs = RelaxDB.reduce_result(result)
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_next_and_prev(docs, design_doc, view, view_keys)
|
36
|
+
unless docs.empty?
|
37
|
+
no_docs = docs.size
|
38
|
+
offset = docs.offset
|
39
|
+
orig_offset = orig_offset(Query.new(design_doc, view.view_name), view)
|
40
|
+
total_doc_count = total_doc_count(design_doc, view)
|
41
|
+
|
42
|
+
next_key = view_keys.map { |a| docs.last.send(a) }
|
43
|
+
next_key = next_key.length == 1 ? next_key[0] : next_key
|
44
|
+
next_key_docid = docs.last._id
|
45
|
+
next_params = { :startkey => next_key, :startkey_docid => next_key_docid, :descending => @orig_paginate_params.descending }
|
46
|
+
next_exists = !@paginate_params.order_inverted? ? (offset - orig_offset + no_docs < total_doc_count) : true
|
47
|
+
|
48
|
+
prev_key = view_keys.map { |a| docs.first.send(a) }
|
49
|
+
prev_key = prev_key.length == 1 ? prev_key[0] : prev_key
|
50
|
+
prev_key_docid = docs.first._id
|
51
|
+
prev_params = { :startkey => prev_key, :startkey_docid => prev_key_docid, :descending => !@orig_paginate_params.descending }
|
52
|
+
prev_exists = @paginate_params.order_inverted? ? (offset - orig_offset + no_docs < total_doc_count) :
|
53
|
+
(offset - orig_offset == 0 ? false : true)
|
54
|
+
else
|
55
|
+
next_exists, prev_exists = false
|
56
|
+
end
|
57
|
+
|
58
|
+
docs.meta_class.instance_eval do
|
59
|
+
define_method(:next_params) { next_exists ? next_params : false }
|
60
|
+
define_method(:next_query) { next_exists ? "page_params=#{::CGI::escape(next_params.to_json)}" : false }
|
61
|
+
|
62
|
+
define_method(:prev_params) { prev_exists ? prev_params : false }
|
63
|
+
define_method(:prev_query) { prev_exists ? "page_params=#{::CGI::escape(prev_params.to_json)}" : false }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def orig_offset(query, view)
|
68
|
+
if @paginate_params.order_inverted?
|
69
|
+
query.startkey(@orig_paginate_params.endkey).descending(!@orig_paginate_params.descending)
|
70
|
+
else
|
71
|
+
query.startkey(@orig_paginate_params.startkey).descending(@orig_paginate_params.descending)
|
72
|
+
end
|
73
|
+
query.count(1)
|
74
|
+
RelaxDB.retrieve(query.view_path, self, view.view_name, view.map_function).offset
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
data/lib/relaxdb/query.rb
CHANGED
@@ -16,16 +16,17 @@ module RelaxDB
|
|
16
16
|
#
|
17
17
|
class Query
|
18
18
|
|
19
|
-
@@params = %w(key startkey startkey_docid endkey endkey_docid count update descending skip group)
|
19
|
+
@@params = %w(key startkey startkey_docid endkey endkey_docid count update descending skip group group_level)
|
20
20
|
|
21
21
|
@@params.each do |param|
|
22
22
|
define_method(param.to_sym) do |val|
|
23
|
-
|
24
|
-
|
23
|
+
instance_variable_set("@#{param}", val)
|
24
|
+
# null is meaningful to CouchDB. _set allows us to know that a param has been set, even to nil
|
25
|
+
instance_variable_set("@#{param}_set", true)
|
25
26
|
self
|
26
27
|
end
|
27
28
|
end
|
28
|
-
|
29
|
+
|
29
30
|
def initialize(design_doc, view_name)
|
30
31
|
@design_doc = design_doc
|
31
32
|
@view_name = view_name
|
@@ -36,12 +37,24 @@ module RelaxDB
|
|
36
37
|
|
37
38
|
query = ""
|
38
39
|
@@params.each do |param|
|
39
|
-
|
40
|
-
|
40
|
+
val_set = instance_variable_get("@#{param}_set")
|
41
|
+
if val_set
|
42
|
+
val = instance_variable_get("@#{param}")
|
43
|
+
val = val.to_json unless ["startkey_docid", "endkey_docid"].include?(param)
|
44
|
+
query << "&#{param}=#{::CGI::escape(val)}"
|
45
|
+
end
|
41
46
|
end
|
42
47
|
|
43
48
|
uri << query.sub(/^&/, "?")
|
44
49
|
end
|
50
|
+
|
51
|
+
def merge(paginate_params)
|
52
|
+
paginate_params.instance_variables.each do |pp|
|
53
|
+
val = paginate_params.instance_variable_get(pp)
|
54
|
+
method_name = pp[1, pp.length]
|
55
|
+
send(method_name, val) if methods.include? method_name
|
56
|
+
end
|
57
|
+
end
|
45
58
|
|
46
59
|
end
|
47
60
|
|
data/lib/relaxdb/relaxdb.rb
CHANGED
@@ -12,6 +12,10 @@ module RelaxDB
|
|
12
12
|
@@db
|
13
13
|
end
|
14
14
|
|
15
|
+
def logger
|
16
|
+
@@db.logger
|
17
|
+
end
|
18
|
+
|
15
19
|
# Creates the named database if it doesn't already exist
|
16
20
|
def use_db(name)
|
17
21
|
db.use_db(name)
|
@@ -59,7 +63,7 @@ module RelaxDB
|
|
59
63
|
end
|
60
64
|
|
61
65
|
data = JSON.parse(resp.body)
|
62
|
-
|
66
|
+
ViewResult.new(data)
|
63
67
|
end
|
64
68
|
|
65
69
|
# Requests the given view from CouchDB and returns a hash.
|
data/lib/relaxdb/server.rb
CHANGED
@@ -100,7 +100,8 @@ module RelaxDB
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def unesc(path)
|
103
|
-
|
103
|
+
# path
|
104
|
+
path ? ::CGI::unescape(path) : ""
|
104
105
|
end
|
105
106
|
|
106
107
|
def uri
|
@@ -111,6 +112,10 @@ module RelaxDB
|
|
111
112
|
@db
|
112
113
|
end
|
113
114
|
|
115
|
+
def logger
|
116
|
+
@logger
|
117
|
+
end
|
118
|
+
|
114
119
|
private
|
115
120
|
|
116
121
|
def create_db_if_non_existant(name)
|
@@ -9,18 +9,9 @@ module RelaxDB
|
|
9
9
|
@atts = atts
|
10
10
|
end
|
11
11
|
|
12
|
-
def view_name
|
13
|
-
name = "all_sorted_by"
|
14
|
-
|
15
|
-
@atts.each do |att|
|
16
|
-
name += "_#{att}_and"
|
17
|
-
end
|
18
|
-
name[0, name.size-4]
|
19
|
-
end
|
20
|
-
|
21
12
|
def map_function
|
22
13
|
# To guard against non existing attributes in older documents, an OR with an object literal
|
23
|
-
# is inserted for each emitted key
|
14
|
+
# is inserted for each emitted key. The guard can be emitted in 0.9 trunk.
|
24
15
|
# The object literal is the lowest sorting JSON category
|
25
16
|
|
26
17
|
# Create the key from the attributes, wrapping it in [] if the key is composite
|
@@ -36,6 +27,29 @@ module RelaxDB
|
|
36
27
|
}
|
37
28
|
QUERY
|
38
29
|
end
|
30
|
+
|
31
|
+
def reduce_function
|
32
|
+
<<-QUERY
|
33
|
+
function(keys, values, rereduce) {
|
34
|
+
return values.length;
|
35
|
+
}
|
36
|
+
QUERY
|
37
|
+
end
|
38
|
+
|
39
|
+
def view_name
|
40
|
+
name = "all_sorted_by#{suffix}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def reduce_view_name
|
44
|
+
"reduce_by#{suffix}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def suffix
|
48
|
+
s = @atts.inject("") do |s, att|
|
49
|
+
s << "_#{att}_and"
|
50
|
+
end
|
51
|
+
s[0, s.size-4]
|
52
|
+
end
|
39
53
|
|
40
54
|
end
|
41
55
|
|
data/spec/design_doc_spec.rb
CHANGED
@@ -16,7 +16,7 @@ describe RelaxDB::DesignDocument do
|
|
16
16
|
|
17
17
|
it "should create a corresponding document in CouchDB" do
|
18
18
|
RelaxDB::DesignDocument.get("foo").save
|
19
|
-
RelaxDB.load("_design
|
19
|
+
RelaxDB.load("_design%2Ffoo").should_not be_nil
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -26,7 +26,7 @@ describe RelaxDB::DesignDocument do
|
|
26
26
|
it "should delete the corresponding document from CouchDB" do
|
27
27
|
dd = RelaxDB::DesignDocument.get("foo").save
|
28
28
|
dd.destroy!
|
29
|
-
lambda { RelaxDB.load("_design
|
29
|
+
lambda { RelaxDB.load("_design%2Ffoo") }.should raise_error
|
30
30
|
end
|
31
31
|
|
32
32
|
end
|
data/spec/document_spec.rb
CHANGED
@@ -78,6 +78,32 @@ describe RelaxDB::Document do
|
|
78
78
|
|
79
79
|
end
|
80
80
|
|
81
|
+
describe "user defined property reader" do
|
82
|
+
|
83
|
+
it "should not effect normal operation" do
|
84
|
+
o = BespokeReader.new(:val => 101).save
|
85
|
+
o = RelaxDB.load o._id
|
86
|
+
o.val.should == 106
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not modify internal state" do
|
90
|
+
o = BespokeReader.new(:val => 101).save
|
91
|
+
o = RelaxDB.load o._id
|
92
|
+
o.instance_variable_get(:@val).should == 101
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "user defined property writer" do
|
98
|
+
|
99
|
+
it "should not be used" do
|
100
|
+
o = BespokeWriter.new(:val => 101).save
|
101
|
+
o = RelaxDB.load o._id
|
102
|
+
o.val.should == 81
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
81
107
|
describe "loaded objects" do
|
82
108
|
|
83
109
|
it "should contain state as when saved" do
|
@@ -113,7 +139,6 @@ describe RelaxDB::Document do
|
|
113
139
|
end
|
114
140
|
|
115
141
|
it "will result in undefined behaviour when invoked on unsaved objects" do
|
116
|
-
Photo.new.destroy!
|
117
142
|
lambda { Atom.new.destroy! }.should raise_error
|
118
143
|
end
|
119
144
|
|
data/spec/query_spec.rb
CHANGED
@@ -24,21 +24,45 @@ describe RelaxDB::Query do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should contain URL and JSON encoded key when the key has been set" do
|
27
|
-
q = RelaxDB::Query.new("
|
27
|
+
q = RelaxDB::Query.new("", "")
|
28
28
|
q.key("olympus")
|
29
|
-
q.view_path.should == "_view
|
29
|
+
q.view_path.should == "_view//?key=%22olympus%22"
|
30
30
|
end
|
31
31
|
|
32
32
|
it "should honour startkey, endkey and count" do
|
33
|
-
q = RelaxDB::Query.new("
|
33
|
+
q = RelaxDB::Query.new("", "")
|
34
34
|
q.startkey(["olympus"]).endkey(["vesuvius", 3600]).count(100)
|
35
|
-
q.view_path.should == "_view
|
35
|
+
q.view_path.should == "_view//?startkey=%5B%22olympus%22%5D&endkey=%5B%22vesuvius%22%2C3600%5D&count=100"
|
36
36
|
end
|
37
37
|
|
38
|
-
it "should specify
|
38
|
+
it "should specify a null key if key was set to nil" do
|
39
39
|
q = RelaxDB::Query.new("", "")
|
40
40
|
q.key(nil)
|
41
|
-
q.view_path.should == "_view//?key
|
41
|
+
q.view_path.should == "_view//?key=null"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should specify a null startkey if startkey was set to nil" do
|
45
|
+
q = RelaxDB::Query.new("", "")
|
46
|
+
q.startkey(nil)
|
47
|
+
q.view_path.should == "_view//?startkey=null"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should specify a null endkey if endkey was set to nil" do
|
51
|
+
q = RelaxDB::Query.new("", "")
|
52
|
+
q.endkey(nil)
|
53
|
+
q.view_path.should == "_view//?endkey=null"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not JSON encode the startkey_docid" do
|
57
|
+
q = RelaxDB::Query.new("", "")
|
58
|
+
q.startkey_docid("foo")
|
59
|
+
q.view_path.should == "_view//?startkey_docid=foo"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not JSON encode the endkey_docid" do
|
63
|
+
q = RelaxDB::Query.new("", "")
|
64
|
+
q.endkey_docid("foo")
|
65
|
+
q.view_path.should == "_view//?endkey_docid=foo"
|
42
66
|
end
|
43
67
|
|
44
68
|
end
|
data/spec/spec_models.rb
CHANGED
@@ -12,6 +12,23 @@ class Primitives < RelaxDB::Document
|
|
12
12
|
|
13
13
|
end
|
14
14
|
|
15
|
+
class BespokeReader < RelaxDB::Document
|
16
|
+
property :val
|
17
|
+
def val; @val + 5; end
|
18
|
+
end
|
19
|
+
|
20
|
+
class BespokeWriter < RelaxDB::Document
|
21
|
+
property :val
|
22
|
+
def val=(v); @val = v - 10; end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Letter < RelaxDB::Document
|
26
|
+
|
27
|
+
property :letter
|
28
|
+
property :number
|
29
|
+
|
30
|
+
end
|
31
|
+
|
15
32
|
class Invite < RelaxDB::Document
|
16
33
|
|
17
34
|
property :message
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paulcarey-relaxdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Carey
|
@@ -60,6 +60,8 @@ files:
|
|
60
60
|
- lib/relaxdb/extlib.rb
|
61
61
|
- lib/relaxdb/has_many_proxy.rb
|
62
62
|
- lib/relaxdb/has_one_proxy.rb
|
63
|
+
- lib/relaxdb/paginate_params.rb
|
64
|
+
- lib/relaxdb/paginator.rb
|
63
65
|
- lib/relaxdb/query.rb
|
64
66
|
- lib/relaxdb/references_many_proxy.rb
|
65
67
|
- lib/relaxdb/relaxdb.rb
|