169 posts
Posted 09 May 2013 - 06:42 PM
Hello, I was just trying to make a simple 'include' function, here is the code for it:
function include(localP)
path = shell.resolve(localP)
local f = fs.open(path, 'r')
local str = f.readAll()
f.close()
local tbl = loadstring(str)()
return tbl
end
local updater = include('apis/updater')
Here is the apis/updater file:
local runningDir = shell.getRunningProgram()
return {
testF = function()
return 'hello world'
end,
}
For some reason, it seems that it doesn't let me access the Shell API.
Error:
string:1: attempt to index ? (a nil value)
Any help would be nice. I know there is the os.loadAPI function, but I don't want to use that.
1190 posts
Location
RHIT
Posted 09 May 2013 - 07:29 PM
Your problem lies here:
local tbl = loadstring(str)()
loadstring is given a limited environment, and therefore you'll be unable to access the shell API from it.
169 posts
Posted 09 May 2013 - 07:34 PM
Yeah, I figured it had something to do with loadstring() since this is my first time using it.
Is there any way that I can approach the include function in a way that allows me to still use all the APIs?
1583 posts
Location
Germany
Posted 09 May 2013 - 07:36 PM
I think you can use setfenv() but I don't understand it, so… I can't help :D/>/>
1190 posts
Location
RHIT
Posted 09 May 2013 - 07:42 PM
Yeah, I figured it had something to do with loadstring() since this is my first time using it.
Is there any way that I can approach the include function in a way that allows me to still use all the APIs?
As Freack says, you can use setfenv to set your environment of the function returned by loadstring.
local function include(f)
--[[local file = fs.open(f, "r")
local c = file.readAll()
file.close()]] There's no need for all of this. Just use loadfile.
local tEnv = {} --Create the environment we'll store the file functions in
setmetatable(tEnv, {__index = _G}) --Set the __index metamethod of tEnv so that when we try to call something that doesn't exist in the loadfile env (shell.getRunningProgram, for example), it will access the values in _G instead
local api = loadfile(f)
setfenv(api, tEnv) --Go ahead and set the environment of the loaded code to our tEnv
api() --Run it
return tEnv --Return the table
end
169 posts
Posted 09 May 2013 - 08:10 PM
-snip-
Still get the same error for some reason.
1190 posts
Location
RHIT
Posted 09 May 2013 - 08:11 PM
-snip-
Still get the same error for some reason.
Really? That's strange. I tried it and it's working fine for me.
Here's the code again in case I typed something up wrong:
function include(_f)
local f = shell.resolve(_f)
if not fs.exists(f) then
return false, "That file does not exist!"
end
local tEnv = {}
setmetatable(tEnv, {__index = _G})
local loaded = loadfile(f)
setfenv(loaded, tEnv)
loaded()
return tEnv
end
local test = include "/test2" --Here's my test: it worked perfectly fine for me
for i,v in pairs(test) do
print(i..": "..tostring(v))
end
1583 posts
Location
Germany
Posted 09 May 2013 - 08:19 PM
-snip-
Still get the same error for some reason.
Really? That's strange. I tried it and it's working fine for me.
Here's the code again in case I typed something up wrong:
function include(_f)
local f = shell.resolve(_f)
if not fs.exists(f) then
return false, "That file does not exist!"
end
local tEnv = {}
setmetatable(tEnv, {__index = _G})
local loaded = loadfile(f)
setfenv(loaded, tEnv)
loaded()
return tEnv
end
local test = include "/test2" --Here's my test: it worked perfectly fine for me
for i,v in pairs(test) do
print(i..": "..tostring(v))
end
There's a little typo in the third line:
"_f" not "f" ;)/>
169 posts
Posted 09 May 2013 - 08:20 PM
There's a little typo in the third line:
"_f" not "f" ;)/>
That's intentional, read the line above it.
@Bubba: Did you put shell.getRunningProgram() (or any other Shell API function) in your test file?
1583 posts
Location
Germany
Posted 09 May 2013 - 08:22 PM
Oh yea, now i see it.
I have to read better ^_^/>
1190 posts
Location
RHIT
Posted 09 May 2013 - 08:34 PM
There's a little typo in the third line:
"_f" not "f" ;)/>
That's intentional, read the line above it.
@Bubba: Did you put shell.getRunningProgram() (or any other Shell API function) in your test file?
Spoiler
Yup. It worked fine. Here's the contents of my test2 file:
function test()
print(shell.getRunningProgram())
end
And the output:
test: function: 2cc7d960
Oh hang on a sec. I didn't actually try the loaded function and now I'm seeing that it's not working *facepalm*. Hang on a second while I debug this.
169 posts
Posted 09 May 2013 - 08:37 PM
Try running the test function.
local test = include('/test2')
test.test()
1190 posts
Location
RHIT
Posted 09 May 2013 - 08:40 PM
Okay, I'm really derping today. You can easily load shell yourself by doing this:
local tEnv = {shell=shell}
But I'm not 100% sure why the setmetatable bit isn't working as it should. Oh well, doing the above code fixes the problem.
169 posts
Posted 09 May 2013 - 08:42 PM
Okay, I'm really derping today. You can easily load shell yourself by doing this:
local tEnv = {shell=shell}
But I'm not 100% sure why the setmetatable bit isn't working as it should. Oh well, doing the above code fixes the problem.
Yeah, I just noticed that too. Thanks, I'll try to find a better way around it if I can.