I'm fairly sure that in Lua, you can use a given metatable's __index
, __newindex
, and __call
to roughly replicate Ruby's method_missing
. And I somewhat have:
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
local metahack = {
__call = function(self, ...)
return func(selfs, name, ...)
end
}
return setmetatable({}, metahack)
end
setmetatable(selfs, meta)
end
_G:method_missing(function(self, name, ...)
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
end
end)
test_print("I like me some method_missing abuse!")
print(this_should_be_nil)
My problem is this: While the syntax is similar, and I can certainly use it to replicate the functionality, it introduces a breaking error. Every single variable that you use in the context of the table you apply a method_missing
to is never nil, since I have to return an object that can be called in order to pass the buck
of the potential call from the index function to an actual call.
i.e. After defining a global method_missing as above, attempting to call undefined method 'test_print' runs as expected, but the value of test_print when indexed is non-nil, and other methods/variables that aren't responded to, like this_should_be_nil
are non-nil.
So is it possible to avoid this pitfall? Or can the syntax not be bent to support this modification without modifying the language source itself? I imagine the difficulty arises in how in Ruby, indexing and calling are analogous, whereas in Lua they are distinct.
You can avoid this problem by making nil
value callable.
Unfortunatelly, this can be done only from host code (i.e., C program), not from Lua script.
Pascal code:
function set_metatable_for_any_value_function(L: Plua_State): Integer; cdecl;
begin // set_metatable_for_any_value(any_value, mt)
lua_setmetatable(L, -2);
Result := 0;
end;
procedure Test_Proc;
var
L: Plua_State;
const
Script =
'set_metatable_for_any_value(nil, ' +
' { ' +
' __call = function() ' +
' print "This method is under construction" ' +
' end ' +
' } ' +
') ' +
'print(nonexisting_method == nil) ' +
'nonexisting_method() ';
begin
L := luaL_newstate;
luaL_openlibs(L);
lua_pushcfunction(L, lua_CFunction(@set_metatable_for_any_value_function));
lua_setglobal(L, 'set_metatable_for_any_value');
luaL_dostring(L, Script);
lua_close(L);
end;
Output:
true
This method is under construction
You have identified the problem well: it is not, as far as I know, possible to solve that issue in pure Lua.
EDIT: I was wrong, you can by making nil
callable. See other answers. It is still a bad idea IMO. The main use case for method_missing
is proxy objects and you can solve that in another way. method_missing
on Kernel
(Ruby) / _G
(Lua) is terrible :)
What you could do is only handle some methods, for instance if you know you expect methods that start by test_
:
local function is_handled(method_name)
return method_name:sub(1,5) == "test_"
end
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
if is_handled(name) then
local metahack = {
__call = function(self, ...)
return func(selfs, name, ...)
end
}
return setmetatable({}, metahack)
end
end
setmetatable(selfs, meta)
end
_G:method_missing(function(self, name, ...)
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
end
end)
test_print("I like me some method_missing abuse!")
print(this_should_be_nil)
Now maybe the question should be: why do you want to replicate method_missing
, and can you avoid it? Even in Ruby it is advised to avoid the use of method_missing
and prefer dynamic method generation when possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With