Zelda Wiki

Want to contribute to this wiki?
Sign up for an account, and get started!

Come join the Zelda Wiki community Discord server!

READ MORE

Zelda Wiki
Advertisement
ZW Logo White
Getting Involved Getting Involved
Getting Involved
Discord Discord Logo
Discord
Knight Challenges Knight Challenges Icon
Knight Challenges
Image edit request and support center Gimp Logo
Image Requests
Guidelines:Main Guidelines Icon
Guidelines

Overview

Modules are Lua scripts for making templates using a full-fledged programming language. You can ask questions about Zelda Wiki modules in the #modules-and-templates Discord channel.

Templates[]

Main article: Help:Templates

Modules exist as a means to create more complex templates. In order to contribute to modules, you must first understand how templates are used. You should also know to create a basic template without modules to get a sense of when Lua scripts are needed and why.

Getting Started[]

In order to contribute to modules, you'll need to learn basic programming in Lua. You'll also need to know the specifics of Lua scripting on MediaWiki.

Lua for Beginners[]

There are many excellent online resources designed to teach programming fundamentals... But not in Lua. Fortunately, Lua is a relatively small language. Jump in and try to learn through the Exercises.

If you would prefer more guided lessons, try Codeacademy's Python 2 course or Khan Academy's Intro to JavaScript. Lua is closer to Python than to JavaScript, but Khan Academy's course has the benefit of video talk-throughs. Learning JavaScript would allow you to build Gadgets or write personal modifications to the wiki's interface.

If you would prefer a book, try An Introduction to Programming in Go by Caleb Doxsey. Due to the nature of Go, this book teaches more general-purpose computing fundamentals than what you would need for writing wiki modules. Were you to pursue programming beyond the wiki, those concepts would likely serve you well.

Lua for Developers[]

If you already have some programming experience in other languages, read Learn Lua in 15 minutes.

Note the following in particular:

Particularities Further reading
tables are the only built-in object type. Any use of the term "array" actually refers to a table with integer keys.
Generally arrays are 1-indexed as opposed to 0-indexed.
local tbl = {"foo"}
tbl[1]
> "foo"
  • The ipairs iterator is for integer key-value pairs in a table.
  • The pairs iterator is for all key-value pairs, integer or otherwise.
The length operator for a table is #.

# replaces the table.getn seen in the online version of Programming in Lua, which is for Lua 5.0. getn won't work here, as the wiki is on Lua 5.1 at the time of writing.

table.setn was also deprecated and is unusable in 5.1. Unfortunately, the alternative __len field is only available in Lua 5.2.

Tables, nil, and the # operator don't interact the way similar constructs do in other languages.
#{ nil, nil, "foo" }
> 3

local tbl = {}
tbl[3] = "foo"
#tbl
> 0

tbl[1] = "bar"
#tbl
> 1

tbl[2] = "baz"
#tbl
> 3

Generally, "array" functions only look at the consecutive indices starting from 1.

Scribunto[]

For a broader and more in-depth guide, see Gamepedia Help Wiki.
See also the full Scribunto/Lua reference manual.

Scribunto is the name of the extension that enables Lua modules. The Lua on you see on MediaWiki is almost the same as standard Lua except for some changes to library functions and packages. The main difference is in how Lua scripts are invoked.

#invoke[]

The #invoke parser function is what bridges the gap between templates and module scripts. For example, the transcluded content of Template:Figurine is as follows:

