Panduan Gaya Pengodean OpenResty
API7.ai
December 15, 2022
Banyak bahasa pemrograman memiliki spesifikasi pengkodean mereka sendiri untuk memberi tahu para pengembang tentang beberapa konvensi di bidang tersebut, menjaga gaya penulisan kode tetap konsisten, dan menghindari beberapa kesalahan umum. PEP 8 Python adalah contoh yang sangat baik dari hal ini, dan hampir semua pengembang Python telah membaca spesifikasi pengkodean yang ditulis oleh penulis Python.
OpenResty belum memiliki spesifikasi pengkodeannya sendiri, dan beberapa pengembang berulang kali ditinjau dan diminta untuk mengubah gaya kode mereka setelah mengirimkan PR, yang menghabiskan banyak waktu dan tenaga yang sebenarnya bisa dihindari.
Ada dua alat Lint di OpenResty yang dapat membantu Anda mendeteksi gaya kode secara otomatis: luacheck dan lj-releng. Yang pertama adalah alat Lint umum di dunia Lua dan OpenResty, sedangkan yang kedua adalah alat Lint yang ditulis dalam Perl oleh OpenResty sendiri.
Untuk diri saya sendiri, saya memasang plugin luacheck di editor VS Code sehingga saya memiliki alat untuk memberikan saran otomatis saat menulis kode; dan di CI sebuah proyek, saya menjalankan kedua alat tersebut, misalnya:
luacheck -q lua ./utils/lj-releng lua/*.lua lua/apisix/*.lua
Bagaimanapun, satu alat lagi untuk pengujian tidak pernah menjadi hal yang buruk.
Namun, kedua alat ini lebih fokus pada pendeteksian variabel global, panjang per baris, dan gaya kode paling dasar lainnya, yang masih jauh dari tingkat detail Python PEP 8, dan tidak ada dokumentasi yang bisa Anda jadikan referensi.
Jadi hari ini, berdasarkan pengalaman saya dalam proyek open-source yang terkait dengan OpenResty, saya telah merangkum dokumentasi gaya pengkodean OpenResty. Spesifikasi ini juga konsisten dengan gaya kode beberapa gateway API seperti APISIX dan Kong.
Indentasi
Di OpenResty, kami menggunakan 4 spasi sebagai penanda indentasi, meskipun Lua tidak memerlukan sintaks seperti itu. Berikut adalah dua contoh kode yang salah dan benar.
--Tidak if a then ngx.say("hello") end
--Ya if a then ngx.say("hello") end
Untuk kemudahan, kita dapat menyederhanakan operasi dengan mengubah tab menjadi 4 spasi di editor yang Anda gunakan.
Spasi
Di kedua sisi operator, diperlukan spasi untuk memisahkan mereka. Berikut adalah dua contoh kode yang salah dan benar.
--Tidak local i=1 local s = "apisix"
--Ya local i = 1 local s = "apisix"
Baris Kosong
Banyak pengembang membawa konvensi pengembangan dari bahasa lain ke OpenResty, seperti menambahkan titik koma di akhir baris:
--Tidak if a then ngx.say("hello"); end;
Namun sebenarnya, menambahkan titik koma membuat kode Lua terlihat sangat jelek, yang tidak perlu. Selain itu, Anda seharusnya tidak mengubah beberapa baris kode menjadi satu baris untuk menghemat baris agar terlihat ringkas. Melakukan hal itu akan membuat Anda tidak tahu bagian kode mana yang salah saat Anda mencari kesalahan.
--Tidak if a then ngx.say("hello") end
--Ya if a then ngx.say("hello") end
Selain itu, fungsi perlu dipisahkan oleh dua baris kosong.
--Tidak local function foo() end local function bar() end
--Ya local function foo() end local function bar() end
Jika ada beberapa cabang if elseif, mereka juga perlu dipisahkan oleh satu baris kosong.
--Tidak if a == 1 then foo() elseif a== 2 then bar() elseif a == 3 then run() else error() end
--Ya if a == 1 then foo() elseif a== 2 then bar() elseif a == 3 then run() else error() end
Panjang Maksimum per Baris
Setiap baris tidak boleh melebihi 80 karakter; jika melebihi itu, kita perlu memotong baris dan menyelaraskannya. Dan saat menyelaraskan pemotongan baris, kita perlu mencerminkan korespondensi antara baris atas dan bawah. Untuk contoh di bawah ini, argumen fungsi pada baris kedua harus berada di sebelah kanan tanda kurung kiri pada baris pertama.
--Tidak return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst, conf.default_conn_delay)
--Ya return limit_conn_new("plugin-limit-conn", conf.conn, conf.burst, conf.default_conn_delay)
Jika itu adalah penyelarasan penggabungan string, kita perlu menempatkan .. di baris berikutnya.
--Tidak return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn" .. "plugin-limit-conn")
--Ya return limit_conn_new("plugin-limit-conn" .. "plugin-limit-conn" .. "plugin-limit-conn")
Variabel
Poin ini juga ditekankan beberapa kali dalam artikel sebelumnya: kita harus selalu menggunakan variabel lokal daripada variabel global.
--Tidak i = 1 s = "apisix"
--Ya local i = 1 local s = "apisix"
Untuk penamaan variabel, gaya snake_case harus digunakan.
--Tidak local IndexArr = 1 local str_Name = "apisix"
--Ya local index_arr = 1 local str_name = "apisix"
Untuk konstanta, di sisi lain, gaya all-caps harus digunakan.
--Tidak local max_int = 65535 local server_name = "apisix" --Ya local MAX_INT = 65535 local SERVER_NAME = "apisix"
Tabel
Di OpenResty, kami menggunakan table.new untuk mengalokasikan tabel terlebih dahulu.
--Tidak local t = {} for i = 1, 100 do t[i] = i end
--Ya local new_tab = require "table.new" local t = new_tab(100, 0) for i = 1, 100 do t[i] = i end
Selain itu, perhatikan bahwa Anda tidak boleh menggunakan nil dalam array, dan jika Anda harus menggunakan null, gunakan ngx.null untuk menandakannya.
--Tidak local t = {1, 2, nil, 3} --Ya local t = {1, 2, ngx.null, 3}
String
Jangan pernah menggabungkan string pada jalur kode yang panas.
--Tidak local s = "" for i = 1, 100000 do s = s .. "a" end
--Ya local t = {} for i = 1, 100000 do t[i] = "a" end local s = table.concat(t, "")
Fungsi
Penamaan fungsi juga mengikuti snake_case.
--Tidak local function testNginx() end
--Ya local function test_nginx() end
Dan fungsi harus mengembalikan nilai secepat mungkin.
--Tidak local function check(age, name) local ret = true if age < 20 then ret = false end if name == "a" then ret = false end -- do something else return ret
--Ya local function check(age, name) if age < 20 then return false end if name == "a" then return false end -- do something else return true
Modul
Semua library yang require harus dilokalkan:
--Tidak local function foo() local ok, err = ngx.timer.at(delay, handler) end
--Ya local timer_at = ngx.timer.at local function foo() local ok, err = timer_at(delay, handler) end
Untuk konsistensi gaya, require dan ngx juga perlu dilokalkan:
--Tidak local core = require("apisix.core") local timer_at = ngx.timer.at local function foo() local ok, err = timer_at(delay, handler) end
--Ya 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
Penanganan Kesalahan
Untuk fungsi yang mengembalikan informasi kesalahan, informasi kesalahan harus dinilai dan diproses:
--Tidak local sock = ngx.socket.tcp() local ok = sock:connect("www.google.com", 80) ngx.say("successfully connected to google!")
--Ya 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!")
Dan dalam kasus fungsi yang ditulis sendiri, informasi kesalahan harus dikembalikan sebagai parameter kedua dalam format string:
--Tidak local function foo() local ok, err = func() if not ok then return false end return true end
--Tidak local function foo() local ok, err = func() if not ok then return false, {msg = err} end return true end
--Ya local function foo() local ok, err = func() if not ok then return false, "failed to call func(): " .. err end return true end
Ringkasan
Ini adalah versi awal dari panduan gaya pengkodean, dan kami akan membuatnya tersedia di GitHub untuk pembaruan dan pemeliharaan yang berkelanjutan. Anda dipersilakan untuk membagikan spesifikasi ini sehingga lebih banyak pengguna OpenResty dapat terlibat.