InstanceBuilder - A module simplifying setting instance properties

Let’s be honest, writing out every single property when you create a new instance is annoying and tedious. For example:

local textLabel = Instance.new("TextLabel");
textLabel.Size = UDim2.new(1, 0, 1, 0);
textLabel.Position = UDim2.new(0, 0.5, 0.5, 0);
textLabel.RichText = true;
textLabel.Text = "<b>My Label!</b>";
textLabel.TextColor3 = Color3.fromRGB(50, 50, 50);
textLabel.Name = "MyLabel";
textLabel.Parent = myScreen;

There’s a solution to this. It’s known as method chaining. I’m used to using this pretty frequently in Java and figured I would try to port this concept over for use with instances. So the code above becomes:

local CustomInstances = require(script.CustomInstances);
CustomInstances.new("TextLabel")
  .size(UDim2.new(1, 0, 1, 0))
  .pos(UDim2.new(0.5, 0, 0.5, 0))
  .rich(true)
  .text("<b>My Label!</b>")
  .textColor(Color3.fromRGB(50, 50, 50))
  .name("MyLabel")
  .parent(myScreen);

Alternatively you can chain each method on the same line I simply prefer using new lines in this instance to prevent eye sores.

You can also append .get() to get the Roblox instance itself back:

local part = CustomInstances.new("Part").name("A Part").parent(workspace).get();

A lot of these methods have aliases, however, you can use the property name itself except in camel case if you’re not familiar with the aliases. E.g. let’s say you wanted to set the TextTransparency property of a text label. Normally you would do label.TextTransparency = 1. The method alternative of this is label.textTransparency(1). Once again notice how the method uses camel case.

I primarily made this for fun and I don’t have the time to implement support for every single Roblox class. If you want to add support for a specific instance simply create a module script under the CustomInstances module and name it the instance name. Some instances such as Parts inherit properties from other classes (e.g. BaseParts). There’s also support for this by prefixing the module with C_ then appending the class name.

Download the modules here:
CustomInstances.rbxm (3.6 KB)

I’ll probably add more classes and reupload every once in a while with the updated modules. Hope you enjoy.

11 Likes

I admit that I constantly have to face this challenge when scripting. Especially when I make all my UI through scripts. I’ve wanted to make something like what you’ve done; it is a very clever idea. However, it’s a bit too much work to add all the modules. I’ve gone ahead and scripted my own version of the module that also implements method chaining.

I made a recursive algorithm (Set()) that iteratively generates a new string and checks if it exists for the specific instance. The advantage of this is that you don’t have to manually add properties for the instances. Furthermore, it doesn’t matter what letters you capitalize or not. One more thing I implemented is that objects aren’t instantiated by the module, they are passed in through the constructor. This is simply because I wanted to use the auto-complete feature in the script editor :smile:. An example of this can be seen below.

Example

local part = module.new(Instance.new("Part"))
.parent(workspace)
.canCollide(true)
.cancollide(true) --also works

--to directly get the object, just do part._instance

Module

--V3N0M_Z

local function Set(self, property, value, indexData)
	
	if not indexData then
		indexData = {1}
	elseif #indexData > 5 or string.upper(property) == property then
		error("Invalid property!")
	else
		for iteration, index in ipairs(indexData) do
			iteration = #indexData + 1 - iteration
			if indexData[iteration] == string.len(property) then
				if indexData[iteration - 1] and indexData[iteration - 1] < string.len(property) then
					indexData[iteration - 1] += 1
					indexData[iteration] = 1
				elseif indexData[iteration] < string.len(property) then
					indexData[iteration] += 1
				else
					for iteration, _ in ipairs(indexData) do
						indexData[iteration] = iteration
					end
					table.insert(indexData, #indexData + 1)
				end
			elseif iteration == #indexData then
				indexData[iteration] += 1
				while table.find(indexData, indexData[iteration]) and iteration ~= table.find(indexData, indexData[iteration]) do
					indexData[iteration] += 1
				end
			end
		end
	end
	
	local success, msg = pcall(function() self._instance[property] = value end)
	if success then
		return self
	elseif not string.find(msg, "valid member") then
		error(msg)
	end

	local newProperty = ""
	for index, letter in ipairs(string.split(property, "")) do
		if table.find(indexData, index) then
			newProperty = newProperty..string.upper(letter)
		else
			newProperty = newProperty..string.lower(letter)
		end
	end
	property = newProperty
	
	return Set(self, property, value, indexData)
end

return {
	new = function(_instance)
		return setmetatable({
			_instance = _instance;
		}, {
			__index = function(self, property)
				return function(value)
					return Set(self, string.lower(property), value)
				end
			end;
		})
	end;
}
2 Likes

It’s a wrapper to make instance creation easier? I literally provided an example in the main post showcasing that using the module results in having to write half as much code. If you want to set each property individually be my guest but the module itself is not useless the entire purpose is that it has a use.

the first example is 1 line longer because of the module require line, if we don’t count it it’s the same amount of lines

honestly this is even more of an eye sore and is harder to read than separating into lines, and besides that we can do

local part = Instance.new("Part") part.Transparency = 1 part.Name = "A Part"

Not much difference and no need to .get() it back

I’m kind of confused

EDIT: If you are talking about this module helping you skip the “property = value” part and condensing it into “.property(value)” then honestly it’s not worth getting a whole modulescript and requiring it, and you end up wasting space on the “()” part anyway

If you have no experience in other languages besides Lua I see why this might be confusing as it’s not common practice in Lua since it isn’t an OOP language. That said, method chaining and in particular builders are powerful and extremely used in languages such as Java, C#, Python, etc. (basically any language that has actual implementation of OOP). I will admit that the way I implemented it in this module is lackluster and can be significantly improved upon but that’s not the point. Method chaining is more efficient in code and generally looks more appealing than independently setting each property. If you dislike it that’s fine but calling the module pointless is a stretch.

I know about method chaining and I do have a bit of experience in java but this is not how it works in lua

So then how is this more powerful in lua?