Instance Verifier | Ensure Instances Have All The Parts and Attributes Your Code Needs!

Download Module Here

Anytime I have a piece of code that relies on a model using a specific structure I constantly find myself writing long and confusing if statements to verify the structure. This cannot only be tiresome but leads to nearly unreadable code at times.

The Old Way

Here's an example of how you would verify this door module the traditional way:

function verifyDoor(door)
	
	local isDoor = false

	if typeof(door) == "Instance" then
		if door:GetAttribute("Locked") and typeof(door:GetAttribute("Locked")) == "boolean" then
			if door:FindFirstChild("TouchZone") and door:FindFirstChild("PathfindingPart") then
				if door.PathfindingPart:FindFirstChild("PathFindingModifier") then
					if door:FindFirstChild("Doors") and door.Doors:FindFirstChild("Right") and door:FindFirstChild("Left") then
						isDoor = true
					end
				end
				
			end
		end
	end
	
	
	return isDoor 
end

As You can see it’s very messy and this is only an example for a less complex object. Imagine doing this for something like a Spaceship or etc. Now lets write this exact same logic with our new Instance Verifier Module:

The Instance Verifier Way

local InstanceVerifier = require(game:GetService("ReplicatedStorage"):WaitForChild("CoreComponents"):WaitForChild("InstanceVerifier"))

local doorModel = script.Parent:Clone()
	
local doorVerifier = InstanceVerifier.new("Door") 
local pathfindingHelper
local doors

doorVerifier:AddAttribute("Locked","boolean")
doorVerifier:AddInstance("TouchZone")

--Ability to add in nested children requirements
pathfindingHelper = doorVerifier:AddInstance("PathfindingPart")
pathfindingHelper:AddInstance("PathfindingModifier")

doors = doorVerifier:AddInstance("Doors")
doors:AddInstance("Right")
doors:AddInstance("Left")


----Now when we want to verifiy a door model all we have to do is

local verified, foundErrorsTable = doorVerifier:Verify(doorModel)

if not verified then
  print(foundErrorsTable)
end

Now isn’t this just much cleaner :slight_smile:

The best part is this module is very powerful as it supports ALL instance sub-classes and an unlimited amount of nested children your instance may require. Do you have a Surface Gui that needs to have specific buttons before being initiated? NO PROBLEM.

Easy Debugging

The Verify() function returns a table of all missing aspects of your instance so you can easily pinpoint what an attributes/parts and etc your instance is missing. You can also use the SetPrintWarning function so you can know this info at runtime too.

Debugging

Download Module Here

API Documentation

new

local verifier = verifier.new("optionalName")
Parameters

name : optional, used to identify which verifier is which when printing out which parts of a instance failed.

Returns

InstanceVerifier


AddAttribute

verifier:AddAttribute("Speed", "number")
Parameters

attributeName : mandatory, the name of the attribute you would like the verifier to look for.
attributeType : mandatory, the type of the attribute you are looking for

Returns

InstanceVerifier


AddInstance

verifier:AddInstance("Part", "BasePart")
Parameters

instanceName : mandatory, the name of the instance you would like the verifier to look for.
instanceType : optional, the type of the instance you are looking for

Returns

InstanceVerifier

Important Note
>The verifier returned is not the original verifier used to call this function. This new verifier is linked to the added instance. this was done so adding in nested children is very easy and intuitive. Example:
local BeachVerifier = InstanceVerifier.new()
local WaterVerifier = orgVerifier:AddInstance("Water") -- Child of Beach
local FishVerifier = WaterVerifier:AddInstance("Fish") -- Child of Water
FishVerifier:AddAttribute("SwimSpeed", "number") 

BeachVerifier:Verify() -- Will call WaterVerifier:Verify() and FishVerifier:Verify()


--- This functionality allows for what I call intuitive linking

local BeachVerifier = InstanceVerifier.new()

BeachVerifier:AddInstance("Water"):AddInstance("Fish"):AddAttribute("SwimSpeed","number")

BeachVerifier:Verify() -- Will call  Verify() for Water and Fish instances



Verify

local verified, failedChecks = verifier:Verify(doorModel,true)
Parameters

instance : mandatory, The instance you would like to verify

performanceMode : optional, initiates a return as soon as the first failed check is found.
example: if a part is missing multiple attributes this verification will only return the first missing attribute in the failed checks table.

Returns

boolean : representing whether the instance was sucessfully verified

