您的位置:首页 > 编程语言 > Lua

lua模仿java里面的模板引擎

2014-11-24 15:10 363 查看
由于需求的变更,以前使用的都是java的模板引擎,现在api都采用lua来开发,所以以前的那套只能重新写了,在网上找了很久类似的例子,总算到找了一个比较不错的类似模板引擎,lua-resty-template 。

原程序的地址如下:
https://github.com/bungle/lua-resty-template
我的需求如下:

1、前端提供多种(4种)不同版式的专题页面

2、每次访问的时候专题页版式要随机选取

3、方便前端人员配置

前端会不定期的做一些专题;相关信息数据来源其实固定不变,(当然这只是相对时间,可能过一些时间会取自不同的专题数据,所以这里还是要比较灵活)

我的思路大概如下。

1、将所需要的专题数据存入redis里面,定期刷新redis.确保有新数据录入的时候能及时获取(我这里大概是30分钟更新一次)

2、获数据的时候先从redis里面取,如果没有就查一次表。确定redis出现问题时也能及时的获取数据。

3、用lua生静态模板并将数据写入到页面当。

4、测试上线。

相关的代码如下:

fileUtils.lua

-----------------------------------------------

module(...,package.seeall)

--读文件

function readFile(filepath)
local path = filepath[1]
--ngx.say(filepath)
local f = assert(io.open(path,'r'))
local str = f:read("*a")
f:close()
return str

end

--写文件

function writeFile(srcFilePath,contentStr )
local f = assert(io.open(srcFilePath,'w'))
f:write(contentStr)
f:close()

end

--------------------------------

dataUtil.lua

--------------------------------

module(..., package.seeall)

--联连redis

function getRedis()

        local redis = (require "resty.redis"):new()

        redis:set_timeout(10000)

        redis:set_keepalive(10000, 3000)

        if not redis:connect("127.0.0.1", 6379) then

            ngx.log(ngx.ERR, "failed to connect to redis")

            return false, nil

        end

    return true,redis

end

--关闭redis

function closeRedis(red)

    local ok, err = red:close()

    if not ok then

        ngx.say("failed to close: ", err)

        return

    end

end

--打开mysql连接

function getMySQL()

   -- if not ngx.ctx.my_sql then

        local mysql = require "resty.mysql"

        local db, err = mysql:new()

        if not db then

            ngx.log(ngx.ERR, "failed to instantiate mysql: ", err)

            return false, nil 

        end

        db:set_timeout(60000) -- 1 sec

        local ok, err = db:set_keepalive(10000, 3000)

        local ok, err, errno, sqlstate = db:connect{

            host = "127.0.0.1",

            port = 3306,

            database = "TestTaginfo",

            user = "root",

            password = "root",

            max_packet_size = 1024 * 1024 }

        if not ok then

            ngx.log(ngx.ERR, "failed to connect: ", err, ": ", errno, " ", sqlstate)

            return false, nil

        end

    return true, db

end

--关闭连接

function closeDB(db)

    local ok, err = db:close()

    if not ok then

        ngx.say("failed to close: ", err)

        return

    end

end

-----------------------------------------------

template.lua

-------------------------------------------------

local setmetatable = setmetatable

local tostring = tostring

local setfenv = setfenv

local concat = table.concat

local assert = assert

local open = io.open

local load = load

local type = type

