akdubya-cushion 0.5.1
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/LICENSE +176 -0
- data/README.rdoc +226 -0
- data/Rakefile +65 -0
- data/lib/cushion.rb +97 -0
- data/lib/cushion/core_ext.rb +27 -0
- data/lib/cushion/database.rb +310 -0
- data/lib/cushion/design.rb +33 -0
- data/lib/cushion/document.rb +117 -0
- data/lib/cushion/server.rb +208 -0
- data/spec/cushion_spec.rb +9 -0
- data/spec/database_spec.rb +223 -0
- data/spec/helpers.rb +2 -0
- data/spec/server_spec.rb +119 -0
- metadata +75 -0
data/lib/cushion.rb
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'restclient'
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
dir = File.dirname(__FILE__) + '/cushion/'
|
|
6
|
+
|
|
7
|
+
require dir + 'core_ext'
|
|
8
|
+
require dir + 'server'
|
|
9
|
+
require dir + 'database'
|
|
10
|
+
require dir + 'document'
|
|
11
|
+
require dir + 'design'
|
|
12
|
+
|
|
13
|
+
DEFAULT_COUCH_HOST = "http://127.0.0.1:5984"
|
|
14
|
+
|
|
15
|
+
module Cushion
|
|
16
|
+
VERSION = '0.5.1'
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
|
|
20
|
+
# Returns a Cushion::Server instance.
|
|
21
|
+
def server(*args)
|
|
22
|
+
Server.new(*args)
|
|
23
|
+
end
|
|
24
|
+
alias_method :new, :server
|
|
25
|
+
|
|
26
|
+
# Returns a Cushion::Database instance rooted at +url+. Does not attempt
|
|
27
|
+
# to create the database on the server.
|
|
28
|
+
def db(url)
|
|
29
|
+
parsed = parse(url)
|
|
30
|
+
server = Cushion.new(parsed[:host])
|
|
31
|
+
server.db(parsed[:db])
|
|
32
|
+
end
|
|
33
|
+
alias_method :database, :db
|
|
34
|
+
|
|
35
|
+
# Creates a database at +url+ if it does not exist. Returns a Cushion::Database
|
|
36
|
+
# instance.
|
|
37
|
+
def db!(url)
|
|
38
|
+
parsed = parse(url)
|
|
39
|
+
server = Cushion.new(parsed[:host])
|
|
40
|
+
server.db!(parsed[:db])
|
|
41
|
+
end
|
|
42
|
+
alias_method :database!, :db!
|
|
43
|
+
|
|
44
|
+
# Drops and recreates a database at +url+, returning a Cushion::Database
|
|
45
|
+
# instance.
|
|
46
|
+
def recreate(url)
|
|
47
|
+
parsed = parse(url)
|
|
48
|
+
server = Cushion.new(parsed[:host])
|
|
49
|
+
server.recreate(parsed[:db])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Utility method for converting parameters into url query strings.
|
|
53
|
+
def paramify_url(url, params = {})
|
|
54
|
+
if params && !params.empty?
|
|
55
|
+
query = params.collect do |k,v|
|
|
56
|
+
v = v.to_json if %w{key startkey endkey}.include?(k.to_s)
|
|
57
|
+
"#{k}=#{CGI.escape(v.to_s)}"
|
|
58
|
+
end.join("&")
|
|
59
|
+
url = "#{url}?#{query}"
|
|
60
|
+
end
|
|
61
|
+
url
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Handles document id escaping.
|
|
65
|
+
def escape_docid(id)
|
|
66
|
+
/^_design\/(.*)/ =~ id ? "_design/#{CGI.escape($1)}" : CGI.escape(id)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Base64 encodes inline attachments.
|
|
70
|
+
def encode_attachments(attachments)
|
|
71
|
+
attachments.each do |k,v|
|
|
72
|
+
next if v['stub']
|
|
73
|
+
v['data'] = base64(v['data'])
|
|
74
|
+
end
|
|
75
|
+
attachments
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def base64(data)
|
|
79
|
+
Base64.encode64(data).gsub(/\s/,'')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Sets the RestClient proxy.
|
|
83
|
+
def proxy(url)
|
|
84
|
+
RestClient.proxy = url
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def parse(url)
|
|
90
|
+
parsed = URI.parse(url)
|
|
91
|
+
{
|
|
92
|
+
:host => "#{parsed.scheme}://#{parsed.host}:#{parsed.port}",
|
|
93
|
+
:db => parsed.path[1, parsed.path.length] # strip off the leading slash
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
end # class << self
|
|
97
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# This file must be loaded after the JSON gem and any other library that beats up the Time class.
|
|
2
|
+
class Time
|
|
3
|
+
# This date format sorts lexicographically
|
|
4
|
+
# and is compatible with Javascript's <tt>new Date(time_string)</tt> constructor.
|
|
5
|
+
# Note this this format stores all dates in UTC so that collation
|
|
6
|
+
# order is preserved. (There's no longer a need to set <tt>ENV['TZ'] = 'UTC'</tt>
|
|
7
|
+
# in your application.)
|
|
8
|
+
|
|
9
|
+
def to_json(options = nil)
|
|
10
|
+
u = self.utc
|
|
11
|
+
%("#{u.strftime("%Y/%m/%d %H:%M:%S +0000")}")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module RestClient
|
|
16
|
+
def self.copy(url, headers={})
|
|
17
|
+
Request.execute(:method => :copy,
|
|
18
|
+
:url => url,
|
|
19
|
+
:headers => headers)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.move(url, headers={})
|
|
23
|
+
Request.execute(:method => :move,
|
|
24
|
+
:url => url,
|
|
25
|
+
:headers => headers)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
require 'base64'
|
|
3
|
+
|
|
4
|
+
module Cushion
|
|
5
|
+
class Database
|
|
6
|
+
attr_reader :server, :name
|
|
7
|
+
|
|
8
|
+
def initialize(server, name)
|
|
9
|
+
raise ArgumentError, "server must be an a Cushion::Server" unless server.kind_of?(Cushion::Server)
|
|
10
|
+
# TODO: CouchDB has strict db naming requirements. Should validate.
|
|
11
|
+
@name = CGI.escape(name.to_s)
|
|
12
|
+
@server = server
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Retrieves information about this database.
|
|
16
|
+
def info(headers = {})
|
|
17
|
+
server.get(@name, headers)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Compacts this database.
|
|
21
|
+
def compact(headers = {})
|
|
22
|
+
server.post("#{@name}/_compact", nil, headers)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Creates this database on the server.
|
|
26
|
+
def create(headers = {})
|
|
27
|
+
server.put(@name, nil, headers)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Deletes this database from the server.
|
|
31
|
+
def delete(headers = {})
|
|
32
|
+
server.delete(@name, headers)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Query the default +all_docs+ view. Set the +keys+ option to perform a
|
|
36
|
+
# key-based multi-document fetch. Set the +headers+ option to pass
|
|
37
|
+
# custom request headers.
|
|
38
|
+
#
|
|
39
|
+
def all_docs(params = {})
|
|
40
|
+
keys = params.delete(:keys)
|
|
41
|
+
opts = params.delete(:headers) || {}
|
|
42
|
+
path = Cushion.paramify_url("#{@name}/_all_docs", params)
|
|
43
|
+
if keys
|
|
44
|
+
server.post(path, {:keys => keys}, opts)
|
|
45
|
+
else
|
|
46
|
+
server.get(path, opts)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Retrieves a single document by +id+. Set the +headers+ option to pass
|
|
51
|
+
# custom request headers.
|
|
52
|
+
def open_doc(id, params = {})
|
|
53
|
+
opts = params.delete(:headers) || {}
|
|
54
|
+
slug = Cushion.escape_docid(id)
|
|
55
|
+
path = Cushion.paramify_url("#{@name}/#{slug}", params)
|
|
56
|
+
server.get(path, opts)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Retrieves a single document by +id+ and returns a <tt>Cushion::Document</tt>
|
|
60
|
+
# linked to this database.
|
|
61
|
+
#
|
|
62
|
+
def doc(id, params = {})
|
|
63
|
+
result = open_doc(id, params)
|
|
64
|
+
ndoc = Cushion::Document.new(result)
|
|
65
|
+
ndoc.database = self
|
|
66
|
+
ndoc
|
|
67
|
+
end
|
|
68
|
+
alias_method :document, :doc
|
|
69
|
+
|
|
70
|
+
# Saves the params hash to the database as a document, encoding inline
|
|
71
|
+
# attachments and generating a UUID if no +_id+ is supplied. A +_rev+ attribute
|
|
72
|
+
# must be supplied to update an existing document. Set the +headers+ option
|
|
73
|
+
# to pass custom request headers.
|
|
74
|
+
#
|
|
75
|
+
def save_doc(params = {})
|
|
76
|
+
opts = params.delete(:headers) || {}
|
|
77
|
+
if params['_attachments']
|
|
78
|
+
params['_attachments'] = Cushion.encode_attachments(params['_attachments'])
|
|
79
|
+
end
|
|
80
|
+
if params['_id']
|
|
81
|
+
slug = Cushion.escape_docid(params['_id'])
|
|
82
|
+
server.put("#{@name}/#{slug}", params, opts)
|
|
83
|
+
else
|
|
84
|
+
slug = params['_id'] = server.next_uuid
|
|
85
|
+
server.put("#{@name}/#{slug}", params, opts)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Deletes a single document by +id+ and +rev+.
|
|
90
|
+
def delete_doc(id, rev, headers = {})
|
|
91
|
+
slug = Cushion.escape_docid(id)
|
|
92
|
+
server.delete("#{@name}/#{slug}?rev=#{rev}", headers)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Creates, updates and deletes multiple documents in this database according
|
|
96
|
+
# to the supplied +docs+ array. Set the +_deleted+ attribute to true to delete
|
|
97
|
+
# an individual doc. Set the +_rev+ attribute to update an individual doc. Leave
|
|
98
|
+
# out the +_rev+ attribute to create a new doc. Example:
|
|
99
|
+
#
|
|
100
|
+
# docs = [
|
|
101
|
+
# { "_id" => "0", "_rev" => "123456", "_deleted" => true }, #=> Delete this doc
|
|
102
|
+
# { "_id" => "1", "_rev" => "32486671", "foo" => "bar" }, #=> Update this doc
|
|
103
|
+
# { "_id" => "2", "baz" => "bat" } #=> Create this doc
|
|
104
|
+
# ]
|
|
105
|
+
#
|
|
106
|
+
# db.bulk_docs(docs)
|
|
107
|
+
#
|
|
108
|
+
# +bulk_docs+ returns a hash of updated doc ids and revs, as follows:
|
|
109
|
+
#
|
|
110
|
+
# {
|
|
111
|
+
# "ok" => true,
|
|
112
|
+
# "new_revs" => [
|
|
113
|
+
# { "id" => "0", "rev" => "3682408536" },
|
|
114
|
+
# { "id" => "1", "rev" => "3206753266" },
|
|
115
|
+
# { "id" => "2", "rev" => "426742535" }
|
|
116
|
+
# ]
|
|
117
|
+
# }
|
|
118
|
+
#
|
|
119
|
+
# Set the +use_uuids+ option to generate UUIDs from the server cache before
|
|
120
|
+
# saveing. Set the +headers+ option to pass custom request headers.
|
|
121
|
+
#
|
|
122
|
+
def bulk_docs(docs, params = {})
|
|
123
|
+
headers = params.delete(:headers) || {}
|
|
124
|
+
opts = { :use_uuids => true }.merge(params)
|
|
125
|
+
if (opts[:use_uuids])
|
|
126
|
+
ids, noids = docs.partition{|d|d['_id']}
|
|
127
|
+
uuid_count = [noids.length, server.uuid_batch_count].max
|
|
128
|
+
noids.each do |doc|
|
|
129
|
+
nextid = server.next_uuid(uuid_count) rescue nil
|
|
130
|
+
doc['_id'] = nextid if nextid
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
server.post("#{@name}/_bulk_docs", {:docs => docs}, headers)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# A convenience method for deleting multiple documents at once. Just pass in
|
|
137
|
+
# an array of saved +docs+ and the correct deletion params are
|
|
138
|
+
# automatically set. (See #bulk_docs)
|
|
139
|
+
#
|
|
140
|
+
def bulk_delete(docs, params = {})
|
|
141
|
+
deletions = docs.map { |doc| { '_id' => doc['_id'], '_rev' => doc['_rev'], '_deleted' => true } }
|
|
142
|
+
bulk_docs(deletions, params.merge(:use_uuids => false))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Completely purges the supplied document revisions from the database.
|
|
146
|
+
# +doc_revs+ is a hash of document ids, each containing an array of revisions
|
|
147
|
+
# to be deleted. Example:
|
|
148
|
+
#
|
|
149
|
+
# doc_revs = {
|
|
150
|
+
# "1" => ["12345", "42836"],
|
|
151
|
+
# "2" => ["572654"],
|
|
152
|
+
# "3" => ["34462"]
|
|
153
|
+
# }
|
|
154
|
+
#
|
|
155
|
+
# db.purge(doc_revs)
|
|
156
|
+
#
|
|
157
|
+
def purge(doc_revs, headers = {})
|
|
158
|
+
server.post("#{@name}/_purge", doc_revs, headers)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Copies the document identified by +source_id+ to a new or existing document
|
|
162
|
+
# at +destination_id+. Set the +dest_rev+ option to overwrite an existing doc.
|
|
163
|
+
# Set the +headers+ option to pass custom request headers.
|
|
164
|
+
#
|
|
165
|
+
def copy_doc(source_id, dest_id, params = {})
|
|
166
|
+
headers = params.delete(:headers) || {}
|
|
167
|
+
dest_rev = params[:dest_rev]
|
|
168
|
+
slug = Cushion.escape_docid(source_id)
|
|
169
|
+
destination = if dest_rev
|
|
170
|
+
"#{dest_id}?rev=#{dest_rev}"
|
|
171
|
+
else
|
|
172
|
+
dest_id
|
|
173
|
+
end
|
|
174
|
+
server.copy("#{@name}/#{slug}", destination, headers)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Moves the document identified by +source_id+ and +src_rev+ to a new or
|
|
178
|
+
# existing document at +destination_id+. Set the +dest_rev+ option to
|
|
179
|
+
# overwrite an existing doc. Set the +headers+ option to pass custom
|
|
180
|
+
# request headers.
|
|
181
|
+
#
|
|
182
|
+
def move_doc(source_id, dest_id, src_rev, params = {})
|
|
183
|
+
headers = params.delete(:headers) || {}
|
|
184
|
+
dest_rev = params[:dest_rev]
|
|
185
|
+
slug = Cushion.escape_docid(source_id)
|
|
186
|
+
destination = if dest_rev
|
|
187
|
+
"#{dest_id}?rev=#{dest_rev}"
|
|
188
|
+
else
|
|
189
|
+
dest_id
|
|
190
|
+
end
|
|
191
|
+
server.move("#{@name}/#{slug}?rev=#{src_rev}", destination, headers)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Supply +funcs+ to create a temporary view and run a query against it. Set
|
|
195
|
+
# the +keys+ option to perform a key-based multi-document fetch against the
|
|
196
|
+
# temp view. Example:
|
|
197
|
+
#
|
|
198
|
+
# temp_view = { :map => "function(doc){emit(doc.status,null)}" }
|
|
199
|
+
# db.temp_view(temp_view, :key => "verified")
|
|
200
|
+
#
|
|
201
|
+
# Set the +headers+ option to pass custom request headers.
|
|
202
|
+
#
|
|
203
|
+
# Temp view queries are slow, so they should only be used as a convenience
|
|
204
|
+
# during development.
|
|
205
|
+
#
|
|
206
|
+
def temp_view(funcs, params = {})
|
|
207
|
+
keys = params.delete(:keys)
|
|
208
|
+
headers = params.delete(:headers) || {}
|
|
209
|
+
funcs = funcs.merge({:keys => keys}) if keys
|
|
210
|
+
path = Cushion.paramify_url("#{@name}/_temp_view", params)
|
|
211
|
+
server.post(path, funcs, headers)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Query a saved view identified by +view+. Set the +keys+ param to perform a
|
|
215
|
+
# key-based multi-document fetch. Example:
|
|
216
|
+
#
|
|
217
|
+
# db.view("people/by_first_name", :key => "gomer")
|
|
218
|
+
#
|
|
219
|
+
# Set the +headers+ option to pass custom request headers.
|
|
220
|
+
#
|
|
221
|
+
def view(view, params = {})
|
|
222
|
+
keys = params.delete(:keys)
|
|
223
|
+
headers = params.delete(:headers) || {}
|
|
224
|
+
path = Cushion.paramify_url("#{@name}/_view/#{view}", params)
|
|
225
|
+
if keys
|
|
226
|
+
server.post(path, {:keys => keys}, headers)
|
|
227
|
+
else
|
|
228
|
+
server.get(path, headers)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Query the show template identified by +design+, +show_template+ and +id+.
|
|
233
|
+
# The results are not parsed by default. Set the +headers+ option to pass
|
|
234
|
+
# custom request headers.
|
|
235
|
+
#
|
|
236
|
+
def show(design, show_template, id, params = {})
|
|
237
|
+
defaults = { :accept => "text/html;text/plain;*/*" }
|
|
238
|
+
headers = params.delete(:headers) || {}
|
|
239
|
+
slug = Cushion.escape_docid(id)
|
|
240
|
+
path = Cushion.paramify_url("#{@name}/_show/#{design}/#{show_template}/#{slug}", params)
|
|
241
|
+
server.get(path, defaults.merge(headers))
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Query the list template identified by +design+, +list_template+ and +view+.
|
|
245
|
+
# The results are not parsed by default. Set the +headers+ option to pass
|
|
246
|
+
# custom request headers.
|
|
247
|
+
#
|
|
248
|
+
def list(design, list_template, view, params = {})
|
|
249
|
+
defaults = { :accept => "text/html;text/plain;*/*" }
|
|
250
|
+
headers = params.delete(:headers) || {}
|
|
251
|
+
path = Cushion.paramify_url("#{@name}/_list/#{design}/#{list_template}/#{view}", params)
|
|
252
|
+
server.get(path, defaults.merge(headers))
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Issues a request to the CouchDB external server identified by +process+.
|
|
256
|
+
# Calls to external must include a request +verb+. Set the +headers+ option
|
|
257
|
+
# to pass custom request headers.
|
|
258
|
+
#
|
|
259
|
+
def external(verb, process_path, params = {})
|
|
260
|
+
path = Cushion.paramify_url("_#{process_path}", params[:query])
|
|
261
|
+
server.request(verb, path, :body => params[:body], :headers => params[:headers])
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Uses open-uri to open the attachment identified by +id+ and +filename+.
|
|
265
|
+
# Returns a Tempfile.
|
|
266
|
+
#
|
|
267
|
+
def open_attachment(id, filename, params = {})
|
|
268
|
+
slug = Cushion.escape_docid(id)
|
|
269
|
+
fname = CGI.escape(filename)
|
|
270
|
+
path = Cushion.paramify_url("#{@name}/#{slug}/#{fname}", params)
|
|
271
|
+
server.open_attachment(path)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Saves an attachment under the document identified by +id+ and +rev+ with
|
|
275
|
+
# the supplied +filename+ and +data+. Content type defaults to
|
|
276
|
+
# 'application/octet-stream'. Change it by passing <code>:content_type => "foo/bar"</code>
|
|
277
|
+
# as the last param.
|
|
278
|
+
#
|
|
279
|
+
def save_attachment(id, rev, filename, data, headers = {})
|
|
280
|
+
defaults = { :content_type => "application/octet-stream" }
|
|
281
|
+
slug = Cushion.escape_docid(id)
|
|
282
|
+
fname = CGI.escape(filename)
|
|
283
|
+
server.put("#{@name}/#{slug}/#{fname}?rev=#{rev}", data, defaults.merge(headers))
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Deletes an attachment under the document identified by +id+ and +rev+ with
|
|
287
|
+
# the supplied +filename+.
|
|
288
|
+
#
|
|
289
|
+
def delete_attachment(id, rev, filename, headers = {})
|
|
290
|
+
slug = Cushion.escape_docid(id)
|
|
291
|
+
fname = CGI.escape(filename)
|
|
292
|
+
server.delete("#{@name}/#{slug}/#{fname}?rev=#{rev}", headers)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Renames an attachment under the document identified by +id+ and +rev+.
|
|
296
|
+
# +oldname+ is the name of the existing attachment and +newname+ is the
|
|
297
|
+
# desired name.
|
|
298
|
+
#
|
|
299
|
+
def rename_attachment(id, rev, oldname, newname)
|
|
300
|
+
temp = open_attachment(id, oldname)
|
|
301
|
+
res = save_attachment(id, rev, newname, temp.read, :content_type => temp.content_type)
|
|
302
|
+
if res["ok"] == true
|
|
303
|
+
res = delete_attachment(id, res["rev"], oldname)
|
|
304
|
+
else
|
|
305
|
+
res
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
end
|
|
310
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Cushion
|
|
2
|
+
class Design < Document
|
|
3
|
+
def view(view_name, query={})
|
|
4
|
+
view_name = view_name.to_s
|
|
5
|
+
view_slug = "#{name}/#{view_name}"
|
|
6
|
+
defaults = (self['views'][view_name] && self['views'][view_name]["couchrest-defaults"]) || {}
|
|
7
|
+
database.view(view_slug, defaults.merge(query))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def name
|
|
11
|
+
id.sub('_design/','') if id
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def name=(newname)
|
|
15
|
+
self['_id'] = "_design/#{newname}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def language
|
|
19
|
+
self['language']
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Sets this design document's language.
|
|
23
|
+
def language=(lang)
|
|
24
|
+
self['language'] = lang
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def save
|
|
28
|
+
raise ArgumentError, "_design docs require a name" unless name && name.length > 0
|
|
29
|
+
self.language = "javascript" unless language
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|