Fandom

Wikia Developers Wiki

Lua templating/Tips and tricks

< Lua templating

861pages on
this wiki
Add New Page
Talk8 Share

This page lists various tips and tricks for the creation, modding, and debugging of Lua-powered templates.

Designing modules/templates

Lua style guide

It is usually a good idea to maintain some consistency in the design of modules, especially those used on a lot of pages. See this wiki's proposed Style guide for more information. Also try out a beautifier (e.g.

lua-beautify).

Loading and parsing data structured data

The easiest way to load data is by saving it in a Lua table, storing it in a page and using mw.loadData( 'Module:Name' ) or require( 'Module:Name' ). But sometimes it may be useful to keep the data as it is and parse it. For example when retrieving JSON data, one way to quickly retrieve everything is to store the json in a page like Dev Wiki:Storage.js and then retrieve it using mw.title.makeTitle('pagename').getContent() and the decode function of the Module:Json or parsing it using Lua's string library.

Note this will be an expensive call and will register as a link to that page.

Require

The lua engine only allows a maximum of (see this) calls to "require" (e.g. require("Module:zoo")) .

Types of modules

There are three types of modules:

  • Global modules - these are stored in dev.wikia.com, and can be either modules for templates, or helper modules. They are typically loaded using the syntax like 'require("Dev:ModuleName")' e.g. require("Dev:Utility") . Note that this is case sensitive.
  • Modules for templates - these modules must contain a frame argument in one of the functions.
  • Helper modules - these modules aren't intended for inclusion on pages but to be used by other modules.

Preview

The preview button allows one to see what the module will display before it is saved. It is a good idea always to work with two pages, one with the actual module being edited and one test page that invokes the module. Rather than refreshing the test page a simple preview will save time.

Arguments

Parent or child arguments

One important concept is the difference between parent (use through a template) and child parameters (using a module directly). Read more about arguments here.

Incorrectly passed arguments

