Next up: validators!
I think this is a fun topic. I'm not going to go over the code to make the validator class (you can download the attached file and skim over the source yourself, if you feel so inclined), but I will provide an example and an explanation of how it works. Remember that if you place validator.lua into micromacro/lib, you want to require 'validator', but if you place it in the script's directory, you want to call include('validator.lua').
Additionally, I found and fixed a minor bug with table.find() but haven't uploaded a fixed binary on that yet (and you wouldn't bother to download it just for this, anyways), so 'in' or 'type' might behave strangely at the moment.
Moving on, what's a validator? It is a class that validates user inputs. If they don't match the expected value, an error should be returned. A good example of this is in registration forms on websites: you want to validate that the username is between x and y characters, doesn't already exist, their email address is unique, their password is "good", and so on.
What we want to do is get a table of key/value pairs for the inputs; the key being the field name (such as "username") and the value being the actual user input (ie. "JohnDoe2015"). We then also provide a list of rules that should be applied to each field. Lets skip all of the technical explanation and just show how these look:
Code: Select all
local rules = {
something = 'required',
something2 = 'required',
someNumber = 'required|min:10',
someNumber2 = 'required|min:10',
someString = 'min:1|max:10',
someString2 = 'min:1|max:10',
aTable = 'type:number',
aValue = 'type:number,string',
enum = 'in:red,blue,green',
}
local inputs = {
something = 'This has a value.',
something2 = nil,
someNumber = 15,
someNumber2 = 5,
someString = 'works',
someString2 = 'This should not pass validation',
aTable = {1, 2, 3},
aValue = 12,
enum = 'purple',
};
Pretty simple. You will see that the rules are provided as a string (which will be parsed by the validator); the pipe (|) seperates the rules, and colon (:) lets us pass additional info into the rule (seperated by commas). 'required' means that the field
must contain a value (any value!) and accepts nothing, while 'min' requests that the value is either (if string) not less than x characters, or (if number) not less than x, and 'max' is the same as 'min' except makes sure we don't exceed some value. 'type' is used to validate that a given field contains the Lua type expected, and 'in' validates that the field contains one of the given values.
Side note: all validator functions (except requires) should return
true (meaning everything is OK) if there is no value given. Why? Because if it is required, you should be passing the required rule; everything else should still be accepted if not required.
Each rule is simply a function inside the Validator class (you can create your own sub-class with expanded rules!) that accepts the field name, the field value, and (optionally) extra parameters, and will return either true (passes validation) or false plus the error message (if it does not pass validation).
Now all we need to do to validate our inputs is to create an instance of Validator (or your sub-class, if you prefer) and call passes() on it. If it passes, then great, we can move onto the next step. If it fails, we should probably display the errors encountered:
Code: Select all
local validator = Validator(inputs, rules);
if( validator:passes() ) then
print("Passed");
else
print("Did not pass, errors:");
for i,v in pairs(validator:getErrors()) do
printf("\tErr:%s\n", v);
end
end
And with the given example, we should receive:
Did not pass, errors:
Err:The someNumber2 field must be at least 10.
Err:The aValue field must be one of these: number, string.
Err:The aString field must be one of these: number.
Err:The someString2 field cannot be longer than 10 characters.
Err:The something2 field is required.
Err:The enum field must be one of these: red, blue, green.
*Note: Since we didn't order the tables, they can come out in any order when we use pairs(). That's why these aren't in the same order as in the code.
So, where does this become useful? One place I thought it would be absolutely great is with skill rotations. Remember how much of a pain it was to setup skills with the RoM bot? Well, why not just use a validator? Validator functions could be used to ensure that the target is in range, the skill is off cooldown, our HP is the correct percentage, or anything else you can imagine.
By the way, if you guys are still reading this, please let me know what you think of my ramblings. Does this help at all? Interesting or thought-provoking? Too wordy or overly-simplified? Please be honest.