chill 6 → 8

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.
Files changed (2) hide show
  1. data/library/chill.rb +394 -61
  2. metadata +24 -9
@@ -4,7 +4,30 @@ require 'uuid'
4
4
  require 'rest-client'
5
5
  require 'uri'
6
6
 
7
+ # The main ChillDB module - This is where it all starts
8
+ #
9
+ # Throughout these docs you'll find classes under ChillDB. In a real
10
+ # application you'll call <tt>ChillDB.goes :SomethingOrOther</tt> and from
11
+ # then on, substitute <tt>ChillDB</tt> for <tt>SomethingOrOther</tt> when
12
+ # refering to class names. ChillDB effectively creates a copy of itself linked
13
+ # to a database called SomethingOrOther on the local couchdb server. Check out
14
+ # ChillDB.goes for more details on getting started. Throughout the
15
+ # documentation, you'll see KittensApp as a placeholder - imagine
16
+ # <tt>ChillDB.goes :KittensApp</tt> has been run beforehand.
7
17
  module ChillDB
18
+ # Creates a copy of ChillDB linked to a database named with the first
19
+ # argument. You can also provide host, port, user, and pass options as a
20
+ # hash to connect chill to a remote couchdb server or just provide
21
+ # authentication info. Once ChillDB.goes has been called, you'll use the
22
+ # database_name instead of ChillDB when refering to chill's classes
23
+ # throughout your app.
24
+ #
25
+ # Example:
26
+ # ChillDB.goes :KittensApp
27
+ #
28
+ # # load 'frederick' from the KittensApp database
29
+ # # on the locally installed couch server
30
+ # KittensApp['frederick'] #=> <ChillDB::Document>
8
31
  def self.goes database_name, *args
9
32
  submod = Module.new do
10
33
  extend ChillDB
@@ -17,25 +40,55 @@ module ChillDB
17
40
  Object.const_set(database_name, submod)
18
41
  end
19
42
 
20
- # stores a list of templates which can be used to make a new document
43
+ # #templates stores a collection of new document templates. This is a handy
44
+ # shortcut to hold your different types of documents. Templates
45
+ # automatically have a property called 'kind' which is assigned to the
46
+ # template's name, which can be handy when writing views. They also provide
47
+ # a great place to write in default values, and are just generally handy as
48
+ # a place to keep a little note to yourself what fields you might expect in
49
+ # a type of document.
50
+ #
51
+ # Example:
52
+ # KittensApp.templates(
53
+ # cat: {
54
+ # color: 'unknown',
55
+ # softness: 5,
56
+ # likes: ['exploding', 'cupcakes'],
57
+ # dislikes: ['dark matter']
58
+ # }
59
+ # )
60
+ #
61
+ # # use the template, extend it with specific info, and save it!
62
+ # new_cat = KittensApp.template(:cat).merge(
63
+ # color: 'octarine',
64
+ # softness: 13,
65
+ # _id: 'bjorn'
66
+ # ).commit!
21
67
  def templates obj
22
68
  @@templates.merge!(obj) if obj and obj.is_a? Hash
23
69
  return @@templates
24
70
  end
25
71
 
26
- # get a new document consisting of a template
72
+ # Gets a copy of a template previously defined using #templates. further
73
+ # info on usage is in the description of #templates.
27
74
  def template kind
28
- properties = @@templates[kind].dup
75
+ properties = @@templates[kind.to_sym].dup
29
76
  properties[:kind] = kind.to_s
30
77
  ChillDB::Document.new(@@database, properties)
31
78
  end
32
79
 
33
- # get a design with a particular name - or make one!
80
+ # Loads or creates a new ChillDB::Design with a specified name. Designs are
81
+ # used to create views, which create cached high speed document indexes for
82
+ # searching, sorting, and calculating.
34
83
  def design name
35
84
  ChillDB::Design.new @@database, name
36
85
  end
37
86
 
38
- # get or make a document with a particular id/name, or just a blank new one
87
+ # Loads or creates a document with a specified _id. If no _id is specified
88
+ # a new blank document is created which will be assigned a fresh UUID as
89
+ # it's _id when saved unless you specify one before committing it.
90
+ #
91
+ # Returns a ChillDB::Document
39
92
  def document id = false
40
93
  if id.respond_to? :to_ary
