Guidelines:Modules: Difference between revisions

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search
(Undid revision 781463 by HylianKing (Talk))
Tags: Replaced Undo
No edit summary
Line 2: Line 2:
{{Big|Overview|3}}<br>
{{Big|Overview|3}}<br>


'''Modules''' are {{Wp|Lua (programming language)|Lua}} scripts for making [[Help:Templates|templates]] using a full-fledged programming language.
'''Modules''' are {{Wp|Lua (programming language)|Lua}} scripts for making [[Help:Templates|templates]] using a full-fledged programming language. You can ask questions about Zelda Wiki modules in the <code>#modules</code> [{{Discord}} Discord] channel.


==Overview==
==Getting Started==
<!-- 00110011 01100100 00100000 01101101 01101111 01100100 01100101 01101100 00100000 01101111 01100110 00100000 01100001 00100000 01110100 01100101 01100001 01110000 01101111 01110100 -->
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===
====Beginners====
====Developers====
If you already have some programming experience in other languages, note the following particularities of Lua.
 
{| class="wikitable"
! Particularities
! Further reading
|-
| <code>table</code>s are the only built-in object type. Any use of the term "array" actually refers to a table with integer keys.
 
The language offers prototype-based inheritance through '''metatables'''.
|
* [https://www.lua.org/pil/2.5.html Tables]
* [https://www.lua.org/pil/2.5.html Metatables and Metamethods]
|-
| Generally tables are 1-indexed as opposed to 0-indexed.
<syntaxhighlight lang="lua">
local tbl = {"foo"}
tbl[1]
> "foo"
</syntaxhighlight>
|
* [https://www.lua.org/pil/11.1.html Arrays]
|-
|
* The <code>ipairs</code> iterator is for <u>integer</u> key-value pairs in a table.
* The <code>pairs</code> iterator is for <u>all</u> key-value pairs, integer or otherwise.
|
* [https://www.lua.org/pil/4.3.5.html Generic <code>for</code>]
* [https://www.lua.org/pil/4.3.4.html Numeric <code>for</code>]
|-
| The length operator for a table is <code>#</code>.
 
<code>#</code> replaces the <code>table.getn</code> seen in the online version of ''Programming in Lua'', which is for Lua 5.0. <code>getn</code> won't work here as the wiki is on Lua 5.1 (at the time of writing).
 
<code>table.setn</code> was also deprecated and is unusable in 5.1. Unfortunately, the alternative <code>__len</code> field is only available in Lua 5.2.
|
|-
| Tables, <code>nil</code>, and the <code>#</code> operator don't interact the way similar constructs do in other languages.
<syntaxhighlight lang="lua">
#{ nil, nil, "foo" }
> 3
 
local tbl = {}
tbl[3] = "foo"
#tbl
> 0
 
tbl[1] = "bar"
#tbl
> 1
 
tbl[2] = "baz"
#tbl
> 3
</syntaxhighlight>
|
|}
 
===Scribunto===
:''For a broader, more in-depth guide, see [[gphelp:Extension:Scribunto|Gamepedia Help Wiki]].''
:''See also the full {{Mediawiki|Extension:Scribunto/Lua reference manual|Scribunto/Lua reference manual}}.''
'''Scribunto''' is the name of the extension that enables modules. It is the term used for concepts that are Lua-related but specific to MediaWiki.
 
The Lua on you see on MediaWiki is almost the same as standard Lua except for {{Mediawiki|Lua reference manual#Differences from standard Lua|some changes}} to library functions and packages. The main difference is in how Lua scripts are invoked.
 
====<code>#invoke</code>====
The <code>#invoke</code> {{Mediawiki|Help:Extension:ParserFunctions|parser function}} is what bridges the gap between templates and module scripts.{{Note|It is possible to invoke a module from any page, but it is almost always best to wrap the invocation in a template.}} For example, the {{Mediawiki|Transclusion|transcluded}} content of [[Template:Figurine]] is as follows:
<pre>
{{#invoke:Figurine|Main}}
</pre>
 
This invokes [[Module:Figurine]], which looks be something like:
 
<syntaxhighlight lang="lua">
local p = {}
local h = {}
local utilsGame = require("Module:UtilsGame")
...
 
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
</syntaxhighlight>
 
A page can invoke any function that is a field on the module's '''export table'''. The export table is the <code>table</code> object returned by the module, which is always named <code>p</code> by convention. In this case, <code>Main</code> is the only function in the export table.
 
====<code>args</code>====
Functions called via <code>#invoke</code> are passed a {{Mediawiki|Extension:Scribunto/Lua reference manual#Frame object|Frame object}}. <code>frame.args</code> is a <code>table</code> of the arguments to <code>#invoke</code>—there are none in the above example. However, <code>frame:getParent().args</code> represents the ''template'' arguments. For example, if a page has <code><nowiki>{{Figurine|TMC|Minish Ezlo}}</nowiki></code>, then <code>frame:getParent().args[1]</code> evaluates to the string <code>TMC</code> in that invocation.
 
The <code>#</code> operator and most other table functions don't work on <code>frame.args</code>.
 
====<code>require</code>====
A module can import another module and use its exported functions. This is done with the <code>require</code> function, as shown in the above example with [[Module:UtilsGame]].
 
====<code>mw</code>====
Scribunto adds several libraries that are pre-loaded on all modules as the <code>mw</code> object. The following libraries are of note, in addition to the {{Mediawiki|Extension:Scribunto/Lua reference manual#Base functions|base functions}}:
 
{| class="wikitable"
! Library
! Usage example
|-
|<code>{{Mediawiki|Extension:Scribunto/Lua reference manual#Text library|mw.text}}</code>
|[[Module:List]]
|-
|<code>{{Mediawiki|Extension:Scribunto/Lua reference manual#Title library|mw.title}}</code>
|[[Module:Subpage List]]
|-
|<code>{{Mediawiki|Extension:Scribunto/Lua reference manual#HTML library|mw.html}}</code>
|[[Module:Infobox2]]
|}
 
===Testing and Debugging===
You should never be in a situation where you're blindly submitting code and hoping that it works. In the '''Preview page with this''' form just below the "Save changes" button, input a page name in the '''Page title''' field and click "Show preview" next to it. This will show you how that page will look with your changes to the module. A good page to preview is the module documentation page when it has use cases (as [[Module:UtilsMarkup/doc]] does, for example), or the corresponding template documentation when it has usage examples (e.g. [[Template:Term/Documentation]]). Consequently, [[Module:Documentation/doc|creating documentation examples]] ahead of time and previewing them can be an effective way to develop modules.{{Note|This is in fact a form of {{Wp|test-driven development}}.}}
 
===Exercises===
 
==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:
* [[Module:UtilsGame]]
* [[Module:UtilsMarkup]]
* [[Module:UtilsTable]]
 
A complete list of utility modules can be found [https://zelda.gamepedia.com/Special:PrefixIndex?prefix=Utils&namespace=828 here].
 
Leverage utility modules as much as possible so that the wiki's modules stay {{Wp|Don't repeat yourself|DRY}}.
 
===Higher-Order Functions===
In utility modules such as [[Module:UtilsTable]], you'll often see functions like these:
<syntaxhighlight lang="lua">
utilsTable.isEqual({})({})
> true
</syntaxhighlight>
 
You might've expected the usual function syntax <code>utilsTable.isEqual({}, {})</code> instead. The above is an example of a '''higher-order function'''—a function that returns a function. The function call above is shorthand for:
<syntaxhighlight lang="lua">
local isEmpty = utilsTable.isEqual({})
isEmpty({})
> true
</syntaxhighlight>
 
Functions are written this way for increased reusability. They are often composed with the other type of higher-order function—one that accepts a function as an argument:
<syntaxhighlight lang="lua">
local isEmpty = utilsTable.isEqual({})
local notEmpty = utilsFunction.negate(isEmpty)
local magicWords = utilsTable.filter(notEmpty)({ {}, {}, {"Kooloo"}, {"Limpah"} })
utilsTable.flatten(magicWords)
> { "Kooloo", "Limpah" }
</syntaxhighlight>
 
{{Notes}}


{{Guidelines Nav}}
{{Guidelines Nav}}

Revision as of 03:45, 16 March 2020

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 [Discord Discord] channel.

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

Beginners

Developers

If you already have some programming experience in other languages, note the following particularities of Lua.

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.

The language offers prototype-based inheritance through metatables.

Generally tables 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

Scribunto

For a broader, 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 modules. It is the term used for concepts that are Lua-related but specific to MediaWiki.

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.[note 1] For example, the transcluded content of Template:Figurine is as follows:

{{#invoke:Figurine|Main}}

This invokes Module:Figurine, which looks be something like:

local p = {}
local h = {}
local utilsGame = require("Module:UtilsGame")
...

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. However, frame:getParent().args represents the template arguments. For example, if a page has {{Figurine|TMC|Minish Ezlo}}, then frame:getParent().args[1] evaluates to the string TMC in 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:UtilsGame.

mw

Scribunto adds several libraries that are pre-loaded on all modules 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:Infobox2

Testing and Debugging

You should never be in a situation where you're blindly submitting code and hoping that it works. In the Preview page with this form just below the "Save changes" button, input a page name in the Page title field and click "Show preview" next to it. This will show you how that page will look with your changes to the module. A good page to preview is the module documentation page when it has use cases (as Module:UtilsMarkup/doc does, for example), or the corresponding template documentation when it has usage examples (e.g. Template:Term/Documentation). Consequently, creating documentation examples ahead of time and previewing them can be an effective way to develop modules.[note 2]

Exercises

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 complete list of utility modules can be found here.

Leverage utility modules as much as possible so that the wiki's modules stay DRY.

Higher-Order Functions

In utility modules such as Module:UtilsTable, you'll often see functions like these:

utilsTable.isEqual({})({})
> true

You might've expected the usual function syntax utilsTable.isEqual({}, {}) instead. The above is an example of a higher-order function—a function that returns a function. The function call above is shorthand for:

local isEmpty = utilsTable.isEqual({})
isEmpty({})
> true

Functions are written this way for increased reusability. They are often composed with the other type of higher-order function—one that accepts a function as an argument:

local isEmpty = utilsTable.isEqual({})
local notEmpty = utilsFunction.negate(isEmpty)
local magicWords = utilsTable.filter(notEmpty)({ {}, {}, {"Kooloo"}, {"Limpah"} })
utilsTable.flatten(magicWords)
> { "Kooloo", "Limpah" }

Notes

  1. It is possible to invoke a module from any page, but it is almost always best to wrap the invocation in a template.
  2. This is in fact a form of test-driven development.