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 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.11"
9
- s.date = "2008-09-11"
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/*_spec.rb']
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 = nil
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 and database name.
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 = nil
34
+ def documents params = {}
35
+ keys = params.delete(:keys)
33
36
  url = CouchRest.paramify_url "#{@root}/_all_docs", params
34
- CouchRest.get url
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 recommended, as you don't get any performance benefit from CouchDB's materialized views. Can be quite slow on large databases.
38
- def temp_view funcs, params = nil
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 paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
44
- def view name, params = nil
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
- CouchRest.get url
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 the document as the id for PUT, or request a new UUID from CouchDB, if no <tt>_id</tt> is present on the document. IDs are attached to documents on the client side because POST has the curious property of being automatically retried by proxies in the event of network segmentation and lost responses.
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 missing ids, supply one from the uuid cache.
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 <tt>_rev</tt>.
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 catastrophic. Use with care!
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
@@ -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
- # load a document from the database
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
- view = fetch_view(view_name, opts)
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/model.rb</tt>.
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
- self.meta_class.instance_eval do
283
- define_method method_name do |*args|
284
- query = opts.merge(args[0] || {})
285
- query[:raw] = true if query[:reduce]
286
- unless design_doc_fresh
287
- refresh_design_doc
288
- end
289
- raw = query.delete(:raw)
290
- view_name = "#{design_doc_slug}/#{method_name}"
291
- view = fetch_view(view_name, query)
292
- process_view_results view, raw
293
- end
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 process_view_results view, raw=false
340
+ def fetch_view_with_docs name, opts, raw=false, &block
305
341
  if raw
306
- view
342
+ fetch_view name, opts, &block
307
343
  else
308
- # TODO this can be optimized once the include-docs patch is applied
309
- view['rows'].collect{|r|new(database.get(r['id']))}
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 row = parse_line(view.gets)
15
- yield row
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
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
  describe "couchapp" do
4
4
  before(:all) do
5
- @fixdir = File.expand_path(File.dirname(__FILE__)) + '/fixtures/couchapp-test'
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,{:startkey => "b", :endkey => "z"})
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,{:key => "wild"})
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,{:count => 1})
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',{:startkey => "b", :endkey => "z"})
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',{:key => "wild"})
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',{:count => 1})
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 = File.dirname(__FILE__) + '/../../fixtures/attachments/couchdb.png'
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
- it "should list documents with keys and such" do
418
- 9.times do |i|
419
- @db.save({'_id' => "doc#{i}",'another' => 'doc', 'will-exist' => 'here'})
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(:all) do
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 == 2
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', '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", "really junk", "crazy bob"]
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/", :endkey => "_design/\u9999"
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/", :endkey => "_design/\u9999"
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 :created_at
463
- Article.by_created_at
464
- newdocs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
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__) + '/spec_helper'
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 = File.expand_path(File.dirname(__FILE__)) + '/fixtures/couchapp'
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 = File.expand_path(File.dirname(__FILE__)) + '/fixtures/couchapp'
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 = File.dirname(__FILE__) + '/fixtures/views'
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 = File.dirname(__FILE__) + '/fixtures/attachments'
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 = File.dirname(__FILE__) + '/fixtures/attachments'
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 = File.dirname(__FILE__) + '/fixtures/attachments/'
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__) + '/spec_helper'
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
2
 
3
3
  describe CouchRest::Pager do
4
4
  before(:all) do
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
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 == 1000
20
+ count.should == 1001
21
21
  end
22
22
 
23
23
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/../lib/couchrest'
2
2
 
3
+ FIXTURE_PATH = File.dirname(__FILE__) + '/fixtures'
4
+
3
5
  COUCHHOST = "http://localhost:5984"
4
6
  TESTDB = 'couchrest-test'
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.11
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-09-11 00:00:00 -07:00
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/file_manager_spec.rb
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"