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

[1.7.4] Bugs in os.loadAPI

Started by machinesmith, 12 March 2016 - 01:45 PM
machinesmith #1
Posted 12 March 2016 - 02:45 PM
VERSION:
[indent=1]I say 1.7.4, but really it occurs through all versions of Computercraft.[/indent]

DESCRIPTION:

[indent=1]Issue 1:[/indent]
[indent=2]os.loadAPI doesn't handle files with extensions. When loading an api os.loadAPI doesn't strip the extention of the file being loaded when inserting it into the global table (_G), meaning the only way to access the api is by indexing the global table with the full file name i.e _G["filename.lua"].[/indent]
[indent=2]Solution: replace fs.getName with string.gsub(file, "(.*/)(.*)(%..*)$", "%2"), though really os.loadAPI should sanitize file names for other characters that are illegal name character[/indent]
[indent=1]Issue 2:[/indent]
[indent=2]os.loadAPI incorrectly copies the loaded files environment. When loading a file os.loadAPI peforms a shallow copy of the api's environment, meaning modifications of the api's variables by the program that imported it, will not be seen by the api.[/indent]
[indent=2]Solution: replace the shallow copy with a proxy table to the original api environment.[/indent]

REPRODUCTION STEPS:

[indent=1]Issue 1 Example:[/indent]
[indent=2]create the following files:

--name this file api.lua
variable = true
function make_false()
variable = false
end

os.loadAPI("api.lua") --should create table in _G called api, not api.lua[/indent]
api.make_false() --will error because api is nil
[/indent]
[indent=1]
Issue 2 Example:[/indent]
[indent=2]rename the file in the first example to just api
create the following files:

os.loadAPI("api")
api.make_false()
print(api.variable) --prints true, since it's a copy of the original
[/indent]
apemanzilla #2
Posted 13 March 2016 - 12:02 AM
Really, I just avoid os.loadAPI in general. I prefer to do stuff like this:

API file

local myApi = {}

function myApi.double(x)
  return x*2
end

return myApi

Usage

local api = dofile("myApi.lua")
print(api.double(5))

Bam, no worrying about file extensions, no pollution of _G (even when run as a program), no copying at all. This doesn't fix the problems with os.loadAPI, but it works around them.
Edited on 12 March 2016 - 11:03 PM
CrazedProgrammer #3
Posted 13 March 2016 - 06:31 PM
Really, I just avoid os.loadAPI in general. I prefer to do stuff like this:

API file

local myApi = {}

function myApi.double(x)
  return x*2
end

return myApi

Usage

local api = dofile("myApi.lua")
print(api.double(5))

Bam, no worrying about file extensions, no pollution of _G (even when run as a program), no copying at all. This doesn't fix the problems with os.loadAPI, but it works around them.
This is the best way to build APIs.
Another positive: You can easily include the API code in the program file for less clutter in the filesystem and easier distribution through Pastebin.
Edited on 13 March 2016 - 05:32 PM
ElvishJerricco #4
Posted 13 March 2016 - 07:03 PM
I think issue 1 is a part of a larger issue. CC in general doesn't respect file extensions. Programs with file extensions require you to enter the file extension when trying to run the program, which makes sense if you come from any other command line environment. It is consistent then for os.loadAPI not to respect file extensions.

Issue 2 is by design I think. It is undesirable for users to be able to modify an API.

That said, CC really does need a better API system.
Bomb Bloke #5
Posted 13 March 2016 - 09:35 PM
Programs with file extensions require you to enter the file extension when trying to run the program, which makes sense if you come from any other command line environment.

'cept anything DOS-based, of course, where all executable extensions are inferred.

It is undesirable for users to be able to modify an API.

For example, think about what happens if you're running multiple tabs which both want to use the same API, and one starts "reconfiguring" it.

If you want a set of functions "customised" for your script, then global variables (such as those in an API) simply aren't suitable, and you should load yourself something up within a local scope.
Lupus590 #6
Posted 13 March 2016 - 09:55 PM
If you want a set of functions "customised" for your script, then global variables (such as those in an API) simply aren't suitable, and you should load yourself something up within a local scope.

An API which gives you an object is what you would want, that's if you really want it to be a global API