lua语言非常灵活,开发者发挥的空间很大,模拟oop,使用xml等,此篇文章将向大家介绍如何添加访问控制,不用此来实现 lua自动载入机制 提示开发效率。
类似java、as3等语言,访问没有定义的属性、方法、或者非公有属性时会报错,再编译阶段这些错就能暴漏出来,lua则不能,这些访问对他都是合法的,如果你使用lua做一些较大型的开发,这种能合法调用但不合法的访问,会给你带来很多坑,访问控制作者认为还是非常有必要的。
lua中导入一个moudle,常规做法是使用require(“**.**.**”),lua自动载入机制目的就是省略这一步,一定程度上有助于开发效率的提升。
先来看看访问控制类
ImmutableMetatable.lua
--- Author: terran
-- Date: 27/9/2013 15:03
local ImmutableMetatable = {};
local M = ImmutableMetatable;
function declare (item,name, initval)
rawset(item, name, initval)
item.ImmutableMetatable__declaredNames[name] = true
end
function M.__newindex(t, n, v)
if(rawget(t,"ImmutableMetatable__declaredNames") == nil) then
error("attempt to write to undeclared var "..n, 2)
return;
end
if not t.ImmutableMetatable__declaredNames[n] then
local handler = t.ImmutableMetatable__writeErrorHandler;
if(handler == nil or not handler(t, n, v)) then
error("attempt to write to undeclared var. "..n, 2)
end
else
rawset(t,n,v)
end
end
function M.__index(t, n)
if(rawget(t,"ImmutableMetatable__declaredNames") == nil) then
error("attempt to read undeclared var. "..n, 2)
return;
end
if not t.ImmutableMetatable__declaredNames[n] then
local handler = t.ImmutableMetatable__readErrorHandler;
if(handler == nil or not handler(t, n)) then
error("attempt to read undeclared var. "..n, 2)
else
return t[n];
end
else
return nil
end
end
function ImmutableMetatable.extend(item,readErrorHandler,writeErrorHandler)
declare(item, "ImmutableMetatable__declaredNames", {});
declare(item, "ImmutableMetatable__readErrorHandler", readErrorHandler);
declare(item, "ImmutableMetatable__writeErrorHandler", writeErrorHandler);
return setmetatable(item,M);
end
return M;
该类的使用比较简单:比如
local a = {};
ImmutableMetatable.extend(a,readErr,wirteErr);
这样在对a进行不存在定义的访问时,就能捕捉到错误。
比如:调用print(a.b),而a中并没有属性b的定义,则会回调readErr函数,做一些自定义的处理,如果该回调函数返回false,则直接抛出异常,否则正常继续;执行a.b = 12,同样的原理,会调用wirteErr回调函数。
lua文件的自动载入机制就是利用这个访问控制的readErr回调函数来实现的。接下来可以看看具体的实现代码。机制核心类
ImmutableMetatable = require("core.ImmutableMetatable");
local luaClasses = require("LuaClasses");
local readErr = function(t,n)
if(luaClasses[n] ~= nil) then
local name = luaClasses[n];
declare(t,n,require(luaClasses[n]));
return true;
end
return false;
end
ImmutableMetatable.extend(_G,readErr);
可以看到ImmutableMetatable直接作用到了全局(_G),这样访问任何一个未定义的全局变量或方法时都会跑到readErr函数中,此时我们可以检查该变量是否是我们的一个moudle,如果是,自动require进来,这样就完成了整个过程。代码中的LuaClasses是所有lua文件的枚举类,当然也不需要去手动维护,有一个小工具自动生成,先来看看他得格式:
local LuaClasses = {};
LuaClasses.Config = "Config";
LuaClasses.AppBase = "core.AppBase";
LuaClasses.audio = "core.audio";
LuaClasses.BindingUtils = "core.bind.BindingUtils";
LuaClasses.BindObject = "core.bind.BindObject";
LuaClasses.Alert = "core.components.flex.Alert";
LuaClasses.ArrayCollection = "core.components.flex.ArrayCollection";
LuaClasses.BasicLayout = "core.components.flex.BasicLayout";
return LuaClasses
结合自动载入的核心逻辑,如果你直接访问ArrayCollection,程序会自动到LuaClasses中找到对应“core.components.flex.ArrayCollection”,这个值即为ArrayCollection.lua文件的存放路径。
下面给出LuaClasses.lua文件的生成工具,也是用lua下得,在mac下使用。
--------------------------------
--------------------------------
-- 此工具为工程lua classes文件生成工具,可遍历
-- 指定目录,将所有lua文件遍历输出到LuaClasses.lua中。
-- 用法:
-- 1、命令行输入: lua LuaClassesMaker.lua
--
--Author: terran.tian
--Date : 9/13/2013 16:35
---------------------------------
---------------------------------
local function split(str, delimiter)
if (delimiter=='') then return false end
local pos,arr = 0, {}
for st,sp in function() return string.find(str, delimiter, pos, true) end do
table.insert(arr, string.sub(str, pos, st - 1))
pos = sp + 1
end
table.insert(arr, string.sub(str, pos))
return arr
end
local function scandir(directory)
local i, t, popen = 0, {}, io.popen
local cmd = string.format('find %q -name "*.lua" -o -name "*.xml"' ,directory)
print("cmd: "..cmd)
for filename in popen(cmd):lines() do
i = i + 1
t[i] = filename
end
return t
end
local function reloadquest(dir,foldName)
local file=scandir(dir)
for i=1,#file do
local filename = string.gsub(file[i],dir,foldName)
print(filename)
end
end
local function writeFile(path,data)
file = io.open(path,"w")
file:write(data)
file:close()
end
local function main()
local folder = "../assets/scripts/";
local files = scandir(folder);
local tempArr = split(folder,"/");
local folderName = tempArr[#tempArr];
local content = "--[[\
This file was generated automatically\
Any modifies will by revert during nect generation.\
Any question ,contact terran.tian@foxmail.com for help\
%q\
]]\n"
content = string.format(content,os.date("%c"));
content = content.."local LuaClasses = {};\n"
for i=1,#files do
local filepath = string.gsub(files[i],folder .."/","")
tempArr = split(filepath,"/");
local filename = split(tempArr[#tempArr],".")[1];
if(filename ~= "init") then
filepath = string.gsub(filepath,"/",".");
filepath = string.gsub(filepath,".lua","");
local record = string.format('LuaClasses.%s = %q;\n',filename,filepath);
content = content..record;
print(record);
end
end
content = content.."return LuaClasses;"
writeFile(folder.."/LuaClasses.lua",content)
end
main();