Safehou-se (Pt 5)

HomeBlog


The long awaited final Safehou-se update... (until I inevitably find another feature I want to implement)... nested loops are now possible on Safehou-se!

When I say this has been my most challenging project to date... I mean this has probably been my most challenging project to date. Each time I fixed a bug, it seemed five more would pop up. All in all, this project took some of the most hardcore debugging I've done in quite some time... and in the end, I believe Safehou-se is now compatible with nested loops of all kinds!

Fixing Data Structures

The first step was converting my data structure to be recursive. Easy enough. This was just changing Array<Map<string, string>> to Array<Map<string, Variable>>. The real trouble came next in implementing this...

Constructor

In order to construct the new model, I no longer could simply match regex on {{!Variable Name!}} and {{%Loop Name% %end%}} instances as this would cause the loop to match the first %end%}} in a nested loop. Instead, I now had to iterate through the template character by character, building out the model by tracking how many loops I was currently in. Luckily, this was a very similar problem to Valid Parentheses (shoutout to LeetCode grinding!), so I simply used a tracker array to keep track of which loop I was in.

Some issues I came across:

  • I could not perform str.substring(i + 2, j + 1) but I could perform str.substring(i + 3, j + 1). For some reason, the fix to this was changing str.substring(i + 2, j + 1) to "" + str.substring(i + 2, j + 1). I believe there's some weird stuff going on in TypeScript with escaping % characters.
  • I had to make sure that when adding a loop it wasn't a duplicate loop (used for tabs, as the ID is used in two separate looped areas). For those cases, I'd have to iterate through everything again in order to add to the map rather than creating a new map
  • Type checking was a big problem at first. Because Variables could be either a string or a Array of string, Variable maps, I had to make sure each time I accessed a Variable, I performed an if/else check.

toString()

I moved my original toString() into a helper function and then recursively ran it on itself. I still had some issues with nesting at first and creating the replacement String, but overall, it was a pretty simple fix.

Update, Add to Loop, Delete

I created a helper function for this in order to split the "roadmap" I received from the form into an array. For example, the string %Loop%0{{%Variable%}} would be split into ['%Loop%', '0', '{{%Variable%}}']. I could then follow the map and use if/else statements to figure out the Variable type. It's important to note I had to perform error checking at each step just to make sure that I wasn't reading a NaN or the incorrect string.

Creating the "Frontend"

Yes, yes, I know all of Next.JS is frontend. Yes, I will continue to refer to the GUI as the Frontend and all the model stuff I did as the backend. It's just easier to separate the two into those words.

When building out the form, I could no longer use a simple if/else for String vs Array. I had a few things I tried in the process:

  • toTemplate() function in the model class
    • setting it as innerHTML made the function that actually did something with it fail to load
  • Form and FormInput classes in the code-filler.tsx
    • every time a change was made in the form, the form would reload the user would have to click into the input box again

In the end, I had to perform a full refractor in order to get Form and FormInput to work. The reason the form kept reloading was because with useStates, each time the class would be built again. The credit goes to Carter for directing me to a solution; I had to move the classes out of the function and create a new Template variable each time an update was made. Additionally, a switch had to be made from using useStates to creating function callbacks in props.

Finally, I had to do the same thing for adding/deleting loops. While it seemed difficult at first, I picked it up after a good night's rest and finished it within an hour.

Overall, this refractor took me 4 days, a much longer time frame than my previous Safehou-se sprints.

Closing Words

If you're interesting in checking out how all of this came together, I have included some loop examples in the docs!