Coding Session #18: Metagaming code, Powerful Stuff
Brute force and ignorance baby, that's how most of us get started in the coding game. You go from point A to point B and so on following all the rules along the way. As your skills progress your code becomes less iterative, better organized and reusable, a bit faster, more maintainable, and generally more elegant. Good for us.
[image unavailable: LM_original]
But what does it take to reduce your code by 10x's? Make it significantly faster? Changing the rules of course!
[image unavailable: LM_rewritten]
A simple example: you need to show and hide a bunch of elements. The brute force approach is to show/hide each element individually. If you change the rules and group the elements, you can hide/show the group.
Changing the rules typically means defining or redefining how the target of your code is described. If you describe something well, you can reduce your code significantly. It can also impact the speed of your code. For example, the same query run on a non-indexed and an indexed table. The index was added to change the rules in such a way that the same query runs significantly faster.
In this coding session, we take some 3,100+ lines of code with 200+ms performance and reduce to ~300 lines of code and ~20ms performance. All in about five hours. If you look through the original code, there has to be days of coding effort in there.
We did this by defining the rules in our favor, not just accepting what was there. Specific to Servoy, there are a number of ways to accomplish this.
Naming conventions
Any object (form, label, field, etc) you name in Servoy can be accessed via that name. All of the objects properties can be modified at runtime this way.
Add in some naming conventions to the names and it's easy to modify groups of objects. ["sectionA_lbl_1","sectionA_lbl_2","sectionA_lbl_3",…] can be used to scope code to a particular set of objects.
// categories of objects example (starts with "tile_")
var allElems = elements.allnames
allElems.sort()
for (var i = 0; i < allElems.length; i++) {
var elem = allElems[i]
if (elem.indexOf('tile_') == 0) {
// do something
}
}
An order component in the name ["lbl_1","lbl_2","lbl_3",…] can be used to cycle through the objects easily.
// ordered objects example (ends with a number "category_label_n")
var allElems = elements.allnames
allElems.sort()
var elementArray = []
for (var i = 0; i < allElems.length; i++) {
var elem = allElems[i]
if (elem.indexOf('tile_') == 0) {
// put element into array slot n
elementArray[elementName.split("_")[2]] = elementName
}
}
// process object array
for (var j = 0; j < elementArray.length; i++) {
// do something with elements[elementArray[j]]
}
Grouping objects
You can add another level of meta data organization by grouping objects together (select some objects, right-click and group them together) and giving the group a name. Many of the runtime properties available to various discrete objects are available to groups as well.
Instead of having to figure out a set of objects via their individual names, you can just use the group name and with one line of code apply a runtime property change (ex: hiding or showing a bunch of objects).
Design-time Properties
The list and types of information you may want to store with an object can get extensive (or variable) as you get comfortable with this approach. Eventually, using the name as your ad hoc storage vehicle can get a bit unwieldily. To the rescue: Servoy's designTimeProperties.
designTimeProperties are basically key/value pairs that can be associated with most any Servoy object (via the properties view).
[image unavailable: Design-time-properties]
You can assign as many key/value pairs as you want and the code for getting and setting the values is very straight-forward.
// design time property example
function control(event) {
var elementName = event.getElementName()
var direction = forms[controller.getName()].elements[elementName].getDesignTimeProperty('direction')
var tileName = "tile_" + forms[controller.getName()].elements[elementName].getDesignTimeProperty('tile')
switch (direction) {
case "up":
// only run if currently expanded
if (_state[tileName] == "expanded") {
up(tileName)
}
break
case "down":
// only run if currently collapsed
if (_state[tileName] == "collapsed") {
down(tileName)
}
break
}
}
Solution Model
Servoy's solution model functionality construct and deconstruct entire forms and all of their elements (and even assign events) at runtime. It's basically the nuclear bomb approach coding object behavior from meta data.
// solution model
var _frm = solutionModel.getForm(formName);
var _comp = _frm.getComponents();
for (var i in _comp) {
_lname = _comp[i].name;
_lnameShort = utils.stringLeft(_lname,5).toLowerCase();
if ( _lnameShort == "vtile" || _lnameShort == "htile" || _lnameShort == "xtile") {
_objName = _comp[i].name;
// keep going for 100's of lines of code....
Solution model can be used sparingly to grab object meta data or extensively to build up entire interfaces from meta data. While giving you the most extreme flexibility, it is also the most code intensive approach by far.
Putting it all together
With the right meta data structures, you can simplify your coding tasks—often dramatically. Servoy gives you all the tools—from simple to extreme flexibility—it is up to you to combine everything together in the optimum arrangement to achieve the most productive results.