diff --git a/src/launchers/orbit b/src/launchers/orbit index fac223e..726c5b7 100755 --- a/src/launchers/orbit +++ b/src/launchers/orbit @@ -56,7 +56,7 @@ if opts.l or opts.log then io.stdout = log io.stderr = log print = function (...) - local nargs = select('#', ...) + local nargs = select("#", ...) for i = 1, nargs-1 do log:write(tostring((select(i, ...)))) log:write("\t") @@ -91,16 +91,15 @@ if not app then app = _G[name:match("(.+)%.")] end if type(app) == "table" then app = app.run end xavante.httpd.handle_request = xavante.vhostshandler { - [""] = xavante.urlhandler { - ["/"] = wsx.makeHandler (app, nil, path, path), - } + [""] = xavante.urlhandler { + ["/"] = wsx.makeHandler (app, nil, path, path), + } } xavante.httpd.register ("*", tonumber(opts.p or opts.port) or 8080, "Xavante 2.0") xavante.start_message(function (ports) - print(string.format("Starting Orbit server at port %s", - table.concat(ports, ", "))) - end) + print(string.format("Starting Orbit server at port %s", table.concat(ports, ", "))) +end) xavante.start() diff --git a/src/orbit.lua b/src/orbit.lua index d445b76..1600a12 100644 --- a/src/orbit.lua +++ b/src/orbit.lua @@ -182,15 +182,17 @@ _M.web_methods = {} local web_methods = _M.web_methods local function flatten(t) - local res = {} - for _, item in ipairs(t) do - if type(item) == "table" then - res[#res + 1] = flatten(item) - else - res[#res + 1] = item - end - end - return table.concat(res) + local res = {} + + for _, item in ipairs(t) do + if type(item) == "table" then + res[#res + 1] = flatten(item) + else + res[#res + 1] = item + end + end + + return table.concat(res) end local function make_tag(name, data, class) @@ -198,8 +200,7 @@ local function make_tag(name, data, class) if not data then return "<" .. name .. class .. "/>" elseif type(data) == "string" then - return "<" .. name .. class .. ">" .. data .. - "" + return "<" .. name .. class .. ">" .. data .. "" else local attrs = {} for k, v in pairs(data) do @@ -207,77 +208,79 @@ local function make_tag(name, data, class) table.insert(attrs, k .. '="' .. tostring(v) .. '"') end end - local open_tag = "<" .. name .. class .. " " .. - table.concat(attrs, " ") .. ">" + local open_tag = "<" .. name .. class .. " " .. table.concat(attrs, " ") .. ">" local close_tag = "" - return open_tag .. flatten(data) .. close_tag - end + return open_tag .. flatten(data) .. close_tag + end end function _M.new(app_module) - if type(app_module) == "string" then - app_module = { _NAME = app_module } - else - app_module = app_module or {} - end - for k, v in pairs(app_module_methods) do - app_module[k] = v - end - app_module.run = function (wsapi_env) - return _M.run(app_module, wsapi_env) - end - app_module.real_path = wsapi.app_path or "." - app_module.mapper = { default = true } - app_module.not_found = function (web) - web.status = "404 Not Found" - return [[ - Not Found -

Not found!

]] - end - app_module.server_error = function (web, msg) - web.status = "500 Server Error" - return [[ - Server Error -
]] .. msg .. [[
]] - end - app_module.reparse = REPARSE - app_module.dispatch_table = { get = {}, post = {}, put = {}, delete = {} } - return app_module + if type(app_module) == "string" then + app_module = { _NAME = app_module } + else + app_module = app_module or {} + end + + for k, v in pairs(app_module_methods) do + app_module[k] = v + end + + app_module.run = function (wsapi_env) + return _M.run(app_module, wsapi_env) + end + + app_module.real_path = wsapi.app_path or "." + app_module.mapper = { default = true } + app_module.not_found = function (web) + web.status = "404 Not Found" + return [[Not Found

Not found!

]] + end + + app_module.server_error = function (web, msg) + web.status = "500 Server Error" + return [[Server Error
]] .. msg .. [[
]] + end + + app_module.reparse = REPARSE + app_module.dispatch_table = { + get = {}, + post = {}, + put = {}, + delete = {} + } + + return app_module end local function serve_file(app_module) - return function (web) - local filename = web.real_path .. web.path_info - return app_module:serve_static(web, filename) - end + return function (web) + local filename = web.real_path .. web.path_info + return app_module:serve_static(web, filename) + end end function app_module_methods.dispatch_get(app_module, func, ...) - for _, pat in ipairs{ ... } do - table.insert(app_module.dispatch_table.get, { pattern = pat, - handler = func }) - end + for _, pat in ipairs{ ... } do + table.insert(app_module.dispatch_table.get, { pattern = pat, handler = func }) + end end function app_module_methods.dispatch_post(app_module, func, ...) - for _, pat in ipairs{ ... } do - table.insert(app_module.dispatch_table.post, { pattern = pat, - handler = func }) - end + for _, pat in ipairs{ ... } do + table.insert(app_module.dispatch_table.post, { pattern = pat, handler = func }) + end end function app_module_methods.dispatch_put(app_module, func, ...) - for _, pat in ipairs{ ... } do - table.insert(app_module.dispatch_table.put, { pattern = pat, - handler = func }) - end + for _, pat in ipairs{ ... } do + table.insert(app_module.dispatch_table.put, { pattern = pat, handler = func }) + end end function app_module_methods.dispatch_delete(app_module, func, ...) - for _, pat in ipairs{ ... } do - table.insert(app_module.dispatch_table.delete, { pattern = pat, - handler = func }) - end + for _, pat in ipairs{ ... } do + table.insert(app_module.dispatch_table.delete, { pattern = pat, handler = func }) + end end function app_module_methods.dispatch_wsapi(app_module, func, ...) @@ -289,104 +292,107 @@ function app_module_methods.dispatch_wsapi(app_module, func, ...) end function app_module_methods.dispatch_static(app_module, ...) - app_module:dispatch_get(serve_file(app_module), ...) + app_module:dispatch_get(serve_file(app_module), ...) end function app_module_methods.serve_static(app_module, web, filename) - local ext = string.match(filename, "%.([^%.]+)$") - if app_module.use_xsendfile then - web.headers["Content-Type"] = _M.mime_types[ext] or - "application/octet-stream" - web.headers["X-Sendfile"] = filename - return "xsendfile" - else - local file = io.open(filename, "rb") - if not file then - return app_module.not_found(web) - else - web.headers["Content-Type"] = _M.mime_types[ext] or - "application/octet-stream" - local contents = file:read("*a") - file:close() - return contents - end - end + local ext = string.match(filename, "%.([^%.]+)$") + if app_module.use_xsendfile then + web.headers["Content-Type"] = _M.mime_types[ext] or "application/octet-stream" + web.headers["X-Sendfile"] = filename + return "xsendfile" + else + local file = io.open(filename, "rb") + if not file then + return app_module.not_found(web) + else + web.headers["Content-Type"] = _M.mime_types[ext] or "application/octet-stream" + local contents = file:read("*a") + file:close() + return contents + end + end end local function newtag(name) local tag = {} setmetatable(tag, { - __call = function (_, data) - return make_tag(name, data) - end, - __index = function(_, class) - return function (data) - return make_tag(name, data, class) - end - end - }) + __call = function (_, data) + return make_tag(name, data) + end, + __index = function (_, class) + return function (data) + return make_tag(name, data, class) + end + end + }) return tag end local function htmlify_func(func) local tags = {} - local env = { H = function (name) - local tag = tags[name] - if not tag then - tag = newtag(name) - tags[name] = tag - end - return tag - end - } + + local env = { + H = function (name) + local tag = tags[name] + if not tag then + tag = newtag(name) + tags[name] = tag + end + return tag + end + } + local old_env = getfenv(func) - setmetatable(env, { __index = function (env, name) - if old_env[name] then - return old_env[name] - else - local tag = newtag(name) - rawset(env, name, tag) - return tag - end - end }) + setmetatable(env, { + __index = function (env, name) + if old_env[name] then + return old_env[name] + else + local tag = newtag(name) + rawset(env, name, tag) + return tag + end + end + }) + setfenv(func, env) end function _M.htmlify(app_module, ...) - if type(app_module) == "function" then - htmlify_func(app_module) - for _, func in ipairs{...} do - htmlify_func(func) - end - else - local patterns = { ... } - for _, patt in ipairs(patterns) do - if type(patt) == "function" then - htmlify_func(patt) - else - for name, func in pairs(app_module) do - if string.match(name, "^" .. patt .. "$") and - type(func) == "function" then - htmlify_func(func) - end - end - end + if type(app_module) == "function" then + htmlify_func(app_module) + for _, func in ipairs{...} do + htmlify_func(func) + end + else + local patterns = { ... } + for _, patt in ipairs(patterns) do + if type(patt) == "function" then + htmlify_func(patt) + else + for name, func in pairs(app_module) do + if string.match(name, "^" .. patt .. "$") and type(func) == "function" then + htmlify_func(func) + end + end end - end + end + end end app_module_methods.htmlify = _M.htmlify function app_module_methods.model(app_module, ...) - if app_module.mapper.default then - local table_prefix = (app_module._NAME and app_module._NAME .. "_") or "" - if not orm then - orm = require "orbit.model" - end - app_module.mapper = orm.new(app_module.mapper.table_prefix or table_prefix, - app_module.mapper.conn, app_module.mapper.driver, app_module.mapper.logging) - end - return app_module.mapper:new(...) + if app_module.mapper.default then + local table_prefix = (app_module._NAME and app_module._NAME .. "_") or "" + if not orm then + orm = require "orbit.model" + end + app_module.mapper = orm.new(app_module.mapper.table_prefix or table_prefix, app_module.mapper.conn, app_module.mapper.driver, app_module.mapper.logging) + end + + return app_module.mapper:new(...) end function web_methods:redirect(url) @@ -458,8 +464,8 @@ end for name, func in pairs(wsutil) do web_methods[name] = function (self, ...) - return func(...) - end + return func(...) + end end local function dispatcher(app_module, method, path, index) @@ -471,27 +477,34 @@ local function dispatcher(app_module, method, path, index) local item = app_module.dispatch_table[method][index] local captures if type(item.pattern) == "string" then - captures = { string.match(path, "^" .. item.pattern .. "$") } + captures = { string.match(path, "^" .. item.pattern .. "$") } else - captures = { item.pattern:match(path) } + captures = { item.pattern:match(path) } end if #captures > 0 then - for i = 1, #captures do - if type(captures[i]) == "string" then - captures[i] = wsutil.url_decode(captures[i]) - end - end - return item.handler, captures, item.wsapi, index + for i = 1, #captures do + if type(captures[i]) == "string" then + captures[i] = wsutil.url_decode(captures[i]) + end + end + return item.handler, captures, item.wsapi, index end end end end local function make_web_object(app_module, wsapi_env) - local web = { status = "200 Ok", response = "", - headers = { ["Content-Type"]= "text/html" }, - cookies = {} } + local web = { + status = "200 Ok", + response = "", + headers = { + ["Content-Type"] = "text/html" + }, + cookies = {} + } + setmetatable(web, { __index = web_methods }) + web.vars = wsapi_env web.prefix = app_module.prefix or wsapi_env.SCRIPT_NAME web.suffix = app_module.suffix @@ -504,11 +517,11 @@ local function make_web_object(app_module, wsapi_env) local req = wsreq.new(wsapi_env) local res = wsres.new(web.status, web.headers) web.set_cookie = function (_, name, value) - res:set_cookie(name, value) - end + res:set_cookie(name, value) + end web.delete_cookie = function (_, name, path) - res:delete_cookie(name, path) - end + res:delete_cookie(name, path) + end web.path_info = req.path_info web.path_translated = wsapi_env.PATH_TRANSLATED if web.path_translated == "" then web.path_translated = wsapi_env.SCRIPT_FILENAME end @@ -520,15 +533,14 @@ local function make_web_object(app_module, wsapi_env) end function _M.run(app_module, wsapi_env) - local handler, captures, wsapi_handler, index = dispatcher(app_module, - string.lower(wsapi_env.REQUEST_METHOD), - wsapi_env.PATH_INFO) + local handler, captures, wsapi_handler, index = dispatcher(app_module, string.lower(wsapi_env.REQUEST_METHOD), wsapi_env.PATH_INFO) handler = handler or app_module.not_found captures = captures or {} if wsapi_handler then - local ok, status, headers, res = xpcall(function () - return handler(wsapi_env, unpack(captures)) - end, debug.traceback) + local ok, status, headers, res = xpcall( + function () return handler(wsapi_env, unpack(captures)) end, + debug.traceback + ) if ok then return status, headers, res else @@ -538,25 +550,24 @@ function _M.run(app_module, wsapi_env) local web, res = make_web_object(app_module, wsapi_env) repeat local reparse = false - local ok, response = xpcall(function () - return handler(web, unpack(captures)) - end, function(msg) return debug.traceback(msg) end) + local ok, response = xpcall( + function () return handler(web, unpack(captures)) end, + function (msg) return debug.traceback(msg) end + ) if not ok then res.status = "500 Internal Server Error" res:write(app_module.server_error(web, response)) else if response == REPARSE then - reparse = true - handler, captures, wsapi_handler, index = dispatcher(app_module, - string.lower(wsapi_env.REQUEST_METHOD), - wsapi_env.PATH_INFO, index) - handler, captures = handler or app_module.not_found, captures or {} - if wsapi_handler then - error("cannot reparse to WSAPI handler") - end + reparse = true + handler, captures, wsapi_handler, index = dispatcher(app_module, string.lower(wsapi_env.REQUEST_METHOD), wsapi_env.PATH_INFO, index) + handler, captures = handler or app_module.not_found, captures or {} + if wsapi_handler then + error("Cannot reparse to WSAPI handler") + end else - res.status = web.status - res:write(response) + res.status = web.status + res:write(response) end end until not reparse @@ -564,4 +575,3 @@ function _M.run(app_module, wsapi_env) end return _M - diff --git a/src/orbit/cache.lua b/src/orbit/cache.lua index 83017c2..a9ab85b 100644 --- a/src/orbit/cache.lua +++ b/src/orbit/cache.lua @@ -3,116 +3,120 @@ local lfs = require "lfs" module("orbit.cache", package.seeall) local function pathinfo_to_file(path_info) - local atom = path_info:find("/xml$") - if atom then - path_info = path_info:sub(2, atom - 1) - else - path_info = path_info:sub(2, #path_info) - end - path_info = string.gsub(path_info, "/", "-") - if path_info == "" then path_info = "index" end - if atom then - return path_info .. '.xml' - else - return path_info .. '.html' - end + local atom = path_info:find("/xml$") + if atom then + path_info = path_info:sub(2, atom - 1) + else + path_info = path_info:sub(2, #path_info) + end + path_info = string.gsub(path_info, "/", "-") + if path_info == "" then path_info = "index" end + if atom then + return path_info .. ".xml" + else + return path_info .. ".html" + end end function get(cache, key) if not cache.base_path then - local headers = {} - if key:find("/xml$") then - headers["Content-Type"] = "text/xml" - else - headers["Content-Type"] = "text/html" - end - return cache.values[key], headers + local headers = {} + if key:find("/xml$") then + headers["Content-Type"] = "text/xml" + else + headers["Content-Type"] = "text/html" + end + return cache.values[key], headers else - local filename = cache.base_path .. "/" .. pathinfo_to_file(key) - local web = { headers = {} } - if lfs.attributes(filename, "mode") == "file" then - local response = cache.app:serve_static(web, filename) - return response, web.headers - end + local filename = cache.base_path .. "/" .. pathinfo_to_file(key) + local web = { + headers = {} + } + if lfs.attributes(filename, "mode") == "file" then + local response = cache.app:serve_static(web, filename) + return response, web.headers + end end end local function writefile(filename, contents) local file = assert(io.open(filename, "wb")) if lfs.lock(file, "w") then - file:write(contents) - lfs.unlock(file) - file:close() + file:write(contents) + lfs.unlock(file) + file:close() else - file:close() + file:close() end end function set(cache, key, value) if not cache.base_path then - cache.values[key] = value + cache.values[key] = value else - local filename = cache.base_path .. "/" .. pathinfo_to_file(key) - writefile(filename, value) + local filename = cache.base_path .. "/" .. pathinfo_to_file(key) + writefile(filename, value) end end local function cached(cache, f) return function (web, ...) - local body, headers = cache:get(web.path_info) - if body then - for k, v in pairs(headers) do - web.headers[k] = v - end - return body - else - local key = web.path_info - local body = f(web, ...) - cache:set(key, body) - return body - end - end + local body, headers = cache:get(web.path_info) + if body then + for k, v in pairs(headers) do + web.headers[k] = v + end + return body + else + local key = web.path_info + local body = f(web, ...) + cache:set(key, body) + return body + end + end end function invalidate(cache, ...) - for _, key in ipairs{...} do - if not cache.base_path then - cache.values[key] = nil - else - local filename = cache.base_path .. "/" .. pathinfo_to_file(key) - os.remove(filename) - end - end + for _, key in ipairs{...} do + if not cache.base_path then + cache.values[key] = nil + else + local filename = cache.base_path .. "/" .. pathinfo_to_file(key) + os.remove(filename) + end + end end function nuke(cache) - if not cache.base_path then - cache.values = {} - else - for file in lfs.dir(cache.base_path) do - if file ~= "." and file ~= ".." then - os.remove(cache.base_path .. "/" .. file) - end + if not cache.base_path then + cache.values = {} + else + for file in lfs.dir(cache.base_path) do + if file ~= "." and file ~= ".." then + os.remove(cache.base_path .. "/" .. file) end - end + end + end end function new(app, base_path) - local values - if not base_path then - values = {} - else - local dir = lfs.attributes(base_path, "mode") - if not dir then - assert(lfs.mkdir(base_path)) - elseif dir ~= "directory" then - error("base path of cache " .. base_path .. " not a directory") - end - end - local cache = { app = app, values = values, - base_path = base_path } - setmetatable(cache, { __index = _M, __call = function (tab, f) - return cached(tab, f) - end }) - return cache + local values + if not base_path then + values = {} + else + local dir = lfs.attributes(base_path, "mode") + if not dir then + assert(lfs.mkdir(base_path)) + elseif dir ~= "directory" then + error("base path of cache " .. base_path .. " not a directory") + end + end + local cache = { app = app, values = values, base_path = base_path } + setmetatable(cache, { + __index = _M, + __call = function(tab, f) + return cached(tab, f) + end + }) + return cache end diff --git a/src/orbit/model.lua b/src/orbit/model.lua index 0d86eae..94b8266 100644 --- a/src/orbit/model.lua +++ b/src/orbit/model.lua @@ -79,7 +79,7 @@ function convert.boolean(v, driver) elseif driver == "postgres" then return v == "t" else - error("driver not supported") + error("Driver not supported") end end @@ -90,9 +90,14 @@ end function convert.datetime(v) local year, month, day, hour, min, sec = string.match(v, "(%d+)%-(%d+)%-(%d+) (%d+):(%d+):(%d+)") - return os.time({ year = tonumber(year), month = tonumber(month), - day = tonumber(day), hour = tonumber(hour), - min = tonumber(min), sec = tonumber(sec) }) + return os.time({ + year = tonumber(year), + month = tonumber(month), + day = tonumber(day), + hour = tonumber(hour), + min = tonumber(min), + sec = tonumber(sec) + }) end convert.timestamp = convert.datetime @@ -103,7 +108,7 @@ local function convert_types(row, meta, driver) if conv then row[k] = conv(v, driver) else - error("no conversion for type " .. meta[k].type) + error("No conversion for type " .. meta[k].type) end end end @@ -172,9 +177,9 @@ local function escape_values(row) else local esc = escape[m.type] if esc then - row_escaped[m.name] = esc(row[m.name], row.driver, row.model.conn) + row_escaped[m.name] = esc(row[m.name], row.driver, row.model.conn) else - error("no escape function for type " .. m.type) + error("No escape function for type " .. m.type) end end end @@ -227,7 +232,7 @@ local function parse_condition(dao, condition, args) elseif type(args[j]) == "table" then local values = {} for _, value in ipairs(args[j]) do - values[#values + 1] = escape[dao.meta[part].type](value, dao.driver, dao.model.conn) + values[#values + 1] = escape[dao.meta[part].type](value, dao.driver, dao.model.conn) end parts[i] = part .. " IN (" .. table.concat(values,", ") .. ")" else @@ -242,48 +247,46 @@ end local function build_inject(project, inject, dao) local fields = {} if project then - for i, field in ipairs(project) do - fields[i] = dao.table_name .. "." .. field .. " as " .. field - end + for i, field in ipairs(project) do + fields[i] = dao.table_name .. "." .. field .. " AS " .. field + end else - for i, field in ipairs(dao.meta) do - fields[i] = dao.table_name .. "." .. field.name .. " as " .. field.name - end + for i, field in ipairs(dao.meta) do + fields[i] = dao.table_name .. "." .. field.name .. " AS " .. field.name + end end local inject_fields = {} local model = inject.model for _, field in ipairs(inject.fields) do - inject_fields[model.name .. "_" .. field] = - model.meta[field] - fields[#fields + 1] = model.table_name .. "." .. field .. " as " .. - model.name .. "_" .. field + inject_fields[model.name .. "_" .. field] = model.meta[field] + fields[#fields + 1] = model.table_name .. "." .. field .. " AS " .. model.name .. "_" .. field end setmetatable(dao.meta, { __index = inject_fields }) - return table.concat(fields, ", "), dao.table_name .. ", " .. - model.table_name, model.name .. "_id = " .. model.table_name .. ".id" + return table.concat(fields, ", "), dao.table_name .. ", " .. model.table_name, model.name .. "_id = " .. model.table_name .. ".id" end local function build_query_by(dao, condition, args) local parts = parse_condition(dao, condition, args) local order = "" local field_list, table_list, select, limit - if args.distinct then select = "select distinct " else select = "select " end - if tonumber(args.count) then limit = " limit " .. tonumber(args.count) else limit = "" end - if args.order then order = " order by " .. args.order end + if args.distinct then select = "SELECT DISTINCT " else select = "SELECT " end + if tonumber(args.count) then limit = " LIMIT " .. tonumber(args.count) else limit = "" end + if args.order then order = " ORDER BY " .. args.order end if args.inject then - if #parts > 0 then parts[#parts + 1] = "and" end - field_list, table_list, parts[#parts + 1] = build_inject(args.fields, args.inject, - dao) + if #parts > 0 then + parts[#parts + 1] = "AND" + end + field_list, table_list, parts[#parts + 1] = build_inject(args.fields, args.inject, dao) + else if args.fields then - field_list = table.concat(args.fields, ", ") + field_list = table.concat(args.fields, ", ") else - field_list = "*" + field_list = "*" end table_list = dao.table_name end - local sql = select .. field_list .. " from " .. table_list .. - " where " .. table.concat(parts, " ") .. order .. limit + local sql = select .. field_list .. " FROM " .. table_list .. " WHERE " .. table.concat(pairs, " ") .. order .. limit if dao.model.logging then log_query(sql) end return sql end @@ -315,18 +318,19 @@ end function model_methods:new(name, dao) dao = dao or {} - dao.model, dao.name, dao.table_name, dao.meta, dao.driver = self, name, - self.table_prefix .. name, {}, self.driver + dao.model, dao.name, dao.table_name, dao.meta, dao.driver = self, name, self.table_prefix .. name, {}, self.driver setmetatable(dao, { __index = dao_index }) - local sql = "select * from " .. dao.table_name .. " limit 0" + local sql = "SELECT * FROM " .. dao.table_name .. " LIMIT 0" if self.logging then log_query(sql) end local cursor, err = self.conn:execute(sql) if not cursor then error(err) end local names, types = cursor:getcolnames(), cursor:getcoltypes() cursor:close() for i = 1, #names do - local colinfo = { name = names[i], - type = type_names[self.driver](types[i]) } + local colinfo = { + name = names[i], + type = type_names[self.driver](types[i]) + } dao.meta[i] = colinfo dao.meta[colinfo.name] = colinfo end @@ -337,23 +341,30 @@ function recycle(fresh_conn, timeout) local created_at = os.time() local conn = fresh_conn() timeout = timeout or 20000 - return setmetatable({}, { __index = function (tab, meth) - tab[meth] = function (tab, ...) - if created_at + timeout < os.time() then - created_at = os.time() - pcall(conn.close, conn) - conn = fresh_conn() - end - return conn[meth](conn, ...) - end - return tab[meth] - end - }) + return setmetatable({}, { + __index = function (tab, meth) + tab[meth] = function (tab, ...) + if created_at + timeout < os.time() then + created_at = os.time() + pcall(conn.close, conn) + conn = fresh_conn() + end + return conn[meth](conn, ...) + end + return tab[meth] + end + }) end function new(table_prefix, conn, driver, logging) driver = driver or "sqlite3" - local app_model = { table_prefix = table_prefix or "", conn = conn, driver = driver or "sqlite3", logging = logging, models = {} } + local app_model = { + table_prefix = table_prefix or "", + conn = conn, + driver = driver or "sqlite3", + logging = logging, + models = {} + } setmetatable(app_model, { __index = model_methods }) return app_model end @@ -362,22 +373,20 @@ function dao_methods.find(dao, id, inject) if not type(id) == "number" then error("find error: id must be a number") end - local sql = "select * from " .. dao.table_name .. - " where id=" .. id + local sql = "SELECT * FROM " .. dao.table_name .. " WHERE id=" .. id if dao.logging then log_query(sql) end return fetch_row(dao, sql) end condition_parser = re.compile([[ - top <- {~ * ~} - s <- %s+ -> ' ' / '' - condition <- ( '(' ')' / ) ( )* - simple <- (%func '?') -> apply / / - - field <- ! {[_%w]+('.'[_%w]+)?} - op <- {~ [!<>=~]+ / ((%s+ -> ' ') ! %w+)+ ~} - conective <- [aA][nN][dD] / [oO][rR] - ]], { func = lpeg.Carg(1) , apply = function (f, field, op) return f(field, op) end }) + top <- {~ * ~} + s <- %s+ -> " " / "" + condition <- ( "(" ")" / ) ( )* + simple <- (%func "?") -> apply / / + field <- ! {[_%w]+("."[_%w]+)?} + op <- {~ [!<>=~]+ / ((%s+ -> " ") ! %w+)+ ~} + conective <- [aA][nN][dD] / [oO][rR] +]], { func = lpeg.Carg(1) , apply = function (f, field, op) return f(field, op) end }) local function build_query(dao, condition, args) local i = 0 @@ -388,51 +397,46 @@ local function build_query(dao, condition, args) condition = "" end if condition ~= "" then - condition = " where " .. - condition_parser:match(condition, 1, - function (field, op) - i = i + 1 - if not args[i] then - return "id=id" - elseif type(args[i]) == "table" and args[i].type == "query" then - return field .. " " .. op .. " (" .. args[i][1] .. ")" - elseif type(args[i]) == "table" then - local values = {} - for j, value in ipairs(args[i]) do - values[#values + 1] = field .. " " .. op .. " " .. - escape[dao.meta[field].type](value, dao.driver, dao.model.conn) - end - return "(" .. table.concat(values, " or ") .. ")" - else - return field .. " " .. op .. " " .. - escape[dao.meta[field].type](args[i], dao.driver, dao.model.conn) - end - end) + condition = " WHERE " .. condition_parser:match(condition, 1, function (field, op) + i = i + 1 + if not args[i] then + return "id=id" + elseif type(args[i]) == "table" and args[i].type == "query" then + return field .. " " .. op .. " (" .. args[i][1] .. ")" + elseif type(args[i]) == "table" then + local values = {} + for j, value in ipairs(args[i]) do + values[#values + 1] = field .. " " .. op .. " " .. escape[dao.meta[field].type](value, dao.driver, dao.model.conn) + end + return "(" .. table.concat(values, " OR ") .. ")" + else + return field .. " " .. op .. " " .. escape[dao.meta[field].type](args[i], dao.driver, dao.model.conn) + end + end) end + local order = "" - if args.order then order = " order by " .. args.order end + if args.order then order = " ORDER BY " .. args.order end local field_list, table_list, select, limit - if args.distinct then select = "select distinct " else select = "select " end - if tonumber(args.count) then limit = " limit " .. tonumber(args.count) else limit = "" end + if args.distinct then select = "SELECT DISTINCT " else select = "SELECT " end + if tonumber(args.count) then limit = " LIMIT " .. tonumber(args.count) else limit = "" end if args.inject then local inject_condition - field_list, table_list, inject_condition = build_inject(args.fields, args.inject, - dao) + field_list, table_list, inject_condition = build_inject(args.fields, args.inject, dao) if condition == "" then - condition = " where " .. inject_condition + condition = " WHERE " .. inject_condition else - condition = condition .. " and " .. inject_condition + condition = condition .. " AND " .. inject_condition end else if args.fields then - field_list = table.concat(args.fields, ", ") + field_list = table.concat(args.fields, ", ") else - field_list = "*" + field_list = "*" end table_list = table.concat({ dao.table_name, unpack(args.from or {}) }, ", ") end - local sql = select .. field_list .. " from " .. table_list .. - condition .. order .. limit + local sql = select .. field_list .. " FROM " .. table_list .. condition .. order .. limit if dao.model.logging then log_query(sql) end return sql end @@ -442,8 +446,7 @@ function dao_methods.find_first(dao, condition, args) end function dao_methods.find_all(dao, condition, args) - return fetch_rows(dao, build_query(dao, condition, args), - (args and args.count) or (condition and condition.count)) + return fetch_rows(dao, build_query(dao, condition, args), (args and args.count) or (condition and condition.count)) end function dao_methods.new(dao, row) @@ -463,8 +466,7 @@ local function update(row) for k, v in pairs(row_escaped) do table.insert(updates, k .. "=" .. v) end - local sql = "update " .. row.table_name .. " set " .. - table.concat(updates, ", ") .. " where id = " .. row.id + local sql = "UPDATE " .. row.table_name .. " SET " .. table.concat(updates, ", ") .. " WHERE id = " .. row.id if row.model.logging then log_query(sql) end local ok, err = row.model.conn:execute(sql) if not ok then error(err) end @@ -486,9 +488,7 @@ local function insert(row) table.insert(columns, k) table.insert(values, v) end - local sql = "insert into " .. row.table_name .. - " (" .. table.concat(columns, ", ") .. ") values (" .. - table.concat(values, ", ") .. ")" + local sql = "INSERT INTO " .. row.table_name .. " (" .. table.concat(columns, ", ") .. ") VALUES (" .. table.concat(values, ", ") .. ")" if row.model.logging then log_query(sql) end local ok, err = row.model.conn:execute(sql) if ok then @@ -508,7 +508,7 @@ end function dao_methods.delete(row) if row.id then - local sql = "delete from " .. row.table_name .. " where id = " .. row.id + local sql = "DELETE FROM " .. row.table_name .. " WHERE id = " .. row.id if row.model.logging then log_query(sql) end local ok, err = row.model.conn:execute(sql) if ok then row.id = nil else error(err) end diff --git a/src/orbit/ophandler.lua b/src/orbit/ophandler.lua index 8ebc2ec..f77c490 100644 --- a/src/orbit/ophandler.lua +++ b/src/orbit/ophandler.lua @@ -13,9 +13,9 @@ local wscom = require "wsapi.common" -- Returns the Orbit Pages handler ------------------------------------------------------------------------------- local function makeHandler (diskpath, params) - params = setmetatable({ modname = params.modname or "orbit.pages" }, { __index = params or {} }) - local op_loader = wscom.make_isolated_launcher(params) - return wsxav.makeHandler(op_loader, nil, diskpath) + params = setmetatable({ modname = params.modname or "orbit.pages" }, { __index = params or {} }) + local op_loader = wscom.make_isolated_launcher(params) + return wsxav.makeHandler(op_loader, nil, diskpath) end return { makeHandler = makeHandler } diff --git a/src/orbit/pages.lua b/src/orbit/pages.lua index c27fe61..7c854e9 100644 --- a/src/orbit/pages.lua +++ b/src/orbit/pages.lua @@ -30,17 +30,17 @@ function load(filename, contents) filename = filename or contents local template = template_cache[filename] if not template then - if not contents then - local file = io.open(filename) - if not file then - return nil - end - contents = file:read("*a") - file:close() - if contents:sub(1,3) == BOM then contents = contens:sub(4) end - end - template = cosmo.compile(remove_shebang(contents)) - template_cache[filename] = template + if not contents then + local file = io.open(filename) + if not file then + return nil + end + contents = file:read("*a") + file:close() + if contents:sub(1,3) == BOM then contents = contens:sub(4) end + end + template = cosmo.compile(remove_shebang(contents)) + template_cache[filename] = template end return template end @@ -48,15 +48,14 @@ end local function env_index(env, key) local val = _G[key] if not val and type(key) == "string" then - local template = - load(env.web.real_path .. "/" .. key .. ".op") + local template = load(env.web.real_path .. "/" .. key .. ".op") if not template then return nil end return function (arg) - arg = arg or {} - if arg[1] then arg.it = arg[1] end - local subt_env = setmetatable(arg, { __index = env }) - return template(subt_env) - end + arg = arg or {} + if arg[1] then arg.it = arg[1] end + local subt_env = setmetatable(arg, { __index = env }) + return template(subt_env) + end end return val end @@ -76,8 +75,8 @@ local function make_env(web, initial) if not f then error(err .. " in \n" .. arg[1]) end setfenv(f, env) local ok, res = pcall(f) - if not ok and (type(res)~= "table" or res[1] ~= abort) then - error(res .. " in \n" .. arg[1]) + if not ok and (type(res)~= "table" or res[1] ~= abort) then + error(res .. " in \n" .. arg[1]) elseif ok then return res or "" else @@ -85,13 +84,13 @@ local function make_env(web, initial) end end env["if"] = function (arg) - if type(arg[1]) == "function" then arg[1] = arg[1](select(2, unpack(arg))) end - if arg[1] then - cosmo.yield{ it = arg[1], _template = 1 } - else - cosmo.yield{ _template = 2 } - end - end + if type(arg[1]) == "function" then arg[1] = arg[1](select(2, unpack(arg))) end + if arg[1] then + cosmo.yield{ it = arg[1], _template = 1 } + else + cosmo.yield{ _template = 2 } + end + end function env.redirect(target) if type(target) == "table" then target = target[1] end web:redirect(target) @@ -110,8 +109,8 @@ local function make_env(web, initial) end function env.include(name, subt_env) local filename - if type(name) == "table" then - name = name[1] + if type(name) == "table" then + name = name[1] subt_env = name[2] end if name:sub(1, 1) == "/" then @@ -146,13 +145,13 @@ end function fill(web, template, env) if template then local ok, res = xpcall(function () return template(make_env(web, env)) end, - function (msg) - if type(msg) == "table" and msg[1] == abort then - return msg - else - return traceback(msg) - end - end) + function (msg) + if type(msg) == "table" and msg[1] == abort then + return msg + else + return traceback(msg) + end + end) if not ok and (type(res) ~= "table" or res[1] ~= abort) then error(res) elseif ok then @@ -170,10 +169,8 @@ function handle_get(web) if res then return res else - web.status = 404 - return [[ - Not Found -

