יחידה:ParamValidator: הבדלים בין גרסאות בדף

תוכן שנמחק תוכן שנוסף
ניסוי
החלפת המנוע. הוספת פונקציה calculateViolations( frame, subpages ) שאפשר לקרוא לה ממודול אחר.
שורה 7:
 
 
it exports two functions: validateParams(), called from page using {{#invoke: }} and returns a string,
 
and calculateViolations(), called from another module, and returns a table with a list of violations, keyed on violation type as described below.
it exports a single function: validateParams, which expects a frame object
both use the metadata in the templatedata object to validate the parameters. the list of detected violations appear below:
 
it retrieves the template's templatedata, and uses it to validate the parameters passed to the template.
if any violations found, it returns am appropriate string. this string will typically include some maintenance category, and/or error message.
the operation of the module is controlled by a single parameter passed to it. this string should be valid JSON describing the actions.
the fields of this structure has the following keys, with string or null value:
 
* "no-templatedata": no valid tempaltedata was found in tempalte page, or documentation subpage
שורה 23 ⟵ 19:
* "empty-deprecated": parameters with empty value, marked as "deprecated" in tempaltedata
* "empty-required": missing or empty parameter marked as "required" in tempaltedata
 
calculateViolations() returns a table, keyed on violation type, whose values are tables of parameter name : parameter value
* "any": at least one of the above properies has non-empty value, and the corresponding error actually exists.
* "multiple": more than one of the above properies (not including "any") has non-empty value, and the corresponding error actually exists.
 
it retrieves the template's templatedata, and uses it to validate the parameters passed to the template.
if any violations found, it returns am appropriate string. this string will typically include some maintenance category, and/or error message.
the operation of the module is controlled by a single parameter passed to it. this string should be valid JSON describing the actions.
the fields of this structure has the following keys, with string or null value:
if any of these error-conditions exists, and "options" has non-empty value for the corresponding key, will be appended to the output,
after replacing the follwing tokens:
שורה 54 ⟵ 62:
]=]
 
function extract_optionsempty( frames )
return not s or type( s ) == 'string' and mw.text.trim( s ) == ''
local options
end
local args = frame.args
 
function extract_options( frame )
local options = not empty( args.options ) and mw.text.jsonDecode( args.options ) or {}
local ok, options = pcall( mw.text.jsonDecode, frame.args.options )
 
options = ok and options or {}
local n = 1
local n, more = 0
while not empty( args['options' .. n] ) do
repeat
local more = mw.text.jsonDecode( args['options' .. n] )
for k, v in pairs(more) do options[k] = v end
n = n + 1
ok, more = pcall( mw.text.jsonDecode, frame.args['options' .. n] )
end
if ok then
 
for k, v in pairs( more ) do options[k] = v end
end
until not ok
return options
end
 
function build_namelist( optionstemplate_name, template_namesp )
local res = { template_name }
local sp = options and options['doc-subpage']
if sp then
if type( sp ) == 'string' then sp = { sp } end
שורה 80 ⟵ 89:
end
 
-- this is the function to be called by other modules. it expects the frame, and then an optional list of subpages, e.g. { "Documentation" }.
function empty( s )
-- if second parameter is nil, only tempalte page will be searched for templatedata.
return not s or type( s ) == 'string' and mw.text.trim( s ) == ''
function calculateViolations( frame, subpages )
end
 
function validateParams( frame )
local options = extract_options( frame )
 
-- this can be made more sophisticated later, e.g. for "wiki page" and such
function compatible( val, typ )
if typ == 'number' and not mw.language.getCurrentLanguage():parseNumber( val ) then return false end
return true
end
local t_frame = frame:getParent()
local t_args, template_name = t_frame.args, t_frame:getTitle()
local td_source = build_namelist( optionstemplate_name, template_namesubpages )
local templatedata = require( 'Module:ReadTd' ).ReadTemplateData( td_source )
local td_ptd_params = templatedata and templatedata.params -- now we have options, template args, and td params metadata. we can work now.
local report = ''
if not td_params then return { ['no-templatedata'] = { [''] = '' } } end
-- from this point on, we know templatedata is valid.
 
local res = {} -- before returning to caller, we'll prune empty tables
 
-- handle undeclared and deprecated
for p_name, value in pairs( t_args ) do
local tp_param, noval, numeric, table_name = td_params[p_name], empty( value ), tonumber( p_name )
if not tp_param then -- not in TD: this is called undeclared
-- calculate the relevant table for this undeclared parameter, based on parameter and value types
table_name =
noval and numeric and 'empty-undeclared-numeric' or
noval and not numeric and 'empty-undeclared' or
not noval and numeric and 'undeclared-numeric' or
'undeclared' -- tzvototi nishar.
else -- in td: test for depracation and mistype. if deprecated, no further tests
table_name = tp_param.deprecated and not noval and 'deprecated' or
tp_param.deprecated and noval and 'empty-deprecated' or
not compatible( tp_param.type, value ) and 'incompatible'
end
-- report it.
if table_name then
res[table_name] = res[table_name] or {}
res[table_name][p_name] = value
end
end
 
-- test for empty/missing paraeters declared "required"
for p_name, param in pairs( td_params ) do
if param.required and empty( t_args[p_name] ) then
res['empty-required'] = res['empty-required'] or {}
res['empty-required'][p_name] = ''
end
end
return res
end
 
