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.
- data/library/chill.rb +394 -61
- metadata +24 -9
data/library/chill.rb
CHANGED
@@ -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
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
61
|
-
#
|
62
|
-
|
63
|
-
|
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
|
-
|
67
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
240
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
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
|
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
|
-
#
|
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
|
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.
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
403
|
-
#
|
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
|
-
#
|
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
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
879
|
+
"<ChillDB::BulkUpdateError>:\n" + @failures.map { |failure|
|
547
880
|
document, error = failure
|
548
|
-
" '#{document['_id']}'
|
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: '
|
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-
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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.
|
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
|