Skip to main content

SecuredMetatable

A lightweight metatable wrapper built for security and maintain proper code edicate among custom user objects and instances with built in protections and helpful features while still providing the full raw metatable api.

Syntax Result
RegularProperty Denotes a Standard/Default property Anything that doesn't classify as something below. This is the default property that is read-only and must be manipulated with a special syntax see #page:
_PrivateProperty Denotes a Private/Protected property 1 Underscore that cannot be indexed normally Object[Index]. Must be manipulated with a special syntax see #page:
__Metamethod Denotes a Metamethod 2 Underscores. NOTE that some metamethods are assigned within the secured class and cannot be changed. IE: __index, __newindex, __metatable, These are static and cannot be changed
___UnprotectedProperty Denotes a Free/Unprotected property 3 Underscores. This property will have NO read or write protections. This is manipulable with regular table syntax

Syntax Example

local T = {
	["Test1"] = true, -- Creates a default property Read-Only
	["_Protected"] = true, -- Creates a Protected property Internal only
	["__Metamethod"] = "Test", -- Creates a Metamethod property must be a legal metamethod AND must be written in the metamethod table (THIS WILL ERROR)
	["___UnProtected"] = "Test", -- Creates a Unprotected property without Read and Write restrictions
}

Types

WrappedMetatable

type WrappedMetatable = (T & MT & MM)

Table, metatable, and metamethods all linked together, metamethods are private

structure

The Protected Metatable class is a read only wrapper that enacts a read only function apart of the __newindex metamethod, a sandboxed __index table, and the __metatable flag being enacted and is only destroyable by the :Destroy function.

Property index syntax

The Protected Metatable object has built in index syntax for different properties. First Both the Table and Metatable share these syntax. Such as: Protected, Metamethod, UnProtected, and standard syntax structures.

MetaMethods

since V 2.0.0
</>
interface MetaMethods {
__index((
selfT,
index(keyof(T) | any)
) → () | {[any]any})?,--

self[keyof(T)] Indexes the table attached (Not the actual Metatable)

__newindex((
selfT,
indexany,
valueany
) → () | {[any]any})?,--

self[keyof(T)] = V Adds new value (locked for read only)

__call((
selfT,
...any
) → any)?--

self() function call

__concat((
selfT,
valueV
) → T & V)?,--

self..value Concatenation (combine)

__unm((selfT) → T)?,--

-self Unary (inverse)

__iter(typeof(next))?,--

Generalized iteration (Type of next as default)

__add((
selfT,
valuenumber
) → number)?,--

self+value Addition (Adds a number)

__sub((
selfT,
valuenumber
) → number)?,--

self-value Subtraction (Subtracts a number)

__mul((
selfT,
valuenumber
) → number)?,--

self*value Multiplication (Multiples a number)

__div((
selfT,
valuenumber
) → number)?,--

self/value Division (Divides a number)

__idiv((
selfT,
valuenumber
) → number)?,--

self//value Floor division (Floor divides a number)

__mod((
selfT,
valuenumber
) → number)?,--

self%value Modulus (Modulo's by a number)

__pow((
selfT,
valuenumber
) → number)?,--

self^value Exponentiation (Exponentiates a number)

__tostring((selfT) → string)?,--

tostring(self) -> printable string ()

__len((selfT) → number)?,--

#self Array length (length)

__eq((
selfT,
valueany
) → boolean)?,--

self==value Equality operator (compares a value to be equal)

__lt((
selfT,
valueany
) → boolean)?,--

self<value Less than operator (compares a value to be less than)

__le((
selfT,
valueany
) → boolean)?,--

self<=value Less than or equal operator (compares a value to be less than or equal)

__mode("K" | "V" | "KV")?,--

Sets the garbage collection rule of the table

__metatablestring--

Locks metatable

__destroy((
selfT,
OptionalBooleanArgument:boolean
) → ())?,--

custom function to be assigned when 'Destroy is called'

__typeKeystring?,--

The index needed to access the private and default metatable,

}

A strongly typed metatable of all lua 5.1 metamethods + extras

Forbidden

Metamethods: __index, __newindex, and __metatable are custom and constant and cannot be written or read from. __tostring and __typeKey are the only exceptions, by default they are assigned to flag but are EDITABLE when writing the metamethod table.

Comparisons

__eq, __lt, __le Requires two values with the same metamethod function and basic type (table/userdata/etc.); does not work with a table and another random table, or with a userdata and a table.

New Metamethod

__destroy can be assigned to be ran before the auto cleanup of :Destroy(). __typeKey is a optional parameter default is Flag this is the index of the Metatable object when you want to access private and default variables ie: helper functions

Metamethods and their status

Metamethod Write Safety Read Safety
__index False False
__newindex False False
__call True False
__concat True False
__unm True False
__add True False
__sub True False
__mul True False
__div True False
__idiv True False
__mod True False
__pow True False
__tostring True True
__metatable False True
__eq True False
__lt True False
__le True False
__mode True False
__len True False
__iter True False
__destroy True False
__typeKey True True

Functions

NewMetatable

SecuredMetatable.NewMetatable(
TableT,--

The front table template and inital values

MetatableMT,--

The metatable template and inital values

Flagstring,--

The string that is assigned to __metatable, __tostring, and __typeKey by default

MetamethodsMetaMethods--

The metamethod container

) → WrappedMetatable--

A special securely wrapped metatable

Creates a new Wrapped Metatable object

Function

SecuredClasses returns a function that is called to create a WrappedMetatable class NOT a dictionary

