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

Class API

Started by Exerro, 14 January 2014 - 11:56 AM
Exerro #1
Posted 14 January 2014 - 12:56 PM
The Class API, an API designed to make OOP far easier in computercraft / Lua.
A class is essentially a way of creating objects with certain values, some can be extendable and some can extend other classes.

This API allows you to create a class object that can extend other classes and create static or normal objects.

The main function in this API is
 class:create( name [,data] ) 
this will return a class object, that allows you to set/get parents, set the data and meta values for the class, and obviously create objects

The other function is
 class:getClass( name ) 
This will return a class that has been created with the name [name] or false if it doesn't exist

To set a class parent, use
 Class:setParentClass( parent ) 
The use of is that values from the parent class will be accessible in objects unless the class redefines them
An example of this is shown in the explanation of Class:newObject
To get the class parent, use
 Class:getParentClass( ) 
To get the root class, use
 Class:getRootParentClass( ) 

To change the class name, use
 Class:setType( name ) 
This will change the class name, kind of obviously…
To get the class name, use
 Class:getType( ) 

To add a child class to a class, use
 Class:newChildClass( name, data ) 
This is the same as doing

local myclass = class:create( name, data )
myclass:setParentClass( Class )
To get all child classes, ( including those defined with Class:setParent ), use
 Class:getChildClasses( ) 

To create a normal object, use
 Class:newObject( ... ) 
This will create an object that has all the values from the parent class, and all the classes that parent the parent class
For example
Spoiler

drawable = class:create( "Drawable", {
	name = "Random string";
	render = function( self )
		print( self.name )
	end;
} )
picture = drawable:newChildClass( "Picture", {
	name = "Other random string";
} )
object = picture:newObject( )
object:render( )
-- > Other random string
You can also call Class( ) to create a new object of that class
i.e. Class( ) is the same as
 Class:newObject( ) 

To create a static class, use
 Class:newStaticObject( ... ) 
( You can also make Class:newObject( ) create a static object by default if you set Class.static to true. This will not affect previously created objects. )
A static object basically makes it impossible to change any of the variables in the object. However this is not the same for indexes in tables because of how metatables work.
Example:
Spoiler

myclass = class:create( "MyClass", {
	stringValue = "Hello World";
} )
local myobject = myclass:newStaticObject( )
print( myobject.stringValue )
--> Hello World
myobject.stringValue = "Another String Value" -- this is ignored
print( myobject.stringValue )
--> Hello World
However, if you had a table with stringValue in it, you would then be able to change [table].stringValue

This can be useful, however it would be very awkward to use if you could not define any variables when you create an object
To run some code when an object is created, you need to define the Class.new function
An example is below:
Spoiler

myclass = class:create( "MyClass", { } )
function myclass:new( object, meta, data, args )
	-- self would be the class
	-- object would be the thing that is going to be returned
	-- meta is the metatable for the object, with some functions in it and a __index pointing to data
	-- data is the private table holding all the values for the object
	-- in a non static object, if you did object.newValue = "value" then it would go into the data table
	-- there is no way to access the data table past this point unless you make a copy of it in this function
	-- args is the table of arguments passed in by Class:newObject( ... )
	data.randomValue = args[1] or "Something"
end
local object = myclass( )
print( object.randomValue )
--> Something
local object2 = myclass( "Hello World" )
print( object.randomValue )
--> Hello World
This could be useful in a vector class, i.e.
 myvector = Vector( x, y ) 

There are also some functions that objects get and have no way of changing ( without modifying the metatable ( which is impossible with a static class with no Class:new( ) function ) )
 Object:type( full ) 
if full is true then
returns the object type including all parents concatenated with a "." i.e. "Drawable.Shape.Triangle"
otherwise
returns the object type i.e. "Triangle"

 Object:typeOf( type, single ) 
if single is true then
returns whether the object type is equal to type i.e. Object:typeOf( "Drawable", true ) = false
otherwise
returns whether any of the object's parents type is equal to type i.e. Object:typeOf( "Drawable" ) = true

 Object:getClass( ) 
returns the object class

 Object:getRootClass( ) 
returns the root object class
i.e. Object:getRootClass( ) = Drawable [Class Object]

 Object:getValues( ) 
returns a table of all the values an object has
using
for k, v in pairs( Object ) do
will not work, however using
 for k, v in pairs( Object:getValues( ) ) do 
will work


Some tips/pointers:
If you have a class with a table value in it, any modifications to indexes in that table will change the class data not the object data. This is something that is possible to fix but would require a lot of work and would most probably break a lot of other things, so don't expect a fix any time soon.
However it is possible to fix this yourself using a couple of techniques.
Method one: In Class:new( ) use this code for all the tables in the class

data[table_name_as_string] = { }
for k, v in pairs( self[table_name_as_string] ) do
	data[table_name_as_string][k] = v
end
An alternative is to define the table in the Class:new( ) function
Any classes I have made so far use this second method ( defining the table in Class:new( ) ) which is just as simple as copying and pasting some code if you have written it already.

Pastebin link: pastebin get 9bg8Fuje class
If you choose to use this API, you should know that rather than using os.loadAPI( ) you should just run it. It will return a table with the api
For example

local classAPIUnloaded = loadfile( "ClassAPI" )
if classAPIUnloaded then
	classAPILoaded = classAPIUnloaded( )
end
The API also creates a table "new" which can be used to create objects
For example if you have a class called "MyClass" you could call new.MyClass( … ) to create an object of MyClass

Thanks for reading
Edited on 14 January 2014 - 01:25 PM
surferpup #2
Posted 02 February 2014 - 05:48 PM
I have yet to crack into OOP and classes in Lua. It is one of those intimidating topics for me. Thanks for the post and API. Now I have something to start toying with to work on OOP and Lua.