Let me know if there are any issues/features you’d like, or dont, I’m not your boss! You’re free to use this with or without credit, I honestly don’t care, so go mad!
NOTES:
- Currently instances are just converted to a path ( e.g. workspace:WaitForChild( “Base” ) ). I would like to support creation of instances too however not sure how I’d differentiate between what you want to occur. I’m thinking maybe an option called Create which is a function that when returns true for an object the object is created instead of located.
- Cyclic tables only work if the table is being ToString’d with a name so the function can do name.Cycle = name
- Userdata, threads and functions won’t work for obvious reasons
- Works with any object, not just tables, e.g. ToString( “Hi” )
Here’s a link to the model on Roblox for you to take/require ( returns a function ):
And here’s a link to the module on github:
Options it allows ( Examples of uses below) ( Several taken from Inspect ):
Space = " " -- Uses this character anywhere my script adds a space
Tab = " " -- Uses this character anywhere my script adds a tab
NewLine = "\n" -- Uses this character anywhere my script adds a new line that is required for it to be valid lua ( Can be replaced with spaces or semi-colons and still be valid lua )
SecondaryNewLine = "\n" -- Uses this character anywhere my script adds a new line that is just for visuals sake
MaxDepth = math.huge -- Maximum depth in a table this will go
MaxDepthReplacement = nil -- Defaults to { ... }, this string will be used for a table that bypasses the max depth
Process = function ( Obj, Name, Options, Tabs, Cyclic, Key, CyclicObjs, WaitedFor, NumKey ) -- A function called every time an object is processed ( Name will be formatted with tabs / spaces already )
A few tests of things it can handle:
1.
>local Instance_Test = print( Stringify( { game.Lighting.Candy, workspace.Base, workspace.Base.Base } ) )
{
game:GetService( "Lighting" ):WaitForChild( "Candy" ),
workspace:WaitForChild( "Base" ),
workspace.Base:WaitForChild( "Base" )
}
>local WaitForChild_Test = print( Stringify( { game.Lighting.Candy, workspace.Base.Base.Base, workspace.Base, workspace.Base.Base } ) )
{
game:GetService( "Lighting" ):WaitForChild( "Candy" ),
workspace:WaitForChild( "Base" ):WaitForChild( "Base" ):WaitForChild( "Base" ),
workspace.Base,
workspace.Base.Base
}
>local Depth_Test = print( Stringify( { 1, { 2, { 3, { 4, { 5, { 6, } } } } } }, "depth_test", { MaxDepth = 3, Space = " ", Tab = "", NewLine = " ", SecondaryNewLine = "" } ) )
depth_test = { 1, { 2, { 3, { ... } } } }
>local Depth_Test_2 = print( Stringify( { 1, { 2, { 3, { 4, { 5, { 6, } } } } } }, "depth_test", { MaxDepth = 3, MaxDepthReplacement = "", Space = " ", Tab = "", NewLine = " ", SecondaryNewLine = "" } ) )
depth_test = { 1, { 2, { 3, } } }
>local String_Test = print( Stringify( { "Hi!\" lol", '"Hi there!" :P', "String with new\n line!", [[String with non-processed new line \n]], "String with \\special %! \\n \\\" ' [[ characters", "New line with [[\n]]" }, "Hm that's odd" ) )
getfenv( )[ "Hm that's odd" ] = {
'Hi!" lol',
'"Hi there!" :P',
[[String with new
line!]],
"String with non-processed new line \\n",
"String with \\special %! \\n \\\" ' [[ characters",
[[New line with \[\[
\]\]]]
}
>local Number_Test = print( Stringify( { 1, 2, math.huge }, "Uh.what" ) )
Uh.what = {
1,
2,
tonumber( "1.#INF" )
}
>local Process_Test = print( Stringify( { workspace.Base, true }, nil, { Process = function ( Obj, Name, Options, Tabs, Cyclic, Key, CyclicObjs, WaitedFor, NumKeyr ) if typeof( Obj ) == "Instance" then return Name .. 'Instance.new( "Part" )' end end } ) )
{
Instance.new( "Part" ),
true
}
>local t = { a = { true, [ "1a" ] = { CFrame.new( 1, 0, 1 ), [ "t.1" ] = true }, [ true ] = { CFrame.new( 1, 0, 1 ) } }, [ "1" ] = 2, _a1 = true, { }, { }, 2, 3 } t.t = t.a[ "1a" ] t[ "1t" ] = t
>local Cyclic_Table_Test = print( Stringify( t, "t" ) )
t = {
{ },
{ },
2,
3,
[ "1" ] = 2,
_a1 = true,
t = {
CFrame.new( 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1 ),
[ "t.1" ] = true
},
a = {
true,
[ true ] = {
CFrame.new( 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1 )
}
}
}
t.a[ "1a" ] = t.t
t[ "1t" ] = t
>local t = { a = { true, [ "1a" ] = { CFrame.new( 1, 0, 1 ), [ "t.1" ] = true }, [ true ] = { CFrame.new( 1, 0, 1 ) } }, [ "1" ] = 2, _a1 = true, { }, { }, 2, 3 } t.t = t.a[ "1a" ] t[ "1t" ] = t
>local Space_Tab_NewLine_SecondaryNewLine_Test = print( Stringify( t, "t", { Space = "", Tab = "", NewLine = " ", SecondaryNewLine = "" } ) )
t={{},{},2,3,["1"]=2,_a1=true,t={CFrame.new(1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1),["t.1"]=true},a={true,[true]={CFrame.new(1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1)}}} t["1t"]=t t.a["1a"]=t.t
>local t = setmetatable( { a = 1, b = 2 }, { __index = { c = 3 }, __newindex = function ( ... ) rawset( ... ) end } )
>local metatable_test = print( Stringify( t, "metatable_test" ) )
metatable_test = setmetatable( {
a = 1,
b = 2
}, {
__index = {
c = 3
},
__newindex = function ( ) error( "Can't run ToString functions" ) end
}