{{#invoke:Figurine|Main}}

This invokes Module:Figurine, which could look something like:

local p = {}
local h = {}

local Franchise = require("Module:Franchise")
...

function p.Main(frame)
    local args = frame:getParent().args
    local result = h.getFigurine(args[1], args[2])
    return result
end

function h.getFigurine(game, name)
...
end

return p

A page can invoke any function that is a field on the module's export table. The export table is the table object returned by the module, which is always named p by convention. In this case, Main is the only function in the export table.

args[]

Functions called via #invoke are passed a Frame object.

  • frame.args is a table of the arguments to #invoke. (There are none in the above example.)
  • frame:getParent().args is a table of the arguments to the template.
    • Example: If a page has {{Figurine|TMC|Minish Ezlo}}, then frame:getParent().args[1] evaluates to the string TMC for that invocation.

The # operator and most other table functions don't work on frame.args.

require[]

A module can import another module and use its exported functions. This is done with the require function, as shown in the above example with Module:Franchise.

mw[]

Scribunto pre-loads several MediaWiki-related libraries as the mw object. The following libraries are of note, in addition to the base functions:

Library Usage example
mw.text Module:List
mw.title Module:Subpage List
mw.html Module:Infobox

Debugging[]

See also gphelp:Extension:Scribunto#Debugging

You should never be in a situation where you're blindly submitting code and hoping that it works. The first two exercises of these guidelines cover how to debug with previewing, logging, and the debug console.

Always ensure that your code does not produce script errors. Please fix or revert any changes that do. Keep an eye on Category:Articles Using Invalid Arguments in Template Calls as well.

Testing and Documentation[]

Main article: Module:Documentation

When coding a module, the best page to preview is often the corresponding template's documentation. If it exists, the page should have usage examples for every available feature. In fact, an effective way to write modules is actually to write the documentation examples before the code itself. When you're writing the actual code, previewing that documentation page will tell you if your code is working—and will give you material to debug with if it isn't. The practice of writing test cases before the code is called test-driven development.

You should also write module documentation for any Lua function meant to be used by other modules. Module documentation can double as automated unit tests via the expect property. If the output of the function does not equal what is expected, the page is added to Category:Modules with failing tests.

Utility Modules[]

A utility module is a library-type module meant to be used by other modules, rather than being invoked by a template. Most template-facing modules use at least one of these:

A list of utility modules is available at Category:Utility Modules. Leverage utility modules as much as possible so that the wiki's codebase stays DRY.

Exercises[]

Try the available exercises to test and develop your understanding of Lua, Scribunto, and Zelda Wiki's utilities.

Style[]

Once you are able to produce working code, make sure it adheres to Zelda Wiki's coding standards.

It is particularly important to use a consistent naming pattern, as plain text searching is the only way to observe function usage.

Importing[]

Use lower camelCase for imported utility modules
TFH Red Link desperate
local UtilsString = require("Module:UtilsString")
TFH Green Link ok
local utilsString = require("Module:UtilsString")
Avoid importing submodules unless absolutely necessary
TFH Red Link desperate
local utilsMarkupLink = require("Module:UtilsMarkup/Link")
TFH Green Link ok
local utilsMarkup = require("Module:UtilsMarkup")
Don't index the result of the require expression.
TFH Red Link desperate
local list = require("Module:UtilsMarkup").list
TFH Green Link ok If you must assign the function separately:
local utilsMarkup = require("Module:UtilsMarkup")
...
local list = utilsMarkup.list

Strings[]

Default to double quotes
TFH Green Link ok
local foo = "foo"
local bar = '<span class="baz">bar</span>'
TFH Red Link desperate
local foo = 'foo'

Errors[]

Template calls must not throw script errors on purpose. Use warnings and error categories instead.
TFH Green Link ok
function(foo)
  if not foo then
    mw.addWarning("'foo' is required.")
    return "[[Category:Pages with Invalid Arguments]]"
  end
  local bar = foo.bar
  ...
end

Module:UtilsArg can help with this.

TFH Red Link desperate
function(foo)
  local bar = foo.bar -- throws an error if foo is nil
  ...
end
TFH Red Link desperate
function(foo)
  if not foo then
    error("foo cannot be nil")
  end
  local bar = foo.bar
  ...
end

Profiling[]

Our MediaWiki installation comes with a Lua Profiler which indicates how much time and memory Lua takes up when loading a page on the wiki. It will also show the top ten Lua functions which contribute the most time usage.

To generate a profiler report, preview a page on the wiki. The report is located at the bottom of the page under the editing area, in the Parser profiling data: section which may be collapsed by default. It looks something like this:

The Lua Profile section only appears when total Lua time usage on the page is above 1000ms.

Lua time usage 1.253/7.000 seconds
Lua memory usage 15,186,425/52,428,800 bytes
Lua Profile
Scribunto_LuaSandboxCallback::query 200 ms 14.7%
recursiveClone <mwInit.lua:41> 200 ms 14.7%
Scribunto_LuaSandboxCallback::preprocess 140 ms 10.3%
Scribunto_LuaSandboxCallback::get 120 ms 8.8%
? 100 ms 7.4%
init <Module:UtilsTable/Inspect> 60 ms 4.4%
init <Module:UtilsString> 60 ms 4.4%
Scribunto_LuaSandboxCallback::newTitle 60 ms 4.4%
Scribunto_LuaSandboxCallback::callParserFunction 60 ms 4.4%
init <Module:UtilsArg> 40 ms 2.9%
[others] 320 ms 23.5%
Advertisement