These can cause problems if they contain special characters such as =. The result will be that the module may not correctly interpret the passed arguments if they are, for example, part of a URL string, e.g. {{hello|https://www.google.com/search?q=lua+templating+tips+and+tricks}}.

Passing arguments from one module to another

One of the greatest advantages of Lua is the ability to re-use existing code efficiently. So passing arguments to and from one module is important. There are several ways to achieve this:

  • Check if passed argument is a frame or regular object:
-- Source: http://en.wikipedia.org/wiki/Module:Infobox
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
    origArgs = frame:getParent().args
else
    origArgs = frame
end
  • Create one function for the frame and a second for the arguments (recommended):
-- Module:Say Hello
local p = {}
 
function p._sayHello( name )
    return 'Hello, ' .. name .. '!'
end
 
function p.main( frame )
    local name = frame.args[1] or frame:getParent().args[1]
 
    return p._sayHello( name )
end
 
return p
  • Use frame.preprocess()—this is not recommended because it is inefficient and fails to take advantage of powerful Lua methods:
local p = {}
 
function p.main(frame)
    local name = frame.args[1]
 
    -- Calls Module:Say Hello
    return frame.preprocess( '{{#invoke:Say Hello|main|' .. name .. '}}' )
end
 
return p

Available frame API

Wikia may not always have access to the most recent MediaWiki's Scribunto API, so it's best to be aware of the differences when designing a module. The following table shows which frame methods are currently available on Wikia, and provides links to the appropriate documentation.

NameAvailable?TypeDocs
argsYestablelink
callParserFunctionNoUnknownmw link
expandTemplateYesfunctionlink
extensionTagNoUnknownmw link
getParentYesfunctionlink
getTitleYesfunctionlink
newChildNoUnknownmw link
preprocessYesfunctionlink
getArgumentYesfunctionlink
newParserValueYesfunctionlink
newTemplateParserValueYesfunctionlink
argumentPairsYesfunctionlink

Debugging modules

Cache

One very important thing to remember when debugging a module is that wikia keeps a cache of  data. So changes made in modules may not always be immediately visible in a page. One way to get around this is to append ?debug=true or ?action=purge (e.g. http://dev.wikia.com/wiki/Lua_templating/Tips_and_tricks?debug=true) to all pages that are being debugged. See Help:purge for more information.

Debugging console

  • Debugging functions using the console - One can debug functions written in the module text area using the debug console. To do this one merely needs to call the function, e.g. p.helloworld('howdy'). Note that functions need to be part of the main table, specifically p, and functions outside this table cannot be called this way.
  • Console shows two types of errors - One of the error relates to a syntax problem in the console itself, and the other relates to errors in the module text area. Simply typing an expression will show one or both errors, e.g.: '='.
  • Print debug output - Output can be printed to the console using mw.log('test').
  • Frame - The frame is nil (empty) using the debug console, as it is created during a module being used through #invoke.

Emulating the frame object using the console

A frame object is generally nil, but for the purposes of debugging on the console it can be emulated:

local childFrame = {
    -- Child frame's arguments (change as needed)
    args = { 'es', 'en', 'n', 'n', 'n' },
 
    getParent = function ()
        -- Parent frame's arguments (change as needed)
        local pArgs = { 'es', 'en', 'n', 'n', 'n', '2011-06-29' }
 
        return { args = pArgs }
    end
}
 
-- Returns all values in parent frame
function test( frame )
    local args = frame:getParent().args
    local output = mw.text.listToText( args )
 
    mw.log( output )
end
 
test( childFrame )

Testing modules

One way to test modules is by emulating the template call as shown above. This will make it easier to preview the output before saving the page and deploying the module.

Unit testing

There are several ways of testing modules/functions therein. One way to achieve this is by using Wikipedia's "UnitTests" framework

Implementing modules

Modules can either be implemented and used as stand-alone using {{#invoke:}} or they can be used in conjunction with templates.

Working with templates or pages

While working with templates it is important to use the frame's parent, and to bear in mind that these modules may not work when called directly if they aren't coded properly:

--Module:Hello
local p = {}
 
function p.main( frame )
    local name = frame.args[1]
 
    name = name or ""
    return 'Hello, ' .. name .. '!'
end
 
return p
Template:hello
{{#invoke:hello|main}}
{{#invoke:Hello|main|World}} output :Hello world <!-- works -->
{{Hello|World}}              output :<script error>     <!-- doesn't work -->

The code above will only work from a page that calls the "invoke" directly, however if calling from a template it will simply raise a syntax error. In order to make the above code work in templates and regular pages, a change will have to be made:

--Module:Hello
local p = {}
 
function p.main( frame )
    local name = frame.args[1] or frame:getParent().args[1]
 
    return 'Hello, ' .. name .. '!'
end
 
return p
Template:hello
{{#invoke:hello|main}}
{{#invoke:Hello|main|World}} output :Hello world  <!-- works -->
{{Hello|World}}              output :Hello world  <!-- works -->

The above code will work because it tests whether the frame arguments from either the template or the page have any value.

Converting

Why Convert?

See the Scribunto presentation.

Choosing templates to convert

The best way to find out which templates need conversion is to view the pages below and improve the templates or the structure to reduce the parsing time (time it takes to load):

  • Special:ParserSpeed
  • Category:Pages with too many expensive parser function calls
  • Category:Pages where node count is exceeded
  • Category:Pages where template include size is exceeded
  • Category:Pages containing omitted template arguments
  • Category:Pages where expansion depth is exceeded

For an in-depth explanation see template limits.

Wikimedia modules

Wikimedia modules may either work as is when they are added to wikia or may require some tweaks (if they use a different scribunto extension).

Wikitext modules

Main article: Lua templating/Converting Wikitext templates

Switch parser function

One way to quickly convert switch parser functions is to use a Lua module such as Module:Switch. This will greatly reduce the time it takes to make it Lua compatible and also fix the node count problem. Although it is advisable to eventually convert the tables to Lua.

Documentation

Some approaches for documenting the code itself are described in Global Lua Modules/Codedoc, and LDoc.

Categorization

It is possible to categorize a module in a hacky way with wikitext, just like any other page, provided the category is not within <syntaxhighlight> (a.k.a. <source>), <pre>, or <nowiki> tags.

-- <pre>
local p = {}
 
function p.main( frame )
    local name = frame.args[1] or frame:getParent().args[1]
 
    return 'Hello, ' .. name .. '!'
end
 
return p
-- </pre>
-- [[Category:Lua]]

Unlike most other pages, categories are not listed at the bottom of Module pages.

Performance (optimization)

Main article: Lua templating/Optimisation

Some general guidelines include, using local functions or variables, avoiding nested functions, using arrays instead of tables, and using local variables instead of indexing tables.

Lua or template profiler

A little known feature of Scribunto (Lua) is that it has a profiler that shows how long it takes to process and render all lua content. Specifically, if the lua code takes more than a second to execute the advanced profiler shows the most time consuming api calls or functions. This can be seen in the page source code by searching for "NewPP" text or by using a simple user-script (PP-report-Greasemonkey-script) [1]:

NewPP limit report
...
Lua Profile:
    Scribunto_LuaSandboxCallback::gsub                     460 ms     37.7%
    time                                                    80 ms      6.6%
    Scribunto_LuaSandboxCallback::find                      60 ms      4.9%
    addNamed <Module:File_link:39>                          60 ms      4.9%
    getArgs <Module:Arguments:55>                           60 ms      4.9%
    datediff <Module:Utility:76>                            40 ms      3.3%

Note that the string.gsub function is the worst offender taking 460 ms to run possibly because of multiple calls.

Wikitext quirks

Strip markers

Some special tags such as <pre>, <nowiki> may not always evaluate properly when they are tested using boolean or equality operators (e.g. "==", "!=". This is because they have a strip marker that is put into place until it renders the content. So for example, the code below will evaluate to false because the contents are not the same, e.g. variable x has a string like "?UNIQ...QINU?".

--Module:Strip_markers
local p = {}
 
function p.main(frame)
    local x =frame.args[1]
 
    return x == frame:preprocess("<pre>abc</pre>")
end
 
return p
 {{#invoke:strip_markers|main|<pre>abc</pre>}}
Output: false

See also

References

  1. https://en.wikipedia.org/wiki/Wikipedia:Template_limits#How_can_you_find_out.3F

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.