couchdb-ruby-server 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.hound.yml +4 -0
- data/.travis.yml +13 -0
- data/Gemfile +5 -0
- data/HISTORY.md +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +38 -0
- data/Rakefile +16 -0
- data/TODO.md +4 -0
- data/bin/couchdb-ruby-server +18 -0
- data/couchdb-ruby-server.gemspec +28 -0
- data/couchdb-ruby-server.sublime-project +27 -0
- data/lib/couchdb-ruby-server/server.rb +330 -0
- data/lib/couchdb-ruby-server/version.rb +5 -0
- data/lib/couchdb-ruby-server.rb +8 -0
- data/spec/couchdb-ruby-server_spec.rb +952 -0
- data/spec/spec_helper.rb +10 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: aa9cbfeea39a79efb59cfa9b1d278ee06ea5451c
|
4
|
+
data.tar.gz: 1fa6648489a455bc477d446af41b2f84c48eb215
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 196f7dd41438a62fe78b9d98c3f6824b04e29639f82fc9994017ad95d3594c8476f01211bdc985405283d1a80c9674f9a443191a8b72afc2ae57a6a8fee287a9
|
7
|
+
data.tar.gz: 1e4608f3551c2937ce0acc9f2a7f13075e2ccb9b079b3c1935cf7e1537af679053c32649c310aa3393ec442ea5eab0d1777053dba02fa6da703bdf6a67a542b7
|
data/.gitignore
ADDED
data/.hound.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/HISTORY.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
MIT License, http://opensource.org/licenses/MIT
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# couchdb-ruby-server
|
2
|
+
CouchDB query server to run ruby design docs.
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/sinm/couchdb-ruby-server.svg?branch=master)](https://travis-ci.org/sinm/couchdb-ruby-server) [![Gem Version](https://badge.fury.io/rb/couchdb-ruby-server.svg)](http://badge.fury.io/rb/couchdb-ruby-server) [![Dependency Status](https://gemnasium.com/sinm/couchdb-ruby-server.svg)](https://gemnasium.com/sinm/couchdb-ruby-server) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/sinm/couchdb-ruby-server/master/LICENSE.txt) [![Code Climate](https://codeclimate.com/github/sinm/couchdb-ruby-server/badges/gpa.svg)](https://codeclimate.com/github/sinm/couchdb-ruby-server) [![Test Coverage](https://codeclimate.com/github/sinm/couchdb-ruby-server/badges/coverage.svg)](https://codeclimate.com/github/sinm/couchdb-ruby-server) [![Inline docs](http://inch-ci.org/github/sinm/couchdb-ruby-server.svg?branch=master)](http://inch-ci.org/github/sinm/couchdb-ruby-server)
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
`gem install couchdb-ruby-server` as usual.
|
8
|
+
|
9
|
+
Edit your couchdb's local.ini and set this in the `[query_servers]` group:
|
10
|
+
|
11
|
+
ruby = couchdb-ruby-server
|
12
|
+
|
13
|
+
Restart CouchDB then.
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
Design functions (map, reduce, update, etc.) are well defined as `lambda` or `proc` blocks:
|
17
|
+
|
18
|
+
lambda { |doc| emit [1,2,3], doc }
|
19
|
+
|
20
|
+
It looks like any object responding to `#call` will survive.
|
21
|
+
|
22
|
+
[Couchapp](couchapp.org) works well with `rb` files, so use it to manage design files. Just do `echo ruby > language` inside your app's root directory and you're in.
|
23
|
+
|
24
|
+
You may load your own code into the query server context by providing path to a library as an `--require LIB` option. This way you could deserialize documents right into the instances of the library classes instead of plain hashes.
|
25
|
+
|
26
|
+
Actually any tagname for the query server may be set in `local.ini` instead of `ruby` so it's possible to run several query servers e.g. per application. Just remember to copy that tagname into couchapp's `language` file.
|
27
|
+
|
28
|
+
## Tests
|
29
|
+
`bundle exec rake test`. Gem following original [tests](https://github.com/apache/couchdb/blob/master/test/view_server/query_server_spec.rb).
|
30
|
+
|
31
|
+
## Documentation
|
32
|
+
This README is all i could say in a rush. No other documentation provided at this moment, see the sources.
|
33
|
+
|
34
|
+
## If you've found a bug or drawback
|
35
|
+
Don't hesistate to leave a report.
|
36
|
+
|
37
|
+
## License
|
38
|
+
MIT for now.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
Rake::TestTask.new :test do |t|
|
5
|
+
t.pattern = 'spec/**/*_spec.rb'
|
6
|
+
t.libs.push 'spec'
|
7
|
+
# bundle exec rake test TESTOPTS='-v'
|
8
|
+
end
|
9
|
+
|
10
|
+
task :build => :test do
|
11
|
+
system('gem build couchdb-ruby-server.gemspec')
|
12
|
+
# gem install couchdb-ruby-server-1.0.0.gem
|
13
|
+
# gem push couchdb-ruby-server-1.0.0.gem
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => :test
|
data/TODO.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env RUBY_GC_MALLOC_LIMIT=24000000 ruby
|
2
|
+
require 'couchdb-ruby-server'
|
3
|
+
require 'argparser'
|
4
|
+
|
5
|
+
args = ArgParser.new :program => 'couchdb-ruby-server',
|
6
|
+
:version => CouchdbRubyServer::VERSION,
|
7
|
+
:info => 'CouchDB query server to run ruby design docs.',
|
8
|
+
:licence => 'MIT',
|
9
|
+
:copyright=> '2014-2015 sinmsinm@gmail.com',
|
10
|
+
:options => [{ :names => %w[r require],
|
11
|
+
:argument => 'LIB',
|
12
|
+
:multiple => true,
|
13
|
+
:help => 'Aux library to require.' }]
|
14
|
+
args['require'].value.each do |r|
|
15
|
+
require r
|
16
|
+
end
|
17
|
+
|
18
|
+
CouchdbRubyServer.run
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path('../lib/couchdb-ruby-server/version.rb', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'couchdb-ruby-server'
|
6
|
+
s.version = CouchdbRubyServer::VERSION
|
7
|
+
s.authors = ['sinm']
|
8
|
+
s.email = 'sinm.sinm@gmail.com'
|
9
|
+
s.summary = 'CouchDB query server to run ruby design docs'
|
10
|
+
s.description = '== CouchDB query server to run ruby design docs'
|
11
|
+
s.homepage = 'https://github.com/sinm/couchdb-ruby-server'
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.files = `git ls-files -z`.split("\x0")
|
14
|
+
s.test_files = `git ls-files -z spec/`.split("\0")
|
15
|
+
s.require_paths = ['lib']
|
16
|
+
s.bindir = 'bin'
|
17
|
+
s.executables << 'couchdb-ruby-server'
|
18
|
+
# s.extra_rdoc_files = 'README.md'
|
19
|
+
# s.rdoc_options << '--title' << 'couchdb-ruby-server' <<
|
20
|
+
# '--main' << 'README' <<
|
21
|
+
# '--markup' << 'markdown' <<
|
22
|
+
# '--line-numbers'
|
23
|
+
s.add_development_dependency 'bundler', '~> 1'
|
24
|
+
s.add_development_dependency 'rake', '~> 10'
|
25
|
+
s.add_development_dependency 'minitest', '~> 4'
|
26
|
+
s.add_dependency 'argparser', '~> 1'
|
27
|
+
s.add_dependency 'oj', '~> 2.7'
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"folders":
|
3
|
+
[
|
4
|
+
{
|
5
|
+
"path": "/Users/sinm/Projects/couchdb-ruby-server"
|
6
|
+
}
|
7
|
+
],
|
8
|
+
"build_systems":
|
9
|
+
[
|
10
|
+
{
|
11
|
+
"name": "Run tests",
|
12
|
+
"cmd": ["bundle", "exec", "rake", "test"],
|
13
|
+
"selector": "source.rb"
|
14
|
+
}
|
15
|
+
],
|
16
|
+
"settings":
|
17
|
+
{
|
18
|
+
"ensure_newline_at_eof_on_save": true,
|
19
|
+
"rulers":
|
20
|
+
[
|
21
|
+
80
|
22
|
+
],
|
23
|
+
"tab_size": 2,
|
24
|
+
"translate_tabs_to_spaces": true,
|
25
|
+
"trim_trailing_white_space_on_save": true
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,330 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
$stdin.sync = true
|
5
|
+
$stdout.sync = true
|
6
|
+
|
7
|
+
module CouchdbRubyServer
|
8
|
+
class Error < RuntimeError
|
9
|
+
attr_reader :key
|
10
|
+
def initialize key
|
11
|
+
@key = key
|
12
|
+
end
|
13
|
+
end
|
14
|
+
class Fatal < Error
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.update_case fn, args
|
18
|
+
result = fn.call(*args)
|
19
|
+
return ["up", result[0], maybe_wrap_response(result[1])]
|
20
|
+
end
|
21
|
+
|
22
|
+
MIME_TYPES = {
|
23
|
+
:all => "*/*",
|
24
|
+
:text => "text/plain; charset=utf-8",
|
25
|
+
:html => "text/html; charset=utf-8",
|
26
|
+
:xhtml => "application/xhtml+xml",
|
27
|
+
:xml => "application/xml",
|
28
|
+
:js => "text/javascript",
|
29
|
+
:css => "text/css",
|
30
|
+
:ics => "text/calendar",
|
31
|
+
:csv => "text/csv; charset=utf-8",
|
32
|
+
:rss => "application/rss+xml",
|
33
|
+
:atom => "application/atom+xml",
|
34
|
+
:yaml => "application/x-yaml",
|
35
|
+
:multipart_form => "multipart/form-data",
|
36
|
+
:url_encoded_form => "application/x-www-form-urlencoded",
|
37
|
+
:json => "application/json; charset=utf-8"
|
38
|
+
}
|
39
|
+
|
40
|
+
TYPES_MIME = Hash[MIME_TYPES.map {|k, v| [v, k] }]
|
41
|
+
|
42
|
+
def self.to_json(obj)
|
43
|
+
Oj.dump(obj)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.json_parse(str)
|
47
|
+
return nil if str.nil?
|
48
|
+
Oj.load str
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.log message
|
52
|
+
respond ['log', "#{message}"]
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.respond obj
|
56
|
+
puts to_json obj
|
57
|
+
rescue
|
58
|
+
log "respond error converting object to JSON: #{$!.message}\n#{$!.backtrace.join("\n")}"
|
59
|
+
puts 'false'
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.respond_error error, reason
|
63
|
+
if reason.respond_to?(:message) && reason.respond_to?(:backtrace)
|
64
|
+
reason = "#{reason.class.name}: #{reason.message}\n#{reason.backtrace.join("\n")}"
|
65
|
+
end
|
66
|
+
respond ['error', error, reason]
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.respond_fatal error, reason
|
70
|
+
respond_error error, reason
|
71
|
+
exit(2)
|
72
|
+
end
|
73
|
+
|
74
|
+
COMPILED_PROCS = {}
|
75
|
+
|
76
|
+
def self.compile_proc source, binding, name
|
77
|
+
source_hash = source.hash
|
78
|
+
fn = COMPILED_PROCS[source_hash]
|
79
|
+
return fn if fn
|
80
|
+
begin
|
81
|
+
fn = eval source, binding, name, 1
|
82
|
+
raise "#{name} must respond_to #call" unless fn.respond_to?(:call)
|
83
|
+
COMPILED_PROCS[source_hash] = fn
|
84
|
+
rescue Exception
|
85
|
+
respond_error :compilation_error, $!
|
86
|
+
end
|
87
|
+
fn
|
88
|
+
end
|
89
|
+
|
90
|
+
@@map_results = []
|
91
|
+
@@callables = []
|
92
|
+
@@ddocs = {}
|
93
|
+
#list state
|
94
|
+
@@chunks = []
|
95
|
+
@@start_resp = {}
|
96
|
+
@@got_row = false
|
97
|
+
@@last_row = false
|
98
|
+
@@provides_used = []
|
99
|
+
@@resp_mime = nil
|
100
|
+
#view filter
|
101
|
+
@@view_emit = nil
|
102
|
+
|
103
|
+
def self.emit(key, value)
|
104
|
+
if @@view_emit.nil?
|
105
|
+
@@map_results.push [key, value]
|
106
|
+
else
|
107
|
+
@@view_emit = true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.provides(*args, &block)
|
112
|
+
@@provides_used << {:format => args[0], :block => block}.merge(
|
113
|
+
args.size > 1 ? {:mime => args[1]} : {}
|
114
|
+
)
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.start(resp)
|
119
|
+
@@start_resp = resp if resp
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.send(chunk)
|
124
|
+
@@chunks << "#{chunk}"
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.get_row
|
129
|
+
return nil if @@last_row
|
130
|
+
if not @@got_row
|
131
|
+
@@got_row = true
|
132
|
+
render_headers
|
133
|
+
respond ["start", @@chunks, @@start_resp]
|
134
|
+
@@start_resp = {}
|
135
|
+
else
|
136
|
+
respond ["chunks", @@chunks]
|
137
|
+
end
|
138
|
+
@@chunks = []
|
139
|
+
json = json_parse ARGF.gets
|
140
|
+
if json[0] == 'list_end'
|
141
|
+
@@last_row = true
|
142
|
+
return nil
|
143
|
+
elsif json[0] != 'list_row'
|
144
|
+
respond_fatal :list_error, "not a row: #{json[0]}"
|
145
|
+
end
|
146
|
+
json[1]
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
def self.maybe_wrap_response resp
|
151
|
+
{:body=>''}.merge(
|
152
|
+
resp.respond_to?(:to_hash) ? resp.to_hash : {:body=>"#{resp}"})
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.run_reduce(srcs, ks, vs, rereduce, b)
|
156
|
+
fns = srcs.collect { |src| compile_proc(src, b, "reduce (#{src[0..100]}...)") || return }
|
157
|
+
reductions = fns.collect { |fn|
|
158
|
+
begin
|
159
|
+
fn.call(ks, vs, rereduce)
|
160
|
+
rescue
|
161
|
+
log "#{rereduce ? 'rereduce' : 'reduce'} raised exception", $!
|
162
|
+
nil
|
163
|
+
end
|
164
|
+
}
|
165
|
+
respond [true, reductions]
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.reset_list
|
169
|
+
@@got_row = false
|
170
|
+
@@last_row = false
|
171
|
+
@@chunks = []
|
172
|
+
@@start_resp = {}
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.reset_provides
|
176
|
+
@@provides_used = []
|
177
|
+
@@resp_mime = nil
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.render_headers
|
181
|
+
@@start_resp[:headers] ||= {}
|
182
|
+
@@start_resp[:headers]["Content-Type"] ||= @@resp_mime if @@resp_mime
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.run_provides req
|
186
|
+
format = :unknown
|
187
|
+
best_fun = if req['query'] and req['query']['format']
|
188
|
+
format = req['query']['format']
|
189
|
+
@@provides_used.find do |p|
|
190
|
+
p[:format].to_s == format
|
191
|
+
end
|
192
|
+
elsif req['headers'] && (format = req['headers']['Accept'])
|
193
|
+
f = nil
|
194
|
+
format.split(',').each do |a|
|
195
|
+
break if (f = a =~ /^\*\/\*/ ? @@provides_used.first :
|
196
|
+
@@provides_used.find do |p|
|
197
|
+
a == p[:mime] or p[:format] == TYPES_MIME[a]
|
198
|
+
end)
|
199
|
+
end
|
200
|
+
f
|
201
|
+
else
|
202
|
+
@@provides_used.first
|
203
|
+
end
|
204
|
+
raise "format #{format} not supported" unless best_fun
|
205
|
+
raise "no block in provides" unless best_fun[:block] &&
|
206
|
+
best_fun[:block].respond_to?(:call)
|
207
|
+
@@resp_mime = best_fun[:mime] || MIME_TYPES[best_fun[:format]]
|
208
|
+
best_fun[:block].call
|
209
|
+
end
|
210
|
+
|
211
|
+
def self.run
|
212
|
+
log "CouchdbRubyServer started, RUBY_VERSION: #{RUBY_VERSION}"
|
213
|
+
log "RUBY_GC_MALLOC_LIMIT=#{ENV['RUBY_GC_MALLOC_LIMIT']}"
|
214
|
+
while cmd = $stdin.gets
|
215
|
+
cmd = json_parse cmd
|
216
|
+
case cmd[0]
|
217
|
+
when "reset"
|
218
|
+
@@callables = []
|
219
|
+
respond(true)
|
220
|
+
when "add_fun"
|
221
|
+
fn = compile_proc cmd[1], binding, "map (#{cmd[1][0..100]}...)"
|
222
|
+
next unless fn
|
223
|
+
@@callables << fn
|
224
|
+
respond(true)
|
225
|
+
when "map_doc"
|
226
|
+
results = []
|
227
|
+
@@view_emit = nil
|
228
|
+
doc = cmd[1]
|
229
|
+
doc.freeze
|
230
|
+
@@callables.each do |callable|
|
231
|
+
@@map_results = []
|
232
|
+
begin
|
233
|
+
callable.call(doc)
|
234
|
+
results.push(@@map_results)
|
235
|
+
rescue
|
236
|
+
log "map raised exception with doc._id #{doc['_id']}", $!
|
237
|
+
results.push([])
|
238
|
+
end
|
239
|
+
end
|
240
|
+
respond(results)
|
241
|
+
when "reduce"
|
242
|
+
ks, vs = cmd[2].transpose
|
243
|
+
run_reduce(cmd[1], ks, vs, false, binding)
|
244
|
+
when "rereduce"
|
245
|
+
run_reduce(cmd[1], nil, cmd[2], true, binding)
|
246
|
+
when "ddoc"
|
247
|
+
cmd.shift
|
248
|
+
ddoc_id = cmd.shift
|
249
|
+
if ddoc_id == 'new'
|
250
|
+
ddoc_id = cmd.shift
|
251
|
+
@@ddocs[ddoc_id] = cmd.shift
|
252
|
+
respond true
|
253
|
+
else
|
254
|
+
ddoc = @@ddocs[ddoc_id]
|
255
|
+
if not ddoc
|
256
|
+
respond_fatal :query_protocol_error, "uncached design doc: #{ddoc_id}"
|
257
|
+
end
|
258
|
+
point = ddoc
|
259
|
+
path = cmd.shift
|
260
|
+
begin
|
261
|
+
path.each do |level|
|
262
|
+
point = (point[level] || raise("missing #{level} function in #{ddoc_id}"))
|
263
|
+
end
|
264
|
+
rescue
|
265
|
+
respond_error :not_found, $!
|
266
|
+
next
|
267
|
+
end
|
268
|
+
next unless (fn = compile_proc point, binding, "#{ddoc['_id']}/#{path.join("/")}")
|
269
|
+
begin
|
270
|
+
response = nil
|
271
|
+
args = cmd.shift
|
272
|
+
case path[0]
|
273
|
+
when "updates"
|
274
|
+
respond update_case fn, args
|
275
|
+
when "lists"
|
276
|
+
#head = args[0]
|
277
|
+
reset_list
|
278
|
+
reset_provides
|
279
|
+
tail = fn.call(*args)
|
280
|
+
begin
|
281
|
+
tail = run_provides(args[1]) unless @@provides_used.empty?
|
282
|
+
rescue
|
283
|
+
respond_error :not_acceptable, $!
|
284
|
+
next
|
285
|
+
end
|
286
|
+
get_row unless @@got_row
|
287
|
+
@@chunks << tail unless tail.nil?
|
288
|
+
respond ["end", @@chunks]
|
289
|
+
when "shows"
|
290
|
+
respond ["resp", maybe_wrap_response(fn.call(*args))]
|
291
|
+
when "filters"
|
292
|
+
res = []
|
293
|
+
args[0].each do |doc|
|
294
|
+
res.push(fn.call(doc, args[1]) ? true : false)
|
295
|
+
end
|
296
|
+
respond [true, res]
|
297
|
+
when "views"
|
298
|
+
res = []
|
299
|
+
args[0].each do |doc|
|
300
|
+
@@view_emit = false
|
301
|
+
fn.call doc
|
302
|
+
res.push(@@view_emit ? true : false)
|
303
|
+
end
|
304
|
+
respond [true, res]
|
305
|
+
when "validate_doc_update"
|
306
|
+
begin
|
307
|
+
fn.call(*args)
|
308
|
+
respond 1
|
309
|
+
rescue
|
310
|
+
respond({:forbidden => "bad doc"})
|
311
|
+
end
|
312
|
+
else
|
313
|
+
respond_fatal :unknown_command, "unknown ddoc command #{path[0]}"
|
314
|
+
next
|
315
|
+
end # case ddoc
|
316
|
+
rescue Fatal
|
317
|
+
respond_fatal $!.key, $!.message
|
318
|
+
rescue Error
|
319
|
+
respond_error $!.key, $!.message
|
320
|
+
rescue
|
321
|
+
respond_error :render_error, $!
|
322
|
+
end
|
323
|
+
end
|
324
|
+
else
|
325
|
+
respond_fatal :unknown_command, "unknown command #{cmd[0]}"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|