jchris-couchrest 0.9.11 → 0.9.12
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.
- 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"
|