This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Xtansia's profile picture

Allow APIs to have their own metatable

Started by Xtansia, 03 December 2012 - 03:14 PM
Xtansia #1
Posted 03 December 2012 - 04:14 PM
It would be handy if apis could 'set' the metatable of themselves, an example usage could be for the vector api where you could set the __call metamethod so that you could have a 'constructor'.

I have made an example os.loadAPI function, though this maybe isn't the best way to implement it is a proof of concept.
Spoiler

local tAPIsLoading = {}
function os.loadAPI( _sPath )
	local sName = fs.getName( _sPath )
	if tAPIsLoading[sName] == true then
		printError( "API "..sName.." is already being loaded" )
		return false
	end
	tAPIsLoading[sName] = true

	local tEnv = {}
	setmetatable( tEnv, { __index = _G } )
	local fnAPI, err = loadfile( _sPath )
	if fnAPI then
		setfenv( fnAPI, tEnv )
		fnAPI()
	else
		printError( err )
		tAPIsLoading[sName] = nil
		return false
	end

	local tAPI = {}
	for k,v in pairs( tEnv ) do
		if k ~= "__api_meta" then
			tAPI[k] =  v
		end
	end

	local api_meta = rawget(tEnv, "__api_meta")
	if api_meta ~= nil then
		setmetatable(tAPI, api_meta)
	end

	_G[sName] = tAPI
	tAPIsLoading[sName] = nil
	return true
end

And a quick example api:

function hello()
	print("Hello World!")
end

__api_meta = {
	__call = function()
		hello()
	end
}

And using the example api:

os.loadAPI("helloAPI") -- Load the api
helloAPI.hello() -- Outputs "Hello World!"
helloAPI() -- Outputs "Hello World!"
print(tostring(helloAPI.__api_meta)) -- Outputs "nil", the metatable has been removed from the "public" api

– EDIT: Slightly sleeker implementation:

local tAPIsLoading = {}
function os.loadAPI( _sPath )
	local sName = fs.getName( _sPath )
	if tAPIsLoading[sName] == true then
		printError( "API "..sName.." is already being loaded" )
		return false
	end
	tAPIsLoading[sName] = true

	local tEnv = {}
	local api_meta = {}
	setmetatable( tEnv, {
		__index = function(t, k)
			if k == "setapimeta" then
				return function(_m)
					api_meta = _m
				end
			elseif k == "getapimeta" then
				return function()
					return api_meta
				end
			else
				return _G[k]
			end
		end
	} )
	local fnAPI, err = loadfile( _sPath )
	if fnAPI then
		setfenv( fnAPI, tEnv )
		fnAPI()
	else
		printError( err )
		tAPIsLoading[sName] = nil
		return false
	end

	local tAPI = {}
	for k,v in pairs( tEnv ) do
		tAPI[k] =  v
	end

	setmetatable(tAPI, api_meta)

	_G[sName] = tAPI	
	tAPIsLoading[sName] = nil
	return true
end

And example api:

function hello()
	print("Hello World!")
end

setapimeta({
	__call = function()
		hello()
	end
})

for k, v in pairs(getapimeta()) do 
    print(k, " = ", v)
end  -- Outputs "__call = function: #####"

And example usage:

os.loadAPI("helloAPI") -- Load the api
helloAPI.hello() -- Outputs "Hello World!"
helloAPI() -- Outputs "Hello World!"
Edited on 03 December 2012 - 03:50 PM
Sebra #2
Posted 03 December 2012 - 07:00 PM
Look
Xtansia #3
Posted 03 December 2012 - 08:30 PM

Yes I found that thread after posting this,
Doesn't mean I'm not entitled to my opinion/suggestion
ChunLing #4
Posted 04 December 2012 - 07:48 AM
It does mean that you're not following the "Search First" rule…and thus were not entitled to post your opinion on this website, where you agreed to follow such rules as a condition of posting.