41
94
  list = id.to_ary.map { |i| i.to_s }
@@ -49,6 +102,16 @@ module ChillDB
49
102
  end
50
103
  alias_method :[], :document
51
104
 
105
+ # Commit a ChillDB::Document or a Hash to the database with a specific _id.
106
+ # This method is useful for quickly storing bits of information. For more
107
+ # involved applications, using ChillDB::Document objects directly is
108
+ # best.
109
+ #
110
+ # Returns a copy of the data as a ChillDB::Document, with _rev set to it's
111
+ # new value on the server.
112
+ #
113
+ # Example:
114
+ # KittensApp['bigglesworth'] = {kind: 'cat', softness: 11}
52
115
  def []= document, hash
53
116
  raise "Not a hash?" unless hash.is_a? Hash
54
117
  hash = hash.dup
@@ -57,31 +120,58 @@ module ChillDB
57
120
  return ChillDB::Document.new(@@database, hash).commit!
58
121
  end
59
122
 
60
- # commit takes an array of documents, and does a bulk commit to the server (one single request)
61
- # using a bulk commit is faster than calling commit! on each individual document object
62
- def commit! *args
63
- list(args.flatten).commit!
123
+ # Commit an array of ChillDB::Documents and/or Hashes to the server as new or
124
+ # updated documents. This collection can include ChillDB::Document's marked
125
+ # for deletion, and is the best way to update several documents at the same
126
+ # time. All documents which can be committed will be, and any which cause
127
+ # errors will be reported via a raised ChillDB::BulkUpdateErrors.
128
+ def commit! *documents
129
+ list(documents.flatten).commit!
64
130
  end
65
131
 
66
- def delete! *args
67
- list(args.flatten).delete!
132
+ # A shortcut for #commit! which marks the documents for deletion before
133
+ # applying the commit, effectively bulk deleting them. If any deletions fail
134
+ # a ChillDB::BulkUpdateErrors will be raised with info. All deletions which
135
+ # can succeed, will.
136
+ def delete! *documents
137
+ list(documents.flatten).delete!
68
138
  end
69
139
 
70
- # turn an array of documents in to a ChillDB::List
140
+ # creates a new ChillDB::List from an array of ChillDB::Documents and
141
+ # hashes. This method is mainly used internally for #commit! and #delete!
142
+ # You shouldn't need to use this method.
71
143
  def list array = []
72
144
  list = ChillDB::List.from_array array
73
145
  list.database = @@database
74
146
  return list
75
147
  end
76
148
 
77
- # all docs!
149
+ # Queries the server for every document. Returns a ChillDB::List.
150
+ #
151
+ # This method is mainly useful for maintenence and mockups. Using
152
+ # #everything in production apps is strongly discouraged, as it has severe
153
+ # scalability implications - use a ChillDB::Design view instead if you can.
154
+ #
155
+ # Example:
156
+ # # The worst way to look up a document. Never ever do this.
157
+ # all_of_them = KittensApp.everything # download all documents from server
158
+ # fredrick = all_of_them['fredrick'] # locally find just the one you want
159
+ # # Now ruby's garbage collector can happily remove every document you
160
+ # # ever made from memory. Yay!
78
161
  def everything
79
162
  list = ChillDB::List.load(JSON.parse(@@database.http('_all_docs?include_docs=true').get.body))
80
163
  list.database = @@database
81
164
  return list
82
165
  end
83
166
 
84
- # open a resource to the server for a particular document, which you can get, post, etc to. For internal use.
167
+ # Returns this app's ChillDB::Database instance
168
+ def database
169
+ @@database
170
+ end
171
+
172
+ # Gets a reference to a resource on the database server, useful mainly
173
+ # internally. You shouldn't need to use this method unless using Couch
174
+ # features chill doesn't yet have an interface for.
85
175
  def open *args
86
176
  headers = { accept: '*/*' }
87
177
  headers.merge! args.pop if args.last.respond_to? :to_hash
@@ -89,9 +179,23 @@ module ChillDB
89
179
  end
90
180
  end
91
181
 