local HTML_ENTITIES = {

    ["&"] = "&",

    ["<"] = "<",

    [">"] = ">",

    ['"'] = """,

    ["'"] = "'",

    ["/"] = "/"

}

local CODE_ENTITIES = {

    ["{"] = "{",

    ["}"] = "}"

}

local caching, ngx_var, ngx_capture, ngx_null = true

local template = { _VERSION = "1.3", cache = {}, concat = concat }

local function read_file(path)

    local file = open(path, "rb")

    if not file then return nil end

    local content = file:read("*a")

    file:close()

    return content

end

local function load_lua(path)

    return read_file(path) or path

end

local function load_ngx(path)

    local file, location = path, ngx_var.template_location

    if file:sub(1)  == "/" then file = file:sub(2) end

    if location and location ~= "" then

        if location:sub(-1) == "/" then location = location:sub(1, -2) end

        local res = ngx_capture(location .. '/' .. file)

        if res.status == 200 then return res.body end

    end

    local root = ngx_var.template_root or ngx_var.document_root

    if root:sub(-1) == "/" then root = root:sub(1, -2) end

    return read_file(root .. "/" .. file) or path

end

if ngx then

    template.print = ngx.print or print

    template.load  = load_ngx

    ngx_var, ngx_capture, ngx_null = ngx.var, ngx.location.capture, ngx.null

else

    template.print = print

    template.load  = load_lua

end

local load_chunk

if _VERSION == "Lua 5.1" then

    local context = { __index = function(t, k)

        return t.context[k] or t.template[k] or _G[k]

    end }

    if jit then

        load_chunk = function(view)

            return assert(load(view, nil, "tb", setmetatable({ template = template }, context)))

        end

    else

        load_chunk = function(view)

            local func = assert(loadstring(view))

            setfenv(func, setmetatable({ template = template }, context))

            return func

        end

    end

else

    local context = { __index = function(t, k)

        return t.context[k] or t.template[k] or _ENV[k]

    end }

    load_chunk = function(view)

        return assert(load(view, nil, "tb", setmetatable({ template = template }, context)))

    end

end

function template.caching(enable)

    if enable ~= nil then caching = enable == true end

    return caching

end

function template.output(s)

    if s == nil or s == ngx_null then return "" end

    if type(s) == "function" then return template.output(s()) end

    return tostring(s)

end

function template.escape(s, c)

    if type(s) == "string" then

        if c then s = s:gsub("[}{]", CODE_ENTITIES) end

        return s:gsub("[\">/<'&]", HTML_ENTITIES)

    end

    return template.output(s)

end

function template.new(view, layout)

    assert(view, "view was not provided for template.new(view, layout).")

    local render, compile = template.render, template.compile

    if layout then

        return setmetatable({ render = function(self, context)

            local context = context or self

            context.view = compile(view)(context)

            render(layout, context)

        end }, { __tostring = function(self)

            local co
4000
ntext = context or self

            context.view = compile(view)(context)

            return compile(layout)(context)

        end })

    end

    return setmetatable({ render = function(self, context)

        render(view, context or self)

    end }, { __tostring = function(self)

        return compile(view)(context or self)

    end })

end

function template.precompile(view, path, strip)

    local chunk = string.dump(template.compile(view), strip ~= false)

    if path then

        local file = io.open(path, "wb")

        file:write(chunk)

        file:close()

    end

    return chunk

end

function template.compile(view, key, plain)

    assert(view, "view was not provided for template.compile(view, key, plain).")

    if key == "no-cache" then

        return load_chunk(template.parse(view, plain)), false

    end

    key = key or view

    local cache = template.cache

    if cache[key] then return cache[key], true end

    local func = load_chunk(template.parse(view, plain))

    if caching then cache[key] = func end

    return func, false

end

function template.parse(view, plain)

    assert(view, "view was not provided for template.parse(view, plain).")

    if not plain then

        view = template.load(view)

        if view:sub(1, 1):byte() == 27 then return view end

    end

    local c = {

        "context=... or {}",

        "local ___,blocks,layout={},blocks or {}"

    }

    local i, j, s, e = 0, 0, view:find("{", 1, true)

    while s do

        local t = view:sub(s, e + 1)

        if t == "{{" then

            local x, y = view:find("}}", e + 2, true)

            if x then

                if j ~= s then c[#c+1] = "___[#___+1]=[=[" .. view:sub(j, s - 1) .. "]=]" end

                c[#c+1] = "___[#___+1]=template.escape(" .. view:sub(e + 2, x - 1) .. ")"

                i, j = y, y + 1

            end

        elseif t == "{*" then

            local x, y = view:find("*}", e + 2, true)

            if x then

                if j ~= s then c[#c+1] = "___[#___+1]=[=[" .. view:sub(j, s - 1) .. "]=]" end

                c[#c+1] = "___[#___+1]=template.output(" .. view:sub(e + 2, x - 1) .. ")"

                i, j = y, y + 1

            end

        elseif t == "{%" then

            local x, y = view:find("%}", e + 2, true)

            if x then

                if j ~= s then c[#c+1] = "___[#___+1]=[=[" .. view:sub(j, s - 1) .. "]=]" end

                c[#c+1] = view:sub(e + 2, x - 1)

                if view:sub(y + 1, y + 1) == "\n" then

                    i, j = y + 1, y + 2

                else

                    i, j = y, y + 1

                end

            end

        elseif t == "{(" then

            local x, y = view:find(")}", e + 2, true)

            if x then

                if j ~= s then c[#c+1] = "___[#___+1]=[=[" .. view:sub(j, s - 1) .. "]=]" end

                local file = view:sub(e + 2, x - 1)

                local a, b = file:find(',', 2, true)

                if a then

                    c[#c+1] = '___[#___+1]=template.compile([=[' .. file:sub(1, a - 1) .. ']=])(' .. file:sub(b + 1) .. ')'

                else

                    c[#c+1] = '___[#___+1]=template.compile([=[' .. file .. ']=])(context)'

                end

                i, j = y, y + 1

            end

        elseif t == "{-" then

            local x, y = view:find("-}", e + 2, true)

            if x then

                local a, b = view:find(view:sub(e, y), y, true)

                if a then

                    if j ~= s then c[#c+1] = "___[#___+1]=[=[" .. view:sub(j, s - 1) .. "]=]" end

                    c[#c+1] = 'blocks["' .. view:sub(e + 2, x - 1) .. '"]=template.compile([=[' .. view:sub(y + 1, a - 1) .. ']=], "no-cache", true)(context)'

                    i, j = b, b + 1

                end

            end

        elseif t == "{#" then

            local x, y = view:find("#}", e + 2, true)

            if x then

                if j ~= s then c[#c+1] = "___[#___+1]=[=[" .. view:sub(j, s - 1) .. "]=]" end

                i, j = y, y + 1

            end

        end

        i = i + 1

        s, e = view:find("{", i, true)

    end

    c[#c+1] = "___[#___+1]=[=[" .. view:sub(j) .. "]=]"

    c[#c+1] = "return layout and template.compile(layout)(setmetatable({view=template.concat(___),blocks=blocks},{__index=context})) or template.concat(___)"

    return concat(c, "\n")

end

function template.render(view, context, key, plain)

    assert(view, "view was not provided for template.render(view, context, key, plain).")

    return template.print(template.compile(view, key, plain)(context))

end

return template

---------------------------

test-temp.lua

---------------------------

local template = require "resty.template"

local file = require("fileUtils")

local re = require("dataUtil")

local math = require("math")

local cjson = require "cjson"

local path1 = "/data/template/view.html"

local path2 = "/data/template/view_2.html"

local path3 = "/data/template/view_3.html"

local path4 = "/data/template/view_4.html"

local x = {}

local paths = {}

table.insert(x,path1)

table.insert(x,path2)

table.insert(x,path3)

table.insert(x,path4)

ngx.say("rnd=" ..math.random(1,table.getn(x)))

local  srcpath = x[math.random(1,table.getn(x))]

table.insert(paths,srcpath)

local str = file.readFile(paths)

-- Using template.new

local view = template.new(str)

view.names   = { "James", "Jack", "Anne" }

view.message   = "Hello, World!"

local flag,db = re:getMySQL()

 

local res, err, errno, sqlstate =

                    db:query("SELECT `Name`,TagDesc,BigPics,Rank FROM Tag WHERE Id = 1")

                if not res then

                    ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".")

                    return

                end

--[[]]--

--查询专题应用数据

local hql = "SELECT p.Id,p. NAME,p.LogoUrl,p.Version,p.Catalog,p.SubCatalog FROM Tag m INNER JOIN App  p ON m.AppId = p.Id WHERE m.TagId = 1"

local contion = " ORDER BY m.Rank DESC"

local sqls = hql ..contion

--ngx.say(sqls)

local rest, errt, errnot, sqlstatet = db:query(sqls)

                if not rest then

                    ngx.say("bad result: ", errt, ": ", errnot, ": ", sqlstatet, ".")

                    return

                end             

view.app = rest

view.tag = res

view:render()

re.closeDB(db);

ngx.say("hello kity~~~")

--[[

--另一种页面传参方式

local x = template.render(str, {params = res,message = "Hello, World!",names = {"James", "Jack", "Anne" }})

ngx.say(x)

]]--

静态页面大概是:

 <h1>{{message}}</h1>

<hr/>

<br/>

  <ul>
{% for _, name in ipairs(names) do %}
   <li>{{name}}</li>
{% end %}

</ul>

<hr/>

 <ul>
{% for k, v in ipairs(tag) do %}
   <li>{{v.NAME}}</li>
   <li>{{v.TagDesc}}</li>
   <li>{{v.Rank}}</li>
   <li>{{v.BigPics}}</li>
{% end %}

</ul>

 ================================

<br/>

应用

------

{% if not app==nil then %} {% end %}

<table> 

{% for k, v in pairs(app) do %} 

<tr> 

    <td>{{v.Id}}</td> 

    <td>{{v.NAME}}</td> 

    <td>{{v.LogoUrl}}</td> 

    <td>{{v.Version}}</td> 

    <td>{{v.Catalog}}</td> 

</tr> 

{% end %}

</table> 

</body>

</html>

写到这里基本就完成了,这里大家需要注意一点的是lua在不文件之间调用方法的时候,传入的参数要是table类型的,不像我们的java那样直接传String,int类型这样,不然会提示错误 nil值。这里需要注意一下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: