Initiation au Lua avec Scribunto/L'objet Frame

Début de la boite de navigation du chapitre

Ce paragraphe étudie plus en détail l’objet frame que l’on peut considérer comme étant l'interface avec le programme. C'est l’objet frame qui reçoit les paramètres que l’on envoie au programme grâce à la commande #invoke.

L'objet Frame
Icône de la faculté
Chapitre no 12
Leçon : Initiation au Lua avec Scribunto
Chap. préc. :Librairies Scribunto
Chap. suiv. :L'objet Title

Exercices :

Sur l’objet Frame
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Initiation au Lua avec Scribunto : L'objet Frame
Initiation au Lua avec Scribunto/L'objet Frame
 », n'a pu être restituée correctement ci-dessus.

Nature de l’objet frame modifier

Une remarque que l’on peut faire à propos de l’objet frame et de la façon de l’utiliser comme l'accès aux arguments frame.args[1], frame.args[2], etc. est la ressemblance avec les notations relatives aux tables. frame serait une table et frame.args que l’on pourrait aussi noter frame["args"] serait une table, indexée par la chaîne de caractères "args", se trouvant dans la table frame. Par contre frame.args serait une table à accès numérique. C'est pour cela que l’on note ses arguments frame.args[1], frame.args[2], etc.

En réalité, ce que l’on vient de dire n’est pas tout à fait vrai. À l'appel d'un module avec #invoke, la table frame n’est pas créée et remplie tel que l’on vient de le dire. Quand l’on fait appel à un élément de la table numérique frame.arg, une méta-méthode est activée pour demander l’objet souhaité au logiciel MediaWiki. La table frame est en réalité une méta-table remplie de méta-méthodes dont la fonction est de simuler l’existence d'une table qui contiendrait les éléments décrits plus haut alors, qu'en réalité, ils sont demandés au fur et à mesure des besoins au logiciel MediaWiki.

Comme "frame" n’est pas une vraie table, on n’est pas assuré que toutes les fonctions agissant sur les tables vont fonctionner correctement. Nous devons donc connaître la liste des fonctions qui fonctionnent correctement et la liste des fonctions qui ne fonctionnent pas correctement, ces deux listes étant susceptibles d’être modifiées au fil des versions de Lua et de Scribunto.

Les fonctions sur lesquelles, on peut compter actuellement sont ipairs et pairs.

Nous allons donc donner un exemple simple utilisant la fonction ipair.

Dans le Module:Frame, écrivons une fonction p.evalue qui compte combien il y a d'arguments contenant une valeur numérique et combien il y a d'arguments contenant une chaîne de caractères.

local p = {}

function p.evalue(frame)
	local reponse = " "
	local nombre,chaine = 0,0
	for index, objet in ipairs(frame.args) do
		if tonumber(objet) == nil then
			chaine = chaine + 1
		else
			nombre = nombre + 1
		end
	end
	reponse = reponse.."Il y a "..nombre.." nombres et "..chaine.." chaines de caractères."
	return reponse
end

return p