Not found!

]] + web.status = 404 + return "Not Found

Not found!

" end end diff --git a/src/orbit/routes.lua b/src/orbit/routes.lua index f0e597d..3882854 100644 --- a/src/orbit/routes.lua +++ b/src/orbit/routes.lua @@ -6,50 +6,48 @@ local util = require "wsapi.util" local _M = {} -local alpha = lpeg.R('AZ', 'az') -local number = lpeg.R('09') -local asterisk = lpeg.P('*') -local question_mark = lpeg.P('?') -local at_sign = lpeg.P('@') -local colon = lpeg.P(':') -local the_dot = lpeg.P('.') -local underscore = lpeg.P('_') -local forward_slash = lpeg.P('/') +local alpha = lpeg.R("AZ", "az") +local number = lpeg.R("09") +local asterisk = lpeg.P("*") +local question_mark = lpeg.P("?") +local at_sign = lpeg.P("@") +local colon = lpeg.P(":") +local the_dot = lpeg.P(".") +local underscore = lpeg.P("_") +local forward_slash = lpeg.P("/") local slash_or_dot = forward_slash + the_dot local function cap_param(prefix, name, dot) - local inner = (1 - lpeg.S('/' .. (dot or '')))^1 - local close = lpeg.P'/' + (dot or -1) + -1 - return { - cap = lpeg.Carg(1) * slash_or_dot * lpeg.C(inner^1) * #close / function (params, item, delim) params[name] = util.url_decode(item) end, - clean = slash_or_dot * inner^1 * #close, - tag = "param", - name = name, - prefix = prefix - } + local inner = (1 - lpeg.S("/" .. (dot or "")))^1 + local close = lpeg.P"/" + (dot or -1) + -1 + return { + cap = lpeg.Carg(1) * slash_or_dot * lpeg.C(inner^1) * #close / function (params, item, delim) params[name] = util.url_decode(item) end, + clean = slash_or_dot * inner^1 * #close, + tag = "param", + name = name, + prefix = prefix + } end local param_pre = lpeg.C(slash_or_dot) * colon * lpeg.C((alpha + number + underscore)^1) -local param = (param_pre * #(forward_slash + -1) / cap_param) + - (param_pre * #the_dot / function (prefix, name) return cap_param(prefix, name, ".") end) +local param = (param_pre * #(forward_slash + -1) / cap_param) + (param_pre * #the_dot / function (prefix, name) return cap_param(prefix, name, ".") end) local function cap_opt_param(prefix, name, dot) - local inner = (1 - lpeg.S('/' .. (dot or '')))^1 - local close = lpeg.P('/') + lpeg.P(dot or -1) + -1 - return { - cap = (lpeg.Carg(1) * slash_or_dot * lpeg.C(inner) * #close / function (params, item, delim) params[name] = util.url_decode(item) end)^-1, - clean = (slash_or_dot * inner * #lpeg.C(close))^-1, - tag = "opt", - name = name, - prefix = prefix - } + local inner = (1 - lpeg.S("/" .. (dot or "")))^1 + local close = lpeg.P("/") + lpeg.P(dot or -1) + -1 + return { + cap = (lpeg.Carg(1) * slash_or_dot * lpeg.C(inner) * #close / function (params, item, delim) params[name] = util.url_decode(item) end)^-1, + clean = (slash_or_dot * inner * #lpeg.C(close))^-1, + tag = "opt", + name = name, + prefix = prefix + } end local opt_param_pre = lpeg.C(slash_or_dot) * question_mark * colon * lpeg.C((alpha + number + underscore)^1) * question_mark -local opt_param = (opt_param_pre * #(forward_slash + -1) / cap_opt_param) + - (opt_param_pre * #the_dot / function (prefix, name) return cap_opt_param(prefix, name, ".") end) +local opt_param = (opt_param_pre * #(forward_slash + -1) / cap_opt_param) + (opt_param_pre * #the_dot / function (prefix, name) return cap_opt_param(prefix, name, ".") end) local splat = lpeg.P(lpeg.C(forward_slash + the_dot) * asterisk * #(forward_slash + the_dot + -1)) / function (prefix) @@ -73,13 +71,13 @@ local function fold_captures(cap, acc) -- if we have a star match (match everything) if cap.cap == "*" then return { - cap = (lpeg.Carg(1) * (cap.prefix * lpeg.C((1 - acc.clean)^0))^-1 / - function (params, splat) - params.splat = params.splat or {} - if splat and splat ~= "" then - params.splat[#params.splat+1] = util.url_decode(splat) - end - end) * acc.cap, + cap = (lpeg.Carg(1) * (cap.prefix * lpeg.C((1 - acc.clean)^0))^-1 / + function (params, splat) + params.splat = params.splat or {} + if splat and splat ~= "" then + params.splat[#params.splat+1] = util.url_decode(splat) + end + end) * acc.cap, clean = (cap.prefix * (1 - acc.clean)^0)^-1 * acc.clean } end @@ -91,7 +89,7 @@ local function fold_captures(cap, acc) end local function fold_parts(parts, cap) - + if type(cap) == "string" then -- if the capture is a string parts[#parts+1] = { tag = "text", @@ -125,7 +123,7 @@ local function fold_left(t, f, acc) end local route = lpeg.Ct((param + opt_param + splat + rest)^0 * -1) / function (caps) - local forward_slash_at_end = lpeg.P('/')^-1 * -1 + local forward_slash_at_end = lpeg.P("/")^-1 * -1 return fold_right(caps, fold_captures, { cap = forward_slash_at_end, clean = forward_slash_at_end }), fold_left(caps, fold_parts, {}) end @@ -134,17 +132,17 @@ local function build(parts, params) params = params or {} params.splat = params.splat or {} for _, part in ipairs(parts) do - if part.tag == 'param' then - if not params[part.name] then error('route parameter ' .. part.name .. ' does not exist') end - local s = string.gsub (params[part.name], '([^%.@]+)', function (s) return util.url_encode(s) end) + if part.tag == "param" then + if not params[part.name] then error("route parameter " .. part.name .. " does not exist") end + local s = string.gsub (params[part.name], "([^%.@]+)", function (s) return util.url_encode(s) end) res[#res+1] = part.prefix .. s elseif part.tag == "splat" then - local s = string.gsub (params.splat[i] or '', '([^/%.@]+)', function (s) return util.url_encode(s) end) + local s = string.gsub (params.splat[i] or "", "([^/%.@]+)", function (s) return util.url_encode(s) end) res[#res+1] = part.prefix .. s i = i + 1 elseif part.tag == "opt" then if params and params[part.name] then - local s = string.gsub (params[part.name], '([^%.@]+)', function (s) return util.url_encode(s) end) + local s = string.gsub (params[part.name], "([^%.@]+)", function (s) return util.url_encode(s) end) res[#res+1] = part.prefix .. s end else @@ -155,13 +153,13 @@ local function build(parts, params) if #res > 0 then return table.concat(res) end - - return '/' + + return "/" end function _M.R(path) local p, b = route:match(path) - + return setmetatable({ parser = p.cap, parts = b