table : describing where the instance failed it’s verification


VerifyWithError

local verified, failedChecks = verifier:VerifyWithError(doorModel,true)
Description
Exactly the same as Verify() except this will throw an error containing the failedChecks table if the instance fails verification

SetPrintWarning

verifier:SetPrintWarning(true)
Description
Tells the verifier if it should print a warning containg failedChecks anytime an instance fails verification
Parameters

boolean : mandatory, Tells the verifier if it should print a warning containg failedChecks anytime an instance fails verification

Returns

nil


SetDebugMode

verifier:SetDebugMode(true)
Description
Used for debugging this module. You'll most likely never touch it
Parameters

boolean : mandatory, determines if debug mode should be turned on or off

Returns

nil


12 Likes

Awesome resource so far! Will definitely save time implementing smart guard clauses with this.

2 Likes

uh just don’t nest if statements?

Absolutely correct… well in a way. Using nested if statements has a bunch of problems including readability, difficulty debugging and poor flexibility. This acts a boilerplate that makes all those things extremely easy. Using this has saved me a massive amount of time verses using nested if statements which is why I decided to open source it. I promise you it’s power really shows during scale. If you don’t believe go ahead and look at the code.

TLDR: Think of this like driving a car over riding a bike. One is easier and less time consuming to use but both will get you to your destination.

i’m talking about the difference between

if act then
    if act2 then
        if act3 then
            something
        end
    end
end
    if not act then return end
    if not act2 then return end
    if not act3 then return end
    something

your system complicates something further where it could be done better by just using better un-nested guard clauses

that’s literally it

this is an unnecessary library
i see no use for it

1 Like

TLDR : Like I said in my previous reply the module is a boilerplate over your method which leads to better readability, error debugging and overall better code. It carries all the advantages and more of your approach with none of the potential drawbacks.

The Philosphy

There are multiple issues I have with you approach but I will admit I did not think of this solution. Firstly, College professors have taught me that you only want one way in and one way out of your function. While they’re are occasions where it’s okay to do the multiple return like this. it’s a general rule to stay away from it for debugging purposes. I remember the one time I moved away from this philosophy and it was hell debugging but your mileage may vary.

While this a valid solution I would avoid it like to plague due to the that philosophy .

The 2 Advantages Over Your Solution

But with that being the case my module has 2 advantages over this approach. One being readability especially when dealing with nested children. If your method where to deal with multiple nested children with their own nested attributes & etc it would get messy fast.

The biggest one is the ease of debugging as this module will explain everything wrong with your instance. In your case your if statements may stop at the first error making it tedious to debug. Instance Verifier carries all the advantages and more of your approach with none of the potential drawbacks.

So lets compare your method to Instance Verifier directly using the door example

---This takes for ever to write compared to my module and it's hard to visualize required model's tree 
---Also there's little debugging capability and requiers code that would make it easily become sphagetti code

function verifyDoor(door)
	
	if not typeof(door) == "Instance"  then return end;
	if not door:GetAttribute("Locked") then return end;
	if not door:FindFirstChild("TouchZone") then return end;
	if not door:FindFirstChild("PathFindingModifier") then return end;
	if not door.PathFindingModifier:FindFirstChild("PathfindingModifier") and  not typeof(door.PathfindingModifier) == "PathfindingModifer" then return end
	if not door:FindFirstChild("Doors") then return end;
	if not door.Doors:FindFirstChild("Right") and not door.Doors:FindFirstChild("Left") then return end;
	
	return true
	
end



--Compared to this. Visualzing the tree is easier, inherently has massive debug potential and code is readable

local doorVerifier = InstanceVerifier.new("Door") 
local pathfindingHelper
local doors

doorVerifier:AddAttribute("Locked","boolean")
doorVerifier:AddInstance("TouchZone")

--Ability to add in nested children requirements
pathfindingHelper = doorVerifier:AddInstance("PathfindingPart")
pathfindingHelper:AddInstance("PathfindingModifier")

doors = doorVerifier:AddInstance("Doors")
doors:AddInstance("Right")
doors:AddInstance("Left")


----Now when we want to verifiy a door model all we have to do is

local verified, foundErrorsTable = doorVerifier:Verify(doorModel)

the most efficient and most readable way.

if arg1 and arg2 and arg3 then
   prinr("yes")
end

Read the post above yours as I go through why that isn’t the best strategy :sweat_smile: