{"id":"thenexusavenger/nexus-instance","name":"nexus-instance","scope":"thenexusavenger","platform":"roblox","description":"Utility for creating custom instances in Luau.","version":"4.1.2","latest":"4.1.2","versions":["4.1.1-dev","4.1.1-dev2","4.1.1","4.1.2-dev1","4.1.2"],"license":"MIT","licenseRating":"safe","licenseCaveats":["The package archive does not include its license text; the license is declared in its manifest metadata."],"licenseVerified":false,"dependencies":{},"integrity":"58dbcc2e1a47a9903c2c759afbac8dd829967b37fe73d5bb3a654ab7c136ed87","likes":0,"downloads":0,"install":"forest install thenexusavenger/nexus-instance","url":"https://forest.dev/p/roblox/thenexusavenger/nexus-instance","files":"https://api.forest.dev/ai/package/roblox/thenexusavenger/nexus-instance/files","readme":"# Nexus-Instance\r\nNexus Instance is a utility for creating custom instances\r\nwith property change events while still providing proper\r\ntyping in Luau.\r\n\r\n## Usage\r\n### Simple Classes\r\nClasses with metatables are created mostly the same, except:\r\n- `NexusInstance.ToInstance` is called before using or returning\r\n  the class.\r\n- The recommended type of `self` becomes `NexusInstance<Class>`.\r\n\r\nThe return type of `NexusInstance.ToInstance` is\r\n`NexusInstanceClass<TClass, TConstructor>`, which should be\r\ncasted to. The generic types are:\r\n- `TClass`: Type of the class without properties. Typically will be\r\n  `typeof(MyClass)` where `MyClass` is the table of the class.\r\n- `TConstructor`: Type of the constructor function. It should be the\r\n  arguments of `new(...)` with the return of `NexusInstance<MyClass>`.\r\n\r\n```luau\r\n--!strict\r\nlocal NexusInstance = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"NexusInstance\"))\r\n\r\nlocal TestClass = {}\r\nTestClass.__index = TestClass\r\n\r\n--Exported test class type and Nexus Instance version (optional).\r\nexport type TestClass = {\r\n    TestProperty: string,\r\n} & typeof(setmetatable({}, TestClass))\r\nexport type NexusInstanceTestClass = NexusInstance.NexusInstance<TestClass>\r\n\r\n--Optional constructor when `new` is called.\r\nfunction TestClass.__new(self: NexusInstanceTestClass, Argument: string): ()\r\n    self.TestProperty = Argument\r\nend\r\n\r\n--Custom function.\r\nfunction TestClass.ChangeValue(self: NexusInstanceTestClass, NewValue: string): ()\r\n    self.TestProperty = NewValue\r\nend\r\n\r\n--Optional destroy function to clear resources.\r\n--Events are cleaned internally (`Changed`, `GetPropertyChangedSignal`, and `CreateEvent` only).\r\n--The version returned by NexusInstance.ToInstance will always have a Destroy method.\r\nfunction TestClass.Destroy(self: NexusInstanceTestClass): ()\r\n    --Clear resources.\r\nend\r\n\r\n--Create the class to return in the ModuleScript, or use within the script.\r\n--The constructor (second generic type) should match inputs for `__new` without the `self`.\r\nlocal ReturnedTestClass = NexusInstance.ToInstance(TestClass) :: NexusInstance.NexusInstanceClass<typeof(TestClass), (Argument: string) -> (NexusInstanceTestClass)>\r\n\r\n\r\n\r\n--Create and destroy the type.\r\nlocal TestObject = ReturnedTestClass.new(\"TestValue\")\r\nprint(TestObject.TestProperty) --\"TestValue\"\r\nTestObject:ChangeValue(\"NewValue\")\r\nprint(TestObject.TestProperty) --\"NewValue\"\r\nTestObject:Destroy()\r\n```\r\n\r\n`__new` and `Destroy` are not required and can be omitted.\r\n\r\n```luau\r\n--!strict\r\nlocal NexusInstance = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"NexusInstance\"))\r\n\r\nlocal TestClass = {}\r\nTestClass.__index = TestClass\r\n\r\n--Exported test class type and Nexus Instance version (optional).\r\nexport type TestClass = {\r\n    TestProperty: string?, --No constructor initializes this.\r\n} & typeof(setmetatable({}, TestClass))\r\nexport type NexusInstanceTestClass = NexusInstance.NexusInstance<TestClass>\r\n\r\n--Custom function.\r\nfunction TestClass.ChangeValue(self: NexusInstanceTestClass, NewValue: string): ()\r\n    self.TestProperty = NewValue\r\nend\r\n\r\n--Create the class to return in the ModuleScript, or use within the script.\r\nlocal ReturnedTestClass = NexusInstance.ToInstance(TestClass) :: NexusInstance.NexusInstanceClass<typeof(TestClass), () -> (NexusInstanceTestClass)>\r\n\r\n\r\n\r\n--Create and destroy the type.\r\nlocal TestObject = ReturnedTestClass.new()\r\nprint(TestObject.TestProperty) --nil\r\nTestObject:ChangeValue(\"NewValue\")\r\nprint(TestObject.TestProperty) --\"NewValue\"\r\nTestObject:Destroy()\r\n```\r\n\r\n### Inheritance\r\nInheritance is nearly the same as doing so with metatables, except for\r\nthe calls to `NexusInstance.ToInstance`.\r\n\r\n```luau\r\n--!strict\r\nlocal NexusInstance = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"NexusInstance\"))\r\n\r\n--Define TestClass1 (potentially in a ModuleScript).\r\nlocal TestClass1 = {}\r\nTestClass1.__index = TestClass1\r\n\r\nexport type TestClass1 = {\r\n    TestProperty1: string,\r\n} & typeof(setmetatable({}, TestClass1))\r\nexport type NexusInstanceTestClass1 = NexusInstance.NexusInstance<TestClass1>\r\n\r\nfunction TestClass1.__new(self: NexusInstanceTestClass1, Input: string)\r\n    self.TestProperty1 = Input\r\nend\r\n\r\nfunction TestClass1.ChangeValue1(self: NexusInstanceTestClass1, NewValue: string): ()\r\n    self.TestProperty1 = NewValue\r\nend\r\n\r\nlocal TestClass1NexusInstance = NexusInstance.ToInstance(TestClass1) :: NexusInstance.NexusInstanceClass<typeof(TestClass1), (Input: string) -> (NexusInstanceTestClass1)>\r\n\r\n\r\n\r\n--Define TestClass2 (potentially in a different ModuleScript).\r\nlocal TestClass2 = {}\r\nTestClass2.__index = TestClass2\r\nsetmetatable(TestClass2, TestClass1NexusInstance) --TestClass1NexusInstance would be returned instead of TestClass1.\r\n\r\nexport type TestClass2 = {\r\n    TestProperty2: string,\r\n} & typeof(setmetatable({}, TestClass2)) & TestClass1\r\nexport type NexusInstanceTestClass2 = NexusInstance.NexusInstance<TestClass2>\r\n\r\nfunction TestClass2.__new(self: NexusInstanceTestClass2, Input1: string, Input2: string)\r\n    TestClass1.__new(self, Input1) --Remember to call the parent constructor!\r\n    self.TestProperty2 = Input2\r\nend\r\n\r\nfunction TestClass2.ChangeValue2(self: NexusInstanceTestClass2, NewValue: string): ()\r\n    self.TestProperty2 = NewValue\r\nend\r\n\r\nlocal TestClass2NexusInstance = NexusInstance.ToInstance(TestClass2) :: NexusInstance.NexusInstanceClass<typeof(TestClass2), (Input1: string, Input2: string) -> (NexusInstanceTestClass2)>\r\n        \r\n\r\n\r\n--Use the classes.\r\nlocal TestObject1 = TestClass1NexusInstance.new(\"TestValue1\")\r\nprint(TestObject1.TestProperty1) --\"TestValue1\"\r\nTestObject1:ChangeValue1(\"NewValue1\")\r\nprint(TestObject1.TestProperty1) --\"NewValue1\"\r\nTestObject1:Destroy()\r\n\r\nlocal TestObject2 = TestClass2NexusInstance.new(\"TestValue1\", \"TestValue2\")\r\nprint(TestObject2.TestProperty1) --\"TestValue1\"\r\nprint(TestObject2.TestProperty2) --\"TestValue2\"\r\nTestObject2:ChangeValue1(\"NewValue1\")\r\nTestObject2:ChangeValue2(\"NewValue2\")\r\nprint(TestObject2.TestProperty1) --\"NewValue1\"\r\nprint(TestObject2.TestProperty2) --\"NewValue2\"\r\nTestObject2:Destroy()\r\n```\r\n\r\n### Metatable Passthrough\r\nClasses can define some metatable methods, which will be passed\r\nthrough. `__index` and `__newindex` are not supported for this.\r\n\r\n```luau\r\n--!strict\r\nlocal NexusInstance = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"NexusInstance\"))\r\n\r\nlocal TestClass = {}\r\nTestClass.__index = TestClass\r\n\r\n--Exported test class type and Nexus Instance version (optional).\r\nexport type TestClass = typeof(setmetatable({}, TestClass))\r\nexport type NexusInstanceTestClass = NexusInstance.NexusInstance<TestClass>\r\n\r\n--__tostring metatable method.\r\nfunction TestClass.__tostring(self: NexusInstanceTestClass): ()\r\n    return \"TestClass\"\r\nend\r\n\r\n--Create the class to return in the ModuleScript, or use within the script.\r\nlocal ReturnedTestClass = NexusInstance.ToInstance(TestClass) :: NexusInstance.NexusInstanceClass<typeof(TestClass), () -> (NexusInstanceTestClass)>\r\n\r\n\r\n\r\n--Create and destroy the type.\r\nlocal TestObject = ReturnedTestClass.new()\r\nprint(tostring(TestObject)) --\"TestClass\"\r\nTestObject:Destroy()\r\n```\r\n\r\n### Property Changes\r\nSimilar to normal Roblox `Instance`s, there is a `Changed` event\r\nthat is fired when any property changes, and `GetPropertyChangedSignal`\r\nto listen to a specific property changing.\r\n\r\n```luau\r\nTestObject.Changed:Connect(function(PropertyName)\r\n    print(`Property {PropertyName} changed.`)\r\nend)\r\nTestObject:GetPropertyChangedSignal(\"TestProperty\"):Connect(function()\r\n    print(\"PropertyName changed.\")\r\nend)\r\n```\r\n\r\nChanged events can be ignored using `HidePropertyChanges`.\r\n`HideNextPropertyChange` can be used to only hide the next property change.\r\n\r\n```luau\r\nTestObject.Changed:Connect(function(PropertyName)\r\n    print(`Property {PropertyName} changed.`)\r\nend)\r\nTestObject:GetPropertyChangedSignal(\"TestProperty\"):Connect(function()\r\n    print(\"PropertyName changed.\")\r\nend)\r\n\r\nTestObject:HidePropertyChanges(\"TestProperty\") --This makes it so changed events never invokes for TestProperty.\r\nTestObject:HideNextPropertyChange(\"TestProperty\") --This makes it so only the next changed event doesn't get invoked for TestProperty.\r\n```\r\n\r\n`OnAnyPropertyChanged` and `OnPropertyChanged` also exist. Unlike\r\nthe events, they will immediately invoke after a property change\r\n(as opposed to waiting on deferred events). They do not respect\r\nhidden changed events and will always be invoked.\r\n\r\n```luau\r\nTestObject:OnAnyPropertyChanged(function(PropertyName, Value)\r\n    print(`Property {PropertyName} changed to {Value}.`)\r\nend)\r\nTestObject:OnPropertyChanged(\"TestProperty\", function(Value)\r\n    print(`TestProperty changed to {Value}.`)\r\nend)\r\n```\r\n\r\n### Property Transformers\r\nWhen a property is set, it is able to be transformed before\r\nbeing stored and invoked with changed events. Generic transforms\r\nwill always run before property-speicifc ones.\r\n\r\n```luau\r\nTestObject:AddGenericPropertyTransform(function(Index, Value)\r\n    return `{Value}_{Index}_1`\r\nend)\r\nTestObject:OnAnyPropertyChanged(\"TestProperty\", function(Value)\r\n    return `{Value}_2`\r\nend)\r\n\r\nTestObject.TestProperty = \"NewValue\"\r\nprint(TestObject.TestProperty) --\"NewValue_TestProperty_1_2\"\r\n```\r\n\r\n### Custom Events\r\n`TypedEvent` exists for custom events. Compared to `BindableEvent`:\r\n- `TypedEvent`s have typing for the arguments.\r\n- Arguments that are passed retain their original table/function\r\n  references, instead of being encoded away.\r\n\r\n`CreateEvent` is always recommended unless being used outside\r\nof an instance, since `CreateEvent` will handle disconnecting\r\nthe event when the instance is destroyed.\r\n\r\n```luau\r\n--!strict\r\nlocal NexusInstance = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"NexusInstance\"))\r\n\r\nlocal TestClass = {}\r\nTestClass.__index = TestClass\r\n\r\n--Exported test class type and Nexus Instance version (optional).\r\nexport type TestClass = {\r\n    TestEvent: NexusInstance.TypedEvent<string>,\r\n} & typeof(setmetatable({}, TestClass))\r\nexport type NexusInstanceTestClass = NexusInstance.NexusInstance<TestClass>\r\n\r\n--Optional constructor when `new` is called.\r\nfunction TestClass.__new(self: NexusInstanceTestClass): ()\r\n    self.TestEvent = self:CreateEvent() :: NexusInstance.TypedEvent<string>\r\nend\r\n\r\n--Create the class to return in the ModuleScript, or use within the script.\r\nlocal ReturnedTestClass = NexusInstance.ToInstance(TestClass) :: NexusInstance.NexusInstanceClass<typeof(TestClass), () -> (NexusInstanceTestClass)>\r\n\r\n\r\n\r\n--Create and destroy the type.\r\nlocal TestObject = ReturnedTestClass.new()\r\nTestObject.TestEvent:Connect(function(Message)\r\n    print(Message)\r\nend)\r\nTestObject.TestEvent:Fire(\"Test message\") --Prints \"Test message\"\r\nTestObject:Destroy() --Disconnects TestEvent.\r\n```\r\n\r\n## Contributing\r\nBoth issues and pull requests are accepted for this project.\r\n\r\n## License\r\nNexus Instance is available under the terms of the MIT \r\nLicense. See [LICENSE](LICENSE) for details.\r\n","readmeBorrowedFrom":{"version":"4.1.1-dev2","source":"wally"},"readmeTruncated":false}