initial commit
This commit is contained in:
21
External/LibTSMClass/LICENSE.txt
vendored
Normal file
21
External/LibTSMClass/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2019-present TradeSkillMaster LLC (https://tradeskillmaster.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
375
External/LibTSMClass/LibTSMClass.lua
vendored
Normal file
375
External/LibTSMClass/LibTSMClass.lua
vendored
Normal file
@@ -0,0 +1,375 @@
|
||||
--- LibTSMClass Library
|
||||
-- Allows for OOP in lua through the implementation of classes. Many features of proper classes are supported including
|
||||
-- inhertiance, polymorphism, and virtual methods.
|
||||
-- @author TradeSkillMaster Team (admin@tradeskillmaster.com)
|
||||
-- @license MIT
|
||||
-- @module LibTSMClass
|
||||
|
||||
local Lib = {}
|
||||
local private = { classInfo = {}, instInfo = {}, constructTbl = nil }
|
||||
-- Set the keys as weak so that instances of classes can be GC'd (classes are never GC'd)
|
||||
setmetatable(private.instInfo, { __mode = "k" })
|
||||
local SPECIAL_PROPERTIES = {
|
||||
__init = true,
|
||||
__tostring = true,
|
||||
__dump = true,
|
||||
__class = true,
|
||||
__isa = true,
|
||||
__super = true,
|
||||
__name = true,
|
||||
__as = true,
|
||||
}
|
||||
local RESERVED_KEYS = {
|
||||
__super = true,
|
||||
__isa = true,
|
||||
__class = true,
|
||||
__name = true,
|
||||
__as = true,
|
||||
}
|
||||
local DEFAULT_INST_FIELDS = {
|
||||
__init = function(self)
|
||||
-- do nothing
|
||||
end,
|
||||
__tostring = function(self)
|
||||
return private.instInfo[self].str
|
||||
end,
|
||||
__dump = function(self)
|
||||
return private.InstDump(self)
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Public Library Functions
|
||||
-- ============================================================================
|
||||
|
||||
function Lib.DefineClass(name, superclass, ...)
|
||||
if type(name) ~= "string" then
|
||||
error("Invalid class name: "..tostring(name), 2)
|
||||
end
|
||||
local abstract = false
|
||||
for i = 1, select('#', ...) do
|
||||
local modifier = select(i, ...)
|
||||
if modifier == "ABSTRACT" then
|
||||
abstract = true
|
||||
else
|
||||
error("Invalid modifier: "..tostring(modifier), 2)
|
||||
end
|
||||
end
|
||||
|
||||
local class = setmetatable({}, private.CLASS_MT)
|
||||
private.classInfo[class] = {
|
||||
name = name,
|
||||
static = {},
|
||||
superStatic = {},
|
||||
superclass = superclass,
|
||||
abstract = abstract,
|
||||
isStaticReference = false,
|
||||
}
|
||||
while superclass do
|
||||
for key, value in pairs(private.classInfo[superclass].static) do
|
||||
if not private.classInfo[class].superStatic[key] then
|
||||
private.classInfo[class].superStatic[key] = { class = superclass, value = value }
|
||||
end
|
||||
end
|
||||
private.classInfo[superclass].subclassed = true
|
||||
superclass = superclass.__super
|
||||
end
|
||||
return class
|
||||
end
|
||||
|
||||
function Lib.ConstructWithTable(tbl, class, ...)
|
||||
private.constructTbl = tbl
|
||||
local inst = class(...)
|
||||
assert(not private.constructTbl and inst == tbl, "Internal error!")
|
||||
return inst
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Instance Metatable
|
||||
-- ============================================================================
|
||||
|
||||
private.INST_MT = {
|
||||
__newindex = function(self, key, value)
|
||||
if RESERVED_KEYS[key] then
|
||||
error("Can't set reserved key: "..tostring(key), 2)
|
||||
end
|
||||
if private.classInfo[self.__class].static[key] ~= nil then
|
||||
private.classInfo[self.__class].static[key] = value
|
||||
elseif not private.instInfo[self].hasSuperclass then
|
||||
-- we just set this directly on the instance table for better performance
|
||||
rawset(self, key, value)
|
||||
else
|
||||
private.instInfo[self].fields[key] = value
|
||||
end
|
||||
end,
|
||||
__index = function(self, key)
|
||||
-- This method is super optimized since it's used for every class instance access, meaning function calls and
|
||||
-- table lookup is kept to an absolute minimum, at the expense of readability and code reuse.
|
||||
local instInfo = private.instInfo[self]
|
||||
|
||||
-- check if this key is an instance field first, since this is the most common case
|
||||
local res = instInfo.fields[key]
|
||||
if res ~= nil then
|
||||
instInfo.currentClass = nil
|
||||
return res
|
||||
end
|
||||
|
||||
-- check if it's the special __super field or __as method
|
||||
if key == "__super" then
|
||||
if not instInfo.hasSuperclass then
|
||||
error("The class of this instance has no superclass.", 2)
|
||||
end
|
||||
-- The class of the current class method we are in, or nil if we're not in a class method.
|
||||
local methodClass = instInfo.methodClass
|
||||
-- We can only access the superclass within a class method and will use the class which defined that method
|
||||
-- as the base class to jump to the superclass of, regardless of what class the instance actually is.
|
||||
if not methodClass then
|
||||
error("The superclass can only be referenced within a class method.", 2)
|
||||
end
|
||||
return private.InstAs(self, private.classInfo[instInfo.currentClass or methodClass].superclass)
|
||||
elseif key == "__as" then
|
||||
return private.InstAs
|
||||
end
|
||||
|
||||
-- reset the current class since we're not continuing the __super chain
|
||||
local class = instInfo.currentClass or instInfo.class
|
||||
instInfo.currentClass = nil
|
||||
|
||||
-- check if this is a static key
|
||||
local classInfo = private.classInfo[class]
|
||||
res = classInfo.static[key]
|
||||
if res ~= nil then
|
||||
return res
|
||||
end
|
||||
|
||||
-- check if it's a static field in the superclass
|
||||
local superStaticRes = classInfo.superStatic[key]
|
||||
if superStaticRes then
|
||||
res = superStaticRes.value
|
||||
return res
|
||||
end
|
||||
|
||||
-- check if this field has a default value
|
||||
res = DEFAULT_INST_FIELDS[key]
|
||||
if res ~= nil then
|
||||
return res
|
||||
end
|
||||
|
||||
return nil
|
||||
end,
|
||||
__tostring = function(self)
|
||||
return self:__tostring()
|
||||
end,
|
||||
__metatable = false,
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Class Metatable
|
||||
-- ============================================================================
|
||||
|
||||
private.CLASS_MT = {
|
||||
__newindex = function(self, key, value)
|
||||
local classInfo = private.classInfo[self]
|
||||
if classInfo.subclassed then
|
||||
error("Can't modify classes after they are subclassed", 2)
|
||||
end
|
||||
if classInfo.static[key] then
|
||||
error("Can't modify or override static members", 2)
|
||||
end
|
||||
if RESERVED_KEYS[key] then
|
||||
error("Reserved word: "..tostring(key), 2)
|
||||
end
|
||||
local isMethod = type(value) == "function"
|
||||
if classInfo.isStaticReference then
|
||||
-- we are defining a static class function, not a class method
|
||||
assert(isMethod)
|
||||
classInfo.isStaticReference = false
|
||||
isMethod = false
|
||||
end
|
||||
if isMethod then
|
||||
-- We wrap class methods so that within them, the instance appears to be of the defining class
|
||||
classInfo.static[key] = function(inst, ...)
|
||||
local instInfo = private.instInfo[inst]
|
||||
if not instInfo.isClassLookup[self] then
|
||||
error(format("Attempt to call class method on non-object (%s)!", tostring(inst)), 2)
|
||||
end
|
||||
if not instInfo.hasSuperclass then
|
||||
-- don't need to worry about methodClass so just call the function directly
|
||||
return value(inst, ...)
|
||||
else
|
||||
local prevMethodClass = instInfo.methodClass
|
||||
instInfo.methodClass = self
|
||||
return private.InstMethodReturnHelper(prevMethodClass, instInfo, value(inst, ...))
|
||||
end
|
||||
end
|
||||
else
|
||||
classInfo.static[key] = value
|
||||
end
|
||||
end,
|
||||
__index = function(self, key)
|
||||
local classInfo = private.classInfo[self]
|
||||
assert(not classInfo.isStaticReference)
|
||||
-- check if it's the special __isa method which all classes implicitly have
|
||||
if key == "__isa" then
|
||||
return private.ClassIsA
|
||||
elseif key == "__name" then
|
||||
return classInfo.name
|
||||
elseif key == "__super" then
|
||||
return classInfo.superclass
|
||||
elseif key == "__static" then
|
||||
classInfo.isStaticReference = true
|
||||
return self
|
||||
elseif classInfo.static[key] ~= nil then
|
||||
return classInfo.static[key]
|
||||
end
|
||||
error(format("Invalid static class key (%s)", tostring(key)), 2)
|
||||
end,
|
||||
__tostring = function(self)
|
||||
return "class:"..private.classInfo[self].name
|
||||
end,
|
||||
__call = function(self, ...)
|
||||
if private.classInfo[self].abstract then
|
||||
error("Attempting to instantiate an abstract class!", 2)
|
||||
end
|
||||
-- Create a new instance of this class
|
||||
local inst = private.constructTbl or {}
|
||||
local instStr = strmatch(tostring(inst), "table:[^0-9a-fA-F]*([0-9a-fA-F]+)")
|
||||
setmetatable(inst, private.INST_MT)
|
||||
local hasSuperclass = private.classInfo[self].superclass and true or false
|
||||
private.instInfo[inst] = {
|
||||
class = self,
|
||||
fields = {
|
||||
__class = self,
|
||||
__isa = private.InstIsA,
|
||||
},
|
||||
str = private.classInfo[self].name..":"..instStr,
|
||||
isClassLookup = {},
|
||||
hasSuperclass = hasSuperclass,
|
||||
currentClass = nil,
|
||||
}
|
||||
if not hasSuperclass then
|
||||
-- set the static members directly on this object for better performance
|
||||
for key, value in pairs(private.classInfo[self].static) do
|
||||
if not SPECIAL_PROPERTIES[key] then
|
||||
rawset(inst, key, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
local c = self
|
||||
while c do
|
||||
private.instInfo[inst].isClassLookup[c] = true
|
||||
c = private.classInfo[c].superclass
|
||||
end
|
||||
if private.constructTbl then
|
||||
-- re-set all the object attributes through the proper metamethod
|
||||
for k, v in pairs(inst) do
|
||||
rawset(inst, k, nil)
|
||||
inst[k] = v
|
||||
end
|
||||
private.constructTbl = nil
|
||||
end
|
||||
if select("#", inst:__init(...)) > 0 then
|
||||
error("__init must not return any values", 2)
|
||||
end
|
||||
return inst
|
||||
end,
|
||||
__metatable = false,
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
function private.InstMethodReturnHelper(class, instInfo, ...)
|
||||
-- reset methodClass now that the function returned
|
||||
instInfo.methodClass = class
|
||||
return ...
|
||||
end
|
||||
|
||||
function private.InstIsA(inst, targetClass)
|
||||
return private.instInfo[inst].isClassLookup[targetClass]
|
||||
end
|
||||
|
||||
function private.InstAs(inst, targetClass)
|
||||
local instInfo = private.instInfo[inst]
|
||||
instInfo.currentClass = targetClass
|
||||
if not targetClass then
|
||||
error(format("Requested class does not exist!"), 2)
|
||||
elseif not instInfo.isClassLookup[targetClass] then
|
||||
error(format("Object is not an instance of the requested class (%s)!", tostring(targetClass)), 2)
|
||||
end
|
||||
-- For classes with no superclass, we don't go through the __index metamethod, so can't use __as
|
||||
if not instInfo.hasSuperclass then
|
||||
error("The class of this instance has no superclass.", 2)
|
||||
end
|
||||
-- We can only access the superclass within a class method.
|
||||
if not instInfo.methodClass then
|
||||
error("The superclass can only be referenced within a class method.", 2)
|
||||
end
|
||||
return inst
|
||||
end
|
||||
|
||||
function private.ClassIsA(class, targetClass)
|
||||
while class do
|
||||
if class == targetClass then return true end
|
||||
class = class.__super
|
||||
end
|
||||
end
|
||||
|
||||
function private.InstDump(inst)
|
||||
local instInfo = private.instInfo[inst]
|
||||
local tbl = instInfo.hasSuperclass and instInfo.fields or inst
|
||||
print(instInfo.str.." {")
|
||||
for key, value in pairs(tbl) do
|
||||
local valueStr = nil
|
||||
if type(value) == "table" then
|
||||
if private.classInfo[value] or private.instInfo[value] then
|
||||
-- this is a class or instance of a class
|
||||
valueStr = tostring(value)
|
||||
elseif next(value) then
|
||||
valueStr = "{ ... }"
|
||||
else
|
||||
valueStr = "{}"
|
||||
end
|
||||
elseif type(value) == "string" or type(value) == "number" or type(value) == "boolean" then
|
||||
valueStr = tostring(value)
|
||||
end
|
||||
if valueStr then
|
||||
print(format(" |cff88ccff%s|r=%s", tostring(key), valueStr))
|
||||
end
|
||||
end
|
||||
print("}")
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Initialization Code
|
||||
-- ============================================================================
|
||||
|
||||
do
|
||||
-- register with LibStub
|
||||
local libStubTbl = LibStub:NewLibrary("LibTSMClass", 1)
|
||||
if libStubTbl then
|
||||
for k, v in pairs(Lib) do
|
||||
libStubTbl[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- register with TSM
|
||||
local addonName, addonTable = ...
|
||||
if addonName == "TradeSkillMaster" then
|
||||
local tsmModuleTbl = addonTable.Init("LibTSMClass")
|
||||
for k, v in pairs(Lib) do
|
||||
tsmModuleTbl[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
243
External/LibTSMClass/README.md
vendored
Normal file
243
External/LibTSMClass/README.md
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
# LibTSMClass
|
||||
|
||||
The LibTSMClass library allows for writing objected-oriented code in lua! There are many OOP / class libraries out there for lua, but none of them had all the features which we needed for TradeSkillMaster, were easily imported into WoW, and were sufficiently performant.
|
||||
|
||||
## Features
|
||||
|
||||
### Class Definition
|
||||
|
||||
To define a new class, simply use the `LibTSMClass.DefineClass()` method of the library:
|
||||
```lua
|
||||
local MyClass = LibTSMClass.DefineClass("MyClass")
|
||||
```
|
||||
|
||||
This function takes at least one argument, which is the name of the class. This class name is primarily used to make debugging easier, by leveraging it in the `__tostring()` metamethod for both the class and instances of the class.
|
||||
|
||||
### Instantiation
|
||||
|
||||
The class can be called as a function to create an instance of the class.
|
||||
|
||||
```lua
|
||||
local classInst = MyClass()
|
||||
```
|
||||
|
||||
If a table containing existing attributes already exists, it can be converted into an instance of the class via the `LibTSMClass.ConstructWithTable()` method.
|
||||
|
||||
```lua
|
||||
local tbl = { existingValue = 2 }
|
||||
local classInst = LibTSMClass.ConstructWithTable(tbl, MyClass)
|
||||
print(classInst.existingValue) -- prints 2
|
||||
```
|
||||
|
||||
### Static Attributes
|
||||
|
||||
Static fields are allowed on all classes and can be accessed by instances of the class.
|
||||
|
||||
```lua
|
||||
MyClass.staticValue = 31
|
||||
print(MyClass.staticValue) -- prints 31
|
||||
local classInst = MyClass()
|
||||
print(classInst.staticValue) -- prints 31
|
||||
```
|
||||
|
||||
### Method Definition
|
||||
|
||||
Classes define their methods by simply defining the functions on the class object which was previously created.
|
||||
|
||||
```lua
|
||||
function MyClass.SayHi(self)
|
||||
print("Hello from MyClass!")
|
||||
end
|
||||
function MyClass.GetValue(self)
|
||||
return self._value
|
||||
end
|
||||
```
|
||||
|
||||
### Constructor
|
||||
|
||||
The constructor is a special class method with a name of `__init()` and is called whenever a class is instantiated. Any arguments passed when instantiating the class will be passed along to the constructor. Note that the constructor should never return any values.
|
||||
|
||||
```lua
|
||||
function MyClass.__init(self, value)
|
||||
self._value = value
|
||||
end
|
||||
function MyClass.GetValue(self)
|
||||
return self._value
|
||||
end
|
||||
local classInst = MyClass(42)
|
||||
print(classInst:GetValue()) -- prints 42
|
||||
```
|
||||
|
||||
### Inheritance
|
||||
|
||||
Classes can be sub-classed by specifying their base class when defining them. Any methods which are defined on the base class can then be overridden. The subclass is also allowed to access any methods or properties of its base class.
|
||||
|
||||
```lua
|
||||
local MySubClass = LibTSMClass.DefineClass("MySubClass", MyClass)
|
||||
function MySubClass.SayHi(self)
|
||||
print("Hello from MySubClass")
|
||||
end
|
||||
```
|
||||
|
||||
### Accessing the Base Class
|
||||
|
||||
In order to explicitly access a method or attribute of the parent class, the `__super` attribute can be used. This is generally used to call the parent class's implementation of a given method. Note that the `__super` attribute can only be accessed from within a class method. This attribute can be used multiple times to continue to walk up the chain of parent classes for cases where there is more than one level of sub-classing.
|
||||
|
||||
```lua
|
||||
function MySubClass.SayHiAll(self)
|
||||
print("Hello from MySubClass")
|
||||
end
|
||||
function MySubClass.GetValue(self)
|
||||
return self.__super:GetValue() + 2
|
||||
end
|
||||
```
|
||||
|
||||
Note that `__super` may also be used on class objects themselves, including outside of any class methods.
|
||||
|
||||
```lua
|
||||
MyClass.staticValue = 2
|
||||
MySubClass.staticValue = 5
|
||||
print(MySubClass.__super.staticValue) -- prints 2
|
||||
```
|
||||
|
||||
Another mechanism for accessing an explicit parent class from a subclass is by using the special `__as` instance method. This can be especially useful when there is a long chain of inheritance.
|
||||
|
||||
```lua
|
||||
function MySubClass.GetValue(self)
|
||||
return self:__as(MyClass):GetValue() + 2
|
||||
end
|
||||
```
|
||||
|
||||
### Other Useful Attributes
|
||||
|
||||
#### `__tostring()`
|
||||
|
||||
Every class and instance has a special `__tostring()` method which can be used to convert it to a string. This is generally useful for debugging. Classes can override this method in order to provide a custom implementation.
|
||||
|
||||
```lua
|
||||
function MySubClass.__tostring(self)
|
||||
return "MySubClass with a value of "..self._value
|
||||
end
|
||||
local classInst = MyClass(0)
|
||||
print(classInst) -- prints "MyClass:00B8C688"
|
||||
print(MySubClass) -- prints "class:MySubClass"
|
||||
local subClassInst = MySubClass(3)
|
||||
print(subClassInst) -- prints "MySubClass with a value of 3"
|
||||
```
|
||||
|
||||
#### `__name`
|
||||
|
||||
The `__name` attribute is provided on all classes to look up the name of the class.
|
||||
|
||||
```lua
|
||||
print(MyClass.__name) -- prints "MyClass"
|
||||
```
|
||||
|
||||
### `__dump()`
|
||||
|
||||
All instances have a special `__dump()` method which can be used to pretty-print the fields of class for debugging. Similarly to `__tostring()`, the default implementation may be overridden in order to provide a custom implementation.
|
||||
|
||||
```lua
|
||||
local classInst = MyClass(0)
|
||||
classInst:__dump()
|
||||
-- prints [[
|
||||
MyClass:00B8C688 {
|
||||
_value = 0
|
||||
}
|
||||
]]
|
||||
```
|
||||
|
||||
#### `__class`
|
||||
|
||||
The special `__class` field is provided on every instance in order to introspect the class to which the instance belongs.
|
||||
|
||||
```lua
|
||||
local classInst = MyClass(0)
|
||||
print(classInst.__class) -- prints "class:MyClass"
|
||||
```
|
||||
|
||||
#### `__isa()`
|
||||
|
||||
In order to test whether or not an instance belongs to a given class, the `__isa` method is provided on all instances.
|
||||
|
||||
```lua
|
||||
local classInst = MyClass(3)
|
||||
print(classInst:__isa(MyClass)) -- prints true
|
||||
print(classInst:__isa(MySubClass)) -- prints false
|
||||
```
|
||||
|
||||
### Virtual Methods
|
||||
|
||||
One of the most powerful features of LibTSMClass is support for virtual class methods. What this means is that within a base class method, an instance of a class is still treated as its an instance of its actual class, not the base class. This is best demonstrated with an example:
|
||||
|
||||
```lua
|
||||
function MyClass.GetMagicNumber(self)
|
||||
return 99
|
||||
end
|
||||
function MyClass.PrintMagicNumber(self)
|
||||
print(self:GetMagicNumber())
|
||||
end
|
||||
function MySubClass.GetMagicNumber(self)
|
||||
return 88
|
||||
end
|
||||
local subClassInst = MySubClass(0)
|
||||
subClassInst:PrintMagicNumber() -- prints 88
|
||||
```
|
||||
|
||||
### Abstract Classes
|
||||
|
||||
An abstract class is one which can't be directly instantiated. Other than this restriction, abstract classes behave exactly the same as normal classes, including the ability to be sub-classed. This is useful in order to define a common interface which multiple child classes are expected to adhere to. An abstract class is defined by passing an extra argument when defining the class as shown below:
|
||||
```lua
|
||||
local AbstractClass = LibTSMClass.DefineClass("AbstractClass", nil, "ABSTRACT")
|
||||
```
|
||||
|
||||
## Limitations, Gotchas, and Notes
|
||||
|
||||
### Access Restrictions (Private / Protected)
|
||||
|
||||
All instance variables and class methods are publicly accessible. While it's possible for LibTSMClass to be extended to allow for enforcing private / protected access restrictions, it's not currently implemented in order to keep the library as simple and performant as possible. With that being said, a general convention of adding an leading underscore to things which shouldn't be used externally (i.e. private members / methods) is encouraged. If true private members are needed, another alternative is to create scope-limited lookup tables or functions within the file where the class is defined.
|
||||
|
||||
### Classes are Immutable and Should be Atomically Defined
|
||||
|
||||
One gotcha of LibTSMClass is that all methods and static fields of a class must be fully defined before that class is subclassed or instantiated. This means that changing the definition of a class at runtime is not supported, and may lead to undefined behavior. Along the same lines, once a class's methods are defined, they may not be changed later on.
|
||||
|
||||
### Highly-Performant Base Classes
|
||||
|
||||
Inheritance is one of the most powerful uses of OOP, and LibTSMClass fully supports it. However, for cases where performance is of the utmost importance, LibTSMClass is heavily optimized to reduce the overhead of a class which does not subclass anything to be as close to direct table access as possible (without metamethod calls).
|
||||
|
||||
## Example
|
||||
|
||||
A basic example of the library is below:
|
||||
```lua
|
||||
local LibTSMClass = LibStub("LibTSMClass")
|
||||
local MyClass = LibTSMClass.DefineClass("MyClass")
|
||||
|
||||
function MyClass.__init(self, value)
|
||||
self._value = value
|
||||
end
|
||||
|
||||
function MyClass.GetValue(self)
|
||||
return self._value
|
||||
end
|
||||
|
||||
function MyClass.SetValue(self, value)
|
||||
self._value = value
|
||||
end
|
||||
|
||||
local MySubClass = LibTSMClass.DefineClass("MySubClass", MyClass)
|
||||
|
||||
function MySubClass.AddValue(self, value)
|
||||
self:SetValue(self:GetValue() + value)
|
||||
end
|
||||
|
||||
local obj = MySubClass(4)
|
||||
print(obj:GetValue()) -- 4
|
||||
obj:SetValue(10)
|
||||
print(obj:GetValue()) -- 10
|
||||
obj:AddValue(5)
|
||||
print(obj:GetValue()) -- 15
|
||||
```
|
||||
|
||||
## License and Contributes
|
||||
|
||||
LibTSMClass is licensed under the MIT license. See LICENSE.txt for more information. If you would like to contribute to LibTSMClass, opening an issue or submitting a pull request against the [LibTSMClass Bitbucket project](https://bitbucket.org/tradeskillmasteraddon/libtsmclass) is highly encouraged.
|
||||
Reference in New Issue
Block a user