If you have been creating templates on your wiki, sooner or later you may have to learn how to use parser functions to create more complex code. Be it either {{#if:}}
or {{#switch:}}
, these help you to have more powerful building blocks for your content.
However, at some point you may face an issue: using too many #if
makes it harder to read the source code. Imagine you wanted to auto-categorize the pages for episodes in your wiki about a TV show with the following rules:
- If there is a
{{{season}}}
parameter, it will add[[Category:Episodes from season {{{season}}}]]
. - If the
{{{season}}}
parameter is missing, it will add[[Category:Episodes without a season]]
so you can find it easily.
In a template, you would need an #if:
as simple as:
{{#if:{{{season|}}}|[[Category:Episodes from season {{{season}}}]]|[[Category:Episodes without a season]]}}
So many {{ }}
and [[ ]]
may start to look confusing, but it isn't terrible... yet. What if we needed to add a new rule?
- If there is a
{{{special}}}
parameter, which determines whether the episode is a special or a regular episode, it must instead add[[Category:Special episodes from season {{{season}}}]]
.
How could we do this? Maybe something like:
{{#if:{{{season|}}}|{{#if:{{{special|}}}|[[Category:Special episodes from season {{{season}}}]]|[[Category:Episodes from season {{{season}}}]]}}|[[Category:Episodes with no season]]}}
Now it is getting a little harder to read, isn't it? We just added a single rule to our logic! Parser functions shine at simple checks, but they get more complicated as you try to implement a more complex logic using them. What should we do, then? As a programming language, Lua is a more powerful tool that helps you to add advanced logic to your templates, while being easier to read if you are familiar with it. But before we can convert the previous example, we need to understand what is it and how can you use it on your wikis through modules.
Lua quickstart
Lua scripts are available in the Module: namespace in all wikis by default. Just like all of your templates start with Template:, all Lua scripts are in pages that start with Module:. Most modules will look like:
local p = {}
function p.main(frame)
-- code
end
return p
Let's do a quick summary of the previous example:
local p
is a variable, which stores an empty table{}
. You need it to keep all of your exported functions bundled together so they can bereturn
-ed at the end. Other modules and pages will be able to use anything in the exported table.- We are creating a function
main
inside our tablep
.frame
is an argument all functions receive when they are called from a page; you can read more about the frame object, but for now we only need to know that it allows you to access the parameters used in the page. For example, in "{{Template|First|Second|hello=world}}
", it would give you access toFirst parameter
,Second parameter
and the value of a parameter namedhello
, which isworld
.
Keep in mind: main
is just an example! You can use any valid name for your functions.
Creating our module
Now, let's get into the actual code! Focus on the new content, which is everything inside the main
function.
local p = {}
function p.categorize(frame)
local args = require('Dev:Arguments').getArgs(frame)
local season = args.season
local special = args.special
if season then
if special then
return string.format( '[[Category:Special episodes from season %s]]', season )
else
return string.format( '[[Category:Episodes from season %s]]', season )
end
else
return '[[Category:Episodes with no season]]'
end
end
return p
Step by step
Let's take a look at what all of this is.
- Accessing the parameters
- There are other ways to access the parameters passed to your module from the page, but the most recommended one is to use Arguments, a Global Lua Module available in all wikis. It exports a function
getArgs
that will give you a table with all arguments from yourframe
object. Using the same template call we had before, "{{Template|First|Second|hello=world}}
", it will give you all arguments in a table like:
{ "First", "Second", hello = "world" }
- Remember that Lua tables start indexing from 1! So you could access any parameter you need as
args[1]
(First),args[2]
(Second) andargs.hello
(world). Accessing any key that doesn't exist will return anil
value, equivalent to JavaScript'sundefined
or Python'sNone
.
- Implementing your logic
- You should already be familiar with
if
statements in programming. Lua's syntax may be different, but easy to understand. - Just like we did before using parser functions, we first check if the
season
parameter was set. If it wasn't, we would have got anil
value from ourargs
table, which would be evaluated asfalse
. - The other part that may have you wondering its meaning is
string.format
. Lua has different ways to "interpolate" strings, and this is just one of them. The first parameter of this function is your template string, where each instance of%s
will be substituted by the following parameters in order (in this case, the value stored inseason
).- If
season
is set, check ifspecial
is also set.- If
special
is set, add a category[[Category:Special episodes from season %s]]
, where%s
will be replaced with the value inseason
. - Else, add a category
[[Category:Episodes from season %s]]
.
- If
- If
season
is not set, add a category[[Category:Episodes with no season]]
.
- If
- Just like a template, the value returned from your module will be inserted into the article.
How do I use it?
If we had the previous example in Module:Episode
, you could call it in any page using {{#invoke:Episode|categorize|season=1}}
. However, you should avoid this. Although the syntax is very similar to templates, it might potentially confuse new editors: #invoke
? Why categorize
? Where is this template?
Usually, your editors will never use modules directly, but instead through templates. For example, in your Template:Episode Infobox
you could call your module at the end as: {{#invoke:Episode|categorize}}
.
You may be wondering: how will it know the season? Well, your frame
object will have access to any parameters passed to {{Episode Infobox}}
! And thanks to the Arguments global module we used before, it will give you easy access to these parameters without having to modify your module. Isn't it great?
Summary
This blog only gave you an idea of how you can use Lua to simplify the logic in your templates, hopefully you will be able to apply it in better scenarios so they are easier to maintain! Here is a short list of when you should consider using Lua:
- Your template has many parser functions, especially if they are nested.
- You need to process any number of parameters. Instead of manually adding
{{{parameter 1}}}
,{{{parameter 2}}}
,{{{parameter 3}}}
and so on as you need, you can manage them using Lua. - Although centralizing your data is considered a bad practice for wikis, sometimes it may be the best solution for your needs. And the best way to centralize data is through Lua modules.
Keep in mind that Lua modules require someone that knows the language to be able to maintain it. Try to follow good programming practices in your module so other people are able to understand what should it do and easily navigate through it if they need to fix anything. For example: use descriptive variables (local ip
doesn't give as much information as local item_price
) and split your code into different functions to make it easier to read. And, of course, add some comments to keep it clear for others!
You have at your disposal all the tools available in a programming language to create powerful modules to use in your articles, instead of only relying on parser functions and other basic features.