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.