That's okay, cause this is not on the list of "For the love of sanity, stop posting these" suggestions. It is in fact a fine enough suggestion that simply has some security issues to be worked out before it can be implemented.
Sebra #5
Posted 04 December 2012 - 07:49 PM
I did not say your suggestion is bad.You suggest two predefined functions for API to use.
But my way is better. I suggest API itself prepare result, else old method used.
I've seen it somewhere in inet and like it. It is more universal.

You implement both reading and writing to api_meta. Why not direct access?
Lyqyd #6
Posted 05 December 2012 - 07:16 AM
It does mean that you're not following the "Search First" rule…and thus were not entitled to post your opinion on this website, where you agreed to follow such rules as a condition of posting.

You need to tone this waaay back. It's an uncommon suggestion, and was posted with an example implementation, making it a valuable post. Let's drop the meta-discussion on the legitimacy (or lack thereof) of the post itself.
ChunLing #7
Posted 05 December 2012 - 11:04 AM
Sorry. But, I do think that there would be some value to having this discussion in the context of the previous discussions related to the subject. So Sebra had a point. And tomass was dismissive of it in a manner that indicated ignorance of a significant distinction, between the right to have an opinion and the right to use someone else's property to express it. An understanding of the difference between the right to have an opinion and right to use someone else's property in a manner contrary to their expressed wishes as a means of expressing your opinion is the essential foundation of any civilized discourse.

Now, we are not here to discuss the nature and requirements of civilized discourse. But we do need to occasionally be aware of them, or this forum will not be useful to anyone.
Lyqyd #8
Posted 05 December 2012 - 11:24 AM
Sorry. But, I do think that there would be some value to having this discussion in the context of the previous discussions related to the subject. So Sebra had a point.

He did. He also made that point in a manner which could be perceived as offensive, so it is no surprise if Tomass replied in an offended tone. It did not warrant further comment at that point.

An understanding of the difference between the right to have an opinion and right to use someone else's property in a manner contrary to their expressed wishes as a means of expressing your opinion is the essential foundation of any civilized discourse.

Enforcing the rules to whatever degree is deemed necessary is a task of the moderation team. Expressing your opinion that someone shouldn't have posted (which makes no sense, since the post adds useful content to the forum) is backseat moderating, which is highly discouraged. In the future, if you feel a post should not have been made, please feel free to report it rather than berating the author.

Now, let's get back to discussion of the suggestion, please.
ChunLing #9
Posted 05 December 2012 - 06:15 PM
I did not express the opinion that tomass shouldn't have posted. I explicitly said that it was okay (in my opinion), since I thought the post valuable. I simply stated the distinction between the right to an opinion and violating an agreement.

The force of all laws ultimately rests in the positive understanding of the entire community that there is some reason for them, that they are not just a pretext for some people to exercise unlimited arbitrary power over others. I would consider myself to be "backseat moderating" if I were to harass and insult posters as a way of preventing them from feeling free to participate (or if I were to actually carry out activities, such as thread locking and user banning, that are intended to be restricted to mods). But pointing out the distinction between the right to an opinion and the liberty to violate the rights of the owners of the site is not "backseat moderating" even if it has the effect of helping to keep a discussion civil.
Sebra #10
Posted 05 December 2012 - 07:16 PM
Sorry. But, I do think that there would be some value to having this discussion in the context of the previous discussions related to the subject. So Sebra had a point.

He did. He also made that point in a manner which could be perceived as offensive, so it is no surprise if Tomass replied in an offended tone. It did not warrant further comment at that point.
OMNEG ;)/> What in a single word was offensive? And I cannot cound Tomas's reply as offensive too.
Sorry if you misunderstood it.
Cruor #11
Posted 05 December 2012 - 09:04 PM
Locked as requested.
Cloudy #12
Posted 05 December 2012 - 10:29 PM
I know this is now locked - but guys, was this really worth arguing about? :/