Guide de style de codage OpenResty
API7.ai
December 15, 2022
De nombreux langages de développement ont leurs propres spécifications de codage pour indiquer aux développeurs certaines conventions dans le domaine, afin de maintenir un style de code cohérent et d'éviter certains pièges courants. Le PEP 8 de Python en est un excellent exemple, et presque tous les développeurs Python ont lu cette spécification de codage écrite par les auteurs de Python.
OpenResty n'a pas encore sa propre spécification de codage, et certains développeurs sont régulièrement revus et invités à modifier leur style de code après avoir soumis des PR, ce qui consomme beaucoup de temps et d'efforts qui pourraient être évités.
Il existe deux outils Lint dans OpenResty qui peuvent vous aider à détecter automatiquement le style de code : luacheck
et lj-releng
. Le premier est un outil Lint commun dans le monde Lua et OpenResty, et le second est un outil Lint écrit en Perl
par OpenResty lui-même.
Personnellement, j'installe le plugin luacheck
dans l'éditeur VS Code pour avoir un outil de suggestion automatique lorsque j'écris du code ; et dans le CI d'un projet, j'exécute les deux outils, par exemple :
luacheck -q lua
./utils/lj-releng lua/*.lua lua/apisix/*.lua
Après tout, un outil de test supplémentaire n'est jamais une mauvaise chose.
Cependant, ces deux outils se concentrent davantage sur la détection des variables globales, la longueur par ligne et d'autres styles de code de base, ce qui est encore loin du niveau détaillé du PEP 8 de Python, et il n'y a pas de documentation à laquelle se référer.
Aujourd'hui, en me basant sur mon expérience dans les projets open source liés à OpenResty, j'ai résumé la documentation du style de codage OpenResty. Cette spécification est également cohérente avec le style de code de certaines passerelles API comme APISIX et Kong.
Indentation
Dans OpenResty, nous utilisons 4 espaces comme marqueurs d'indentation, bien que Lua ne nécessite pas une telle syntaxe. Voici deux exemples de codes incorrect et correct.
--Non
if a then
ngx.say("hello")
end
--Oui
if a then
ngx.say("hello")
end
Pour plus de commodité, nous pouvons simplifier l'opération en changeant la tabulation en 4 espaces dans l'éditeur que vous utilisez.
Espace
Des deux côtés de l'opérateur, un espace est nécessaire pour les séparer. Voici deux exemples de codes incorrect et correct.
--Non
local i=1
local s = "apisix"
--Oui
local i = 1
local s = "apisix"
Ligne vide
De nombreux développeurs apportent des conventions de développement d'autres langages à OpenResty, comme ajouter un point-virgule à la fin d'une ligne :
--Non
if a then
ngx.say("hello");
end;
Mais en réalité, ajouter des points-virgules rend le code Lua très laid, ce qui est inutile. De plus, vous ne devriez pas transformer plusieurs lignes de code en une seule ligne pour économiser des lignes et être concis. Cela vous laissera sans idée de quelle section de code est en faute lorsque vous localiserez l'erreur.
--Non
if a then ngx.say("hello") end
--Oui
if a then
ngx.say("hello")
end
De plus, les fonctions doivent être séparées par deux lignes vides.
--Non
local function foo()
end
local function bar()
end
--Oui
local function foo()
end
local function bar()
end
S'il y a plusieurs branches if elseif
, elles doivent également être séparées par une ligne vide.
--Non
if a == 1 then
foo()
elseif a== 2 then
bar()
elseif a == 3 then
run()
else
error()
end
--Oui
if a == 1 then
foo()
elseif a== 2 then
bar()
elseif a == 3 then
run()
else
error()
end
Longueur maximale par ligne
Chaque ligne ne doit pas dépasser 80 caractères ; si elle dépasse cela, nous devons faire un saut de ligne et aligner. Et lors de l'alignement des sauts de ligne, nous devons refléter la correspondance entre les lignes supérieure et inférieure. Pour l'exemple ci-dessous, l'argument de la fonction sur la deuxième ligne doit être à droite de la parenthèse gauche sur la première ligne.
--Non
return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst, conf.default_conn_delay)
--Oui
return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst,
conf.default_conn_delay)
S'il s'agit de l'alignement de la concaténation de chaînes, nous devons mettre ..
sur la ligne suivante.
--Non
return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn" ..
"plugin-limit-conn")
--Oui
return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn"
.. "plugin-limit-conn")
Variable
Ce point a également été souligné plusieurs fois dans des articles précédents : nous devrions toujours utiliser des variables locales plutôt que des variables globales.
--Non
i = 1
s = "apisix"
--Oui
local i = 1
local s = "apisix"
En ce qui concerne la nomination des variables, le style snake_case
doit être utilisé.
--Non
local IndexArr = 1
local str_Name = "apisix"
--Oui
local index_arr = 1
local str_name = "apisix"
Pour les constantes, en revanche, le style all-caps
doit être utilisé.
--Non
local max_int = 65535
local server_name = "apisix"
--Oui
local MAX_INT = 65535
local SERVER_NAME = "apisix"
Table
Dans OpenResty, nous utilisons table.new
pour pré-allouer la table.
--Non
local t = {}
for i = 1, 100 do
t[i] = i
end
--Oui
local new_tab = require "table.new"
local t = new_tab(100, 0)
for i = 1, 100 do
t[i] = i
end
De plus, notez que vous ne devez pas utiliser nil
dans le tableau, et si vous devez utiliser null, utilisez ngx.null
pour l'indiquer.
--Non
local t = {1, 2, nil, 3}
--Oui
local t = {1, 2, ngx.null, 3}
Chaîne de caractères
Ne jamais concaténer des chaînes sur un chemin de code critique.
--Non
local s = ""
for i = 1, 100000 do
s = s .. "a"
end
--Oui
local t = {}
for i = 1, 100000 do
t[i] = "a"
end
local s = table.concat(t, "")
Fonction
La nomination des fonctions suit également snake_case
.
--Non
local function testNginx()
end
--Oui
local function test_nginx()
end
Et la fonction doit retourner le plus tôt possible.
--Non
local function check(age, name)
local ret = true
if age < 20 then
ret = false
end
if name == "a" then
ret = false
end
-- faire quelque chose d'autre
return ret
--Oui
local function check(age, name)
if age < 20 then
return false
end
if name == "a" then
return false
end
-- faire quelque chose d'autre
return true
Module
Toutes les bibliothèques require
doivent être local
isées :
--Non
local function foo()
local ok, err = ngx.timer.at(delay, handler)
end
--Oui
local timer_at = ngx.timer.at
local function foo()
local ok, err = timer_at(delay, handler)
end
Pour la cohérence du style, require
et ngx
doivent également être local
isés :
--Non
local core = require("apisix.core")
local timer_at = ngx.timer.at
local function foo()
local ok, err = timer_at(delay, handler)
end
--Oui
local ngx = ngx
local require = require
local core = require("apisix.core")
local timer_at = ngx.timer.at
local function foo()
local ok, err = timer_at(delay, handler)
end
Gestion des erreurs
Pour les fonctions qui retournent des informations d'erreur, les informations d'erreur doivent être jugées et traitées :
--Non
local sock = ngx.socket.tcp()
local ok = sock:connect("www.google.com", 80)
ngx.say("successfully connected to google!")
--Oui
local sock = ngx.socket.tcp()
local ok, err = sock:connect("www.google.com", 80)
if not ok then
ngx.say("failed to connect to google: ", err)
return
end
ngx.say("successfully connected to google!")
Et dans le cas des fonctions que vous écrivez vous-même, les informations d'erreur doivent être retournées comme deuxième paramètre sous forme de chaîne :
--Non
local function foo()
local ok, err = func()
if not ok then
return false
end
return true
end
--Non
local function foo()
local ok, err = func()
if not ok then
return false, {msg = err}
end
return true
end
--Oui
local function foo()
local ok, err = func()
if not ok then
return false, "failed to call func(): " .. err
end
return true
end
Résumé
Ceci est une version initiale du guide de style de codage, et nous la rendrons disponible sur GitHub pour des mises à jour et une maintenance continues. Vous êtes invités à partager cette spécification afin que plus d'utilisateurs d'OpenResty puissent s'impliquer.