182
+
183
+ # A Database abstraction full of internal gizmos and a few external ones too.
184
+ # You can access your Database via <tt>KittensApp.database</tt> (following the
185
+ # <tt>ChillDB.goes :KittensApp</tt> convention)
186
+ #
187
+ # The database object is mainly useful for maintenance. The #info method is
188
+ # neat for looking up stats on how the database is doing, and you can ask for
189
+ # a compaction, to remove old revisions and make database files smaller.
190
+ #
191
+ # ChillDB::Database is mainly used internally and isn't very useful for most
192
+ # chill apps.
92
193
  class ChillDB::Database
93
194
  attr_reader :url, :meta
94
- def initialize name, settings = {}
195
+
196
+ # Initialize a new database reference. This is used internally by
197
+ # ChillDB.goes, and shouldn't be used directly
198
+ def initialize name, settings = {} # :nodoc:
95
199
  @meta = {} # little place to store our things
96
200
  @url = URI::HTTP.build(
97
201
  host: settings[:host] || 'localhost',
@@ -104,14 +208,27 @@ class ChillDB::Database
104
208
  $stderr.puts "New database created at #{@url}" if http('').put('').code == 201
105
209
  end
106
210
 
107
- # ask the server to compact this database
211
+ # Ask the CouchDB server to compact this database, effectively making a copy
212
+ # and moving all recent revisions and data across to the new file. You can
213
+ # still keep using your app while a compact is running, and it shouldn't
214
+ # affect performance much. When using CouchDB, compacting is important as
215
+ # Couch databases don't remove any old deleted or updated documents until
216
+ # #compact! is called. This may seem a bit odd, but it is part of how couch
217
+ # can be so reliable and difficult to corrupt during power failures and
218
+ # extended server outages. Don't worry about unless your database files are
219
+ # getting too big.
220
+ #
221
+ # You can check to see if your couch server is currently compacting with
222
+ # the #info method
108
223
  def compact!
109
224
  request = http('_compact').post('')
110
225
  raise request.body unless request.code == 202
111
226
  return self
112
227
  end
113
228
 
114
- # get info about database
229
+ # Gets a Hash of database configuration and status info from the server
230
+ # as a ChillDB::IndifferentHash. This contains all sorts of interesting
231
+ # information useful for maintenance.
115
232
  def info
116
233
  response = http('').get()
117
234
  IndifferentHash.new.replace(JSON.parse response.body)
@@ -125,8 +242,8 @@ class ChillDB::Database
125
242
  #def revs_limit; http('_revs_limit').get.body.to_i; end
126
243
  #def revs_limit=(v); http('_revs_limit').put(v.to_s); end
127
244
 
128
- # grab a RestClient http resource for this database
129
- def http resource, headers = {}
245
+ # grab a RestClient http resource for this database - useful internally
246
+ def http resource, headers = {} # :nodoc:
130
247
  RestClient::Resource.new((@url + resource).to_s, headers: {accept: 'application/json', content_type: 'application/json'}.merge(headers)) { |r| r }
131
248
  end
132
249
 
@@ -145,10 +262,11 @@ end
145
262
 
146
263
 
147
264
 
148
-
149
- # handles conversion of symbol keys to strings, and method accessors
265
+ # A simple version of Hash which converts keys to strings - so symbols and
266
+ # strings can be used interchangably as keys. Works pretty much like a Hash.
267
+ # and also provides method getters and setters via #method_missing
150
268
  class ChillDB::IndifferentHash < Hash
151
- def initialize *args
269
+ def initialize *args # :nodoc:
152
270
  super(*args) do |hash, key| # indifferent access
153
271
  hash[key.to_s] if Symbol === key
154
272
  end
@@ -163,25 +281,25 @@ class ChillDB::IndifferentHash < Hash
163
281
 
164
282
  # make hash thing indifferent
165
283
  [:merge, :merge!, :replace, :update, :update!].each do |name|
166
- define_method name do |*args,&proc|
284
+ define_method name do |*args,&proc| # :nodoc:
167
285
  super(normalize_hash(args.shift), *args, &proc)
168
286
  end
169
287
  end
170
288
 
171
289
  # make hash thing indifferent
172
290
  [:has_key?, :include?, :key?, :member?, :delete].each do |name|
173
- define_method name do |first, *seconds,&proc|
291
+ define_method name do |first, *seconds,&proc| # :nodoc:
174
292
  first = first.to_s if first.is_a? Symbol
175
293
  super(first, *seconds, &proc)
176
294
  end
177
295
  end
178
296
 
179
- def []= key, value
297
+ def []= key, value # :nodoc:
180
298
  key = key.to_s if key.is_a? Symbol
181
299
  super(key, normalize(value))
182
300
  end
183
301
 
184
- # return an actual hash
302
+ # Convert to a regular ruby Hash
185
303
  def to_hash
186
304
  Hash.new.replace self
187
305
  end
@@ -212,11 +330,15 @@ end
212
330
 
213
331
 
214
332
 
215
-
333
+ # ChillDB Document, normally created from a template via ChillDB.template or
334
+ # from scratch via ChillDB.document or with a specific _id value via
335
+ # ChillDB['document-id'] or ChillDB.document('document-id'). Document
336
+ # represents a document in your database. It works as a hash with indifferent
337
+ # access and method accessors (see also ChillDB::IndifferentHash)
216
338
  class ChillDB::Document < ChillDB::IndifferentHash
217
339
  attr_reader :database
218
340
 
219
- def initialize database, values = false
341
+ def initialize database, values = false # :nodoc:
220
342
  @database = database
221
343
  super()
222
344
 
@@ -227,17 +349,30 @@ class ChillDB::Document < ChillDB::IndifferentHash
227
349
  end
228
350
  end
229
351
 
352
+ # replace all values in this document with new ones, effectively making it
353
+ # a new document
354
+ #
355
+ # Arguments:
356
+ # values: A hash of values
230
357
  def reset values
231
- self.replace values
358
+ raise "Argument must be a Hash" unless values.respond_to? :to_hash
359
+ self.replace values.to_hash
232
360
  self['_id'] ||= UUID.new.generate # generate an _id if we don't have one already
233
361
  end
234
362
 
235
- def self.load database, docid
363
+ # load a documet from a ChillDB::Database, with a specific document id, and
364
+ # optionally a specific revision.
365
+ #
366
+ # Returns:
367
+ # New instance of Document
368
+ def self.load database, docid, revision = nil
236
369
  new(database, _id: docid).load
237
370
  end
238
371
 
239
- # load this document from server in to this local cache
240
- # returns self, or if there's a more specific subclass, a subclassed version of Document
372
+ # load the current document again from the server, fetching any updates or
373
+ # a specific revision.
374
+ #
375
+ # Returns: self
241
376
  def load lookup_revision = nil
242
377
  url = URI(URI.escape self['_id'])
243
378
  lookup_revision ||= self['_rev']
@@ -250,7 +385,11 @@ class ChillDB::Document < ChillDB::IndifferentHash
250
385
  return self
251
386
  end
252
387
 
253
- # stores updates (or newly existingness) of document to origin database
388
+ # Write any changes to the database. If this is a new document, one will be
389
+ # created on the Couch server. If this document has a specific _id value it
390
+ # will be created or updated.
391
+ #
392
+ # Returns: self
254
393
  def commit!
255
394
  return delete! if self['_deleted'] # just delete if we're marked for deletion
256
395
  json = JSON.generate(self)
@@ -263,12 +402,15 @@ class ChillDB::Document < ChillDB::IndifferentHash
263
402
  return self
264
403
  end
265
404
 
266
- # mark this jerk for deletion! (useful for bulk commit)
405
+ # Mark this document for deletion. A commit! is required to delete the
406
+ # document from the server. You can mark several documents for deletion and
407
+ # supply them to ChillDB.commit! for bulk deletion.
267
408
  def delete
268
409
  self.replace('_id'=> self['_id'], '_rev'=> self['_rev'], '_deleted'=> true)
269
410
  end
270
411
 
271
- # delete this jerk immediately
412
+ # Shortcut for document.delete.commit! - deletes document from server
413
+ # immediately, returning the server's response as a Hash.
272
414
  def delete!
273
415
  response = @database.http(URI.escape self['_id']).delete()
274
416
  response = ChillDB::IndifferentHash.new.replace JSON.parse(response.body)
@@ -277,26 +419,60 @@ class ChillDB::Document < ChillDB::IndifferentHash
277
419
  return response
278
420
  end
279
421
 
280
- # set current revision to a different thing
422
+ # set the current document revision, reloading document content from the
423
+ # couch server.
424
+ #
425
+ # Arguments:
426
+ # new_revision: (String)
281
427
  def revision= new_revision
282
428
  load new_revision
283
429
  end
284
430
 
285
- # gets a list of revisions
431
+ # get's the current document revision identifier string
432
+ #
433
+ # Returns: (String) revision identifier
434
+ def revision
435
+ self['_rev']
436
+ end
437
+
438
+ # fetch an array of this document's available revisions from the server
439
+ #
440
+ # Returns: array of revision identifier strings
286
441
  def revisions
287
442
  request = @database.http("#{URI.escape self['_id']}?revs=true").get
288
443
  json = JSON.parse(request.body)
289
444
  json['_revisions']['ids']
290
445
  end
291
446
 
292
- # for integration with url routers in webapps, to_param defaults to _id - which you can use straight up in urls
447
+ # to_param is supplied for integration with url routers in web frameworks
448
+ # like Rails and Camping. Defaults to returning the document's _id. You can
449
+ # just pass documents in to functions which generate urls in your views in
450
+ # many ruby frameworks, and it will be understood as the _id of the document
451
+ #
452
+ # If you have a more intelligent behaviour, monkeypatch in some special
453
+ # behaviour.
454
+ #
455
+ # Returns:
456
+ # (String): self['_id']
293
457
  def to_param
294
458
  self['_id']
295
459
  end
296
460
 
297
- # loose equality check
461
+ # A loose equality check. Two documents are considered equal if their _id
462
+ # and _rev are equal, and they are both of the same class. This is handy
463
+ # when dealing with Array's and ChillDB::List's. For instance, you may want
464
+ # to query one view, then remove the values found in another view
465
+ #
466
+ # Example:
467
+ #
468
+ # kittens = KittensApp.design(:lists).query(:cats)
469
+ # kittens -= KittensApp.design(:lists).query(:adults)
470
+ # # kittens now contains cats who are not adults
471
+ #
472
+ # Of course if you're doing this sort of thing often, you really should
473
+ # consider precomputing it in a view.
298
474
  def == other_doc
299
- other.is_a?(self.class) and (self['_id'] == other['_id']) and (self['_rev'] == other['_rev'])
475
+ (other.class == self.class) and (self['_id'] == other['_id']) and (self['_rev'] == other['_rev'])
300
476
  end
301
477
  end
302
478
 
@@ -304,12 +480,20 @@ end
304
480
 
305
481
 
306
482
 
307
-
483
+ # A special sort of Array designed for storing lists of documents, and
484
+ # particularly the results of ChillDB::Design#query. It works sort of like a
485
+ # hash, in supporting lookups by key, and sort of like an array, in its sorted
486
+ # nature. It keeps the association of keys, documents, values, id's, and also
487
+ # provides a simple way to bulk commit all the documents in the list or delete
488
+ # all of them form the server in one efficient request.
489
+ #
490
+ # The recomended way to create a list from a regular array is ChillDB.list()
491
+ # as some of List's functionality depends on it having a link to the database.
308
492
  class ChillDB::List < Array
309
493
  attr_accessor :total_rows, :offset, :database
310
494
 
311
495
  # creates a new List from a couchdb response
312
- def self.load list, extras = {}
496
+ def self.load list, extras = {} # :nodoc:
313
497
  new_list = self.new
314
498
 
315
499
  raise "#{list['error']} - #{list['reason']}" if list['error']
@@ -323,7 +507,7 @@ class ChillDB::List < Array
323
507
  end
324
508
 
325
509
  # to make a list from a simple array (not a couchdb response...)
326
- def self.from_array array
510
+ def self.from_array array # :nodoc:
327
511
  new_list = self.new
328
512
  new_list.replace array.map do |item|
329
513
  { 'id'=> item['_id'], 'key'=> item['_id'], 'value'=> item, 'doc'=> item }
@@ -331,7 +515,7 @@ class ChillDB::List < Array
331
515
  end
332
516
 
333
517
  # store rows nicely in mah belleh
334
- def rows=(arr)
518
+ def rows=(arr) # :nodoc:
335
519
  self.replace(arr.map { |item|
336
520
  if item['value'].is_a? Hash
337
521
  if item['value'].respond_to?(:[]) && item['value']['_id']
@@ -348,47 +532,81 @@ class ChillDB::List < Array
348
532
  })
349
533
  end
350
534
 
351
- # we are the rows!
535
+ # Grab all the rows - actually just self.
352
536
  def rows
353
537
  self
354
538
  end
355
539
 
540
+ # Returns an Array of all the document _id strings in the list
356
541
  def ids
357
542
  self.map { |i| i['id'] }
358
543
  end
359
544
 
545
+ # Returns an Array of keys in the list
360
546
  def keys
361
547
  self.map { |i| i['key'] }
362
548
  end
363
549
 
550
+ # Returns an Array of all the emitted values in the list
364
551
  def values
365
552
  self.map { |i| i['value'] }
366
553
  end
367
554
 
555
+ # Returns an Array of all the documents in this list.
556
+ #
557
+ # Note that querying a view with ChillDB::Design#query does not include
558
+ # documents by default. Calling ChillDB::Design#query with the include_docs
559
+ # option set to true causes the database to lookup all of the documents.
368
560
  def docs
369
561
  self.map { |i| i['doc'] }
370
562
  end
371
563
 
564
+ # Iterates each key/value pair using a supplied proc.
565
+ #
566
+ # Example:
567
+ #
568
+ # list.each_pair do |key, value|
569
+ # puts "#{key}: #{value.inspect}"
570
+ # end
571
+ #
572
+ # :yeild: key, value
372
573
  def each_pair &proc
373
574
  self.each { |item| proc.call(item['key'], item['value']) }
374
575
  end
375
576
 
376
- # gets an item by id value
577
+ # fetch a document by it's id from this list. Useful only for queries with
578
+ # <pp>{ include_docs: true }</pp> set
377
579
  def id value
378
580
  self.find { |i| i['id'] == value }['doc']
379
581
  end
380
582
 
583
+ # Lookup a key from the list. Returns a ChillDB::IndifferentHash containing:
584
+ #
585
+ # {
586
+ # 'key' => key from database
587
+ # 'value' => emitted value in view, if any
588
+ # 'doc' => document, if view queried with { include_docs: true }
589
+ # 'id' => id of document which emitted this result
590
+ # }
381
591
  def key value
382
- self.find { |i| i['key'] == value }
592
+ ChillDB::IndifferentHash.new.replace(self.find { |i| i['key'] == value })
383
593
  end
384
594
 
595
+ # Lookup a document, by a key in the list. If there are multiple entries
596
+ # in the view with this key, the first document is returned.
597
+ #
598
+ # Note this will return nil for lists returned by ChillDB::Design#query
599
+ # unless the query was done with the include_docs option set to true.
385
600
  def [] key
386
601
  return key(key)['doc'] unless key.respond_to? :to_int
387
602
  super
388
603
  end
389
604
 
390
605
  # make a regular ruby hash version
391
- # if you want docs as values instead of emitted values, use to_h(:doc)
606
+ #
607
+ # By default returns a hash containing <tt>{ key: value }</tt> pairs from
608
+ # the list. Passing :doc as the optional argument instead creates a hash of
609
+ # <tt>{ key: doc }</tt> pairs.
392
610
  def to_h value = :value
393
611
  hash = ChillDB::IndifferentHash.new
394
612
 
@@ -399,14 +617,25 @@ class ChillDB::List < Array
399
617
  return hash
400
618
  end
401
619
 
402
- # commit takes an array of documents, and does a bulk commit to the server (one single request)
403
- # using a bulk commit is faster than calling commit! on each individual document object
620
+ # Commit every document in this list to the server in a single quick
621
+ # request. Every document which can be committed will be, and if any fail
622
+ # a ChillDB::BulkUpdateErrors will be raised.
623
+ #
624
+ # Returns self.
404
625
  def commit!
405
626
  commit_documents! convert
406
627
  end
407
628
  alias_method :commit_all!, :commit!
408
629
 
409
- # remove all the documents in this list from the database
630
+ # Delete every document in this list from the server. If this list was
631
+ # returned by ChillDB::Design#query, your view's values will need to be a
632
+ # Hash containing <tt>rev</tt> or <tt>_rev</tt> - indicating the document's
633
+ # revision. You can also query your view with <tt>{ include_docs: true }</tt>
634
+ # specified as an option. Deletion cannot happen without a revision
635
+ # identifier. All documents which can be deleted, will be. If there are any
636
+ # errors, they'll be raised as a ChillDB::BulkUpdateErrors.
637
+ #
638
+ # Returns self.
410
639
  def delete!
411
640
  # check all the entries have a _rev - if they don't they cannot be deleted!
412
641
  each do |item|
@@ -460,9 +689,29 @@ end
460
689
 
461
690
 
462
691
 
463
-
692
+ # Representing a named design in the Couch database, Design is used to setup
693
+ # views. Views index all of the documents in your database, creating
694
+ # ChillDB::List's for each emitted document. Your views can choose to emit
695
+ # as many entries as you like for each document, or none at all. Views can be
696
+ # as simple as listing all of a certain kind of document, or more complex
697
+ # schemes like extracting every word in a document and generating an index of
698
+ # words in documents for a simple but powerful search engine.
699
+ #
700
+ # For more information on designs and views, CouchDB: The Definitive Guide is
701
+ # a great resource. http://guide.couchdb.org/draft/design.html
702
+ #
703
+ # Example:
704
+ # KittensApp.design(:lists).views(
705
+ # # lists all cats with a softness rating of two or more
706
+ # soft_cats: 'function(doc) {
707
+ # if (doc.kind == "cat" && doc.softness > 1) emit(doc._id, null);
708
+ # }'
709
+ # ).commit!
710
+ #
711
+ # Note that views default to being javascript functions, as this is what couch
712
+ # ships with support for. It is possible to
464
713
  class ChillDB::Design
465
- attr_accessor :name # can rename with that - though leaves old copy on server probably
714
+ attr_accessor :name
466
715
 
467
716
  def initialize database, name
468
717
  @database = database
@@ -477,26 +726,107 @@ class ChillDB::Design
477
726
  return @document
478
727
  end
479
728
 
480
- # adds views
729
+ # Add more views to an existing design. See also #views
481
730
  def add_views collection
482
731
  document['views'].merge! views_preprocess(collection)
483
732
  return self
484
733
  end
485
734
 
486
- # sets views
735
+ # Set the views in this design. Argument is a Hash containing String's for
736
+ # each javascript (by default) view function. Reduce functions can also be
737
+ # included optionally.
738
+ #
739
+ # Example:
740
+ # KittensApp.design(:lists).views(
741
+ # # just a map function
742
+ # adults: 'function(doc) {
743
+ # if (doc.kind == "cat" && doc.age > 4) emit(doc._id, null);
744
+ # }',
745
+ #
746
+ # # a map and a reduce - lookup the overall softness of our kitten database
747
+ # # when queried returns just one value: the number output of the reduce
748
+ # # function.
749
+ # softness: {
750
+ # map: 'function(doc) {
751
+ # if (doc.kind == "cat") emit(doc._id, doc.softness);
752
+ # }',
753
+ # reduce: 'function(key, values, rereduce) {
754
+ # return sum(values); // add them all together
755
+ # }'
756
+ # }
757
+ # ).commit!
758
+ #
759
+ # KittensApp.design(:lists).query(:adults) #=> <ChillDB::List> [a, b, c...]
760
+ # KittensApp.design(:lists).query(:softness) #=> 19
761
+ #
762
+ # Check out http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views for
763
+ # a great introduction to CouchDB view design.
487
764
  def views collection
488
765
  document['views'] = {}
489
766
  add_views collection
490
767
  return self
491
768
  end
492
769
 
493
- # store changes to server
770
+ # get's the current language of this design document. Usually this would be
771
+ # "javascript". If called with an argument, set's the language property and
772
+ # returns self.
773
+ #
774
+ # Example:
775
+ # KittensApp.design(:lists).language #=> "javascript"
776
+ # KittensApp.design(:lists).language(:ruby) #=> KittensApp.design(:lists)
777
+ # KittensApp.design(:lists).language #=> "ruby"
778
+ #
779
+ # # chain it together with views for extra nyan:
780
+ # KittensApp.design(:lists).language(:ruby).views(
781
+ # soft_cats: %q{ proc do |doc|
782
+ # emit doc['_id'], doc['softness'] if doc['softness'] > 1
783
+ # end }
784
+ # )
785
+ #
786
+ # ChillDB doesn't currently include a ruby view server, and it needs to be
787
+ # specifically configured and installed before you can use one. More info
788
+ # in an implementation is at http://theexciter.com/articles/couchdb-views-in-ruby-instead-of-javascript.html
789
+ def language set = nil
790
+ if set
791
+ @document['language'] = set.to_s
792
+ self
793
+ else
794
+ @document['language']
795
+ end
796
+ end
797
+
798
+ # Commit this design document to the server and start the server's process
799
+ # of updating the view's contents. Note that querying the view immediately
800
+ # after a commit may be slow, while the server finishes initially processing
801
+ # it's contents. The more documents you have, the more time this can take.
494
802
  def commit!
495
803
  document['_id'] = "_design/#{@name}"
496
804
  document.commit!
497
805
  end
498
806
 
499
- # query a view - response is an array augmented with methods like arr.total_rows
807
+ # Query a named view. Returns a ChillDB::List, which works like a Hash or an
808
+ # Array containing each result. Optionally pass in arguments. Check out
809
+ # http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options for the
810
+ # definitive list. Some really useful options:
811
+ #
812
+ # Options:
813
+ # include_docs: (true or false) load documents which emitted each entry?
814
+ # key: only return items emitted with this exact key
815
+ # keys: (Array) only return items emitted with a key in this Array
816
+ # range: (Range) shortcut for startkey, endkey, and inclusive_end
817
+ # startkey: return items starting with this key
818
+ # endkey: return items ending with this key
819
+ # startkey_docid: (String) return items starting with this document id
820
+ # endkey_docid: (String) return items ending with this document id
821
+ # inclusive_end: (true or false) defaults true, endkey included in result?
822
+ # limit: (Number) of documents to load before stopping
823
+ # stale: (String) 'ok' or 'update_after' - view need not be up to date.
824
+ # descending: (true or false) direction of search?
825
+ # skip: (Number) skip the first few documents - slow - not recomended!
826
+ # group: (true or false) should reduce grouping by exact identical key?
827
+ # group_level: (Number) first x items in key Array are used to group rows
828
+ # reduce: (true or false) should the reduce function be used?
829
+ # update_seq: (true or false) response includes sequence id of database?
500
830
  def query view, options = {}
501
831
  if options[:range]
502
832
  range = options.delete[:range]
@@ -522,6 +852,7 @@ class ChillDB::Design
522
852
 
523
853
  private
524
854
 
855
+ # handles the little shortcut for specifying only a map function
525
856
  def views_preprocess views_hash
526
857
  views_hash.each do |key, value|
527
858
  views_hash[key] = { map: value } if value.respond_to? :to_str
@@ -533,19 +864,21 @@ end
533
864
 
534
865
 
535
866
 
536
-
867
+ # Represents one or more failure when doing a bulk commit or delete.
537
868
  class ChillDB::BulkUpdateErrors < StandardError
869
+ # Array of failure messages
538
870
  attr_accessor :failures
539
871
 
540
- def initialize *args
872
+ def initialize *args # :nodoc:
541
873
  @failures = args.pop
542
874
  super(*args)
543
875
  end
544
876
 
877
+ # friendly message listing the failures for each document
545
878
  def inspect
546
- "ChillDB::BulkUpdateError:\n" + @failures.map { |failure|
879
+ "<ChillDB::BulkUpdateError>:\n" + @failures.map { |failure|
547
880
  document, error = failure
548
- " '#{document['_id']}': #{error['reason']}"
881
+ " '#{document['_id']}' => #{error['reason']}"
549
882
  }.join('\n')
550
883
  end
551
884
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chill
3
3
  version: !ruby/object:Gem::Version
4
- version: '6'
4
+ version: '8'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-28 00:00:00.000000000 Z
12
+ date: 2012-04-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
16
- requirement: &70131867175840 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: 1.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70131867175840
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rest-client
27
- requirement: &70131867175340 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: 1.6.7
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70131867175340
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.6.7
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: uuid
38
- requirement: &70131867174860 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,7 +53,12 @@ dependencies:
43
53
  version: 2.3.4
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *70131867174860
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.3.4
47
62
  description: A little library to talk to a couchdb. I made it skinny, because couchdb
48
63
  is very simple. I think that's a good thing.
49
64
  email: a@creativepony.com
@@ -72,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
87
  version: '0'
73
88
  requirements: []
74
89
  rubyforge_project:
75
- rubygems_version: 1.8.11
90
+ rubygems_version: 1.8.21
76
91
  signing_key:
77
92
  specification_version: 3
78
93
  summary: A tiny plug to hook ruby in to couchdb