Module:UtilsError

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search

A utility module for error-handling, mainly with respect to argument validation.

Unlike other utilities, this page should not be imported at the top of a module. Instead, import it only at the precise location where you use it. This is so that the module is only linked on pages that actually have errors (see Guidelines:Modules#Performance Optimization).

TFH Red Link desperate.png
Don't
TFH Green Link ok.png
Do
local p = {}

local utilsError = require("Module:UtilsError")

...

  if err then
    utilsError.warn("msg")
  end
local p = {}

...

  if err then
    local utilsError = require("Module:UtilsError")
    utilsError.warn("msg")
  end


This module exports the following functions.

error

error(error, [warning])

Parameters

  • error
    Error message to display.
  • [warning]

    If true, the error message will also be logged as a warning.

    If a string, that message will be logged as the warning - this can be used to provide additional error information to editors. The message will also appear when hovering over the error.

Returns

  • An error message in wikitext, linking to the page which invoked the function. This page, which is usually a template, should have documentation on the error.

Examples

#InputResult
1
error("I AM ERROR")
Error: I AM ERROR
2
error("I AM ERROR 2", true)
Error: I AM ERROR 2
3
error(
  "I AM ERROR",
  'warning message with [[links]] and <code>markup</code> and "quotes"'
)
Error: I AM ERROR

warn

warn(msg, [options])

Parameters

  • msg
    Warning message to log above the edit preview area.
  • [options]
    [traceBack=true]
    If true, prints a stack trace of all module frames. If the invoking frame is a template, the name of the template is appended to the stack trace.
    [omitFrames=0]
    Number of additional frames to omit from the top of the stack trace.
    [includeInstance=true]
    If true, the warning will specify which #invoke call caused the warning. Useful when a template is used multiple times per page. Only accurate for templates with only 1 #invoke function.

Returns

  • Logs the above message, possibly with a stack trace. Preview this page for an example of output. The logged message is returned.

Examples

#InputOutputResult
4
warn("I AM ERROR")
"I AM ERROR"
I AM ERROR
5
warn("I AM ERROR", { omitFrames = 2 })
"I AM ERROR"
I AM ERROR
6
warn("I AM ERROR", { traceBack = false })
"I AM ERROR"
I AM ERROR

local p = {}
local h = {}

local VariablesLua = mw.ext.VariablesLua

function p.error(msg, warnMsg)
	local errorMessage = msg and ("Error: "..msg) or "Error"
	local page = mw.getCurrentFrame():getParent():getTitle()
	local hoverText = page
	if warnMsg then
		if type(warnMsg) == "boolean" then
			warnMsg = msg or "Error"
		end
		warnMsg = p.warn(warnMsg)
		if warnMsg ~= msg and msg ~= nil then
			hoverText = warnMsg
		end
	end
	hoverText = string.gsub(hoverText, '"', "&quot;")
	hoverText = string.gsub(hoverText, "]", "&#93;")
	local err = string.format('[[%s|<strong class="error" title="<nowiki>%s</nowiki>">%s</strong>]]', page, hoverText, errorMessage)
	return mw.getCurrentFrame():preprocess(err)
end

function p.warn(msg, options)
	local options = options or {}
	local omitFrames = options.omitFrames or 0
	local traceBack = options.traceBack
	local includeInstance = options.includeInstance
	
	omitFrames = omitFrames or 0
	local parentFrame = mw.getCurrentFrame():getParent()
	local templateTitle = parentFrame and parentFrame:getTitle()
	if templateTitle and mw.title.new(templateTitle).nsText == "Template" then
		local instanceCount = h.getInstanceCount(templateTitle)
		if instanceCount and includeInstance ~= false then
			msg = string.format("Misuse of [[%s]] at instance %s: %s", templateTitle, instanceCount, msg)
		else
			msg = string.format("Misuse of [[%s]]: %s", templateTitle, msg)
		end
		traceBack = false
	end
	if traceBack ~= false then
		local trace = ""
		local callStack = debug.traceback("", 2)
		for line in string.gmatch(callStack, "\tModule:[^:]*:[^\n]*\n") do
			if omitFrames ~= 0 then
				omitFrames = omitFrames - 1
			else
				line = string.gsub(line, "\tModule:([^:]*):(.*)", '::<span class="hidden-link">[[Module:%1]]:%2</span>') 
				trace = trace .. line .. "\n"
			end
		end
		mw.addWarning(msg.."\n\n"..trace)
	else
		mw.addWarning(msg)
		-- We also log the message as mw.addWarning currently doesn't work if the editor has enabled the preference "Show previews without reloading the page".
		-- Unfortunately neither mw.addWarning nor mw.log work with the visual editor (even in source mode).
		-- An alternative would be to throw an actual script error as most wikis do.
		-- However, full errors degrade the reader experience. 
		-- Many of our validation errors are not so critical that readers should know that they even exist.
		mw.log(msg)
	end
	return msg
end

-- [[Module:UtilsArg]] defines an "instance count" page variable which tracks the number of times a template has been used on the page until now
function h.getInstanceCount(templateTitle)
	local var = VariablesLua.var("instanceCounter"..templateTitle)
	if var ~= "" then
		return var
	else
		return nil
	end
end

function p.Schemas()
	return {
		error = {
			error = {
				type = "string",
				required = true,
				desc = "Error message to display.",
			},
			warning = {
				desc = "<p>If <code>true</code>, the error message will also be logged as a warning.</p><p>If a string, that message will be logged as the warning - this can be used to provide additional error information to editors. The message will also appear when hovering over the error.</p>",
				oneOf = {
					{
						type = "string",
					},
					{
						type = "boolean",
					},
				},
			},
		},
		warn = {
			msg = {
				type  = "string",
				required = true,
				desc = "Warning message to log above the edit preview area.",
			},
			options = {
				type = "record",
				properties = {
					{
						name = "traceBack",
						type = "boolean",
						default = true,
						desc = "If true, prints a stack trace of all module frames. If the invoking frame is a template, the name of the template is appended to the stack trace.",
					},
					{
						name = "omitFrames",
						type = "number",
						default = 0,
						desc = "Number of additional frames to omit from the top of the stack trace."
					},
					{
						name = "includeInstance",
						type = "boolean",
						default = true,
						desc = "If true, the warning will specify which #invoke call caused the warning. Useful when a template is used multiple times per page. Only accurate for templates with only 1 #invoke function.",
					},
				}
			}
		}
	}
end

function p.Documentation()
	return {
		error = {
			params = {"error", "warning"},
			returns = "An error message in wikitext, linking to the page which invoked the function. This page, which is usually a template, should have documentation on the error.",
			cases = {
				resultOnly = true,
				{
					args = {"I AM ERROR"},
				},
				{
					args = {"I AM ERROR 2", true},
				},
				{
					args = {"I AM ERROR", 'warning message with [[links]] and <code>markup</code> and "quotes"'},
				},
			}
		},
		warn = {
			params = {"msg", "options"},
			returns = "Logs the above message, possibly with a stack trace. Preview this page for an example of output. The logged message is returned.",
			cases = {
				{
					args = {"I AM ERROR"},
				},
				{
					args = {"I AM ERROR", { omitFrames = 2 }},
				},
				{
					args = {"I AM ERROR", { traceBack = false }},
				}
			},
		},
	}
end

return p