jchris-couchrest 0.9.11 → 0.9.12
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -3
- data/lib/couchrest.rb +2 -2
- data/lib/couchrest/core/database.rb +42 -13
- data/lib/couchrest/core/model.rb +85 -29
- data/lib/couchrest/helper/streamer.rb +18 -4
- data/spec/couchapp_spec.rb +1 -1
- data/spec/couchrest/core/database_spec.rb +48 -18
- data/spec/couchrest/core/model_spec.rb +93 -11
- data/spec/{file_manager_spec.rb → couchrest/helpers/file_manager_spec.rb} +7 -7
- data/spec/{pager_spec.rb → couchrest/helpers/pager_spec.rb} +1 -1
- data/spec/{streamer_spec.rb → couchrest/helpers/streamer_spec.rb} +2 -2
- data/spec/spec_helper.rb +2 -0
- metadata +12 -5
data/Rakefile
CHANGED
@@ -5,8 +5,8 @@ require 'spec/rake/spectask'
|
|
5
5
|
|
6
6
|
spec = Gem::Specification.new do |s|
|
7
7
|
s.name = "couchrest"
|
8
|
-
s.version = "0.9.
|
9
|
-
s.date = "2008-
|
8
|
+
s.version = "0.9.12"
|
9
|
+
s.date = "2008-10-14"
|
10
10
|
s.summary = "Lean and RESTful interface to CouchDB."
|
11
11
|
s.email = "jchris@grabb.it"
|
12
12
|
s.homepage = "http://github.com/jchris/couchrest"
|
@@ -61,7 +61,7 @@ end
|
|
61
61
|
|
62
62
|
desc "Run all specs"
|
63
63
|
Spec::Rake::SpecTask.new('spec') do |t|
|
64
|
-
t.spec_files = FileList['spec
|
64
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
65
65
|
end
|
66
66
|
|
67
67
|
desc "Print specdocs"
|
data/lib/couchrest.rb
CHANGED
@@ -108,8 +108,8 @@ module CouchRest
|
|
108
108
|
JSON.parse(RestClient.delete(uri))
|
109
109
|
end
|
110
110
|
|
111
|
-
def paramify_url url, params =
|
112
|
-
if params
|
111
|
+
def paramify_url url, params = {}
|
112
|
+
if params && !params.empty?
|
113
113
|
query = params.collect do |k,v|
|
114
114
|
v = v.to_json if %w{key startkey endkey}.include?(k.to_s)
|
115
115
|
"#{k}=#{CGI.escape(v.to_s)}"
|
@@ -5,8 +5,9 @@ module CouchRest
|
|
5
5
|
class Database
|
6
6
|
attr_reader :server, :host, :name, :root
|
7
7
|
|
8
|
-
# Create a CouchRest::Database adapter for the supplied CouchRest::Server
|
9
|
-
#
|
8
|
+
# Create a CouchRest::Database adapter for the supplied CouchRest::Server
|
9
|
+
# and database name.
|
10
|
+
#
|
10
11
|
# ==== Parameters
|
11
12
|
# server<CouchRest::Server>:: database host
|
12
13
|
# name<String>:: database name
|
@@ -16,6 +17,7 @@ module CouchRest
|
|
16
17
|
@server = server
|
17
18
|
@host = server.uri
|
18
19
|
@root = "#{host}/#{name}"
|
20
|
+
@streamer = Streamer.new(self)
|
19
21
|
end
|
20
22
|
|
21
23
|
# returns the database's uri
|
@@ -29,21 +31,40 @@ module CouchRest
|
|
29
31
|
end
|
30
32
|
|
31
33
|
# Query the <tt>_all_docs</tt> view. Accepts all the same arguments as view.
|
32
|
-
def documents params =
|
34
|
+
def documents params = {}
|
35
|
+
keys = params.delete(:keys)
|
33
36
|
url = CouchRest.paramify_url "#{@root}/_all_docs", params
|
34
|
-
|
37
|
+
if keys
|
38
|
+
CouchRest.post(url, {:keys => keys})
|
39
|
+
else
|
40
|
+
CouchRest.get url
|
41
|
+
end
|
35
42
|
end
|
36
43
|
|
37
|
-
# POST a temporary view function to CouchDB for querying. This is not
|
38
|
-
|
44
|
+
# POST a temporary view function to CouchDB for querying. This is not
|
45
|
+
# recommended, as you don't get any performance benefit from CouchDB's
|
46
|
+
# materialized views. Can be quite slow on large databases.
|
47
|
+
def temp_view funcs, params = {}
|
48
|
+
keys = params.delete(:keys)
|
49
|
+
funcs = funcs.merge({:keys => keys}) if keys
|
39
50
|
url = CouchRest.paramify_url "#{@root}/_temp_view", params
|
40
51
|
JSON.parse(RestClient.post(url, funcs.to_json, {"Content-Type" => 'application/json'}))
|
41
52
|
end
|
42
53
|
|
43
|
-
# Query a CouchDB view as defined by a <tt>_design</tt> document. Accepts
|
44
|
-
|
54
|
+
# Query a CouchDB view as defined by a <tt>_design</tt> document. Accepts
|
55
|
+
# paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
|
56
|
+
def view name, params = {}, &block
|
57
|
+
keys = params.delete(:keys)
|
45
58
|
url = CouchRest.paramify_url "#{@root}/_view/#{name}", params
|
46
|
-
|
59
|
+
if keys
|
60
|
+
CouchRest.post(url, {:keys => keys})
|
61
|
+
else
|
62
|
+
if block_given?
|
63
|
+
@streamer.view(name, params, &block)
|
64
|
+
else
|
65
|
+
CouchRest.get url
|
66
|
+
end
|
67
|
+
end
|
47
68
|
end
|
48
69
|
|
49
70
|
# GET a document from CouchDB, by id. Returns a Ruby Hash.
|
@@ -72,7 +93,12 @@ module CouchRest
|
|
72
93
|
JSON.parse(RestClient.put(uri, file, options))
|
73
94
|
end
|
74
95
|
|
75
|
-
# Save a document to CouchDB. This will use the <tt>_id</tt> field from
|
96
|
+
# Save a document to CouchDB. This will use the <tt>_id</tt> field from
|
97
|
+
# the document as the id for PUT, or request a new UUID from CouchDB, if
|
98
|
+
# no <tt>_id</tt> is present on the document. IDs are attached to
|
99
|
+
# documents on the client side because POST has the curious property of
|
100
|
+
# being automatically retried by proxies in the event of network
|
101
|
+
# segmentation and lost responses.
|
76
102
|
def save doc
|
77
103
|
if doc['_attachments']
|
78
104
|
doc['_attachments'] = encode_attachments(doc['_attachments'])
|
@@ -90,7 +116,8 @@ module CouchRest
|
|
90
116
|
end
|
91
117
|
end
|
92
118
|
|
93
|
-
# POST an array of documents to CouchDB. If any of the documents are
|
119
|
+
# POST an array of documents to CouchDB. If any of the documents are
|
120
|
+
# missing ids, supply one from the uuid cache.
|
94
121
|
def bulk_save docs
|
95
122
|
ids, noids = docs.partition{|d|d['_id']}
|
96
123
|
uuid_count = [noids.length, @server.uuid_batch_count].max
|
@@ -101,13 +128,15 @@ module CouchRest
|
|
101
128
|
CouchRest.post "#{@root}/_bulk_docs", {:docs => docs}
|
102
129
|
end
|
103
130
|
|
104
|
-
# DELETE the document from CouchDB that has the given <tt>_id</tt> and
|
131
|
+
# DELETE the document from CouchDB that has the given <tt>_id</tt> and
|
132
|
+
# <tt>_rev</tt>.
|
105
133
|
def delete doc
|
106
134
|
slug = CGI.escape(doc['_id'])
|
107
135
|
CouchRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}"
|
108
136
|
end
|
109
137
|
|
110
|
-
# DELETE the database itself. This is not undoable and could be rather
|
138
|
+
# DELETE the database itself. This is not undoable and could be rather
|
139
|
+
# catastrophic. Use with care!
|
111
140
|
def delete!
|
112
141
|
CouchRest.delete @root
|
113
142
|
end
|
data/lib/couchrest/core/model.rb
CHANGED
@@ -5,18 +5,18 @@ require 'digest/md5'
|
|
5
5
|
# = CouchRest::Model - ORM, the CouchDB way
|
6
6
|
module CouchRest
|
7
7
|
# = CouchRest::Model - ORM, the CouchDB way
|
8
|
-
#
|
8
|
+
#
|
9
9
|
# CouchRest::Model provides an ORM-like interface for CouchDB documents. It
|
10
10
|
# avoids all usage of <tt>method_missing</tt>, and tries to strike a balance
|
11
11
|
# between usability and magic. See CouchRest::Model#view_by for
|
12
12
|
# documentation about the view-generation system.
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# ==== Example
|
15
|
-
#
|
15
|
+
#
|
16
16
|
# This is an example class using CouchRest::Model. It is taken from the
|
17
17
|
# spec/couchrest/core/model_spec.rb file, which may be even more up to date
|
18
18
|
# than this example.
|
19
|
-
#
|
19
|
+
#
|
20
20
|
# class Article < CouchRest::Model
|
21
21
|
# use_database CouchRest.database!('http://localhost:5984/couchrest-model-test')
|
22
22
|
# unique_id :slug
|
@@ -49,6 +49,25 @@ module CouchRest
|
|
49
49
|
# self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'')
|
50
50
|
# end
|
51
51
|
# end
|
52
|
+
#
|
53
|
+
# ==== Examples of finding articles with these views:
|
54
|
+
#
|
55
|
+
# * All the articles by Barney published in the last 24 hours. Note that we
|
56
|
+
# use <tt>{}</tt> as a special value that sorts after all strings,
|
57
|
+
# numbers, and arrays.
|
58
|
+
#
|
59
|
+
# Article.by_user_id_and_date :startkey => ["barney", Time.now - 24 * 3600], :endkey => ["barney", {}]
|
60
|
+
#
|
61
|
+
# * The most recent 20 articles. Remember that the <tt>view_by :date</tt>
|
62
|
+
# has the default option <tt>:descending => true</tt>.
|
63
|
+
#
|
64
|
+
# Article.by_date :count => 20
|
65
|
+
#
|
66
|
+
# * The raw CouchDB view reduce result for the custom <tt>:tags</tt> view.
|
67
|
+
# In this case we'll get a count of the number of articles tagged "ruby".
|
68
|
+
#
|
69
|
+
# Article.by_tags :key => "ruby", :reduce => true
|
70
|
+
#
|
52
71
|
class Model < Hash
|
53
72
|
|
54
73
|
# instantiates the hash by converting all the keys to strings.
|
@@ -86,12 +105,15 @@ module CouchRest
|
|
86
105
|
self.class_database || CouchRest::Model.default_database
|
87
106
|
end
|
88
107
|
|
89
|
-
#
|
108
|
+
# Load a document from the database by id
|
90
109
|
def get id
|
91
110
|
doc = database.get id
|
92
111
|
new(doc)
|
93
112
|
end
|
94
113
|
|
114
|
+
# Load all documents that have the "couchrest-type" field equal to the
|
115
|
+
# name of the current class. Take thes the standard set of
|
116
|
+
# CouchRest::Database#view options.
|
95
117
|
def all opts = {}
|
96
118
|
self.generated_design_doc ||= default_design_doc
|
97
119
|
unless design_doc_fresh
|
@@ -99,8 +121,7 @@ module CouchRest
|
|
99
121
|
end
|
100
122
|
view_name = "#{design_doc_slug}/all"
|
101
123
|
raw = opts.delete(:raw)
|
102
|
-
|
103
|
-
process_view_results view, raw
|
124
|
+
fetch_view_with_docs(view_name, opts, raw)
|
104
125
|
end
|
105
126
|
|
106
127
|
# Cast a field as another class. The class must be happy to have the
|
@@ -244,7 +265,7 @@ module CouchRest
|
|
244
265
|
#
|
245
266
|
# To understand the capabilities of this view system more compeletly,
|
246
267
|
# it is recommended that you read the RSpec file at
|
247
|
-
# <tt>spec/core/
|
268
|
+
# <tt>spec/core/model_spec.rb</tt>.
|
248
269
|
def view_by *keys
|
249
270
|
opts = keys.pop if keys.last.is_a?(Hash)
|
250
271
|
opts ||= {}
|
@@ -252,7 +273,7 @@ module CouchRest
|
|
252
273
|
|
253
274
|
method_name = "by_#{keys.join('_and_')}"
|
254
275
|
self.generated_design_doc ||= default_design_doc
|
255
|
-
|
276
|
+
ducktype = opts.delete(:ducktype)
|
256
277
|
if opts[:map]
|
257
278
|
view = {}
|
258
279
|
view['map'] = opts.delete(:map)
|
@@ -267,7 +288,7 @@ module CouchRest
|
|
267
288
|
key_emit = doc_keys.length == 1 ? "#{doc_keys.first}" : "[#{doc_keys.join(', ')}]"
|
268
289
|
map_function = <<-JAVASCRIPT
|
269
290
|
function(doc) {
|
270
|
-
if (doc['couchrest-type'] == '#{type}' && #{key_protection}) {
|
291
|
+
if (#{!ducktype ? "doc['couchrest-type'] == '#{type}' && " : ""}#{key_protection}) {
|
271
292
|
emit(#{key_emit}, null);
|
272
293
|
}
|
273
294
|
}
|
@@ -276,21 +297,25 @@ module CouchRest
|
|
276
297
|
'map' => map_function
|
277
298
|
}
|
278
299
|
end
|
279
|
-
|
300
|
+
generated_design_doc['views'][method_name]['couchrest-defaults'] = opts
|
280
301
|
self.design_doc_fresh = false
|
302
|
+
method_name
|
303
|
+
end
|
281
304
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
305
|
+
def method_missing m, *args
|
306
|
+
if opts = has_view?(m)
|
307
|
+
query = args.shift || {}
|
308
|
+
view(m, opts.merge(query), *args)
|
309
|
+
else
|
310
|
+
super
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# returns true if the there is a view named this in the design doc
|
315
|
+
def has_view?(view)
|
316
|
+
view = view.to_s
|
317
|
+
if generated_design_doc['views'][view]
|
318
|
+
generated_design_doc['views'][view]["couchrest-defaults"]
|
294
319
|
end
|
295
320
|
end
|
296
321
|
|
@@ -299,21 +324,39 @@ module CouchRest
|
|
299
324
|
database.get("_design/#{design_doc_slug}")
|
300
325
|
end
|
301
326
|
|
327
|
+
# Dispatches to any named view.
|
328
|
+
def view name, query={}, &block
|
329
|
+
unless design_doc_fresh
|
330
|
+
refresh_design_doc
|
331
|
+
end
|
332
|
+
query[:raw] = true if query[:reduce]
|
333
|
+
raw = query.delete(:raw)
|
334
|
+
view_name = "#{design_doc_slug}/#{name}"
|
335
|
+
fetch_view_with_docs(view_name, query, raw, &block)
|
336
|
+
end
|
337
|
+
|
302
338
|
private
|
303
339
|
|
304
|
-
def
|
340
|
+
def fetch_view_with_docs name, opts, raw=false, &block
|
305
341
|
if raw
|
306
|
-
|
342
|
+
fetch_view name, opts, &block
|
307
343
|
else
|
308
|
-
|
309
|
-
|
344
|
+
begin
|
345
|
+
view = fetch_view name, opts.merge({:include_docs => true}), &block
|
346
|
+
view['rows'].collect{|r|new(r['doc'])} if view['rows']
|
347
|
+
rescue
|
348
|
+
# fallback for old versions of couchdb that don't
|
349
|
+
# have include_docs support
|
350
|
+
view = fetch_view name, opts, &block
|
351
|
+
view['rows'].collect{|r|new(database.get(r['id']))} if view['rows']
|
352
|
+
end
|
310
353
|
end
|
311
354
|
end
|
312
355
|
|
313
|
-
def fetch_view view_name, opts
|
356
|
+
def fetch_view view_name, opts, &block
|
314
357
|
retryable = true
|
315
358
|
begin
|
316
|
-
database.view(view_name, opts)
|
359
|
+
database.view(view_name, opts, &block)
|
317
360
|
# the design doc could have been deleted by a rouge process
|
318
361
|
rescue RestClient::ResourceNotFound => e
|
319
362
|
if retryable
|
@@ -383,6 +426,19 @@ module CouchRest
|
|
383
426
|
self['_rev']
|
384
427
|
end
|
385
428
|
|
429
|
+
# Takes a hash as argument, and applies the values by using writer methods
|
430
|
+
# for each key. Raises a NoMethodError if the corresponding methods are
|
431
|
+
# missing. In case of error, no attributes are changed.
|
432
|
+
def update_attributes hash
|
433
|
+
hash.each do |k, v|
|
434
|
+
raise NoMethodError, "#{k}= method not available, use key_accessor or key_writer :#{key}" unless self.respond_to?("#{k}=")
|
435
|
+
end
|
436
|
+
hash.each do |k, v|
|
437
|
+
self.send("#{k}=",v)
|
438
|
+
end
|
439
|
+
save
|
440
|
+
end
|
441
|
+
|
386
442
|
# returns true if the document has never been saved
|
387
443
|
def new_record?
|
388
444
|
!rev
|
@@ -6,15 +6,19 @@ module CouchRest
|
|
6
6
|
end
|
7
7
|
|
8
8
|
# Stream a view, yielding one row at a time. Shells out to <tt>curl</tt> to keep RAM usage low when you have millions of rows.
|
9
|
-
def view name, params = nil
|
9
|
+
def view name, params = nil, &block
|
10
10
|
urlst = /^_/.match(name) ? "#{@db.root}/#{name}" : "#{@db.root}/_view/#{name}"
|
11
11
|
url = CouchRest.paramify_url urlst, params
|
12
|
+
# puts "stream #{url}"
|
13
|
+
first = nil
|
12
14
|
IO.popen("curl --silent #{url}") do |view|
|
13
|
-
view.gets # discard header
|
14
|
-
while
|
15
|
-
|
15
|
+
first = view.gets # discard header
|
16
|
+
while line = view.gets
|
17
|
+
row = parse_line(line)
|
18
|
+
block.call row
|
16
19
|
end
|
17
20
|
end
|
21
|
+
parse_first(first)
|
18
22
|
end
|
19
23
|
|
20
24
|
private
|
@@ -25,6 +29,16 @@ module CouchRest
|
|
25
29
|
JSON.parse($1)
|
26
30
|
end
|
27
31
|
end
|
32
|
+
|
33
|
+
def parse_first first
|
34
|
+
return nil unless first
|
35
|
+
parts = first.split(',')
|
36
|
+
parts.pop
|
37
|
+
line = parts.join(',')
|
38
|
+
JSON.parse("#{line}}")
|
39
|
+
rescue
|
40
|
+
nil
|
41
|
+
end
|
28
42
|
|
29
43
|
end
|
30
44
|
end
|
data/spec/couchapp_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/spec_helper'
|
|
2
2
|
|
3
3
|
describe "couchapp" do
|
4
4
|
before(:all) do
|
5
|
-
@fixdir =
|
5
|
+
@fixdir = FIXTURE_PATH + '/couchapp-test'
|
6
6
|
@couchapp = File.expand_path(File.dirname(__FILE__)) + '/../bin/couchapp'
|
7
7
|
`rm -rf #{@fixdir}`
|
8
8
|
`mkdir -p #{@fixdir}`
|
@@ -22,17 +22,21 @@ describe CouchRest::Database do
|
|
22
22
|
rs['rows'].select{|r|r['key'] == 'wild' && r['value'] == 'and random'}.length.should == 1
|
23
23
|
end
|
24
24
|
it "should work with a range" do
|
25
|
-
rs = @db.temp_view(@temp_view,
|
25
|
+
rs = @db.temp_view(@temp_view, :startkey => "b", :endkey => "z")
|
26
26
|
rs['rows'].length.should == 2
|
27
27
|
end
|
28
28
|
it "should work with a key" do
|
29
|
-
rs = @db.temp_view(@temp_view,
|
29
|
+
rs = @db.temp_view(@temp_view, :key => "wild")
|
30
30
|
rs['rows'].length.should == 1
|
31
31
|
end
|
32
32
|
it "should work with a count" do
|
33
|
-
rs = @db.temp_view(@temp_view,
|
33
|
+
rs = @db.temp_view(@temp_view, :count => 1)
|
34
34
|
rs['rows'].length.should == 1
|
35
35
|
end
|
36
|
+
it "should work with multi-keys" do
|
37
|
+
rs = @db.temp_view(@temp_view, :keys => ["another", "wild"])
|
38
|
+
rs['rows'].length.should == 2
|
39
|
+
end
|
36
40
|
end
|
37
41
|
|
38
42
|
describe "map/reduce query with _temp_view in Javascript" do
|
@@ -98,17 +102,29 @@ describe CouchRest::Database do
|
|
98
102
|
rs['rows'].select{|r|r['key'] == 'wild' && r['value'] == 'and random'}.length.should == 1
|
99
103
|
end
|
100
104
|
it "should work with a range" do
|
101
|
-
rs = @db.view('first/test',
|
105
|
+
rs = @db.view('first/test', :startkey => "b", :endkey => "z")
|
102
106
|
rs['rows'].length.should == 2
|
103
107
|
end
|
104
108
|
it "should work with a key" do
|
105
|
-
rs = @db.view('first/test',
|
109
|
+
rs = @db.view('first/test', :key => "wild")
|
106
110
|
rs['rows'].length.should == 1
|
107
111
|
end
|
108
112
|
it "should work with a count" do
|
109
|
-
rs = @db.view('first/test',
|
113
|
+
rs = @db.view('first/test', :count => 1)
|
110
114
|
rs['rows'].length.should == 1
|
111
115
|
end
|
116
|
+
it "should work with multi-keys" do
|
117
|
+
rs = @db.view('first/test', :keys => ["another", "wild"])
|
118
|
+
rs['rows'].length.should == 2
|
119
|
+
end
|
120
|
+
it "should accept a block" do
|
121
|
+
rows = []
|
122
|
+
rs = @db.view('first/test', :include_docs => true) do |row|
|
123
|
+
rows << row
|
124
|
+
end
|
125
|
+
rows.length.should == 4
|
126
|
+
rs["total_rows"].should == 3
|
127
|
+
end
|
112
128
|
end
|
113
129
|
|
114
130
|
describe "GET (document by id) when the doc exists" do
|
@@ -207,7 +223,7 @@ describe CouchRest::Database do
|
|
207
223
|
|
208
224
|
describe "PUT attachment from file" do
|
209
225
|
before(:each) do
|
210
|
-
filename =
|
226
|
+
filename = FIXTURE_PATH + '/attachments/couchdb.png'
|
211
227
|
@file = File.open(filename)
|
212
228
|
end
|
213
229
|
after(:each) do
|
@@ -414,18 +430,32 @@ describe CouchRest::Database do
|
|
414
430
|
ds['total_rows'].should == 5
|
415
431
|
end
|
416
432
|
|
417
|
-
|
418
|
-
|
419
|
-
|
433
|
+
describe "documents / _all_docs" do
|
434
|
+
before(:each) do
|
435
|
+
9.times do |i|
|
436
|
+
@db.save({'_id' => "doc#{i}",'another' => 'doc', 'will-exist' => 'here'})
|
437
|
+
end
|
438
|
+
end
|
439
|
+
it "should list documents with keys and such" do
|
440
|
+
ds = @db.documents
|
441
|
+
ds['rows'].should be_an_instance_of(Array)
|
442
|
+
ds['rows'][0]['id'].should == "doc0"
|
443
|
+
ds['total_rows'].should == 9
|
444
|
+
end
|
445
|
+
it "should take query params" do
|
446
|
+
ds = @db.documents(:startkey => 'doc0', :endkey => 'doc3')
|
447
|
+
ds['rows'].length.should == 4
|
448
|
+
ds = @db.documents(:key => 'doc0')
|
449
|
+
ds['rows'].length.should == 1
|
450
|
+
end
|
451
|
+
it "should work with multi-key" do
|
452
|
+
rs = @db.documents :keys => ["doc0", "doc7"]
|
453
|
+
rs['rows'].length.should == 2
|
454
|
+
end
|
455
|
+
it "should work with include_docs" do
|
456
|
+
ds = @db.documents(:startkey => 'doc0', :endkey => 'doc3', :include_docs => true)
|
457
|
+
ds['rows'][0]['doc']['another'].should == "doc"
|
420
458
|
end
|
421
|
-
ds = @db.documents
|
422
|
-
ds['rows'].should be_an_instance_of(Array)
|
423
|
-
ds['rows'][0]['id'].should == "doc0"
|
424
|
-
ds['total_rows'].should == 9
|
425
|
-
ds = @db.documents(:startkey => 'doc0', :endkey => 'doc3')
|
426
|
-
ds['rows'].length.should == 4
|
427
|
-
ds = @db.documents(:key => 'doc0')
|
428
|
-
ds['rows'].length.should == 1
|
429
459
|
end
|
430
460
|
|
431
461
|
describe "deleting a database" do
|
@@ -30,6 +30,7 @@ class Course < CouchRest::Model
|
|
30
30
|
cast :questions, :as => ['Question']
|
31
31
|
cast :professor, :as => 'Person'
|
32
32
|
view_by :title
|
33
|
+
view_by :dept, :ducktype => true
|
33
34
|
end
|
34
35
|
|
35
36
|
class Article < CouchRest::Model
|
@@ -38,7 +39,7 @@ class Article < CouchRest::Model
|
|
38
39
|
|
39
40
|
view_by :date, :descending => true
|
40
41
|
view_by :user_id, :date
|
41
|
-
|
42
|
+
|
42
43
|
view_by :tags,
|
43
44
|
:map =>
|
44
45
|
"function(doc) {
|
@@ -133,6 +134,41 @@ describe CouchRest::Model do
|
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
137
|
+
describe "update attributes" do
|
138
|
+
before(:each) do
|
139
|
+
a = Article.get "big-bad-danger" rescue nil
|
140
|
+
a.destroy if a
|
141
|
+
@art = Article.new(:title => "big bad danger")
|
142
|
+
@art.save
|
143
|
+
end
|
144
|
+
it "should work for attribute= methods" do
|
145
|
+
@art['title'].should == "big bad danger"
|
146
|
+
@art.update_attributes('date' => Time.now, :title => "super danger")
|
147
|
+
@art['title'].should == "super danger"
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should flip out if an attribute= method is missing" do
|
151
|
+
lambda {
|
152
|
+
@art.update_attributes('slug' => "new-slug", :title => "super danger")
|
153
|
+
}.should raise_error
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should not change other attributes if there is an error" do
|
157
|
+
lambda {
|
158
|
+
@art.update_attributes('slug' => "new-slug", :title => "super danger")
|
159
|
+
}.should raise_error
|
160
|
+
@art['title'].should == "big bad danger"
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should save" do
|
164
|
+
@art['title'].should == "big bad danger"
|
165
|
+
@art.update_attributes('date' => Time.now, :title => "super danger")
|
166
|
+
loaded = Article.get @art.id
|
167
|
+
loaded['title'].should == "super danger"
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
136
172
|
describe "a model with template values" do
|
137
173
|
before(:all) do
|
138
174
|
@tmpl = WithTemplate.new
|
@@ -326,7 +362,9 @@ describe CouchRest::Model do
|
|
326
362
|
end
|
327
363
|
|
328
364
|
describe "a model with timestamps" do
|
329
|
-
before(:
|
365
|
+
before(:each) do
|
366
|
+
oldart = Article.get "saving-this" rescue nil
|
367
|
+
oldart.destroy if oldart
|
330
368
|
@art = Article.new(:title => "Saving this")
|
331
369
|
@art.save
|
332
370
|
end
|
@@ -381,14 +419,53 @@ describe CouchRest::Model do
|
|
381
419
|
@db = @cr.create_db(TESTDB) rescue nil
|
382
420
|
Course.new(:title => 'aaa').save
|
383
421
|
Course.new(:title => 'bbb').save
|
422
|
+
Course.new(:title => 'ddd').save
|
423
|
+
Course.new(:title => 'eee').save
|
384
424
|
end
|
385
|
-
it "should make the design doc" do
|
425
|
+
it "should make the design doc upon first query" do
|
426
|
+
Course.by_title
|
386
427
|
doc = Course.design_doc
|
387
428
|
doc['views']['all']['map'].should include('Course')
|
388
429
|
end
|
430
|
+
it "should can query via view" do
|
431
|
+
# register methods with method-missing, for local dispatch. method
|
432
|
+
# missing lookup table, no heuristics.
|
433
|
+
view = Course.view :by_title
|
434
|
+
designed = Course.by_title
|
435
|
+
view.should == designed
|
436
|
+
end
|
389
437
|
it "should get them" do
|
390
438
|
rs = Course.by_title
|
391
|
-
rs.length.should ==
|
439
|
+
rs.length.should == 4
|
440
|
+
end
|
441
|
+
it "should yield" do
|
442
|
+
courses = []
|
443
|
+
rs = Course.by_title # remove me
|
444
|
+
Course.view(:by_title) do |course|
|
445
|
+
# puts "course"
|
446
|
+
courses << course
|
447
|
+
end
|
448
|
+
# courses.should == 'x'
|
449
|
+
courses[0]["doc"]["title"].should =='aaa'
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
describe "a ducktype view" do
|
454
|
+
before(:all) do
|
455
|
+
@id = @db.save({:dept => true})['id']
|
456
|
+
end
|
457
|
+
it "should setup" do
|
458
|
+
duck = Course.get(@id) # from a different db
|
459
|
+
duck["dept"].should == true
|
460
|
+
end
|
461
|
+
it "should make the design doc" do
|
462
|
+
@as = Course.by_dept
|
463
|
+
@doc = Course.design_doc
|
464
|
+
@doc["views"]["by_dept"]["map"].should_not include("couchrest")
|
465
|
+
end
|
466
|
+
it "should not look for class" do |variable|
|
467
|
+
@as = Course.by_dept
|
468
|
+
@as[0]['_id'].should == @id
|
392
469
|
end
|
393
470
|
end
|
394
471
|
|
@@ -412,7 +489,8 @@ describe CouchRest::Model do
|
|
412
489
|
end
|
413
490
|
it "should sort correctly" do
|
414
491
|
articles = Article.by_user_id_and_date
|
415
|
-
articles.collect{|a|a['user_id']}.should == ['aaron', 'aaron', 'quentin',
|
492
|
+
articles.collect{|a|a['user_id']}.should == ['aaron', 'aaron', 'quentin',
|
493
|
+
'quentin']
|
416
494
|
articles[1].title.should == 'not junk'
|
417
495
|
end
|
418
496
|
it "should be queryable with couchrest options" do
|
@@ -424,7 +502,8 @@ describe CouchRest::Model do
|
|
424
502
|
|
425
503
|
describe "with a custom view" do
|
426
504
|
before(:all) do
|
427
|
-
@titles = ["very uniq one", "even less interesting", "some fun",
|
505
|
+
@titles = ["very uniq one", "even less interesting", "some fun",
|
506
|
+
"really junk", "crazy bob"]
|
428
507
|
@tags = ["cool", "lame"]
|
429
508
|
@titles.each_with_index do |title,i|
|
430
509
|
u = i % 2
|
@@ -451,17 +530,20 @@ describe CouchRest::Model do
|
|
451
530
|
describe "adding a view" do
|
452
531
|
before(:each) do
|
453
532
|
Article.by_date
|
454
|
-
@design_docs = Article.database.documents :startkey => "_design/",
|
533
|
+
@design_docs = Article.database.documents :startkey => "_design/",
|
534
|
+
:endkey => "_design/\u9999"
|
455
535
|
end
|
456
536
|
it "should not create a design doc on view definition" do
|
457
537
|
Article.view_by :created_at
|
458
|
-
newdocs = Article.database.documents :startkey => "_design/",
|
538
|
+
newdocs = Article.database.documents :startkey => "_design/",
|
539
|
+
:endkey => "_design/\u9999"
|
459
540
|
newdocs["rows"].length.should == @design_docs["rows"].length
|
460
541
|
end
|
461
542
|
it "should create a new design document on view access" do
|
462
|
-
Article.view_by :
|
463
|
-
Article.
|
464
|
-
newdocs = Article.database.documents :startkey => "_design/",
|
543
|
+
Article.view_by :updated_at
|
544
|
+
Article.by_updated_at
|
545
|
+
newdocs = Article.database.documents :startkey => "_design/",
|
546
|
+
:endkey => "_design/\u9999"
|
465
547
|
newdocs["rows"].length.should == @design_docs["rows"].length + 1
|
466
548
|
end
|
467
549
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
2
|
|
3
3
|
describe CouchRest::FileManager do
|
4
4
|
before(:all) do
|
@@ -26,7 +26,7 @@ end
|
|
26
26
|
|
27
27
|
describe CouchRest::FileManager, "generating an app" do
|
28
28
|
before(:all) do
|
29
|
-
@appdir =
|
29
|
+
@appdir = FIXTURE_PATH + '/couchapp'
|
30
30
|
`rm -rf #{@appdir}`
|
31
31
|
`mkdir -p #{@appdir}`
|
32
32
|
CouchRest::FileManager.generate_app(@appdir)
|
@@ -56,7 +56,7 @@ describe CouchRest::FileManager, "pushing an app" do
|
|
56
56
|
@db.delete! rescue nil
|
57
57
|
@db = @cr.create_db(TESTDB) rescue nil
|
58
58
|
|
59
|
-
@appdir =
|
59
|
+
@appdir = FIXTURE_PATH + '/couchapp'
|
60
60
|
`rm -rf #{@appdir}`
|
61
61
|
`mkdir -p #{@appdir}`
|
62
62
|
CouchRest::FileManager.generate_app(@appdir)
|
@@ -86,7 +86,7 @@ describe CouchRest::FileManager, "pushing views" do
|
|
86
86
|
@db = @cr.create_db(TESTDB) rescue nil
|
87
87
|
|
88
88
|
@fm = CouchRest::FileManager.new(TESTDB, COUCHHOST)
|
89
|
-
@view_dir =
|
89
|
+
@view_dir = FIXTURE_PATH + '/views'
|
90
90
|
ds = @fm.push_views(@view_dir)
|
91
91
|
@design = @db.get("_design/test_view")
|
92
92
|
end
|
@@ -119,7 +119,7 @@ describe CouchRest::FileManager, "pushing a directory with id" do
|
|
119
119
|
@db = @cr.create_db(TESTDB) rescue nil
|
120
120
|
|
121
121
|
@fm = CouchRest::FileManager.new(TESTDB, COUCHHOST)
|
122
|
-
@push_dir =
|
122
|
+
@push_dir = FIXTURE_PATH + '/attachments'
|
123
123
|
ds = @fm.push_directory(@push_dir, 'attached')
|
124
124
|
end
|
125
125
|
it "should create a document for the folder" do
|
@@ -143,7 +143,7 @@ describe CouchRest::FileManager, "pushing a directory without id" do
|
|
143
143
|
@db = @cr.create_db(TESTDB) rescue nil
|
144
144
|
|
145
145
|
@fm = CouchRest::FileManager.new(TESTDB, COUCHHOST)
|
146
|
-
@push_dir =
|
146
|
+
@push_dir = FIXTURE_PATH + '/attachments'
|
147
147
|
ds = @fm.push_directory(@push_dir)
|
148
148
|
end
|
149
149
|
it "should use the dirname" do
|
@@ -160,7 +160,7 @@ describe CouchRest::FileManager, "pushing a directory/ without id" do
|
|
160
160
|
@db = @cr.create_db(TESTDB) rescue nil
|
161
161
|
|
162
162
|
@fm = CouchRest::FileManager.new(TESTDB, COUCHHOST)
|
163
|
-
@push_dir =
|
163
|
+
@push_dir = FIXTURE_PATH + '/attachments/'
|
164
164
|
ds = @fm.push_directory(@push_dir)
|
165
165
|
end
|
166
166
|
it "should use the dirname" do
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
2
|
|
3
3
|
describe CouchRest::Streamer do
|
4
4
|
before(:all) do
|
@@ -17,7 +17,7 @@ describe CouchRest::Streamer do
|
|
17
17
|
@streamer.view("_all_docs") do |row|
|
18
18
|
count += 1
|
19
19
|
end
|
20
|
-
count.should ==
|
20
|
+
count.should == 1001
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jchris-couchrest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J. Chris Anderson
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-10-14 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -99,7 +99,10 @@ files:
|
|
99
99
|
- spec/couchrest/core/couchrest_spec.rb
|
100
100
|
- spec/couchrest/core/database_spec.rb
|
101
101
|
- spec/couchrest/core/model_spec.rb
|
102
|
-
- spec/
|
102
|
+
- spec/couchrest/helpers
|
103
|
+
- spec/couchrest/helpers/file_manager_spec.rb
|
104
|
+
- spec/couchrest/helpers/pager_spec.rb
|
105
|
+
- spec/couchrest/helpers/streamer_spec.rb
|
103
106
|
- spec/fixtures
|
104
107
|
- spec/fixtures/attachments
|
105
108
|
- spec/fixtures/attachments/couchdb.png
|
@@ -111,6 +114,12 @@ files:
|
|
111
114
|
- spec/fixtures/couchapp/views/example-map.js
|
112
115
|
- spec/fixtures/couchapp/views/example-reduce.js
|
113
116
|
- spec/fixtures/couchapp-test
|
117
|
+
- spec/fixtures/couchapp-test/my-app
|
118
|
+
- spec/fixtures/couchapp-test/my-app/attachments
|
119
|
+
- spec/fixtures/couchapp-test/my-app/attachments/index.html
|
120
|
+
- spec/fixtures/couchapp-test/my-app/views
|
121
|
+
- spec/fixtures/couchapp-test/my-app/views/example-map.js
|
122
|
+
- spec/fixtures/couchapp-test/my-app/views/example-reduce.js
|
114
123
|
- spec/fixtures/views
|
115
124
|
- spec/fixtures/views/lib.js
|
116
125
|
- spec/fixtures/views/test_view
|
@@ -118,10 +127,8 @@ files:
|
|
118
127
|
- spec/fixtures/views/test_view/only-map.js
|
119
128
|
- spec/fixtures/views/test_view/test-map.js
|
120
129
|
- spec/fixtures/views/test_view/test-reduce.js
|
121
|
-
- spec/pager_spec.rb
|
122
130
|
- spec/spec.opts
|
123
131
|
- spec/spec_helper.rb
|
124
|
-
- spec/streamer_spec.rb
|
125
132
|
- utils/remap.rb
|
126
133
|
- utils/subset.rb
|
127
134
|
has_rdoc: "true"
|