Created
July 6, 2010 07:54
-
-
Save zeen/465135 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local setmetatable = setmetatable; | |
local pcall = pcall; | |
local type = type; | |
local t_concat = table.concat; | |
local print = print; | |
local socket_url = require "socket.url"; | |
local http = require "socket.http"; | |
local ltn12 = require "ltn12"; | |
--local json = require "json"; | |
local json = module:require("couchdb/json"); | |
--module("couchdb") | |
local _M = {}; | |
local function urlcat(url, path) | |
return url:gsub("/*$", "").."/"..path:gsub("^/*", ""); | |
end | |
local doc_mt = {}; | |
doc_mt.__index = doc_mt; | |
function doc_mt:get() | |
return self.db:get(socket_url.escape(self.id)); | |
end | |
function doc_mt:put(val) | |
return self.db:put(socket_url.escape(self.id), val); | |
end | |
function doc_mt:__tostring() | |
return "couchdb.doc("..self.url..")"; | |
end | |
local db_mt = {}; | |
db_mt.__index = db_mt; | |
function db_mt:__tostring() | |
return "couchdb.db("..self.url..")"; | |
end | |
function db_mt:doc(id) | |
local url = urlcat(self.url, socket_url.escape(id)); | |
return setmetatable({ url = url, db = self, id = id }, doc_mt); | |
end | |
function db_mt:get(id) | |
local url = urlcat(self.url, id); | |
local a,b = http.request(url); | |
local r,x = pcall(json.decode, a); | |
if r then a = x; end | |
return a,b; | |
end | |
function db_mt:put(id, value) | |
local url = urlcat(self.url, id); | |
if type(value) == "table" then | |
value = json.encode(value); | |
elseif value ~= nil and type(value) ~= "string" then | |
return nil, "Invalid type"; | |
end | |
local t = {}; | |
local a,b = http.request { | |
url = url, | |
sink = ltn12.sink.table(t), | |
source = ltn12.source.string(value), | |
method = "PUT", | |
headers = { | |
["Content-Length"] = #value, | |
["Content-Type"] = "application/json" | |
} | |
}; | |
a = t_concat(t); | |
local r,x = pcall(json.decode, a); | |
if r then a = x; end | |
return a,b; | |
end | |
local server_mt = {}; | |
server_mt.__index = server_mt; | |
function server_mt:db(name) | |
local url = urlcat(self.url, socket_url.escape(name)); | |
return setmetatable({ url = url }, db_mt); | |
end | |
function server_mt:__tostring() | |
return "couchdb.server("..self.url..")"; | |
end | |
function _M.server(url) | |
return setmetatable({ url = url }, server_mt); | |
end | |
function _M.db(url) | |
return setmetatable({ url = url }, db_mt); | |
end | |
return _M; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local http = require "socket.http"; | |
local url = require "socket.url"; | |
local couchapi = module:require("couchdb/couchapi"); | |
local json = module:require("couchdb/json"); | |
local couchdb_url = assert(module:get_option("couchdb_url"), "Option couchdb_url not specified"); | |
local db = couchapi.db(couchdb_url); | |
local function couchdb_get(key) | |
local a,b = db:doc(key):get() | |
print(json.encode(a)); | |
if b == 404 then return nil; end | |
if b == 200 then b = nil; end | |
return a.payload,b; | |
end | |
local function couchdb_put(key, value) | |
local a,b = db:doc(key):get(); | |
return db:doc(key):put({ payload = value, _rev = a and a._rev }); | |
end | |
local st = require "util.stanza"; | |
local handlers = {}; | |
handlers.accounts = { | |
get = function(self, user) | |
return couchdb_get(self.host.."/"..user.."/account"); | |
end; | |
set = function(self, user, data) | |
return couchdb_put(self.host.."/"..user.."/account", data); | |
end; | |
}; | |
handlers.vcard = { | |
get = function(self, user) | |
return couchdb_get(self.host.."/"..user.."/vcard"); | |
end; | |
set = function(self, user, data) | |
return couchdb_put(self.host.."/"..user.."/vcard", data); | |
end; | |
}; | |
handlers.private = { | |
get = function(self, user) | |
return couchdb_get(self.host.."/"..user.."/private"); | |
end; | |
set = function(self, user, data) | |
return couchdb_put(self.host.."/"..user.."/private", data); | |
end; | |
}; | |
handlers.roster = { | |
get = function(self, user) | |
return couchdb_get(self.host.."/"..user.."/roster"); | |
end; | |
set = function(self, user, data) | |
return couchdb_put(self.host.."/"..user.."/roster", data); | |
end; | |
}; | |
----------------------------- | |
local driver = {}; | |
driver.__index = driver; | |
function driver:open(host, datastore, typ) | |
local cache_key = host.." "..datastore; | |
if self.ds_cache[cache_key] then return self.ds_cache[cache_key]; end | |
local instance = setmetatable({}, self); | |
instance.host = host; | |
instance.datastore = datastore; | |
local handler = handlers[datastore]; | |
if not handler then return nil; end | |
for key,val in pairs(handler) do | |
instance[key] = val; | |
end | |
if instance.init then instance:init(); end | |
self.ds_cache[cache_key] = instance; | |
return instance; | |
end | |
----------------------------- | |
local _M = {}; | |
function _M.new() | |
local instance = setmetatable({}, driver); | |
instance.__index = instance; | |
instance.ds_cache = {}; | |
return instance; | |
end | |
return _M; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local type = type; | |
local t_insert, t_concat, t_remove = table.insert, table.concat, table.remove; | |
local s_char = string.char; | |
local tostring, tonumber = tostring, tonumber; | |
local pairs, ipairs = pairs, ipairs; | |
local next = next; | |
local error = error; | |
local newproxy, getmetatable = newproxy, getmetatable; | |
local print = print; | |
--module("json") | |
local _M = {}; | |
local null = newproxy and newproxy(true) or {}; | |
if getmetatable and getmetatable(null) then | |
getmetatable(null).__tostring = function() return "null"; end; | |
end | |
_M.null = null; | |
local escapes = { | |
["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", | |
["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"}; | |
local unescapes = { | |
["\""] = "\"", ["\\"] = "\\", ["/"] = "/", | |
b = "\b", f = "\f", n = "\n", r = "\r", t = "\t"}; | |
for i=0,31 do | |
local ch = s_char(i); | |
if not escapes[ch] then escapes[ch] = ("\\u%.4X"):format(i); end | |
end | |
local valid_types = { | |
number = true, | |
string = true, | |
table = true, | |
boolean = true | |
}; | |
local special_keys = { | |
__array = true; | |
__hash = true; | |
}; | |
local simplesave, tablesave, arraysave, stringsave; | |
function stringsave(o, buffer) | |
-- FIXME do proper utf-8 and binary data detection | |
t_insert(buffer, "\""..(o:gsub(".", escapes)).."\""); | |
end | |
function arraysave(o, buffer) | |
t_insert(buffer, "["); | |
if next(o) then | |
for i,v in ipairs(o) do | |
simplesave(v, buffer); | |
t_insert(buffer, ","); | |
end | |
t_remove(buffer); | |
end | |
t_insert(buffer, "]"); | |
end | |
function tablesave(o, buffer) | |
local __array = {}; | |
local __hash = {}; | |
local hash = {}; | |
for i,v in ipairs(o) do | |
__array[i] = v; | |
end | |
for k,v in pairs(o) do | |
local ktype, vtype = type(k), type(v); | |
if valid_types[vtype] or v == null then | |
if ktype == "string" and not special_keys[k] then | |
hash[k] = v; | |
elseif (valid_types[ktype] or k == null) and __array[k] == nil then | |
__hash[k] = v; | |
end | |
end | |
end | |
if next(__hash) ~= nil or next(hash) ~= nil or next(__array) == nil then | |
t_insert(buffer, "{"); | |
local mark = #buffer; | |
for k,v in pairs(hash) do | |
stringsave(k, buffer); | |
t_insert(buffer, ":"); | |
simplesave(v, buffer); | |
t_insert(buffer, ","); | |
end | |
if next(__hash) ~= nil then | |
t_insert(buffer, "\"__hash\":["); | |
for k,v in pairs(__hash) do | |
simplesave(k, buffer); | |
t_insert(buffer, ","); | |
simplesave(v, buffer); | |
t_insert(buffer, ","); | |
end | |
t_remove(buffer); | |
t_insert(buffer, "]"); | |
t_insert(buffer, ","); | |
end | |
if next(__array) then | |
t_insert(buffer, "\"__array\":"); | |
arraysave(__array, buffer); | |
t_insert(buffer, ","); | |
end | |
if mark ~= #buffer then t_remove(buffer); end | |
t_insert(buffer, "}"); | |
else | |
arraysave(__array, buffer); | |
end | |
end | |
function simplesave(o, buffer) | |
local t = type(o); | |
if t == "number" then | |
t_insert(buffer, tostring(o)); | |
elseif t == "string" then | |
stringsave(o, buffer); | |
elseif t == "table" then | |
tablesave(o, buffer); | |
elseif t == "boolean" then | |
t_insert(buffer, (o and "true" or "false")); | |
else | |
t_insert(buffer, "null"); | |
end | |
end | |
function _M.encode(obj) | |
local t = {}; | |
simplesave(obj, t); | |
return t_concat(t); | |
end | |
----------------------------------- | |
function _M.decode(json) | |
local pos = 1; | |
local current = {}; | |
local stack = {}; | |
local ch, peek; | |
local function next() | |
ch = json:sub(pos, pos); | |
pos = pos+1; | |
peek = json:sub(pos, pos); | |
return ch; | |
end | |
local function skipwhitespace() | |
while ch and (ch == "\r" or ch == "\n" or ch == "\t" or ch == " ") do | |
next(); | |
end | |
end | |
local function skiplinecomment() | |
repeat next(); until not(ch) or ch == "\r" or ch == "\n"; | |
skipwhitespace(); | |
end | |
local function skipstarcomment() | |
next(); next(); -- skip '/', '*' | |
while peek and ch ~= "*" and peek ~= "/" do next(); end | |
if not peek then error("eof in star comment") end | |
next(); next(); -- skip '*', '/' | |
skipwhitespace(); | |
end | |
local function skipstuff() | |
while true do | |
skipwhitespace(); | |
if ch == "/" and peek == "*" then | |
skipstarcomment(); | |
elseif ch == "/" and peek == "*" then | |
skiplinecomment(); | |
else | |
return; | |
end | |
end | |
end | |
local readvalue; | |
local function readarray() | |
local t = {}; | |
next(); -- skip '[' | |
skipstuff(); | |
if ch == "]" then next(); return t; end | |
t_insert(t, readvalue()); | |
while true do | |
skipstuff(); | |
if ch == "]" then next(); return t; end | |
if not ch then error("eof while reading array"); | |
elseif ch == "," then next(); | |
elseif ch then error("unexpected character in array, comma expected"); end | |
if not ch then error("eof while reading array"); end | |
t_insert(t, readvalue()); | |
end | |
end | |
local function checkandskip(c) | |
local x = ch or "eof"; | |
if x ~= c then error("unexpected "..x..", '"..c.."' expected"); end | |
next(); | |
end | |
local function readliteral(lit, val) | |
for c in lit:gmatch(".") do | |
checkandskip(c); | |
end | |
return val; | |
end | |
local function readstring() | |
local s = ""; | |
checkandskip("\""); | |
while ch do | |
while ch and ch ~= "\\" and ch ~= "\"" do | |
s = s..ch; next(); | |
end | |
if ch == "\\" then | |
next(); | |
if unescapes[ch] then | |
s = s..unescapes[ch]; | |
next(); | |
elseif ch == "u" then | |
local seq = ""; | |
for i=1,4 do | |
next(); | |
if not ch then error("unexpected eof in string"); end | |
if not ch:match("[0-9a-fA-F]") then error("invalid unicode escape sequence in string"); end | |
seq = seq..ch; | |
end | |
s = s..s.char(tonumber(seq, 16)); -- FIXME do proper utf-8 | |
next(); | |
else error("invalid escape sequence in string"); end | |
end | |
if ch == "\"" then | |
next(); | |
return s; | |
end | |
end | |
error("eof while reading string"); | |
end | |
local function readnumber() | |
local s = ""; | |
if ch == "-" then | |
s = s..ch; next(); | |
if not ch:match("[0-9]") then error("number format error"); end | |
end | |
if ch == "0" then | |
s = s..ch; next(); | |
if ch:match("[0-9]") then error("number format error"); end | |
else | |
while ch and ch:match("[0-9]") do | |
s = s..ch; next(); | |
end | |
end | |
if ch == "." then | |
s = s..ch; next(); | |
if not ch:match("[0-9]") then error("number format error"); end | |
while ch and ch:match("[0-9]") do | |
s = s..ch; next(); | |
end | |
if ch == "e" or ch == "E" then | |
s = s..ch; next(); | |
if ch == "+" or ch == "-" then | |
s = s..ch; next(); | |
if not ch:match("[0-9]") then error("number format error"); end | |
while ch and ch:match("[0-9]") do | |
s = s..ch; next(); | |
end | |
end | |
end | |
end | |
return tonumber(s); | |
end | |
local function readmember(t) | |
local k = readstring(); | |
checkandskip(":"); | |
t[k] = readvalue(); | |
end | |
local function fixobject(obj) | |
local __array = obj.__array; | |
if __array then | |
obj.__array = nil; | |
for i,v in ipairs(__array) do | |
t_insert(obj, v); | |
end | |
end | |
local __hash = obj.__hash; | |
if __hash then | |
obj.__hash = nil; | |
local k; | |
for i,v in ipairs(__hash) do | |
if k ~= nil then | |
obj[k] = v; k = nil; | |
else | |
k = v; | |
end | |
end | |
end | |
return obj; | |
end | |
local function readobject() | |
local t = {}; | |
next(); -- skip '{' | |
skipstuff(); | |
if ch == "}" then next(); return t; end | |
if not ch then error("eof while reading object"); end | |
readmember(t); | |
while true do | |
skipstuff(); | |
if ch == "}" then next(); return fixobject(t); end | |
if not ch then error("eof while reading object"); | |
elseif ch == "," then next(); | |
elseif ch then error("unexpected character in object, comma expected"); end | |
if not ch then error("eof while reading object"); end | |
readmember(t); | |
end | |
end | |
function readvalue() | |
skipstuff(); | |
while ch do | |
if ch == "{" then | |
return readobject(); | |
elseif ch == "[" then | |
return readarray(); | |
elseif ch == "\"" then | |
return readstring(); | |
elseif ch:match("[%-0-9%.]") then | |
return readnumber(); | |
elseif ch == "n" then | |
return readliteral("null", null); | |
elseif ch == "t" then | |
return readliteral("true", true); | |
elseif ch == "f" then | |
return readliteral("false", false); | |
end | |
end | |
error("eof while reading value"); | |
end | |
next(); | |
return readvalue(); | |
end | |
function _M.test(object) | |
local encoded = encode(object); | |
local decoded = decode(encoded); | |
local recoded = encode(decoded); | |
if encoded ~= recoded then | |
print("FAILED"); | |
print("encoded:", encoded); | |
print("recoded:", recoded); | |
else | |
print(encoded); | |
end | |
return encoded ~= recoded; | |
end | |
return _M; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment