Luaの関数に動作を追加する。
_G環境、つまりどこからでも参照することのできるLuaの関数に、動作を追加したり、別の動作に差し替えたりする命令を追加します。
trigger.lua
-- trigger.lua -- luaの関数の前後に、イベントを設定する。 -- または、luaの関数を外見はそのままに中身をそっくり入れ替える。 do -- トリガー関数保存変数 local before_list = {} local overwrite_list = {} local after_list = {} -- 上書きされた元の関数の保存変数 local original = {} -- _Gの関数を退避させる。 local stack = function(func_name) if type(_G[func_name])~="function" then NSOkBox(func_name.."は関数ではありません。", "overwrite") NSEnd() end original[func_name] = original[func_name] or _G[func_name] end -- _Gを上書きする。 local override = function(func_name) if not(original[func_name]) then stack(func_name) _G[func_name] = function(...) local arg = {...} if before_list[func_name] then arg = {before_list[func_name](unpack(arg))} end local res = {} if overwrite_list[func_name] then res = {overwrite_list[func_name](unpack(arg))} else res = {original[func_name](unpack(arg))} end if after_list[func_name] then return after_list[func_name](arg, res) end return unpack(res) end end end trigger = {} trigger.before = setmetatable({}, {__newindex=function(t, k, v) override(k) before_list[k] = v end}) trigger.overwrite = setmetatable({}, {__newindex=function(t, k, v) override(k) overwrite_list[k] = v end}) trigger.after = setmetatable({}, {__newindex=function(t, k, v) override(k) after_list[k] = v end}) -- オリジナルの関数を実行する。 org = setmetatable({}, {__index=function(t, k) if original[k] then return original[k] else return _G[k] end end}) end
解説
triggerで、関数に動作を追加します。
local add_func = function(...) return ... end trigger.(timing).(func_name) = add_func
timingの部分には、before, overwrite, afterのどれかが入ります。それ以外は無効です。
その後の、func_nameには、動作を追加したい/差し替えたい関数の名前が入ります。
最後にイコールで新しい関数を代入することで完成です。
beforeの例
NSExecに対し、実行前に与えられた文字列を表示する動作を追加します。
trigger.before.NSExec = function(str) NSOkBox(str, "NSExec") return str end
これで、他の全てのNSExec実行前に、NSExecに与えられる予定の文字列がポップアップします。
beforeタイミングに与えられる関数には、オリジナルの関数に与えられる引数がそのまま与えられます。
そして、この関数は、全く同じか加工した後の引数を返すことが期待されています。
上記の例で言えば、最後のreturn strです。
これは同じ物を返してもいいですし、加工して違うものを返してもよいです。引数のvalidation、汚染チェックや修正が可能です。もちろん、例のように注意に使ったり、あるいはログ保存目的でも構いませんし、条件次第ではNSEnd()してもよいでしょう。デバッグのために、製品版では外したり無効化したりもできるでしょう。
overwirteの例
overwriteでは、オリジナルの関数は実行されずに、ここで設定した新しい関数が実行されます。
新しい関数に与えられる引数は、beforeがあればbeforeの関数が返したものになり、そうでなければユーザーが設定したものになります。
afterの例
afterも基本的にな同じですが、引数が特殊になります。
二つのテーブルが与えられ、前者はオリジナルの関数に与えられた引数の配列であり、後者はその返り値の配列になります。
-- 何もしない例 trigger.after.NSExec = function(arg, res) return unpack(res) end
beforeと同様に、関数の結果を記録したりするのに使えます。
念のため
一つの命令につき、before, overwrite, afterの三つを追加できます。
オリジナルを実行したい時には
orgを使います。
-- トリガーのない生を実行する。
org.NSExec(str)