module CouchdbRubyServer
Constants
- COMPILED_PROCS
- MIME_TYPES
- TYPES_MIME
- VERSION
Public Class Methods
compile_proc(source, binding, name)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 64 def self.compile_proc source, binding, name source_hash = source.hash fn = COMPILED_PROCS[source_hash] return fn if fn begin fn = eval source, binding, name, 1 raise "#{name} must respond_to #call" unless fn.respond_to?(:call) COMPILED_PROCS[source_hash] = fn rescue Exception respond_error :compilation_error, $! end fn end
emit(key, value)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 91 def self.emit(key, value) if @@view_emit.nil? @@map_results.push [key, value] else @@view_emit = true end end
enable_tracing()
click to toggle source
# File lib/couchdb-ruby-server/tracer.rb, line 3 def self.enable_tracing @@trace = true end
get_row()
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 116 def self.get_row return nil if @@last_row if not @@got_row @@got_row = true render_headers respond ["start", @@chunks, @@start_resp] @@start_resp = {} else respond ["chunks", @@chunks] end @@chunks = [] json = Serializer.load($stdin.gets) if json[0] == 'list_end' @@last_row = true return nil elsif json[0] != 'list_row' respond_fatal :list_error, "not a row: #{json[0]}" end json[1] end
log(message)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 38 def self.log message respond ['log', "#{message}"] end
maybe_wrap_response(resp)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 138 def self.maybe_wrap_response resp {:body=>''}.merge( resp.respond_to?(:to_hash) ? resp.to_hash : {:body=>"#{resp}"}) end
provides(*args, &block)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 99 def self.provides(*args, &block) @@provides_used << {:format => args[0], :block => block}.merge( args.size > 1 ? {:mime => args[1]} : {} ) nil end
render_headers()
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 168 def self.render_headers @@start_resp[:headers] ||= {} @@start_resp[:headers]["Content-Type"] ||= @@resp_mime if @@resp_mime end
reset_list()
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 156 def self.reset_list @@got_row = false @@last_row = false @@chunks = [] @@start_resp = {} end
reset_provides()
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 163 def self.reset_provides @@provides_used = [] @@resp_mime = nil end
respond(obj)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 42 def self.respond obj trace {"+ respond(#{obj.inspect})"} puts Serializer.dump(obj) rescue puts('["log", "' + "respond error converting object to JSON: #{$!.message}\n#{$!.backtrace.join("\n")}" + '"]') puts 'false' end
respond_error(error, reason)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 50 def self.respond_error error, reason if reason.respond_to?(:message) && reason.respond_to?(:backtrace) reason = "#{reason.class.name}: #{reason.message}\n#{reason.backtrace.join("\n")}" end respond ['error', error, reason] end
respond_fatal(error, reason)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 57 def self.respond_fatal error, reason respond_error error, reason exit(2) end
run()
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 199 def self.run trace {'+ run'} $stdout.sync = true log "CouchdbRubyServer started, RUBY_VERSION: #{RUBY_VERSION}" #log "RUBY_GC_MALLOC_LIMIT=#{ENV['RUBY_GC_MALLOC_LIMIT']}" while cmd = $stdin.gets cmd = Serializer.load(cmd) trace {"cmd = #{cmd.inspect}"} case cmd[0] when "reset" @@callables = [] respond(true) when "add_fun" fn = compile_proc cmd[1], binding, "map (#{cmd[1][0..100]}...)" next unless fn @@callables << fn respond(true) when "map_doc" results = [] @@view_emit = nil doc = cmd[1] doc.freeze @@callables.each do |callable| @@map_results = [] begin callable.call(doc) results.push(@@map_results) rescue log "map raised exception with doc._id #{doc['_id']}", $! results.push([]) end end respond(results) when "reduce" ks, vs = cmd[2].transpose run_reduce(cmd[1], ks, vs, false, binding) when "rereduce" run_reduce(cmd[1], nil, cmd[2], true, binding) when "ddoc" cmd.shift ddoc_id = cmd.shift if ddoc_id == 'new' ddoc_id = cmd.shift @@ddocs[ddoc_id] = cmd.shift respond true else ddoc = @@ddocs[ddoc_id] if not ddoc respond_fatal :query_protocol_error, "uncached design doc: #{ddoc_id}" end point = ddoc path = cmd.shift begin path.each do |level| point = (point[level] || raise("missing #{level} function in #{ddoc_id}")) end rescue respond_error :not_found, $! next end next unless (fn = compile_proc point, binding, "#{ddoc['_id']}/#{path.join("/")}") begin response = nil args = cmd.shift case path[0] when "updates" respond update_case fn, args when "lists" #head = args[0] reset_list reset_provides tail = fn.call(*args) begin tail = run_provides(args[1]) unless @@provides_used.empty? rescue respond_error :not_acceptable, $! next end get_row unless @@got_row @@chunks << tail unless tail.nil? respond ["end", @@chunks] when "shows" respond ["resp", maybe_wrap_response(fn.call(*args))] when "filters" res = [] args[0].each do |doc| res.push(fn.call(doc, args[1]) ? true : false) end respond [true, res] when "views" res = [] args[0].each do |doc| @@view_emit = false fn.call doc res.push(@@view_emit ? true : false) end respond [true, res] when "validate_doc_update" begin fn.call(*args) respond 1 rescue respond({:forbidden => "bad doc"}) end else respond_fatal :unknown_command, "unknown ddoc command #{path[0]}" next end # case ddoc rescue Fatal respond_fatal $!.key, $!.message rescue Error respond_error $!.key, $!.message rescue respond_error :render_error, $! end end else respond_fatal :unknown_command, "unknown command #{cmd[0]}" end end end
run_provides(req)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 173 def self.run_provides req format = :unknown best_fun = if req['query'] and req['query']['format'] format = req['query']['format'] @@provides_used.find do |p| p[:format].to_s == format end elsif req['headers'] && (format = req['headers']['Accept']) f = nil format.split(',').each do |a| break if (f = a =~ /^\*\/\*/ ? @@provides_used.first : @@provides_used.find do |p| a == p[:mime] or p[:format] == TYPES_MIME[a] end) end f else @@provides_used.first end raise "format #{format} not supported" unless best_fun raise "no block in provides" unless best_fun[:block] && best_fun[:block].respond_to?(:call) @@resp_mime = best_fun[:mime] || MIME_TYPES[best_fun[:format]] best_fun[:block].call end
run_reduce(srcs, ks, vs, rereduce, b)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 143 def self.run_reduce(srcs, ks, vs, rereduce, b) fns = srcs.collect { |src| compile_proc(src, b, "reduce (#{src[0..100]}...)") || return } reductions = fns.collect { |fn| begin fn.call(ks, vs, rereduce) rescue log "#{rereduce ? 'rereduce' : 'reduce'} raised exception", $! nil end } respond [true, reductions] end
send(chunk)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 111 def self.send(chunk) @@chunks << "#{chunk}" nil end
start(resp)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 106 def self.start(resp) @@start_resp = resp if resp nil end
trace(str = nil)
click to toggle source
# File lib/couchdb-ruby-server/tracer.rb, line 6 def self.trace(str = nil) return unless @@trace $stderr.puts("QS_TRACE: #{block_given? ? yield : str}") end
update_case(fn, args)
click to toggle source
# File lib/couchdb-ruby-server/server.rb, line 13 def self.update_case fn, args result = fn.call(*args) return ["up", result[0], maybe_wrap_response(result[1])] end