This module takes care of parsing and validating template input, allowing module developers to focus on core logic. It aims to achieve the same goal as Wikipedia's Module:Arguments but with a different approach:
- Validation is based on a schema that is also used to auto-generate documentation. This guarantees that the documentation is up to date with the validation code.
- No implicit behaviour. If you want to trim a parameter, you specify
trim = true
. If you want to treat empty strings as nil, you specifynilIfEmpty = true
. Module:Arguments does several things "automagically" by default and lacks clarity as a result. - No frame handling. It's up to the caller to consolidate
frame.args
andframe:getParent().args
if needed. This can be done with utilsTable.merge. Most modules typically need only one frame anyway.
Lua error in package.lua at line 80: module 'Module:UtilsArg/Validate' not found.
local p = {}
local h = {}
local i18n = require("Module:I18n")
local s = i18n.getString
local utilsPackage = require("Module:UtilsPackage")
local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")
local utilsValidate = require("Module:UtilsValidate")
p = utilsPackage.submodules({
"Module:UtilsArg/Validate"
}, {
"Validation"
})
local VALIDATORS = {"required", "enum", "deprecated"}
function p.parse(frameArgs, templateSpec)
local args = {}
local err = {
args = {},
categories = {},
}
for k, v in pairs(templateSpec.params) do
local arg = frameArgs[k]
if v.trim then
arg = utilsString.trim(arg)
end
if v.split then
arg = utilsString.split(arg)
end
if v.nilIfEmpty then
arg = utilsString.nilIfEmpty(arg)
end
args[v.name or k] = arg
end
for k, v in pairs(templateSpec.params) do
local argErrors, errorCategories = h.validate(args[v.name or k], v, v.name or k, args)
if #argErrors > 0 then
err.args[v.name or k] = utilsTable.concat(err.args[v.name or k] or {}, argErrors)
end
err.categories = utilsTable.concat(err.categories, errorCategories)
end
if #err.categories == 0 then
err = nil
end
return args, err
end
function h.validate(arg, param, paramName, args)
local errors = {}
local categories = {}
for i, validator in ipairs(VALIDATORS) do
local validatorData = param[validator]
if validator == "enum" and param.enum then
local enum = param.enum
if type(enum) == "function" then
local dependency = args[param.enumDependsOn]
enum = dependency and enum(dependency)
end
validatorData = enum and utilsTable.merge({}, enum, {
reference = param.enumReference
})
end
local err, cat
if validatorData ~= nil then
err, cat = h[validator](validatorData, arg, paramName)
end
if err then
table.insert(errors, {
msg = err,
category = cat
})
table.insert(categories, cat)
end
end
return errors, categories
end
function h.required(required, value, name)
if not required then
return
end
local err = utilsValidate.required(value, name)
if err then
return err, s("cat.invalidArgs")
end
end
function h.enum(enum, value, name)
if not enum then
return
end
local err = utilsValidate.enum(enum)(value, name)
if err then
return err, s("cat.invalidArgs")
end
end
function h.deprecated(deprecated, value, name)
if not deprecated then
return
end
local err = utilsValidate.deprecated(value, name)
if err then
return err, s("cat.deprecatedArgs")
end
end
i18n.loadStrings({
en = {
cat = {
invalidArgs = "Category:Pages with Invalid Arguments",
deprecatedArgs = "Category:Pages with Deprecated Arguments",
},
},
})
p.Schemas = {
parse = {
frameArgs = {
type = "any",
required = true,
desc = "Table of arguments obtained from {{Scribunto Manual|lib=Frame object|frame object}}.",
},
templateSpec = {
type = "any",
required = true,
desc = "[[Module:Documentation#Templates|Template documentation object]].",
}
}
}
p.Documentation = {
parse = {
params = {"frameArgs", "templateSpec"},
returns = {
"A table of arguments parsed from the template input.",
"A table of argument validation errors, or nil if there are none. The error messages are also logged using {{Scribunto Manual|lib=mw.addWarning}}.",
},
cases = {
outputOnly = true,
{
desc = "Positional arguments are assigned to their names.",
snippet = "PositionalAndNamedArgs",
expect = {
{
game = "OoT",
page = "Boss Key",
},
nil
}
},
{
desc = "Validation of required arguments.",
snippet = "RequiredArgs",
expect = {
{
page = "Boss Key"
},
{
categories = {"Category:Pages with Invalid Arguments"},
args = {
game = {
{
category = "Category:Pages with Invalid Arguments",
msg = "<code>game</code> is required but is <code>nil</code>.",
},
},
},
},
}
},
{
desc = "If <code>nilIfEmpty</code> and <code>required</code> are set, then the argument is invalid if it is an empty string.",
snippet = "NilIfEmptyWithRequiredArgs",
expect = {
{},
{
categories = {"Category:Pages with Invalid Arguments"},
args = {
game = {
{
category = "Category:Pages with Invalid Arguments",
msg = "<code>game</code> is required but is <code>nil</code>.",
},
},
},
},
},
},
{
desc = "Validation of deprecated arguments.",
snippet = "Deprecated",
expect = {
{ oldArg = "foo" },
{
categories = {"Category:Pages with Deprecated Arguments"},
args = {
oldArg = {
{
category = "Category:Pages with Deprecated Arguments",
msg = "<code>oldArg</code> is deprecated but has value <code>foo</code>.",
},
},
},
},
}
},
{
desc = "<code>enum</code> validation.",
snippet = "Enum",
expect = {
{
triforce2 = "Limpah",
game = "ALttZ",
triforce1 = "Kooloo",
},
{
categories = {
"Category:Pages with Invalid Arguments",
"Category:Pages with Invalid Arguments",
"Category:Pages with Invalid Arguments",
},
args = {
triforce2 = {
{
category = "Category:Pages with Invalid Arguments",
msg = "<code>triforce2</code> has unexpected value <code>Limpah</code>. For a list of accepted values, refer to [[Triforce]].",
},
},
game = {
{
category = "Category:Pages with Invalid Arguments",
msg = "<code>game</code> has unexpected value <code>ALttZ</code>. For a list of accepted values, refer to [[Data:Franchise]].",
},
},
triforce1 = {
{
category = "Category:Pages with Invalid Arguments",
msg = '<code>triforce1</code> has unexpected value <code>Kooloo</code>. The accepted values are: <code>{"Courage", "Power", "Wisdom"}</code>',
},
},
},
},
},
},
{
desc = "<code>split</code> is used to parse comma-separated strings as arrays. Each array item can be validated against an <code>enum</code>.",
snippet = "SplitEnum",
expect = {
{
games = {"OoT", "fakeGame", "BotW"},
},
{
categories = {"Category:Pages with Invalid Arguments"},
args = {
games = {
{
category = "Category:Pages with Invalid Arguments",
msg = "<code>games[2]</code> has unexpected value <code>fakeGame</code>. For a list of accepted values, refer to [[Data:Franchise]].",
}
}
}
}
}
},
{
desc = "<code>enum</code> can be written as a function, when the list of acceptable values depends on the value of another argument.",
snippet = "EnumDependsOn",
expect = {
{
term = "Dinolfos",
game = "TP"
},
{
categories = {"Category:Pages with Invalid Arguments"},
args = {
term = {
{
category = "Category:Pages with Invalid Arguments",
msg = '<code>term</code> has unexpected value <code>Dinolfos</code>. The accepted values are: <code>{"Dynalfos"}</code>',
},
},
},
},
},
},
{
desc = "If <code>enumDependsOn</code> refers to a required parameter, then <code>enum</code> is not evaluated when that parameter is nil.",
snippet = "EnumDependsOnNil",
expect = {
{ term = "Dinolfos" },
{
categories = {"Category:Pages with Invalid Arguments"},
args = {
game = {
{
category = "Category:Pages with Invalid Arguments",
msg = "<code>game</code> is required but is <code>nil</code>.",
}
},
},
},
},
},
{
desc = "Altogether now",
snippet = "TermStorePass",
expect = {
{
term = "Dinolfos",
games = {"OoT", "MM"},
},
nil
}
},
{
snippet = "TermStoreFail",
expect = {
{
plural = "true",
games = {"YY", "ZZ"},
},
{
categories = {
"Category:Pages with Invalid Arguments",
"Category:Pages with Deprecated Arguments",
"Category:Pages with Invalid Arguments",
},
args = {
term = {
{
category = "Category:Pages with Invalid Arguments",
msg = "<code>term</code> is required but is <code>nil</code>.",
},
},
games = {
{
category = "Category:Pages with Invalid Arguments",
msg = "<code>games[1]</code> has unexpected value <code>YY</code>. For a list of accepted values, refer to [[Data:Franchise]]."
},
-- TODO
-- {
-- category = "Category:Pages with Invalid Arguments",
-- msg = "<code>games[2]</code> has unexpected value <code>ZZ</code>. For a list of accepted values, refer to [[Data:Franchise]]."
-- },
},
plural = {
{
category = "Category:Pages with Deprecated Arguments",
msg = "<code>plural</code> is deprecated but has value <code>true</code>.",
},
},
},
},
},
},
},
},
}
return p