https://en.seminaverbi.bibleget.io/w/index.php?title=Module:Template_test_case&feed=atom&action=history
Module:Template test case - Revision history
2024-03-29T12:54:10Z
Revision history for this page on the wiki
MediaWiki 1.41.0
https://en.seminaverbi.bibleget.io/w/index.php?title=Module:Template_test_case&diff=22159&oldid=prev
Johnrdorazio: 1 revision imported
2020-09-02T20:33:17Z
<p>1 revision imported</p>
<table style="background-color: #fff; color: #202122;" data-mw="interface">
<col class="diff-marker" />
<col class="diff-content" />
<col class="diff-marker" />
<col class="diff-content" />
<tr class="diff-title" lang="en">
<td colspan="2" style="background-color: #fff; color: #202122; text-align: center;">← Older revision</td>
<td colspan="2" style="background-color: #fff; color: #202122; text-align: center;">Revision as of 20:33, September 2, 2020</td>
</tr><tr><td colspan="4" class="diff-notice" lang="en"><div class="mw-diff-empty">(No difference)</div>
</td></tr>
<!-- diff cache key seminaverbi_en:diff:1.41:old-22158:rev-22159 -->
</table>
Johnrdorazio
https://en.seminaverbi.bibleget.io/w/index.php?title=Module:Template_test_case&diff=22158&oldid=prev
en>SUM1: Further tweak
2020-03-09T03:27:02Z
<p>Further tweak</p>
<p><b>New page</b></p><div>--[[<br />
A module for generating test case templates.<br />
<br />
This module incorporates code from the English Wikipedia's "Testcase table"<br />
module,[1] written by Frietjes [2] with contributions by Mr. Stradivarius [3]<br />
and Jackmcbarn,[4] and the English Wikipedia's "Testcase rows" module,[5]<br />
written by Mr. Stradivarius.<br />
<br />
The "Testcase table" and "Testcase rows" modules are released under the<br />
CC BY-SA 3.0 License [6] and the GFDL.[7]<br />
<br />
License: CC BY-SA 3.0 and the GFDL<br />
Author: Mr. Stradivarius<br />
<br />
[1] https://en.wikipedia.org/wiki/Module:Testcase_table<br />
[2] https://en.wikipedia.org/wiki/User:Frietjes<br />
[3] https://en.wikipedia.org/wiki/User:Mr._Stradivarius<br />
[4] https://en.wikipedia.org/wiki/User:Jackmcbarn<br />
[5] https://en.wikipedia.org/wiki/Module:Testcase_rows<br />
[6] https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License<br />
[7] https://en.wikipedia.org/wiki/Wikipedia:Text_of_the_GNU_Free_Documentation_License<br />
]]<br />
<br />
-- Load required modules<br />
local yesno = require('Module:Yesno')<br />
<br />
-- Set constants<br />
local DATA_MODULE = 'Module:Template test case/data'<br />
<br />
-------------------------------------------------------------------------------<br />
-- Shared methods<br />
-------------------------------------------------------------------------------<br />
<br />
local function message(self, key, ...)<br />
-- This method is added to classes that need to deal with messages from the<br />
-- config module.<br />
local msg = self.cfg.msg[key]<br />
if select(1, ...) then<br />
return mw.message.newRawMessage(msg, ...):plain()<br />
else<br />
return msg<br />
end<br />
end<br />
<br />
-------------------------------------------------------------------------------<br />
-- Template class<br />
-------------------------------------------------------------------------------<br />
<br />
local Template = {}<br />
<br />
Template.memoizedMethods = {<br />
-- Names of methods to be memoized in each object. This table should only<br />
-- hold methods with no parameters.<br />
getFullPage = true,<br />
getName = true,<br />
makeHeader = true,<br />
getOutput = true<br />
}<br />
<br />
function Template.new(invocationObj, options)<br />
local obj = {}<br />
<br />
-- Set input<br />
for k, v in pairs(options or {}) do<br />
if not Template[k] then<br />
obj[k] = v<br />
end<br />
end<br />
obj._invocation = invocationObj<br />
<br />
-- Validate input<br />
if not obj.template and not obj.title then<br />
error('no template or title specified', 2)<br />
end<br />
<br />
-- Memoize expensive method calls<br />
local memoFuncs = {}<br />
return setmetatable(obj, {<br />
__index = function (t, key)<br />
if Template.memoizedMethods[key] then<br />
local func = memoFuncs[key]<br />
if not func then<br />
local val = Template[key](t)<br />
func = function () return val end<br />
memoFuncs[key] = func<br />
end<br />
return func<br />
else<br />
return Template[key]<br />
end<br />
end<br />
})<br />
end<br />
<br />
function Template:getFullPage()<br />
if self.template then<br />
local strippedTemplate, hasColon = self.template:gsub('^:', '', 1)<br />
hasColon = hasColon > 0<br />
local ns = strippedTemplate:match('^(.-):')<br />
ns = ns and mw.site.namespaces[ns]<br />
if ns then<br />
return strippedTemplate<br />
elseif hasColon then<br />
return strippedTemplate -- Main namespace<br />
else<br />
return mw.site.namespaces[10].name .. ':' .. strippedTemplate<br />
end<br />
else<br />
return self.title.prefixedText<br />
end<br />
end<br />
<br />
function Template:getName()<br />
if self.template then<br />
return self.template<br />
else<br />
return require('Module:Template invocation').name(self.title)<br />
end<br />
end<br />
<br />
function Template:makeLink(display)<br />
if display then<br />
return string.format('[[:%s|%s]]', self:getFullPage(), display)<br />
else<br />
return string.format('[[:%s]]', self:getFullPage())<br />
end<br />
end<br />
<br />
function Template:makeBraceLink(display)<br />
display = display or self:getName()<br />
local link = self:makeLink(display)<br />
return mw.text.nowiki('{{') .. link .. mw.text.nowiki('}}')<br />
end<br />
<br />
function Template:makeHeader()<br />
return self.heading or self:makeBraceLink()<br />
end<br />
<br />
function Template:getInvocation(format)<br />
local invocation = self._invocation:getInvocation{<br />
template = self:getName(),<br />
requireMagicWord = self.requireMagicWord,<br />
}<br />
if format == 'code' then<br />
invocation = '<code>' .. mw.text.nowiki(invocation) .. '</code>'<br />
elseif format == 'kbd' then<br />
invocation = '<kbd>' .. mw.text.nowiki(invocation) .. '</kbd>'<br />
elseif format == 'plain' then<br />
invocation = mw.text.nowiki(invocation)<br />
else<br />
-- Default is pre tags<br />
invocation = mw.text.encode(invocation, '&')<br />
invocation = '<pre style="white-space: pre-wrap;">' .. invocation .. '</pre>'<br />
invocation = mw.getCurrentFrame():preprocess(invocation)<br />
end<br />
return invocation<br />
end<br />
<br />
function Template:getOutput()<br />
local protect = require('Module:Protect')<br />
-- calling self._invocation:getOutput{...}<br />
return protect(self._invocation.getOutput)(self._invocation, {<br />
template = self:getName(),<br />
requireMagicWord = self.requireMagicWord,<br />
})<br />
end<br />
<br />
-------------------------------------------------------------------------------<br />
-- TestCase class<br />
-------------------------------------------------------------------------------<br />
<br />
local TestCase = {}<br />
TestCase.__index = TestCase<br />
TestCase.message = message -- add the message method<br />
<br />
TestCase.renderMethods = {<br />
-- Keys in this table are values of the "format" option, values are the<br />
-- method for rendering that format.<br />
columns = 'renderColumns',<br />
rows = 'renderRows',<br />
tablerows = 'renderRows',<br />
inline = 'renderInline',<br />
cells = 'renderCells',<br />
default = 'renderDefault'<br />
}<br />
<br />
function TestCase.new(invocationObj, options, cfg)<br />
local obj = setmetatable({}, TestCase)<br />
obj.cfg = cfg<br />
<br />
-- Separate general options from template options. Template options are<br />
-- numbered, whereas general options are not.<br />
local generalOptions, templateOptions = {}, {}<br />
for k, v in pairs(options) do<br />
local prefix, num<br />
if type(k) == 'string' then<br />
prefix, num = k:match('^(.-)([1-9][0-9]*)$')<br />
end<br />
if prefix then<br />
num = tonumber(num)<br />
templateOptions[num] = templateOptions[num] or {}<br />
templateOptions[num][prefix] = v<br />
else<br />
generalOptions[k] = v<br />
end<br />
end<br />
<br />
-- Set general options<br />
generalOptions.showcode = yesno(generalOptions.showcode)<br />
generalOptions.showheader = yesno(generalOptions.showheader) ~= false<br />
generalOptions.showcaption = yesno(generalOptions.showcaption) ~= false<br />
generalOptions.collapsible = yesno(generalOptions.collapsible)<br />
generalOptions.notcollapsed = yesno(generalOptions.notcollapsed)<br />
generalOptions.wantdiff = yesno(generalOptions.wantdiff) <br />
obj.options = generalOptions<br />
<br />
-- Preprocess template args<br />
for num, t in pairs(templateOptions) do<br />
if t.showtemplate ~= nil then<br />
t.showtemplate = yesno(t.showtemplate)<br />
end<br />
end<br />
<br />
-- Set up first two template options tables, so that if only the<br />
-- "template3" is specified it isn't made the first template when the<br />
-- the table options array is compressed.<br />
templateOptions[1] = templateOptions[1] or {}<br />
templateOptions[2] = templateOptions[2] or {}<br />
<br />
-- Allow the "template" option to override the "template1" option for<br />
-- backwards compatibility with [[Module:Testcase table]].<br />
if generalOptions.template then<br />
templateOptions[1].template = generalOptions.template<br />
end<br />
<br />
-- Add default template options<br />
if templateOptions[1].template and not templateOptions[2].template then<br />
templateOptions[2].template = templateOptions[1].template ..<br />
'/' .. obj.cfg.sandboxSubpage<br />
end<br />
if not templateOptions[1].template then<br />
templateOptions[1].title = mw.title.getCurrentTitle().basePageTitle<br />
end<br />
if not templateOptions[2].template then<br />
templateOptions[2].title = templateOptions[1].title:subPageTitle(<br />
obj.cfg.sandboxSubpage<br />
)<br />
end<br />
<br />
-- Remove template options for any templates where the showtemplate<br />
-- argument is false. This prevents any output for that template.<br />
for num, t in pairs(templateOptions) do<br />
if t.showtemplate == false then<br />
templateOptions[num] = nil<br />
end<br />
end<br />
<br />
-- Check for missing template names.<br />
for num, t in pairs(templateOptions) do<br />
if not t.template and not t.title then<br />
error(obj:message(<br />
'missing-template-option-error',<br />
num, num<br />
), 2)<br />
end<br />
end<br />
<br />
-- Compress templateOptions table so we can iterate over it with ipairs.<br />
templateOptions = (function (t)<br />
local nums = {}<br />
for num in pairs(t) do<br />
nums[#nums + 1] = num<br />
end<br />
table.sort(nums)<br />
local ret = {}<br />
for i, num in ipairs(nums) do<br />
ret[i] = t[num]<br />
end<br />
return ret<br />
end)(templateOptions)<br />
<br />
-- Don't require the __TEMPLATENAME__ magic word for nowiki invocations if<br />
-- there is only one template being output.<br />
if #templateOptions <= 1 then<br />
templateOptions[1].requireMagicWord = false<br />
end<br />
<br />
mw.logObject(templateOptions)<br />
<br />
-- Make the template objects<br />
obj.templates = {}<br />
for i, options in ipairs(templateOptions) do<br />
table.insert(obj.templates, Template.new(invocationObj, options))<br />
end<br />
<br />
-- Add tracking categories. At the moment we are only tracking templates<br />
-- that use any "heading" parameters or an "output" parameter.<br />
obj.categories = {}<br />
for k, v in pairs(options) do<br />
if type(k) == 'string' and k:find('heading') then<br />
obj.categories['Test cases using heading parameters'] = true<br />
elseif k == 'output' then<br />
obj.categories['Test cases using output parameter'] = true<br />
end<br />
end<br />
<br />
return obj<br />
end<br />
<br />
function TestCase:getTemplateOutput(templateObj)<br />
local output = templateObj:getOutput()<br />
if self.options.resetRefs then<br />
mw.getCurrentFrame():extensionTag('references')<br />
end<br />
return output<br />
end<br />
<br />
function TestCase:templateOutputIsEqual()<br />
-- Returns a boolean showing whether all of the template outputs are equal.<br />
-- The random parts of strip markers (see [[Help:Strip markers]]) are<br />
-- removed before comparison. This means a strip marker can contain anything<br />
-- and still be treated as equal, but it solves the problem of otherwise<br />
-- identical wikitext not returning as exactly equal.<br />
local function normaliseOutput(obj)<br />
local out = obj:getOutput()<br />
-- Remove the random parts from strip markers.<br />
out = out:gsub('(\127\'"`UNIQ.-)%-%x+%-(QINU`"\'\127)', '%1%2')<br />
return out<br />
end<br />
local firstOutput = normaliseOutput(self.templates[1])<br />
for i = 2, #self.templates do<br />
local output = normaliseOutput(self.templates[i])<br />
if output ~= firstOutput then<br />
return false<br />
end<br />
end<br />
return true<br />
end<br />
<br />
function TestCase:makeCollapsible(s)<br />
local title = self.options.title or self.templates[1]:makeHeader()<br />
if self.options.titlecode then<br />
title = self.templates[1]:getInvocation('kbd')<br />
end<br />
local isEqual = self:templateOutputIsEqual()<br />
local root = mw.html.create('table')<br />
if self.options.wantdiff then<br />
root<br />
:addClass('mw-collapsible')<br />
if self.options.notcollapsed == false then<br />
root<br />
:addClass('mw-collapsed')<br />
end<br />
root<br />
:css('background-color', 'transparent')<br />
:css('width', '100%')<br />
:css('border', 'solid silver 1px')<br />
:tag('tr')<br />
:tag('th')<br />
:css('background-color', isEqual and 'yellow' or '#90a8ee')<br />
:wikitext(title)<br />
:done()<br />
:done()<br />
:tag('tr')<br />
:tag('td')<br />
:newline()<br />
:wikitext(s)<br />
:newline()<br />
else<br />
root<br />
:addClass('mw-collapsible')<br />
if self.options.notcollapsed == false then<br />
root<br />
:addClass('mw-collapsed')<br />
end<br />
if self.options.notcollapsed ~= true or false then<br />
root<br />
:addClass(isEqual and 'mw-collapsed' or nil)<br />
end<br />
root<br />
:css('background-color', 'transparent')<br />
:css('width', '100%')<br />
:css('border', 'solid silver 1px')<br />
:tag('tr')<br />
:tag('th')<br />
:css('background-color', isEqual and 'lightgreen' or 'yellow')<br />
:wikitext(title)<br />
:done()<br />
:done()<br />
:tag('tr')<br />
:tag('td')<br />
:newline()<br />
:wikitext(s)<br />
:newline()<br />
end<br />
return tostring(root)<br />
end<br />
<br />
function TestCase:renderColumns()<br />
local root = mw.html.create()<br />
if self.options.showcode then<br />
root<br />
:wikitext(self.templates[1]:getInvocation())<br />
:newline()<br />
end<br />
<br />
local tableroot = root:tag('table')<br />
<br />
if self.options.showheader then<br />
-- Caption<br />
if self.options.showcaption then<br />
tableroot<br />
:addClass(self.options.class)<br />
:cssText(self.options.style)<br />
:tag('caption')<br />
:wikitext(self.options.caption or self:message('columns-header'))<br />
end<br />
<br />
-- Headers<br />
local headerRow = tableroot:tag('tr')<br />
if self.options.rowheader then<br />
-- rowheader is correct here. We need to add another th cell if<br />
-- rowheader is set further down, even if heading0 is missing.<br />
headerRow:tag('th'):wikitext(self.options.heading0)<br />
end<br />
local width<br />
if #self.templates > 0 then<br />
width = tostring(math.floor(100 / #self.templates)) .. '%'<br />
else<br />
width = '100%'<br />
end<br />
for i, obj in ipairs(self.templates) do<br />
headerRow<br />
:tag('th')<br />
:css('width', width)<br />
:wikitext(obj:makeHeader())<br />
end<br />
end<br />
<br />
-- Row header<br />
local dataRow = tableroot:tag('tr'):css('vertical-align', 'top')<br />
if self.options.rowheader then<br />
dataRow:tag('th')<br />
:attr('scope', 'row')<br />
:wikitext(self.options.rowheader)<br />
end<br />
<br />
-- Template output<br />
for i, obj in ipairs(self.templates) do<br />
if self.options.output == 'nowiki+' then<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(self.options.before)<br />
:wikitext(self:getTemplateOutput(obj))<br />
:wikitext(self.options.after)<br />
:wikitext('<pre style="white-space: pre-wrap;">')<br />
:wikitext(mw.text.nowiki(self.options.before or ""))<br />
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))<br />
:wikitext(mw.text.nowiki(self.options.after or ""))<br />
:wikitext('</pre>')<br />
elseif self.options.output == 'nowiki' then<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(mw.text.nowiki(self.options.before or ""))<br />
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))<br />
:wikitext(mw.text.nowiki(self.options.after or ""))<br />
else<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(self.options.before)<br />
:wikitext(self:getTemplateOutput(obj))<br />
:wikitext(self.options.after)<br />
end<br />
end<br />
<br />
return tostring(root)<br />
end<br />
<br />
function TestCase:renderRows()<br />
local root = mw.html.create()<br />
if self.options.showcode then<br />
root<br />
:wikitext(self.templates[1]:getInvocation())<br />
:newline()<br />
end<br />
<br />
local tableroot = root:tag('table')<br />
tableroot<br />
:addClass(self.options.class)<br />
:cssText(self.options.style)<br />
<br />
if self.options.caption then<br />
tableroot<br />
:tag('caption')<br />
:wikitext(self.options.caption)<br />
end<br />
<br />
for _, obj in ipairs(self.templates) do<br />
local dataRow = tableroot:tag('tr')<br />
<br />
-- Header<br />
if self.options.showheader then<br />
if self.options.format == 'tablerows' then<br />
dataRow:tag('th')<br />
:attr('scope', 'row')<br />
:css('vertical-align', 'top')<br />
:css('text-align', 'left')<br />
:wikitext(obj:makeHeader())<br />
dataRow:tag('td')<br />
:css('vertical-align', 'top')<br />
:css('padding', '0 1em')<br />
:wikitext('→')<br />
else<br />
dataRow:tag('td')<br />
:css('text-align', 'center')<br />
:css('font-weight', 'bold')<br />
:wikitext(obj:makeHeader())<br />
dataRow = tableroot:tag('tr')<br />
end<br />
end<br />
<br />
-- Template output<br />
if self.options.output == 'nowiki+' then<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(self:getTemplateOutput(obj))<br />
:wikitext('<pre style="white-space: pre-wrap;">')<br />
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))<br />
:wikitext('</pre>')<br />
elseif self.options.output == 'nowiki' then<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))<br />
else<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(self:getTemplateOutput(obj))<br />
end<br />
end<br />
<br />
return tostring(root)<br />
end<br />
<br />
function TestCase:renderInline()<br />
local arrow = mw.language.getContentLanguage():getArrow('forwards')<br />
local ret = {}<br />
for i, obj in ipairs(self.templates) do<br />
local line = {}<br />
line[#line + 1] = self.options.prefix or '* '<br />
if self.options.showcode then<br />
line[#line + 1] = obj:getInvocation('code')<br />
line[#line + 1] = ' '<br />
line[#line + 1] = arrow<br />
line[#line + 1] = ' '<br />
end<br />
if self.options.output == 'nowiki+' then<br />
line[#line + 1] = self:getTemplateOutput(obj)<br />
line[#line + 1] = '<pre style="white-space: pre-wrap;">'<br />
line[#line + 1] = mw.text.nowiki(self:getTemplateOutput(obj))<br />
line[#line + 1] = '</pre>'<br />
elseif self.options.output == 'nowiki' then<br />
line[#line + 1] = mw.text.nowiki(self:getTemplateOutput(obj))<br />
else<br />
line[#line + 1] = self:getTemplateOutput(obj)<br />
end<br />
ret[#ret + 1] = table.concat(line)<br />
end<br />
if self.options.addline then<br />
local line = {}<br />
line[#line + 1] = self.options.prefix or '* '<br />
line[#line + 1] = self.options.addline<br />
ret[#ret + 1] = table.concat(line)<br />
end<br />
return table.concat(ret, '\n')<br />
end<br />
<br />
function TestCase:renderCells()<br />
local root = mw.html.create()<br />
local dataRow = root:tag('tr')<br />
dataRow<br />
:css('vertical-align', 'top')<br />
:addClass(self.options.class)<br />
:cssText(self.options.style)<br />
<br />
-- Row header<br />
if self.options.rowheader then<br />
dataRow:tag('th')<br />
:attr('scope', 'row')<br />
:newline()<br />
:wikitext(self.options.rowheader or self:message('row-header'))<br />
end<br />
-- Caption<br />
if self.options.showcaption then<br />
dataRow:tag('th')<br />
:attr('scope', 'row')<br />
:newline()<br />
:wikitext(self.options.caption or self:message('columns-header'))<br />
end<br />
<br />
-- Show code<br />
if self.options.showcode then<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(self:getInvocation('code'))<br />
end<br />
<br />
-- Template output<br />
for i, obj in ipairs(self.templates) do<br />
if self.options.output == 'nowiki+' then<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(self.options.before)<br />
:wikitext(self:getTemplateOutput(obj))<br />
:wikitext(self.options.after)<br />
:wikitext('<pre style="white-space: pre-wrap;">')<br />
:wikitext(mw.text.nowiki(self.options.before or ""))<br />
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))<br />
:wikitext(mw.text.nowiki(self.options.after or ""))<br />
:wikitext('</pre>')<br />
elseif self.options.output == 'nowiki' then<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(mw.text.nowiki(self.options.before or ""))<br />
:wikitext(mw.text.nowiki(self:getTemplateOutput(obj)))<br />
:wikitext(mw.text.nowiki(self.options.after or ""))<br />
else<br />
dataRow:tag('td')<br />
:newline()<br />
:wikitext(self.options.before)<br />
:wikitext(self:getTemplateOutput(obj))<br />
:wikitext(self.options.after)<br />
end<br />
end<br />
<br />
<br />
return tostring(root)<br />
end<br />
<br />
function TestCase:renderDefault()<br />
local ret = {}<br />
if self.options.showcode then<br />
ret[#ret + 1] = self.templates[1]:getInvocation()<br />
end<br />
for i, obj in ipairs(self.templates) do<br />
ret[#ret + 1] = '<div style="clear: both;"></div>'<br />
if self.options.showheader then<br />
ret[#ret + 1] = obj:makeHeader()<br />
end<br />
if self.options.output == 'nowiki+' then<br />
ret[#ret + 1] = self:getTemplateOutput(obj) .. '<pre style="white-space: pre-wrap;">' .. mw.text.nowiki(self:getTemplateOutput(obj)) .. '</pre>'<br />
elseif self.options.output == 'nowiki' then<br />
ret[#ret + 1] = mw.text.nowiki(self:getTemplateOutput(obj))<br />
else<br />
ret[#ret + 1] = self:getTemplateOutput(obj)<br />
end<br />
end<br />
return table.concat(ret, '\n\n')<br />
end<br />
<br />
function TestCase:__tostring()<br />
local format = self.options.format<br />
local method = format and TestCase.renderMethods[format] or 'renderDefault'<br />
local ret = self[method](self)<br />
if self.options.collapsible then<br />
ret = self:makeCollapsible(ret)<br />
end<br />
for cat in pairs(self.categories) do<br />
ret = ret .. string.format('[[Category:%s]]', cat)<br />
end<br />
return ret<br />
end<br />
<br />
-------------------------------------------------------------------------------<br />
-- Nowiki invocation class<br />
-------------------------------------------------------------------------------<br />
<br />
local NowikiInvocation = {}<br />
NowikiInvocation.__index = NowikiInvocation<br />
NowikiInvocation.message = message -- Add the message method<br />
<br />
function NowikiInvocation.new(invocation, cfg)<br />
local obj = setmetatable({}, NowikiInvocation)<br />
obj.cfg = cfg<br />
invocation = mw.text.unstrip(invocation)<br />
-- Decode HTML entities for <, >, and ". This means that HTML entities in<br />
-- the original code must be escaped as e.g. &amp;lt;, which is unfortunate,<br />
-- but it is the best we can do as the distinction between <, >, " and &lt;,<br />
-- &gt;, &quot; is lost during the original nowiki operation.<br />
invocation = invocation:gsub('&lt;', '<')<br />
invocation = invocation:gsub('&gt;', '>')<br />
invocation = invocation:gsub('&quot;', '"')<br />
obj.invocation = invocation<br />
return obj<br />
end<br />
<br />
function NowikiInvocation:getInvocation(options)<br />
local template = options.template:gsub('%%', '%%%%') -- Escape "%" with "%%"<br />
local invocation, count = self.invocation:gsub(<br />
self.cfg.templateNameMagicWordPattern,<br />
template<br />
)<br />
if options.requireMagicWord ~= false and count < 1 then<br />
error(self:message(<br />
'nowiki-magic-word-error',<br />
self.cfg.templateNameMagicWord<br />
))<br />
end<br />
return invocation<br />
end<br />
<br />
function NowikiInvocation:getOutput(options)<br />
local invocation = self:getInvocation(options)<br />
return mw.getCurrentFrame():preprocess(invocation)<br />
end<br />
<br />
-------------------------------------------------------------------------------<br />
-- Table invocation class<br />
-------------------------------------------------------------------------------<br />
<br />
local TableInvocation = {}<br />
TableInvocation.__index = TableInvocation<br />
TableInvocation.message = message -- Add the message method<br />
<br />
function TableInvocation.new(invokeArgs, nowikiCode, cfg)<br />
local obj = setmetatable({}, TableInvocation)<br />
obj.cfg = cfg<br />
obj.invokeArgs = invokeArgs<br />
obj.code = nowikiCode<br />
return obj<br />
end<br />
<br />
function TableInvocation:getInvocation(options)<br />
if self.code then<br />
local nowikiObj = NowikiInvocation.new(self.code, self.cfg)<br />
return nowikiObj:getInvocation(options)<br />
else<br />
return require('Module:Template invocation').invocation(<br />
options.template,<br />
self.invokeArgs<br />
)<br />
end<br />
end<br />
<br />
function TableInvocation:getOutput(options)<br />
return mw.getCurrentFrame():expandTemplate{<br />
title = options.template,<br />
args = self.invokeArgs<br />
}<br />
end<br />
<br />
-------------------------------------------------------------------------------<br />
-- Bridge functions<br />
--<br />
-- These functions translate template arguments into forms that can be accepted<br />
-- by the different classes, and return the results.<br />
-------------------------------------------------------------------------------<br />
<br />
local bridge = {}<br />
<br />
function bridge.table(args, cfg)<br />
cfg = cfg or mw.loadData(DATA_MODULE)<br />
<br />
local options, invokeArgs = {}, {}<br />
for k, v in pairs(args) do<br />
local optionKey = type(k) == 'string' and k:match('^_(.*)$')<br />
if optionKey then<br />
if type(v) == 'string' then<br />
v = v:match('^%s*(.-)%s*$') -- trim whitespace<br />
end<br />
if v ~= '' then<br />
options[optionKey] = v<br />
end<br />
else<br />
invokeArgs[k] = v<br />
end<br />
end<br />
<br />
-- Allow passing a nowiki invocation as an option. While this means users<br />
-- have to pass in the code twice, whitespace is preserved and &lt; etc.<br />
-- will work as intended.<br />
local nowikiCode = options.code<br />
options.code = nil<br />
<br />
local invocationObj = TableInvocation.new(invokeArgs, nowikiCode, cfg)<br />
local testCaseObj = TestCase.new(invocationObj, options, cfg)<br />
return tostring(testCaseObj)<br />
end<br />
<br />
function bridge.nowiki(args, cfg)<br />
cfg = cfg or mw.loadData(DATA_MODULE)<br />
<br />
local code = args.code or args[1]<br />
local invocationObj = NowikiInvocation.new(code, cfg)<br />
args.code = nil<br />
args[1] = nil<br />
-- Assume we want to see the code as we already passed it in.<br />
args.showcode = args.showcode or true<br />
local testCaseObj = TestCase.new(invocationObj, args, cfg)<br />
return tostring(testCaseObj)<br />
end<br />
<br />
-------------------------------------------------------------------------------<br />
-- Exports<br />
-------------------------------------------------------------------------------<br />
<br />
local p = {}<br />
<br />
function p.main(frame, cfg)<br />
cfg = cfg or mw.loadData(DATA_MODULE)<br />
<br />
-- Load the wrapper config, if any.<br />
local wrapperConfig<br />
if frame.getParent then<br />
local title = frame:getParent():getTitle()<br />
local template = title:gsub(cfg.sandboxSubpagePattern, '')<br />
wrapperConfig = cfg.wrappers[template]<br />
end<br />
<br />
-- Work out the function we will call, use it to generate the config for<br />
-- Module:Arguments, and use Module:Arguments to find the arguments passed<br />
-- by the user.<br />
local func = wrapperConfig and wrapperConfig.func or 'table'<br />
local userArgs = require('Module:Arguments').getArgs(frame, {<br />
parentOnly = wrapperConfig,<br />
frameOnly = not wrapperConfig,<br />
trim = func ~= 'table',<br />
removeBlanks = func ~= 'table'<br />
})<br />
<br />
-- Get default args and build the args table. User-specified args overwrite<br />
-- default args.<br />
local defaultArgs = wrapperConfig and wrapperConfig.args or {}<br />
local args = {}<br />
for k, v in pairs(defaultArgs) do<br />
args[k] = v<br />
end<br />
for k, v in pairs(userArgs) do<br />
args[k] = v<br />
end<br />
<br />
return bridge[func](args, cfg)<br />
end<br />
<br />
function p._exportClasses() -- For testing<br />
return {<br />
Template = Template,<br />
TestCase = TestCase,<br />
NowikiInvocation = NowikiInvocation,<br />
TableInvocation = TableInvocation<br />
}<br />
end<br />
<br />
return p</div>
en>SUM1