{{#invoke:Frame|evalue|Laura|54|76|Mouchoir|17|Schtroumpfs|11|2}}, nous donne : Il y a 5 nombres et 3 chaines de caractères.


Arguments avec clé sous forme de chaîne de caratères modifier

Jusqu'à maintenant, nous avons déclaré, dans la commande #invoke, les arguments à la suite les uns des autres comme on déclare les objets d'une table à clé numérique. Le premier argument s'associant automatiquement à la clé 1, le deuxième à la clé 2 et ainsi de suite. Toutefois, de même que l’on a vu que, dans les tables, il est possible de créer des clés sous forme de chaîne de caractères, nous allons voir qu’il en est de même pour la pseudo-table frame.args. Pour cela, il suffit de déclarer, dans la commande #invoke, les arguments avec clé sous forme de chaîne de caractères comme on le ferait pour une table.

Par exemple, on écrira : {{#invoke:''Module''|''fonction''|fleur=rose}}


Il est à noter que la déclaration : {{#invoke:''Module''|''fonction''|["fleur"]=rose}} n’est pas correcte car ["fleur"] sera interprété comme la chaîne de caractère "["fleur"]" (voir l'exemple ci-dessous avec ("personnage"]).

Écrivons un exemple pour voir si cela marche bien !


Dans le Module:Frame, écrivons une fonction p.renvoie qui nous renvoie les clés utilisés avec les objets correspondants.

local p = {}

function p.renvoie(frame)
	local reponse = " "
	for index, objet in pairs(frame.args) do
		reponse = reponse.."<br>À la clé "..index..", il y a l’objet : "..objet
	end
	return reponse
end

return p


{{#invoke:Frame|renvoie|prénom=Laura|nombre=54|76|Mouchoir|17|["personnage"]=Schtroumpfs|nombre premier=11|2}}, nous donne :
À la clé 1, il y a l’objet : 76
À la clé 2, il y a l’objet : Mouchoir
À la clé 3, il y a l’objet : 17
À la clé 4, il y a l’objet : 2
À la clé prénom, il y a l’objet : Laura
À la clé nombre, il y a l’objet : 54
À la clé nombre premier, il y a l’objet : 11
À la clé ["personnage"], il y a l’objet : Schtroumpfs


Dans le programme précédent, nous avons utilisé la fonction pairs. Si nous avions utilisé la fonction ipairs, tous les objets dont la clé est une chaîne de caractères auraient été ignorés.


Fonctions en relation avec l’objet frame modifier

Tous les exemples de ce paragraphe se trouvent dans le Module:Frame.


Les fonctions que nous allons étudier dans ce paragraphe se trouvent dans l’objet frame. Nous commencerons donc par écrire une fonction p.visualisation permettant de les visualiser.

(Ici, contrairement aux paragraphes précédents, nous visualisons ce qui se trouve dans frame et pas ce qui se trouve dans frame.args.)


local p = {}

function p.visualise(frame)
	reponse = ""
	for index, objet in pairs(frame) do
		reponse = reponse.."<br />À la clé "..index..", on trouve un objet de type : "..type(objet)
	end
	return reponse
end

return p


{{#invoke:Frame|visualise}} nous donne :
À la clé newTemplateParserValue, on trouve un objet de type : function
À la clé getParent, on trouve un objet de type : function
À la clé argumentPairs, on trouve un objet de type : function
À la clé extensionTag, on trouve un objet de type : function
À la clé callParserFunction, on trouve un objet de type : function
À la clé preprocess, on trouve un objet de type : function
À la clé getTitle, on trouve un objet de type : function
À la clé args, on trouve un objet de type : table
À la clé newParserValue, on trouve un objet de type : function
À la clé expandTemplate, on trouve un objet de type : function
À la clé getArgument, on trouve un objet de type : function
À la clé newChild, on trouve un objet de type : function


En dehors de la clé args qui référence la table contenant les arguments transmis par la commande #invoke, nous voyons que nous disposons de 11 fonctions que nous allons étudier en détail ci-dessous.

Une petite remarque que l’on peut faire ici est que la notation des fonctions diffère légèrement de ce que l’on avait mentionné auparavant. En effet, au lieu de noter, par exemple, la deuxième fonction de la liste frame.getParent (avec .), nous la noterons frame:getParent (avec :). Cette différence de notation tient au type de propriété à laquelle on accède. Le point permet d'accéder à la propriété définie dans l'objet frame tandis que le deux-points permet d'exécuter une fonction définie sur l'objet.

Autre remarque, les fonctions des librairies sont accessibles comme des propriétés. C'est pourquoi on utilise le point pour accéder à une fonction d'une librairie et non le deux-points car la librairie n’est pas instanciée (n'est pas un objet) : string.len(s). Quand on a à faire à un objet, l'appel à la méthode se fait par les deux-points : s:len().


Comme d'habitude, les fonctions ne seront pas étudiées dans l’ordre !


frame:getParent modifier

Cette fonction permet de connaître, à l'intérieur du module, les arguments du modèle qui a appelé le module où se trouve la fonction frame:getParent.

La fonction frame:getParent retourne une table contenant une table, indexée par la chaîne de caractère "args", qui contient tous les paramètres passés au modèle. Par exemple, si la fonction frame:getParent se trouve dans un module placé dans un modèle dont l'appel se fait ainsi :

{{Essai
  | titre   = stat
  | compte = ouvert
}}

Et si dans le module, nous avons l'instruction :

local model = frame:getParent()

Alors model sera une table contenant la table model.args. La table model.args aura deux éléments : l'élément "stat" indexé par la chaîne de caractère "titre" et l'élément "ouvert" indexé par la chaîne de caractère "compte"


Si le modèle appelant possède un lien qui n’est pas renseigné, alors La table model.args retournera, à l'index correspondant, la chaîne vide "" (et pas nil).


À titre d'exemple, dans le Module:Frame nous écrivons la fonction p.Parent ainsi :

local p = {}

function p.parent(frame)
	local reponse = ""
	local model = frame:getParent()
	for index, objet in pairs(model.args) do
		reponse = reponse.."<br>À la clé "..index..", il y a l’objet : "..objet
	end
	return reponse
end

return p


Nous créons ensuite un Modèle:Parents dans lequel nous écrivons :

Les arguments du présent modèle sont :{{#invoke:Frame|parent}}


Nous donnerons deux exemples.


Premier exemple :

{{Parents|Grenouille|rouge|4}}

nous donne :

Les arguments du présent modèle sont :
À la clé 1, il y a l’objet : Grenouille
À la clé 2, il y a l’objet : rouge
À la clé 3, il y a l’objet : 4



Deuxième exemple :

{{Parents
  | titre   = Planing
  | couleur = vert
  | nombre  = 63
}}

nous donne :

Les arguments du présent modèle sont :
À la clé nombre, il y a l’objet : 63
À la clé titre, il y a l’objet : Planing
À la clé couleur, il y a l’objet : vert



frame:newChild modifier

Cette fonction permet d’utiliser des fonctions qui, normalement, attendent des arguments provenant d'une commande #invoke.

Dans l'exemple ci-dessous la fonction p.replique est rédigée comme si elle devait être utilisée grâce à l'appel {{#invoke:Frame|replique|jardin|maison}} par exemple. La fonction p.child fabrique un nouvel objet frame avec pour arguments "jeudi" et frame.args[2] et appelle la fonction p.replique sous la forme p.replique(newFrame), ce qui est alors équivalent à un appel du type {{#invoke:Frame|replique|jeudi|frame.args[2]}} que l’on n'aurait pas pu faire puisque nous ne pouvons pas utiliser la commande #invoke dans un module.

local p = {}

function p.replique(frame)
	return "Je renvoie "..frame.args[1].." et "..frame.args[2]
end

function p.child(frame)
	newFrame = frame:newChild{args={"Jeudi",frame.args[2]}}
	return p.replique(newFrame)
end

return p

{{#invoke:Frame|child|3|Tulipe|7|janvier}} nous indique : Je renvoie Jeudi et Tulipe


frame:preprocess modifier

Nous avons déjà eu l’occasion d'étudier cette fonction qui permet d'interpréter les modèles avant qu’ils ne soient retournés (voir le chapitre sur la Gestion de l'environnement).

Dans l'exemple ci-dessous, nous n'avons pas retourné directement ce que donnait la fonction frame:preprocess, mais nous avons commencé par stocker le retour dans la variable reponse et nous avons ensuite retourné la variable reponse. Ceci montre que ce que retourne la fonction frame:preprocess peut éventuellement être mémorisé dans une variable et subir un éventuel traitement dans le programme.

local p = {}

function p.process()
	local reponse = ""
	reponse = frame:preprocess("{{Attention|Essai avec le modèle Attention}}")
	return "<br>La fonction a retourné : "..reponse
end

return p

{{#invoke:Frame|process}} nous indique :
La fonction a retourné :

  Essai avec le modèle Attention


frame:expandTemplate modifier

La fonction "frame:expandTemplate" réalise à peu près la même chose que la fonction "frame:preprocess", mais uniquement pour les modèles. Par conséquent, pour les modèles, on préférera utiliser la fonction "frame:expandTemplate" car elle est plus rapide et moins sujette aux erreurs que la fonction "frame:preprocess".

Ci-dessous, nous donnons un exemple, réalisant la même chose, que celui donné pour la fonction "frame:preprocess".

local p = {}

function p.template(frame)
	reponse = frame:expandTemplate({ title = "Attention", args = { "Essai avec le modèle Attention" } })
	return reponse
end

return p

{{#invoke:Frame|template}} nous indique :

  Essai avec le modèle Attention

frame:argumentPairs modifier

L'écriture frame:argumentPairs() est strictement identique à l'écriture pairs( frame.args ) comme nous pouvons le constater dans l'exemple ci-dessous :

local p = {}

function p.pair(frame)
	local reponse = ""
	local nombre,chaine = 0,0
	for index, objet in frame:argumentPairs() do
		reponse = reponse.."<br>À la clé "..index..", il y a l’objet : "..objet
	end
	return reponse
end
return p

{{#invoke:Frame|pair|3|Tulipe|7|janvier}} nous indique :
À la clé 1, il y a l’objet : 3
À la clé 2, il y a l’objet : Tulipe
À la clé 3, il y a l’objet : 7
À la clé 4, il y a l’objet : janvier


En fait la fonction frame:argumentPairs n'existe que pour des raisons de compatibilité avec les versions antérieures de Scribunto.

Aujourd'hui, on préfèrera écrire pairs( frame.args ) à la place de frame:argumentPairs()

frame:callParserFunction modifier

Cette fonction permet de faire appel à un des multiples parseurs (analyseurs) disponibles dans l'extension ParserFunctions. Ces fonctions permettent d'analyser des chaînes de caractères selon certains critères et de s'assurer qu'elles sont valides et utilisables.

local p = {}

function p.callParserFunction(frame)
    return ';callParserFunction\n:' .. frame:callParserFunction('#time', 'Y-m-d H:i:s') .. '\n'
end

return p

{{#invoke:Frame|parser}} nous indique : La fonction a retourné : 2024-03-24 16:29:52 et son type est : string

frame:extensionTag modifier

Cette fonction permet de poser un tag autour d'un texte. Un exemple type est le tag <nowiki> interdisant à l'interpréteur de page d'exécuter un contenu. Ainsi, si un contenu renvoyé par une fonction est susceptible d'être interprété comme un modèle, un lien ou autre, on peut utiliser la fonction frame:extensionTag() pour placer de manière automatique les balises <nowiki>.

Dans l’exemple suivant, on retourne le texte [[texte]] avec et sans l'utilisation de extensionTag.

local p = {}

function p.tag(frame)
	local reponse = ""
	reponse = frame:extensionTag('nowiki', '[[texte]]', {})
	if reponse == nil then
		return "Il y a un problème, la fonction a retourné : nil"
	else
		return "La fonction a retourné : "..reponse.." et son type est : "..type(reponse)
	end
end

function p.notag(frame)
	local reponse = ""
	reponse = '[[texte]]'
	if reponse == nil then
		return "Il y a un problème, la fonction a retourné : nil"
	else
		return "La fonction a retourné : "..reponse.." et son type est : "..type(reponse)
	end
end

return p

Rendu avec extensionTag : {{#invoke:Frame|tag}} nous indique : La fonction a retourné : [[texte]] et son type est : string

Rendu sans extensionTag : {{#invoke:Frame|notag}} nous indique : La fonction a retourné : texte et son type est : string

frame:getArgument modifier

Cette fonction permet de récupérer un argument passé en paramètre au module. Il y a 2 signatures possibles :

  1. frame:getArgument(number) - où number représente l'index de l’argument à récupérer
  2. frame:getArgument(name) - où name représente le nom de la clé associée à l’argument à récupérer

La fonction retourne un objet offrant une méthode expand() permettant de récupérer la valeur interprétée. Cette valeur est obtenu en appelant : objet:expand() où objet est la variable dans laquelle a été stockée le résultat de l'exécution de la fonction.

Le type de l’objet étant une table, on peut en parcourir le contenu avec les fonctions pairs() ou ipairs(). La valeur obtenue sera alors brute, c'est-à-dire non interprétée.

local p = {}

function p.argument(frame)
	local reponse = ""
	reponse = frame:getArgument(1)
	if reponse == nil then
		return "Il y a un problème, la fonction a retourné : nil"
	else
		return "La fonction a retourné : "..reponse:expand().." et son type est : "..type(reponse)
	end
end

function p.namedArgument(frame)
	local reponse = ""
	reponse = frame:getArgument("fleur")
	if reponse == nil then
		return "Il y a un problème, la fonction a retourné : nil"
	else
		return "La fonction a retourné : "..reponse:expand().." et son type est : "..type(reponse)
	end
end

return p

{{#invoke:Frame|argument|3|fleur=Tulipe}} nous indique : La fonction a retourné : 3 et son type est : table

{{#invoke:Frame|namedArgument|3|fleur=Tulipe}} nous indique : La fonction a retourné : Tulipe et son type est : table

frame:newParserValue modifier

local p = {}

return p

{{#invoke:Frame|parval|3|Tulipe|7|janvier}} nous indique : La fonction a retourné une variable de type : table qui contient :


frame:newTemplateParserValue modifier

local p = {}

return p

{{#invoke:Frame|temparval|3|Tulipe|7|janvier}} nous indique : Erreur Lua dans mw.lua à la ligne 435 : frame:newTemplateParserValue: a title is required.

frame:getTitle modifier

Cette fonction renvoie le nom du module Lua invoqué.

local p = {}

function p.title(frame)
	local reponse = ""
	reponse = frame:getTitle()
	if reponse == nil then
		return "Il y a un problème, la fonction a retourné : nil"
	else
		return "La fonction a retourné : "..reponse.." et son type est : "..type(reponse)
	end
end

return p

{{#invoke:Frame|title}} nous indique : La fonction a retourné : Module:Frame et son type est : string

Liens Connexes modifier