Hello developers! I am working on a Object Oriented Trello API at the moment. However, I stumbled upon a huge obstacle, which might make it impossible, however, I am not sure.
Basically, I have a metatable with the .__index metamethod in it. It allows me to “read” properties from Trello objects. Such as Board.Name, etc. However, there is an error:
attempt to yield across metamethod/C-call boundary
It happens when I try to HTTP GET from the metamethod, is there a way to work around this limitation?
Welp, guess I have to give up on this one, I was hoping to be able to create an API that used metamethods to read or edit properties on Trello. Thank you.
Only downside to this is that it won’t necessarily work both ways. For setting data, you’d still have to use the method approach.
In my opinion, using methods makes it more clear about what’s happening. If I access or write to something using dot-notation, I don’t expect it to yield. Getters and setters are good for these things. So in terms of OOP, I think it’s better to approach it with the Java-like coding practices (getters/setters), rather than C# (properties with implicit getters/setters).
Update:
After doing some research, I found out that this is a limitation in Lua 5.1.
In Lua 5.3 and LuaJIT, you can yield in metamethods.
I hope that some day ROBLOX updates to either. I understand it will be very difficult to do so, but I believe it would bring a lot of possibilities in regards of scripting. Stuff like bitwise operators, yielding in metamethods, etc.
You can make it a callback function and put the yielding part in a switch function or a coroutine. For example:
function fn()
end
local trello = {}
setmetatable(trello, {__index = function(self, i)
if i == 'Name' then
spawn(function()
local res = http:PostAsync....
fn(res)
end)
end
end})
Something similar to that approach can work, but I really don’t see why you need to use the meta method for this.
Another solution would be to immediately return a “promise” or another object which will later be updated when the response is ready. For example, you could write:
-- A method to wait for the value
local name = Board.Name.value()
or
-- A callback when the value is ready
Board.Name.ready(print)
or
-- functions that use promises wait for their value
local function newPrint(a)
if isPromise(a) then
print(a.value())
else
print(a)
end
end
local name = Board.Name
newPrint(name)
Promises are a common functional programming pattern (and therefore naturally better than OOP methods which I have a personal vendetta against).
Using PostAsync is not a problem here, I already took care of that part.
The problem is GetAsync, as it needs to wait for the value to be returned.
I am using metamethods to integrate Trello to ROBLOX in a way that would make it seem it is part of ROBLOX, in this case, manipulating the objects in the same manner you would manipulate any other object in the game.
fn is supposed to be an external callback function.
If you insist on using meta-methods for this project, I have created a small code for you. This is as close as you can go with meta-methods yielding.
local http = game:GetService'HttpService'
local mt = {}
setmetatable(mt, {
__newindex = function(self, i, name)
if i == 'Name' then
spawn(function()
-- HTTP Post code to change the name
end)
else
self[i] = name
end
end,
__index = function(self, i)
if i == 'Name' then
local t = {}
function t:wait()
return http:GetAsync'...' -- The http get request
end
return t
end
end,
})
mt.Name = 'something'
print(mt.Name:wait())
You are welcome. You could also create a function that will regularly update the name and save yourself from yielding. But this is less efficient as it will use more requests(Cant compare really, that depends on the usage).