It is currently July 20th, 2019, 6:07 am

Dont assert() with Inline Lua

Discuss the use of Lua in Script measures.
User avatar
jsmorley
Developer
Posts: 19295
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Dont assert() with Inline Lua

jsmorley » April 7th, 2019, 4:34 pm

One of the useful functions built into Lua is the assert() function. It allows you to test for any condition, and if the condition is "false", then it immediately sets an error condition, optionally writes an error string to the Rainmeter log, and exits the function.

This can be very handy when just using Lua from the Update() function, so it will not attempt to do anything after the error is raised by assert() and will just gracefully exit.

However, this can cause Lua to "flood the log" when used with Inline Lua, which is going to be hitting the Lua every time the inline call is made. When you use Inline Lua in a String meter, this is going to be once on every skin update, or by default once a second.

The assert() function will immediately bail out of the entire script after writing the log error, with no opportunity to react in any other way in the Lua. It's going to do that each and every time the script function is called.

The error string parameter on assert() is optional, so you don't have to have it write to the log, but that's going to be all or nothing. It will either never write to the log, or always write to the log. Neither is probably optimal.

A better alternative is something like this:

Skin:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[Variables]

[Lua]
Measure=Script
ScriptFile=#CURRENTPATH#Test.lua
Disabled=1

[MeterTest]
Meter=String
FontSize=12
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
;Text=[&Lua:TestError('A string value')
Text=[&Lua:TestError(1234)]
DynamicVariables=1
Test.lua:

Code: Select all

function TestError(arg)
	
	-- This would replace:
	-- assert(type(arg) == 'string', 'TestError: Argument is not a string value')
	
	if type(arg) ~= 'string' then
		if not floodFlag then
			print('TestError: Argument is not a string value')
			floodFlag = true
		end
		return
	else
		floodFlag = false	
	end

	return 'Success! The value of arg is '..arg
	
end
So the key to this is the floodFlag variable. This can be called anything you like. When the script is initialized, no value for floodFlag is set, and it will be equal to "nil", which is equivalent to "false", or logically "not". The point of it is that ONCE an error is detected, it is set to the boolean "true", so that on all subsequent calls to the function, no error is created in the Rainmeter log. If and when the error is not detected, floodFlag is set to the boolean "false", so it is once again logically "not".

So this results in a behavior very similar to how IfCondition in Rainmeter works, The IfTrueAction only takes place if the test CHANGES from "false" to "true", and does not if the test REMAINS "true" on subsequent updates.

This will work with any condition you want to test for "failure". I used the type() function as an example, as that might be a pretty common test you want to make. You could test for missing values with 'nil', or values out of some range you desire, or any other test for failure.