jchris-couchrest 0.9.12 → 0.12.2
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/{README.rdoc → README.md} +10 -8
- data/Rakefile +60 -39
- data/THANKS.md +18 -0
- data/examples/word_count/markov +1 -1
- data/examples/word_count/views/word_count/count-reduce.js +2 -2
- data/examples/word_count/word_count.rb +2 -2
- data/examples/word_count/word_count_query.rb +2 -2
- data/lib/couchrest/commands/push.rb +8 -4
- data/lib/couchrest/core/database.rb +95 -14
- data/lib/couchrest/core/design.rb +89 -0
- data/lib/couchrest/core/document.rb +63 -0
- data/lib/couchrest/core/model.rb +203 -120
- data/lib/couchrest/core/server.rb +1 -1
- data/lib/couchrest/core/view.rb +4 -0
- data/lib/couchrest/helper/pager.rb +10 -10
- data/lib/couchrest/monkeypatches.rb +1 -1
- data/lib/couchrest.rb +20 -2
- data/spec/couchrest/core/couchrest_spec.rb +33 -23
- data/spec/couchrest/core/database_spec.rb +163 -8
- data/spec/couchrest/core/design_spec.rb +131 -0
- data/spec/couchrest/core/document_spec.rb +130 -0
- data/spec/couchrest/core/model_spec.rb +319 -35
- data/spec/couchrest/helpers/pager_spec.rb +2 -2
- data/spec/fixtures/attachments/README +3 -0
- data/spec/spec_helper.rb +17 -3
- data/utils/remap.rb +2 -2
- data/utils/subset.rb +2 -2
- metadata +24 -33
- data/THANKS +0 -15
- data/bin/couchapp +0 -55
- data/bin/couchview +0 -48
- data/lib/couchrest/helper/file_manager.rb +0 -285
- data/lib/couchrest/helper/templates/example-map.js +0 -8
- data/lib/couchrest/helper/templates/example-reduce.js +0 -10
- data/lib/couchrest/helper/templates/index.html +0 -26
- data/spec/couchapp_spec.rb +0 -82
- data/spec/couchrest/helpers/file_manager_spec.rb +0 -170
data/lib/couchrest/core/model.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'extlib'
|
3
3
|
require 'digest/md5'
|
4
|
+
require File.dirname(__FILE__) + '/document'
|
5
|
+
require 'mime/types'
|
4
6
|
|
5
|
-
# = CouchRest::Model -
|
7
|
+
# = CouchRest::Model - Document modeling, the CouchDB way
|
6
8
|
module CouchRest
|
7
|
-
# = CouchRest::Model -
|
9
|
+
# = CouchRest::Model - Document modeling, the CouchDB way
|
8
10
|
#
|
9
11
|
# CouchRest::Model provides an ORM-like interface for CouchDB documents. It
|
10
12
|
# avoids all usage of <tt>method_missing</tt>, and tries to strike a balance
|
@@ -18,7 +20,7 @@ module CouchRest
|
|
18
20
|
# than this example.
|
19
21
|
#
|
20
22
|
# class Article < CouchRest::Model
|
21
|
-
# use_database CouchRest.database!('http://
|
23
|
+
# use_database CouchRest.database!('http://127.0.0.1:5984/couchrest-model-test')
|
22
24
|
# unique_id :slug
|
23
25
|
#
|
24
26
|
# view_by :date, :descending => true
|
@@ -61,22 +63,19 @@ module CouchRest
|
|
61
63
|
# * The most recent 20 articles. Remember that the <tt>view_by :date</tt>
|
62
64
|
# has the default option <tt>:descending => true</tt>.
|
63
65
|
#
|
64
|
-
# Article.by_date :
|
66
|
+
# Article.by_date :limit => 20
|
65
67
|
#
|
66
68
|
# * The raw CouchDB view reduce result for the custom <tt>:tags</tt> view.
|
67
69
|
# In this case we'll get a count of the number of articles tagged "ruby".
|
68
70
|
#
|
69
71
|
# Article.by_tags :key => "ruby", :reduce => true
|
70
72
|
#
|
71
|
-
class Model <
|
73
|
+
class Model < Document
|
72
74
|
|
73
75
|
# instantiates the hash by converting all the keys to strings.
|
74
76
|
def initialize keys = {}
|
75
|
-
super()
|
77
|
+
super(keys)
|
76
78
|
apply_defaults
|
77
|
-
keys.each do |k,v|
|
78
|
-
self[k.to_s] = v
|
79
|
-
end
|
80
79
|
cast_keys
|
81
80
|
unless self['_id'] && self['_rev']
|
82
81
|
self['couchrest-type'] = self.class.to_s
|
@@ -90,7 +89,7 @@ module CouchRest
|
|
90
89
|
class_inheritable_accessor :casts
|
91
90
|
class_inheritable_accessor :default_obj
|
92
91
|
class_inheritable_accessor :class_database
|
93
|
-
class_inheritable_accessor :
|
92
|
+
class_inheritable_accessor :design_doc
|
94
93
|
class_inheritable_accessor :design_doc_slug_cache
|
95
94
|
class_inheritable_accessor :design_doc_fresh
|
96
95
|
|
@@ -112,23 +111,66 @@ module CouchRest
|
|
112
111
|
end
|
113
112
|
|
114
113
|
# Load all documents that have the "couchrest-type" field equal to the
|
115
|
-
# name of the current class. Take
|
114
|
+
# name of the current class. Take the standard set of
|
116
115
|
# CouchRest::Database#view options.
|
117
|
-
def all opts = {}
|
118
|
-
self.
|
116
|
+
def all opts = {}, &block
|
117
|
+
self.design_doc ||= Design.new(default_design_doc)
|
119
118
|
unless design_doc_fresh
|
120
119
|
refresh_design_doc
|
121
120
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
121
|
+
view :all, opts, &block
|
122
|
+
end
|
123
|
+
|
124
|
+
# Load the first document that have the "couchrest-type" field equal to
|
125
|
+
# the name of the current class.
|
126
|
+
#
|
127
|
+
# ==== Returns
|
128
|
+
# Object:: The first object instance available
|
129
|
+
# or
|
130
|
+
# Nil:: if no instances available
|
131
|
+
#
|
132
|
+
# ==== Parameters
|
133
|
+
# opts<Hash>::
|
134
|
+
# View options, see <tt>CouchRest::Database#view</tt> options for more info.
|
135
|
+
def first opts = {}
|
136
|
+
first_instance = self.all(opts.merge!(:limit => 1))
|
137
|
+
first_instance.empty? ? nil : first_instance.first
|
125
138
|
end
|
126
139
|
|
127
140
|
# Cast a field as another class. The class must be happy to have the
|
128
|
-
# field's primitive type as the argument to it's
|
141
|
+
# field's primitive type as the argument to it's constuctur. Classes
|
129
142
|
# which inherit from CouchRest::Model are happy to act as sub-objects
|
130
143
|
# for any fields that are stored in JSON as object (and therefore are
|
131
144
|
# parsed from the JSON as Ruby Hashes).
|
145
|
+
#
|
146
|
+
# Example:
|
147
|
+
#
|
148
|
+
# class Post < CouchRest::Model
|
149
|
+
#
|
150
|
+
# key_accessor :title, :body, :author
|
151
|
+
#
|
152
|
+
# cast :author, :as => 'Author'
|
153
|
+
#
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# post.author.class #=> Author
|
157
|
+
#
|
158
|
+
# Using the same example, if a Post should have many Comments, we
|
159
|
+
# would declare it like this:
|
160
|
+
#
|
161
|
+
# class Post < CouchRest::Model
|
162
|
+
#
|
163
|
+
# key_accessor :title, :body, :author, comments
|
164
|
+
#
|
165
|
+
# cast :author, :as => 'Author'
|
166
|
+
# cast :comments, :as => ['Comment']
|
167
|
+
#
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# post.author.class #=> Author
|
171
|
+
# post.comments.class #=> Array
|
172
|
+
# post.comments.first #=> Comment
|
173
|
+
#
|
132
174
|
def cast field, opts = {}
|
133
175
|
self.casts ||= {}
|
134
176
|
self.casts[field.to_s] = opts
|
@@ -175,11 +217,9 @@ module CouchRest
|
|
175
217
|
# on the document whenever saving occurs. CouchRest uses a pretty
|
176
218
|
# decent time format by default. See Time#to_json
|
177
219
|
def timestamps!
|
178
|
-
before(:
|
179
|
-
self['updated_at'] = self['created_at'] = Time.now
|
180
|
-
end
|
181
|
-
before(:update) do
|
220
|
+
before(:save) do
|
182
221
|
self['updated_at'] = Time.now
|
222
|
+
self['created_at'] = self['updated_at'] if new_document?
|
183
223
|
end
|
184
224
|
end
|
185
225
|
|
@@ -266,62 +306,34 @@ module CouchRest
|
|
266
306
|
# To understand the capabilities of this view system more compeletly,
|
267
307
|
# it is recommended that you read the RSpec file at
|
268
308
|
# <tt>spec/core/model_spec.rb</tt>.
|
309
|
+
|
269
310
|
def view_by *keys
|
311
|
+
self.design_doc ||= Design.new(default_design_doc)
|
270
312
|
opts = keys.pop if keys.last.is_a?(Hash)
|
271
313
|
opts ||= {}
|
272
|
-
type = self.to_s
|
273
|
-
|
274
|
-
method_name = "by_#{keys.join('_and_')}"
|
275
|
-
self.generated_design_doc ||= default_design_doc
|
276
314
|
ducktype = opts.delete(:ducktype)
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
if opts[:reduce]
|
281
|
-
view['reduce'] = opts.delete(:reduce)
|
282
|
-
opts[:reduce] = false
|
283
|
-
end
|
284
|
-
generated_design_doc['views'][method_name] = view
|
285
|
-
else
|
286
|
-
doc_keys = keys.collect{|k|"doc['#{k}']"}
|
287
|
-
key_protection = doc_keys.join(' && ')
|
288
|
-
key_emit = doc_keys.length == 1 ? "#{doc_keys.first}" : "[#{doc_keys.join(', ')}]"
|
289
|
-
map_function = <<-JAVASCRIPT
|
290
|
-
function(doc) {
|
291
|
-
if (#{!ducktype ? "doc['couchrest-type'] == '#{type}' && " : ""}#{key_protection}) {
|
292
|
-
emit(#{key_emit}, null);
|
293
|
-
}
|
294
|
-
}
|
295
|
-
JAVASCRIPT
|
296
|
-
generated_design_doc['views'][method_name] = {
|
297
|
-
'map' => map_function
|
298
|
-
}
|
315
|
+
unless ducktype || opts[:map]
|
316
|
+
opts[:guards] ||= []
|
317
|
+
opts[:guards].push "(doc['couchrest-type'] == '#{self.to_s}')"
|
299
318
|
end
|
300
|
-
|
319
|
+
keys.push opts
|
320
|
+
self.design_doc.view_by(*keys)
|
301
321
|
self.design_doc_fresh = false
|
302
|
-
method_name
|
303
322
|
end
|
304
323
|
|
305
324
|
def method_missing m, *args
|
306
|
-
if
|
325
|
+
if has_view?(m)
|
307
326
|
query = args.shift || {}
|
308
|
-
view(m,
|
327
|
+
view(m, query, *args)
|
309
328
|
else
|
310
329
|
super
|
311
330
|
end
|
312
331
|
end
|
313
332
|
|
314
|
-
# returns
|
333
|
+
# returns stored defaults if the there is a view named this in the design doc
|
315
334
|
def has_view?(view)
|
316
335
|
view = view.to_s
|
317
|
-
|
318
|
-
generated_design_doc['views'][view]["couchrest-defaults"]
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
# Fetch the generated design doc. Could raise an error if the generated views have not been queried yet.
|
323
|
-
def design_doc
|
324
|
-
database.get("_design/#{design_doc_slug}")
|
336
|
+
design_doc && design_doc['views'] && design_doc['views'][view]
|
325
337
|
end
|
326
338
|
|
327
339
|
# Dispatches to any named view.
|
@@ -331,8 +343,29 @@ module CouchRest
|
|
331
343
|
end
|
332
344
|
query[:raw] = true if query[:reduce]
|
333
345
|
raw = query.delete(:raw)
|
334
|
-
|
335
|
-
|
346
|
+
fetch_view_with_docs(name, query, raw, &block)
|
347
|
+
end
|
348
|
+
|
349
|
+
def all_design_doc_versions
|
350
|
+
database.documents :startkey => "_design/#{self.to_s}-",
|
351
|
+
:endkey => "_design/#{self.to_s}-\u9999"
|
352
|
+
end
|
353
|
+
|
354
|
+
# Deletes any non-current design docs that were created by this class.
|
355
|
+
# Running this when you're deployed version of your application is steadily
|
356
|
+
# and consistently using the latest code, is the way to clear out old design
|
357
|
+
# docs. Running it to early could mean that live code has to regenerate
|
358
|
+
# potentially large indexes.
|
359
|
+
def cleanup_design_docs!
|
360
|
+
ddocs = all_design_doc_versions
|
361
|
+
ddocs["rows"].each do |row|
|
362
|
+
if (row['id'] != design_doc_id)
|
363
|
+
database.delete({
|
364
|
+
"_id" => row['id'],
|
365
|
+
"_rev" => row['value']['rev']
|
366
|
+
})
|
367
|
+
end
|
368
|
+
end
|
336
369
|
end
|
337
370
|
|
338
371
|
private
|
@@ -356,7 +389,7 @@ module CouchRest
|
|
356
389
|
def fetch_view view_name, opts, &block
|
357
390
|
retryable = true
|
358
391
|
begin
|
359
|
-
|
392
|
+
design_doc.view(view_name, opts, &block)
|
360
393
|
# the design doc could have been deleted by a rouge process
|
361
394
|
rescue RestClient::ResourceNotFound => e
|
362
395
|
if retryable
|
@@ -369,10 +402,14 @@ module CouchRest
|
|
369
402
|
end
|
370
403
|
end
|
371
404
|
|
405
|
+
def design_doc_id
|
406
|
+
"_design/#{design_doc_slug}"
|
407
|
+
end
|
408
|
+
|
372
409
|
def design_doc_slug
|
373
410
|
return design_doc_slug_cache if design_doc_slug_cache && design_doc_fresh
|
374
411
|
funcs = []
|
375
|
-
|
412
|
+
design_doc['views'].each do |name, view|
|
376
413
|
funcs << "#{name}/#{view['map']}#{view['reduce']}"
|
377
414
|
end
|
378
415
|
md5 = Digest::MD5.hexdigest(funcs.sort.join(''))
|
@@ -395,16 +432,18 @@ module CouchRest
|
|
395
432
|
end
|
396
433
|
|
397
434
|
def refresh_design_doc
|
398
|
-
did =
|
435
|
+
did = design_doc_id
|
399
436
|
saved = database.get(did) rescue nil
|
400
437
|
if saved
|
401
|
-
|
438
|
+
design_doc['views'].each do |name, view|
|
402
439
|
saved['views'][name] = view
|
403
440
|
end
|
404
441
|
database.save(saved)
|
405
442
|
else
|
406
|
-
|
407
|
-
|
443
|
+
design_doc['_id'] = did
|
444
|
+
design_doc.delete('_rev')
|
445
|
+
design_doc.database = database
|
446
|
+
design_doc.save
|
408
447
|
end
|
409
448
|
self.design_doc_fresh = true
|
410
449
|
end
|
@@ -416,46 +455,42 @@ module CouchRest
|
|
416
455
|
self.class.database
|
417
456
|
end
|
418
457
|
|
419
|
-
# alias for self['_id']
|
420
|
-
def id
|
421
|
-
self['_id']
|
422
|
-
end
|
423
|
-
|
424
|
-
# alias for self['_rev']
|
425
|
-
def rev
|
426
|
-
self['_rev']
|
427
|
-
end
|
428
|
-
|
429
458
|
# 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
|
459
|
+
# for each key. It doesn't save the document at the end. Raises a NoMethodError if the corresponding methods are
|
460
|
+
# missing. In case of error, no attributes are changed.
|
461
|
+
def update_attributes_without_saving hash
|
433
462
|
hash.each do |k, v|
|
434
|
-
raise NoMethodError, "#{k}= method not available, use key_accessor or key_writer :#{
|
463
|
+
raise NoMethodError, "#{k}= method not available, use key_accessor or key_writer :#{k}" unless self.respond_to?("#{k}=")
|
435
464
|
end
|
436
465
|
hash.each do |k, v|
|
437
466
|
self.send("#{k}=",v)
|
438
467
|
end
|
439
|
-
save
|
440
468
|
end
|
441
469
|
|
442
|
-
#
|
443
|
-
|
444
|
-
|
470
|
+
# Takes a hash as argument, and applies the values by using writer methods
|
471
|
+
# for each key. Raises a NoMethodError if the corresponding methods are
|
472
|
+
# missing. In case of error, no attributes are changed.
|
473
|
+
def update_attributes hash
|
474
|
+
update_attributes_without_saving hash
|
475
|
+
save
|
445
476
|
end
|
446
477
|
|
447
|
-
#
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
478
|
+
# for compatibility with old-school frameworks
|
479
|
+
alias :new_record? :new_document?
|
480
|
+
|
481
|
+
# Overridden to set the unique ID.
|
482
|
+
def save bulk = false
|
483
|
+
set_unique_id if new_document? && self.respond_to?(:set_unique_id)
|
484
|
+
super(bulk)
|
485
|
+
end
|
486
|
+
|
487
|
+
# Saves the document to the db using create or update. Raises an exception
|
488
|
+
# if the document is not saved properly.
|
489
|
+
def save!
|
490
|
+
raise "#{self.inspect} failed to save" unless self.save
|
456
491
|
end
|
457
492
|
|
458
|
-
# Deletes the document from the database. Runs the :
|
493
|
+
# Deletes the document from the database. Runs the :destroy callbacks.
|
459
494
|
# Removes the <tt>_id</tt> and <tt>_rev</tt> fields, preparing the
|
460
495
|
# document to be saved to a new <tt>_id</tt>.
|
461
496
|
def destroy
|
@@ -467,35 +502,61 @@ module CouchRest
|
|
467
502
|
result['ok']
|
468
503
|
end
|
469
504
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
505
|
+
# creates a file attachment to the current doc
|
506
|
+
def create_attachment(args={})
|
507
|
+
raise ArgumentError unless args[:file] && args[:name]
|
508
|
+
return if has_attachment?(args[:name])
|
509
|
+
self['_attachments'] ||= {}
|
510
|
+
set_attachment_attr(args)
|
511
|
+
rescue ArgumentError => e
|
512
|
+
raise ArgumentError, 'You must specify :file and :name'
|
513
|
+
end
|
514
|
+
|
515
|
+
# reads the data from an attachment
|
516
|
+
def read_attachment(attachment_name)
|
517
|
+
Base64.decode64(database.fetch_attachment(self.id, attachment_name))
|
518
|
+
end
|
519
|
+
|
520
|
+
# modifies a file attachment on the current doc
|
521
|
+
def update_attachment(args={})
|
522
|
+
raise ArgumentError unless args[:file] && args[:name]
|
523
|
+
return unless has_attachment?(args[:name])
|
524
|
+
delete_attachment(args[:name])
|
525
|
+
set_attachment_attr(args)
|
526
|
+
rescue ArgumentError => e
|
527
|
+
raise ArgumentError, 'You must specify :file and :name'
|
528
|
+
end
|
529
|
+
|
530
|
+
# deletes a file attachment from the current doc
|
531
|
+
def delete_attachment(attachment_name)
|
532
|
+
return unless self['_attachments']
|
533
|
+
self['_attachments'].delete attachment_name
|
534
|
+
end
|
535
|
+
|
536
|
+
# returns true if attachment_name exists
|
537
|
+
def has_attachment?(attachment_name)
|
538
|
+
!!(self['_attachments'] && self['_attachments'][attachment_name] && !self['_attachments'][attachment_name].empty?)
|
477
539
|
end
|
478
540
|
|
479
|
-
#
|
480
|
-
def
|
481
|
-
|
541
|
+
# returns URL to fetch the attachment from
|
542
|
+
def attachment_url(attachment_name)
|
543
|
+
return unless has_attachment?(attachment_name)
|
544
|
+
"#{database.root}/#{self.id}/#{attachment_name}"
|
482
545
|
end
|
483
546
|
|
484
547
|
private
|
485
548
|
|
486
|
-
def save_doc
|
487
|
-
result = database.save self
|
488
|
-
if result['ok']
|
489
|
-
self['_id'] = result['id']
|
490
|
-
self['_rev'] = result['rev']
|
491
|
-
end
|
492
|
-
result['ok']
|
493
|
-
end
|
494
|
-
|
495
549
|
def apply_defaults
|
550
|
+
return unless new_document?
|
496
551
|
if self.class.default
|
497
552
|
self.class.default.each do |k,v|
|
498
|
-
self
|
553
|
+
unless self.key?(k.to_s)
|
554
|
+
if v.class == Proc
|
555
|
+
self[k.to_s] = v.call
|
556
|
+
else
|
557
|
+
self[k.to_s] = Marshal.load(Marshal.dump(v))
|
558
|
+
end
|
559
|
+
end
|
499
560
|
end
|
500
561
|
end
|
501
562
|
end
|
@@ -506,19 +567,41 @@ module CouchRest
|
|
506
567
|
self.class.casts.each do |k,v|
|
507
568
|
next unless self[k]
|
508
569
|
target = v[:as]
|
570
|
+
v[:send] || 'new'
|
509
571
|
if target.is_a?(Array)
|
510
572
|
klass = ::Extlib::Inflection.constantize(target[0])
|
511
573
|
self[k] = self[k].collect do |value|
|
512
|
-
klass.
|
574
|
+
(!v[:send] && klass == Time) ? Time.parse(value) : klass.send((v[:send] || 'new'), value)
|
513
575
|
end
|
514
576
|
else
|
515
|
-
self[k] =
|
577
|
+
self[k] = if (!v[:send] && target == 'Time')
|
578
|
+
Time.parse(self[k])
|
579
|
+
else
|
580
|
+
::Extlib::Inflection.constantize(target).send((v[:send] || 'new'), self[k])
|
581
|
+
end
|
516
582
|
end
|
517
583
|
end
|
518
584
|
end
|
519
585
|
|
586
|
+
def encode_attachment(data)
|
587
|
+
Base64.encode64(data).gsub(/\r|\n/,'')
|
588
|
+
end
|
589
|
+
|
590
|
+
def get_mime_type(file)
|
591
|
+
MIME::Types.type_for(file.path).empty? ?
|
592
|
+
'text\/plain' : MIME::Types.type_for(file.path).first.content_type.gsub(/\//,'\/')
|
593
|
+
end
|
594
|
+
|
595
|
+
def set_attachment_attr(args)
|
596
|
+
content_type = args[:content_type] ? args[:content_type] : get_mime_type(args[:file])
|
597
|
+
self['_attachments'][args[:name]] = {
|
598
|
+
'content-type' => content_type,
|
599
|
+
'data' => encode_attachment(args[:file].read)
|
600
|
+
}
|
601
|
+
end
|
602
|
+
|
520
603
|
include ::Extlib::Hook
|
521
|
-
register_instance_hooks :save, :
|
604
|
+
register_instance_hooks :save, :destroy
|
522
605
|
|
523
606
|
end # class Model
|
524
|
-
end # module CouchRest
|
607
|
+
end # module CouchRest
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module CouchRest
|
2
2
|
class Server
|
3
3
|
attr_accessor :uri, :uuid_batch_count
|
4
|
-
def initialize server = 'http://
|
4
|
+
def initialize server = 'http://127.0.0.1:5984', uuid_batch_count = 1000
|
5
5
|
@uri = server
|
6
6
|
@uuid_batch_count = uuid_batch_count
|
7
7
|
end
|
@@ -5,13 +5,13 @@ module CouchRest
|
|
5
5
|
@db = db
|
6
6
|
end
|
7
7
|
|
8
|
-
def all_docs(
|
8
|
+
def all_docs(limit=100, &block)
|
9
9
|
startkey = nil
|
10
10
|
oldend = nil
|
11
11
|
|
12
|
-
while docrows = request_all_docs(
|
12
|
+
while docrows = request_all_docs(limit+1, startkey)
|
13
13
|
startkey = docrows.last['key']
|
14
|
-
docrows.pop if docrows.length >
|
14
|
+
docrows.pop if docrows.length > limit
|
15
15
|
if oldend == startkey
|
16
16
|
break
|
17
17
|
end
|
@@ -20,13 +20,13 @@ module CouchRest
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def key_reduce(view,
|
23
|
+
def key_reduce(view, limit=2000, firstkey = nil, lastkey = nil, &block)
|
24
24
|
# start with no keys
|
25
25
|
startkey = firstkey
|
26
26
|
# lastprocessedkey = nil
|
27
27
|
keepgoing = true
|
28
28
|
|
29
|
-
while keepgoing && viewrows = request_view(view,
|
29
|
+
while keepgoing && viewrows = request_view(view, limit, startkey)
|
30
30
|
startkey = viewrows.first['key']
|
31
31
|
endkey = viewrows.last['key']
|
32
32
|
|
@@ -37,7 +37,7 @@ module CouchRest
|
|
37
37
|
# we need to do an offset thing to find the next startkey
|
38
38
|
# otherwise we just get stuck
|
39
39
|
lastdocid = viewrows.last['id']
|
40
|
-
fornextloop = @db.view(view, :startkey => startkey, :startkey_docid => lastdocid, :
|
40
|
+
fornextloop = @db.view(view, :startkey => startkey, :startkey_docid => lastdocid, :limit => 2)['rows']
|
41
41
|
|
42
42
|
newendkey = fornextloop.last['key']
|
43
43
|
if (newendkey == endkey)
|
@@ -79,18 +79,18 @@ module CouchRest
|
|
79
79
|
|
80
80
|
private
|
81
81
|
|
82
|
-
def request_all_docs
|
82
|
+
def request_all_docs limit, startkey = nil
|
83
83
|
opts = {}
|
84
|
-
opts[:
|
84
|
+
opts[:limit] = limit if limit
|
85
85
|
opts[:startkey] = startkey if startkey
|
86
86
|
results = @db.documents(opts)
|
87
87
|
rows = results['rows']
|
88
88
|
rows unless rows.length == 0
|
89
89
|
end
|
90
90
|
|
91
|
-
def request_view view,
|
91
|
+
def request_view view, limit = nil, startkey = nil, endkey = nil
|
92
92
|
opts = {}
|
93
|
-
opts[:
|
93
|
+
opts[:limit] = limit if limit
|
94
94
|
opts[:startkey] = startkey if startkey
|
95
95
|
opts[:endkey] = endkey if endkey
|
96
96
|
|
data/lib/couchrest.rb
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
require "rubygems"
|
16
16
|
require 'json'
|
17
17
|
require 'rest_client'
|
18
|
-
require 'extlib'
|
18
|
+
# require 'extlib'
|
19
19
|
|
20
20
|
$:.unshift File.dirname(__FILE__) unless
|
21
21
|
$:.include?(File.dirname(__FILE__)) ||
|
@@ -26,8 +26,13 @@ require 'couchrest/monkeypatches'
|
|
26
26
|
|
27
27
|
# = CouchDB, close to the metal
|
28
28
|
module CouchRest
|
29
|
+
VERSION = '0.12.2'
|
30
|
+
|
29
31
|
autoload :Server, 'couchrest/core/server'
|
30
32
|
autoload :Database, 'couchrest/core/database'
|
33
|
+
autoload :Document, 'couchrest/core/document'
|
34
|
+
autoload :Design, 'couchrest/core/design'
|
35
|
+
autoload :View, 'couchrest/core/view'
|
31
36
|
autoload :Model, 'couchrest/core/model'
|
32
37
|
autoload :Pager, 'couchrest/helper/pager'
|
33
38
|
autoload :FileManager, 'couchrest/helper/file_manager'
|
@@ -69,12 +74,17 @@ module CouchRest
|
|
69
74
|
db = nil if db && db.empty?
|
70
75
|
|
71
76
|
{
|
72
|
-
:host => host || "
|
77
|
+
:host => host || "127.0.0.1:5984",
|
73
78
|
:database => db,
|
74
79
|
:doc => docid
|
75
80
|
}
|
76
81
|
end
|
77
82
|
|
83
|
+
# set proxy for RestClient to use
|
84
|
+
def proxy url
|
85
|
+
RestClient.proxy = url
|
86
|
+
end
|
87
|
+
|
78
88
|
# ensure that a database exists
|
79
89
|
# creates it if it isn't already there
|
80
90
|
# returns it after it's been created
|
@@ -107,6 +117,14 @@ module CouchRest
|
|
107
117
|
def delete uri
|
108
118
|
JSON.parse(RestClient.delete(uri))
|
109
119
|
end
|
120
|
+
|
121
|
+
def copy uri, destination
|
122
|
+
JSON.parse(RestClient.copy(uri, {'Destination' => destination}))
|
123
|
+
end
|
124
|
+
|
125
|
+
def move uri, destination
|
126
|
+
JSON.parse(RestClient.move(uri, {'Destination' => destination}))
|
127
|
+
end
|
110
128
|
|
111
129
|
def paramify_url url, params = {}
|
112
130
|
if params && !params.empty?
|