	local T = {
		ClassName = "SecuredClass"
		_PrivateData = 10,
	}
	
	local MT = {
		GetPrivateData = function(self)
			return self.Key._PrivateData
		end
	}
	
	local MM:SecuredClasses.MetaMethods<typeof(T) & typeof(MT)> = {
		__typeKey = "Key",

		__destroy = function(self)
			print("Destroyed", getmetatable(self)) -- Destroyed SecuredClass
		end,

		__tostring = function(self)
			return `Class: {self.ClassName}`
		end
	}
	
	local SecuredClass = SecuredClasses(T, MT, "SecuredClass", MM)

	SecuredClass:GetPrivateData() -- 10

	print(SecuredClass._PrivateData) -- Nil & Warn
Type union

Due to the nature of generics and the current state of type functions try and keep metamethods out of the Metatable or Table "Leaving them in will not break anything as they are sorted into their correct categories". As this can cause the type union to include non accessible type objects. And due to the experimental state of type functions there isnt a way to programmatically remove them without breaking something else.

Destroy

since V 2.1.1
</>
SecuredMetatable:Destroy(
Nukeboolean--

Complete destroy or optional boolean parameter for __destroy

) → nil

The only way to destroy the metatable when flags are assigned

	-- Inherits properties from above

	local SecuredClass = SecuredClasses(T, MT, "SecuredClass", MM)

	SecuredClass:GetPrivateData() -- 10

	print(SecuredClass, getmetatable(SecuredClass)) -- Class: SecuredClass, SecuredClass

	SecuredClass:Destroy() -- Nil

	print(SecuredClass, getmetatable(SecuredClass)) -- {}, Nil

