chill 6 → 8

Sign up to get free protection for your applications and to get access to all the features.
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