-- this is the "user" version, called with {{#invoke:}} returns a string, as defined by the options parameter
function validateParams( frame )
local options, report, template_name = extract_options( frame ), '', frame:getParent():getTitle()
 
local wrap_report = function()
if empty( report ) then return '' end
local naked = mw.title.new( template_name )['text']
report = ( options['wrapper-prefix'] or "<div class = 'paramvalidator-wrapper'>" )
שורה 108 ⟵ 162:
 
local ignore = function( p_name )
for _, ppattern in ipairs( options.['ignore'] or {} ) do
if tostringstring.match( pp_name, )'^' ==.. tostring(pattern p_name.. '$' ) then return true end
end
return false
שורה 115 ⟵ 169:
 
local replace_macros = function( s, param_names )
if s and ( type( param_names ) == 'table' ) then
local paramstr, paramvaluestr, paramvaluetab = '', '', {}
local k_ar, kv_ar = {}, {}
if for typek, v in pairs( param_names ) == 'table' thendo
paramstr = table.concatinsert( param_names, 'k_ar, 'k )
table.insert( kv_ar, k .. ': ' .. v)
for _, param_name in ipairs( param_names ) do
if not empty( t_args[param_name] ) then
table.insert( paramvaluetab, param_name .. ': ' .. mw.text.trim( t_args[param_name] ) )
end
end
s = mw.ustring.gsub( s, 'paramname', table.concat( k_ar, ', ' ) )
elseif type( param_names ) == 'string' then
s = mw.ustring.gsub( s, 'paramandvalue', table.concat( kv_ar, ', ' ) )
paramstr = param_names
end
if param_names then
s = mw.ustring.gsub( s, 'paramname', paramstr )
end
if #paramvaluetab then
s = mw.ustring.gsub( s, 'paramandvalue', table.concat( paramvaluetab, ', ' ) )
end
return s
שורה 138 ⟵ 182:
 
local report_params = function( key, param_names )
iflocal res = replace_macros( options[key], param_names then)
report = report .. replace_macros ( options[key],res or param_names'' )
return res
end
end
-- assert options is not empty.
assert( next( options ), 'expecting valid "options" parameter')
-- get the errors.
local violations = calculateViolations( frame, options['doc-subpage'] )
 
-- special request of bora: use skip_empty_numeric
local report_table = function( name, tbl )
if violations['empty-undeclared-numeric'] then
if #tbl ~= 0 then report_params( name, tbl ) end
for i = 1, tonumber( options['skip-empty-numeric'] ) or 0 do
end
violations['empty-undeclared-numeric'][i] = nil
 
if not templatedata then
report_params( 'no-templatedata' )
report_params( 'any' )
return wrap_report()
end
 
-- from this point on, we know templatedata is valid.
 
-- undeclared and deprecated contains only non-empty parameters.
-- empty_undeclared contains undeclared parameters with no value
local undeclared, empty_undeclared, empty_required, deprecated, empty_deprecated = {}, {}, {}, {}, {}
local undeclared_numeric, empty_undeclared_numeric = {}, {}
 
local lists_and_names = {
[undeclared] = 'undeclared',
[empty_undeclared] = 'empty-undeclared',
[undeclared_numeric] = 'undeclared-numeric',
[empty_undeclared_numeric] = 'empty-undeclared-numeric',
[deprecated] = 'deprecated',
[empty_deprecated] = 'empty-deprecated',
[empty_required] = 'empty-required'
}
 
-- groups to make the loop simpler.
local undec = { [true] = { [true] = empty_undeclared_numeric, [false] = undeclared_numeric }, [false] = { [true] = empty_undeclared, [false] = undeclared } }
local dep = { [true] = empty_deprecated, [false] = deprecated }
local skip_empty_numeric = tonumber( options['skip-empty-numeric'] ) or 0
 
-- DO THE WORK: if paramete does not appear in TD, add it to undeclared or empty-undeclared.
-- if it does appear, check if it's deprecated, and if so, add it to deprecated or empty-deprecated
for p_name, value in pairs( t_args ) do
if not ignore( p_name ) then
local noval = empty( value )
if not td_p[p_name] then -- fill undeclared or undeclared_empty, depends on noval
local numeric = tonumber( p_name ) ~= nil
local skip = numeric and noval and tonumber( p_name ) <= skip_empty_numeric -- special request of bora
if not skip then
table.insert( undec[numeric][noval], p_name )
end
elseif td_p[p_name].deprecated then
table.insert( dep[noval], p_name )
end
end
end
 
-- handle ignore list, and prune empty violations - in that order!
-- DO MORE WORK: collect "required" parameters that do not exist, or have empty value.
local offenders = 0
for p_name, param in pairs( td_p ) do
for name, tab in pairs( violations ) do
if not ignore( p_name ) and param.required and empty( t_args[p_name] ) then
-- remove ignored parameters from all violations
table.insert( empty_required, p_name )
for pname in pairs( tab ) do if ignore( pname ) then tab[pname] = nil end end
end
-- prune empty violations
if next( tab ) == nil then violations[name] = nil end
-- WORK IS DONE. report the errors.
-- if report then count it.
if violations[name] and report_params( name, tab ) then offenders = offenders + 1 end
end
 
-- WORK IS DONE. report the errors.
local offenders = 0
for list, name in pairs( lists_and_names ) do
if #list ~= 0 and not empty( options[name] ) then
offenders = offenders + 1
report_table( name, list )
end
end
if offenders ~= 0 then report_params( 'any' ) end
if offenders > 1 then report_params( 'multiple' ) end
if offenders ~= 0 then report_params( 'any' ) end -- could have tested for empty( report ), but since we count them anyway...
 
return wrap_report()
שורה 214 ⟵ 219:
 
return {
['validateparams'] = validateParams,
['calculateViolations'] = calculateViolations
}