	SecuredClass:GetPrivateData() -- Attempt to index nil with GetPrivateData
Auto clean-up

Destroy contains a recursive cleaning function that is able to properly disable and deallocate all basic ROBLOX objects. The Nuke parameter is used when you want to call :Destroy on an instance ie: destroying a Basepart or Model. OR If you have a function binded to __destroy you can pass this optional boolean + more if applicable.

__destroy

The custom metamethod __destroy is a metamethod you can assign that will run BEFORE the auto clean-up. It is recommended to only use this metamethod to clean non default ROBLOX objects such as: Signals, Controllers, or user-Created classes then remove the index from the table. If you have a property that is a ROBLOX object such as: instances, threads, or RBXScriptConnections let the auto clean-up remove them instead.

Irreversible

Once :Destroy() is called this action cannot be undone and the metatable will be completely deallocated calling print(CLASS, getmetatable(CLASS)) will return { }, nil

Show raw api
{
    "functions": [
        {
            "name": "NewMetatable",
            "desc": "Creates a new Wrapped Metatable object\n:::info Function\nSecuredClasses returns a function that is called to create a WrappedMetatable class **NOT** a dictionary \n:::\n\n```lua\n\tlocal T = {\n\t\tClassName = \"SecuredClass\"\n\t\t_PrivateData = 10,\n\t}\n\t\n\tlocal MT = {\n\t\tGetPrivateData = function(self)\n\t\t\treturn self.Key._PrivateData\n\t\tend\n\t}\n\t\n\tlocal MM:SecuredClasses.MetaMethods<typeof(T) & typeof(MT)> = {\n\t\t__typeKey = \"Key\",\n\n\t\t__destroy = function(self)\n\t\t\tprint(\"Destroyed\", getmetatable(self)) -- Destroyed SecuredClass\n\t\tend,\n\n\t\t__tostring = function(self)\n\t\t\treturn `Class: {self.ClassName}`\n\t\tend\n\t}\n\t\n\tlocal SecuredClass = SecuredClasses(T, MT, \"SecuredClass\", MM)\n\n\tSecuredClass:GetPrivateData() -- 10\n\n\tprint(SecuredClass._PrivateData) -- Nil & Warn\n```\n\n:::tip Type union\nDue to the nature of `generics` and the current state of `type functions` try and keep metamethods out of the Metatable or Table \"_Leaving them in will not break anything as they are sorted into their correct categories_\". As this can cause the type union to include non accessible type objects. And due to the experimental state of `type functions` there isnt a way to programmatically remove them without breaking something else.\n:::",
            "params": [
                {
                    "name": "Table",
                    "desc": "The front table template and inital values",
                    "lua_type": "T"
                },
                {
                    "name": "Metatable",
                    "desc": "The metatable template and inital values",
                    "lua_type": "MT"
                },
                {
                    "name": "Flag",
                    "desc": "The string that is assigned to __metatable, __tostring, and __typeKey by default",
                    "lua_type": "string"
                },
                {
                    "name": "Metamethods",
                    "desc": "The metamethod container",
                    "lua_type": "MetaMethods"
                }
            ],
            "returns": [
                {
                    "desc": "A special securely wrapped metatable",
                    "lua_type": "WrappedMetatable"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 490,
                "path": "src/init.luau"
            }
        },
        {
            "name": "Destroy",
            "desc": "The only way to destroy the metatable when flags are assigned\n\n```lua\n\t-- Inherits properties from above\n\n\tlocal SecuredClass = SecuredClasses(T, MT, \"SecuredClass\", MM)\n\n\tSecuredClass:GetPrivateData() -- 10\n\n\tprint(SecuredClass, getmetatable(SecuredClass)) -- Class: SecuredClass, SecuredClass\n\n\tSecuredClass:Destroy() -- Nil\n\n\tprint(SecuredClass, getmetatable(SecuredClass)) -- {}, Nil\n\n\tSecuredClass:GetPrivateData() -- Attempt to index nil with GetPrivateData\n```\n\n:::info Auto clean-up\n`Destroy` contains a recursive cleaning function that is able to properly disable and deallocate all basic `ROBLOX` objects. The `Nuke` parameter is used when you want to call `:Destroy` on an instance *ie: destroying a `Basepart` or `Model`*. **OR** If you have a function binded to `__destroy` you can pass this optional boolean + more if applicable.\n:::\n\n:::tip __destroy\nThe custom metamethod `__destroy` is a metamethod you can assign that will run **BEFORE** the auto clean-up. It is recommended to only use this metamethod to clean **non default `ROBLOX` objects** such as: `Signals`, `Controllers`, or `user-Created classes` then **remove** the index from the table. If you have a property that is a `ROBLOX` object such as: `instances`, `threads`, or `RBXScriptConnections` let the auto clean-up remove them instead.\n:::\n\n:::danger Irreversible\nOnce `:Destroy()` is called this action cannot be undone and the metatable will be completely deallocated calling `print(CLASS, getmetatable(CLASS))` will return `{ }, nil`\n:::",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "WrappedMetatable"
                },
                {
                    "name": "Nuke",
                    "desc": "Complete destroy or optional boolean parameter for `__destroy`",
                    "lua_type": "boolean"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "nil"
                }
            ],
            "function_type": "method",
            "since": "V 2.1.1",
            "source": {
                "line": 532,
                "path": "src/init.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "WrappedMetatable",
            "desc": "Table, metatable, and metamethods all linked together, metamethods are private\n\n:::info structure\nThe `Protected Metatable` class is a read only wrapper that enacts a read only function apart of the `__newindex` metamethod, a sandboxed `__index` table, and the `__metatable` flag being enacted and is only destroyable by the `:Destroy` function.\n:::\n\n:::tip Property index syntax\nThe `Protected Metatable` object has built in index syntax for different properties. First **Both the Table and Metatable share these syntax**. Such as: `Protected`, `Metamethod`, `UnProtected`, and `standard` syntax structures.\n:::",
            "lua_type": "(T&MT&MM)",
            "source": {
                "line": 360,
                "path": "src/init.luau"
            }
        },
        {
            "name": "MetaMethods",
            "desc": "A strongly typed metatable of all lua 5.1 metamethods + extras\n\n\n:::danger Forbidden\nMetamethods: `__index`, `__newindex`, and `__metatable` are custom and constant and cannot be written or read from. `__tostring` and `__typeKey` are the only exceptions, by default they are assigned to `flag` but are  **EDITABLE** when writing the metamethod table. \n:::\n\n:::caution Comparisons\n`__eq`, `__lt`, `__le` Requires two values with the same metamethod function and basic type (table/userdata/etc.); does not work with a table and another random table, or with a userdata and a table.\n:::\n\n:::info New Metamethod\n`__destroy` can be assigned to be ran before the auto cleanup of `:Destroy()`. `__typeKey` is a optional parameter _default is `Flag`_ this is the index of the `Metatable` object when you want to access private and default variables _ie: helper functions_\n:::\n\n### Metamethods and their status\n| Metamethod | Write Safety | Read Safety |\n| ---- | --- | --- |\n| __index | **False** |  **False** |\n| __newindex | **False** | **False** |\n| __call | **True** | **False**\n| __concat | **True** | **False** |\n| __unm | **True** | **False** |\n| __add | **True** | **False** |\n| __sub | **True** | **False** |\n| __mul | **True** | **False** |\n| __div | **True** | **False** |\n| __idiv | **True** | **False** |\n| __mod | **True** | **False** |\n| __pow | **True** | **False** |\n| __tostring | **True** | **True** |\n| __metatable | **False** | **True** |\n| __eq | **True** | **False** |\n| __lt | **True** | **False** |\n| __le | **True** | **False** |\n| __mode | **True** | **False** |\n| __len | **True** | **False** |\n| __iter | **True** | **False** |\n| __destroy | **True** | **False** |\n| __typeKey | **True** | **True** |",
            "fields": [
                {
                    "name": "__index",
                    "lua_type": "((self: T, index: (keyof(T) | any)) -> () | {[any]: any} )?,",
                    "desc": "self[keyof(T)] Indexes the table attached (Not the actual Metatable)"
                },
                {
                    "name": "__newindex",
                    "lua_type": "((self: T, index: any, value: any) -> () | {[any]: any})?,",
                    "desc": "self[keyof(T)] = V Adds new value (locked for read only)"
                },
                {
                    "name": "__call",
                    "lua_type": "((self: T, ...any) -> any)?",
                    "desc": "self() function call"
                },
                {
                    "name": "__concat",
                    "lua_type": "((self: T, value: V) -> T & V)?,",
                    "desc": "self..value Concatenation (combine)"
                },
                {
                    "name": "__unm",
                    "lua_type": "((self: T) -> T)?,",
                    "desc": "-self Unary  (inverse)"
                },
                {
                    "name": "__iter",
                    "lua_type": "(typeof(next))?,",
                    "desc": "Generalized iteration (Type of next as default)"
                },
                {
                    "name": "__add",
                    "lua_type": "((self: T, value: number) -> number)?,",
                    "desc": "self+value Addition (Adds a number)"
                },
                {
                    "name": "__sub",
                    "lua_type": "((self: T, value: number) -> number)?,",
                    "desc": "self-value Subtraction (Subtracts a number)"
                },
                {
                    "name": "__mul",
                    "lua_type": "((self: T, value: number) -> number)?,",
                    "desc": "self*value Multiplication (Multiples a number)"
                },
                {
                    "name": "__div",
                    "lua_type": "((self: T, value: number) -> number)?,",
                    "desc": "self/value Division (Divides a number)"
                },
                {
                    "name": "__idiv",
                    "lua_type": "((self: T, value: number) -> number)?,",
                    "desc": "self//value Floor division (Floor divides a number)"
                },
                {
                    "name": "__mod",
                    "lua_type": "((self: T, value: number) -> number)?,",
                    "desc": "self%value Modulus (Modulo's by a number)"
                },
                {
                    "name": "__pow",
                    "lua_type": "((self: T, value: number) -> number)?,",
                    "desc": "self^value Exponentiation (Exponentiates a number)"
                },
                {
                    "name": "__tostring",
                    "lua_type": "((self: T) -> string)?,",
                    "desc": "tostring(self) -> printable string ()"
                },
                {
                    "name": "__len",
                    "lua_type": "((self: T) -> number)?,",
                    "desc": "#self Array length (length)"
                },
                {
                    "name": "__eq",
                    "lua_type": "((self: T, value: any) -> boolean)?,",
                    "desc": "self==value Equality operator (compares a value to be equal)"
                },
                {
                    "name": "__lt",
                    "lua_type": "((self: T, value: any) -> boolean)?,",
                    "desc": "self&lt;value Less than operator (compares a value to be less than)"
                },
                {
                    "name": "__le",
                    "lua_type": "((self: T, value: any) -> boolean)?,",
                    "desc": "self&lt;=value Less than or equal operator (compares a value to be less than or equal)"
                },
                {
                    "name": "__mode",
                    "lua_type": "(\"K\" | \"V\" | \"KV\")?,",
                    "desc": "Sets the garbage collection rule of the table"
                },
                {
                    "name": "__metatable",
                    "lua_type": "string",
                    "desc": "Locks metatable"
                },
                {
                    "name": "__destroy",
                    "lua_type": "((self: T, OptionalBooleanArgument:boolean) -> ())?,",
                    "desc": "custom function to be assigned when &apos;Destroy is called&apos;"
                },
                {
                    "name": "__typeKey",
                    "lua_type": "string?,",
                    "desc": "The index needed to access the private and default metatable,"
                }
            ],
            "since": "V 2.0.0",
            "source": {
                "line": 437,
                "path": "src/init.luau"
            }
        }
    ],
    "name": "SecuredMetatable",
    "desc": " \nA lightweight metatable wrapper built for security and maintain proper code edicate among custom user objects and instances with built in protections and helpful features while still providing the full raw [metatable](https://create.roblox.com/docs/luau/metatables) api.\n| Syntax | Result |\n| ---- | ------- |\n| RegularProperty | Denotes a `Standard/Default` property **_Anything that doesn't classify as something below_**.  This is the default property that is read-only and must be manipulated with a special syntax see [#page:](/docs/Examples#2-creating-a-metadata-table)  |\n| _PrivateProperty | Denotes a `Private/Protected` property **_1 Underscore_** that cannot be indexed normally `Object[Index]`. Must be manipulated with a special syntax see [#page:](/docs/Examples#2-creating-a-metadata-table) |\n| __Metamethod | Denotes a `Metamethod` **_2 Underscores_**.  NOTE that some metamethods are assigned within the secured class and cannot be changed. IE: *__index*, *__newindex*, *__metatable*, These are static and cannot be changed |\n| ___UnprotectedProperty | Denotes a `Free/Unprotected` property **_3 Underscores_**.  This property will have **NO** read or write protections. This is manipulable with regular table syntax |\n\n### Syntax Example\n```lua\nlocal T = {\n\t[\"Test1\"] = true, -- Creates a default property Read-Only\n\t[\"_Protected\"] = true, -- Creates a Protected property Internal only\n\t[\"__Metamethod\"] = \"Test\", -- Creates a Metamethod property must be a legal metamethod AND must be written in the metamethod table (THIS WILL ERROR)\n\t[\"___UnProtected\"] = \"Test\", -- Creates a Unprotected property without Read and Write restrictions\n}\n```",
    "source": {
        "line": 344,
        "path": "src/init.luau"
    }
}