Repository navigation
v2.1.1
Classy v2.1.1
This release is a substantial overhaul of Classy's internal architecture, focused on cleaner separation of responsibilities, stricter type safety, and a more extensible foundation moving forward.
Internal Architecture Overhaul
Classy's internal logic has been extensively refactored and split into dedicated modules:
- Classy — The root module and public entry point.
- ClassyObject — The core of Classy, including
ClassyObjectandAppliedObject. - Alias — Resolves Classy objects by tag.
- Util — Shared utility functions.
- Types — Shared type definitions.
- Registry — Centralized shared state, helping eliminate circular dependencies.
- Constructors — Contains the
constructClassandconstructFunctionconstructables.
The goal of this refactor is simple: make Classy easier to maintain and extend without sacrificing type safety.
Context and Metadata Support
Applications can now receive both a context table and metadata, allowing applied objects to be customized based on how they were created.
For example, components can be applied from a configuration table, with properties automatically passed as metadata:
local componentsList = {
["Inventory"] = {
MaxItemAmount = 20,
},
["Ammo"] = {
UseComponent = "Resource",
StartingAmount = 0,
MaxCount = 0,
},
}
someClassy.applyComponents(someInstance, componentsList)In this example:
InventoryreceivesMaxItemAmountas metadata.Ammocan build from theResourcecomponent instead of theAmmoClassyObject.- Additional properties can be supplied directly through the component configuration.
This makes component-driven systems considerably more flexible.
Example: Kill Part System
Classy supports multiple construction patterns for the same system.
--!strict
local Classy = require(path.to.classy)
local KillPartClass = {}
KillPartClass.__index = KillPartClass
export type KillPart = typeof(setmetatable({} :: {
Part: BasePart,
Janitor: Classy.Janitor,
}, KillPartClass))
function KillPartClass.new(part: Instance, janitor: Classy.Janitor): KillPart
return setmetatable({
Part = part :: BasePart,
Janitor = janitor,
}, KillPartClass)
end
function KillPartClass.Init(self: KillPart)
self:_watchTouchedEvent()
end
function KillPartClass.DoSomething(self: KillPart)
print(self)
end
function KillPartClass.Destroy(self: KillPart)
print("This object has been destroyed!")
end
function KillPartClass._watchTouchedEvent(self: KillPart)
self.Janitor:Add(self.Part.Touched:Connect(function(hit: BasePart)
local humanoid = hit.Parent and hit.Parent:FindFirstChildWhichIsA("Humanoid")
if not humanoid then
return
end
humanoid.Health = 0
end))
endClass-Based Construction
local KillPartClassy = Classy.newClass("KillPart", KillPartClass, {
ClassNames = { "BasePart" },
Ancestors = { workspace },
Logging = true,
})Function-Based Construction
local AnotherExample = Classy.new("KillPart", function(instance: Instance, janitor: Classy.Janitor)
return KillPartClass.new(instance, janitor)
end, {
ClassNames = { "BasePart" },
Ancestors = { workspace },
Logging = true,
})Both approaches construct the same underlying system.
KillPartClassy:Init()
AnotherExample:Init()Once initialized, you can interact with applied objects directly:
KillPartClassy.InstanceAdded:Connect(function(instance, applied)
applied:GetData():DoSomething()
end)