aboutsummaryrefslogtreecommitdiffstats
path: root/lua_style.md
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2018-12-22 14:30:39 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2018-12-22 14:30:39 +0000
commit51b39f712d38cb69ede71d62e84c9079234cdb70 (patch)
tree1b6b51bef972e765fef34d23f75460088df5baa3 /lua_style.md
parent178c262919039cb24c2d20498c3d27e89a4620fc (diff)
downloadrspamd-51b39f712d38cb69ede71d62e84c9079234cdb70.tar.gz
rspamd-51b39f712d38cb69ede71d62e84c9079234cdb70.zip
[Minor] Doc: Add Lua style guide
Diffstat (limited to 'lua_style.md')
-rw-r--r--lua_style.md733
1 files changed, 733 insertions, 0 deletions
diff --git a/lua_style.md b/lua_style.md
new file mode 100644
index 000000000..aec6059dc
--- /dev/null
+++ b/lua_style.md
@@ -0,0 +1,733 @@
+# Lua Style Guide
+
+This style guide contains a list of guidelines that we try to follow for Rspamd.
+
+This guide is forked from https://github.com/Olivine-Labs/lua-style-guide
+
+
+## <a name='TOC'>Table of Contents</a>
+
+ 1. [Types](#types)
+ 1. [Tables](#tables)
+ 1. [Strings](#strings)
+ 1. [Functions](#functions)
+ 1. [Properties](#properties)
+ 1. [Variables](#variables)
+ 1. [Conditional Expressions & Equality](#conditionals)
+ 1. [Blocks](#blocks)
+ 1. [Whitespace](#whitespace)
+ 1. [Commas](#commas)
+ 1. [Semicolons](#semicolons)
+ 1. [Type Casting & Coercion](#type-coercion)
+ 1. [Naming Conventions](#naming-conventions)
+ 1. [Accessors](#accessors)
+ 1. [Constructors](#constructors)
+ 1. [Modules](#modules)
+ 1. [Testing](#testing)
+ 1. [License](#license)
+
+## <a name='types'>Types</a>
+
+ - **Primitives**: When you access a primitive type you work directly on its value
+
+ + `string`
+ + `number`
+ + `boolean`
+ + `nil`
+
+ ```lua
+ local foo = 1
+ local bar = foo
+
+ bar = 9
+
+ print(foo, bar) -- => 1 9
+ ```
+
+ - **Complex**: When you access a complex type you work on a reference to its value
+
+ + `table`
+ + `function`
+ + `userdata`
+
+ ```lua
+ local foo = { 1, 2 }
+ local bar = foo
+
+ bar[0] = 9
+ foo[1] = 3
+
+ print(foo[0], bar[0]) -- => 9 9
+ print(foo[1], bar[1]) -- => 3 3
+ print(foo[2], bar[2]) -- => 2 2
+ ```
+
+ **[[⬆]](#TOC)**
+
+## <a name='tables'>Tables</a>
+
+ - Use the constructor syntax for table property creation where possible.
+
+ ```lua
+ -- bad
+ local player = {}
+ player.name = 'Jack'
+ player.class = 'Rogue'
+
+ -- good
+ local player = {
+ name = 'Jack',
+ class = 'Rogue'
+ }
+ ```
+
+ - Define functions externally to table definition.
+
+ ```lua
+ -- bad
+ local player = {
+ attack = function()
+ -- ...stuff...
+ end
+ }
+
+ -- good
+ local function attack()
+ end
+
+ local player = {
+ attack = attack
+ }
+ ```
+
+ **[[⬆]](#TOC)**
+
+## <a name='strings'>Strings</a>
+
+ - Use single quotes `''` for strings.
+
+ ```lua
+ -- bad
+ local name = "Bob Parr"
+
+ -- good
+ local name = 'Bob Parr'
+
+ -- bad
+ local fullName = "Bob " .. self.lastName
+
+ -- good
+ local fullName = 'Bob ' .. self.lastName
+ ```
+
+ - Strings longer than 80 characters should be written across multiple lines
+ using concatenation. This allows you to indent nicely.
+
+ ```lua
+ -- bad
+ local errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'
+
+ -- bad
+ local errorMessage = 'This is a super long error that \
+ was thrown because of Batman. \
+ When you stop to think about \
+ how Batman had anything to do \
+ with this, you would get nowhere \
+ fast.'
+
+
+ -- bad
+ local errorMessage = [[This is a super long error that
+ was thrown because of Batman.
+ When you stop to think about
+ how Batman had anything to do
+ with this, you would get nowhere
+ fast.]]
+
+ -- good
+ local errorMessage = 'This is a super long error that ' ..
+ 'was thrown because of Batman. ' ..
+ 'When you stop to think about ' ..
+ 'how Batman had anything to do ' ..
+ 'with this, you would get nowhere ' ..
+ 'fast.'
+ ```
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='functions'>Functions</a>
+ - Prefer lots of small functions to large, complex functions. [Smalls Functions Are Good For The Universe](http://kikito.github.io/blog/2012/03/16/small-functions-are-good-for-the-universe/).
+
+ - Prefer function syntax over variable syntax. This helps differentiate
+ between named and anonymous functions.
+
+ ```lua
+ -- bad
+ local nope = function(name, options)
+ -- ...stuff...
+ end
+
+ -- good
+ local function yup(name, options)
+ -- ...stuff...
+ end
+ ```
+
+ - Never name a parameter `arg`, this will take precendence over the `arg` object that is given to every function scope in older versions of Lua.
+
+ ```lua
+ -- bad
+ local function nope(name, options, arg)
+ -- ...stuff...
+ end
+
+ -- good
+ local function yup(name, options, ...)
+ -- ...stuff...
+ end
+ ```
+
+ - Perform validation early and return as early as possible.
+
+ ```lua
+ -- bad
+ local is_good_name = function(name, options, arg)
+ local is_good = #name > 3
+ is_good = is_good and #name < 30
+
+ -- ...stuff...
+
+ return is_bad
+ end
+
+ -- good
+ local is_good_name = function(name, options, args)
+ if #name < 3 or #name > 30 then return false end
+
+ -- ...stuff...
+
+ return true
+ end
+ ```
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='properties'>Properties</a>
+
+ - Use dot notation when accessing known properties.
+
+ ```lua
+ local luke = {
+ jedi = true,
+ age = 28
+ }
+
+ -- bad
+ local isJedi = luke['jedi']
+
+ -- good
+ local isJedi = luke.jedi
+ ```
+
+ - Use subscript notation `[]` when accessing properties with a variable
+ or if using a table as a list.
+
+ ```lua
+ local luke = {
+ jedi = true,
+ age = 28
+ }
+
+ local function getProp(prop)
+ return luke[prop]
+ end
+
+ local isJedi = getProp('jedi')
+ ```
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='variables'>Variables</a>
+
+ - Always use `local` to declare variables. Not doing so will result in
+ global variables to avoid polluting the global namespace.
+
+ ```lua
+ -- bad
+ superPower = SuperPower()
+
+ -- good
+ local superPower = SuperPower()
+ ```
+
+ - Assign variables at the top of their scope where possible. This makes it
+ easier to check for existing variables.
+
+ ```lua
+ -- bad
+ local bad = function()
+ test()
+ print('doing stuff..')
+
+ //..other stuff..
+
+ local name = getName()
+
+ if name == 'test' then
+ return false
+ end
+
+ return name
+ end
+
+ -- good
+ local function good()
+ local name = getName()
+
+ test()
+ print('doing stuff..')
+
+ //..other stuff..
+
+ if name == 'test' then
+ return false
+ end
+
+ return name
+ end
+ ```
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='conditionals'>Conditional Expressions & Equality</a>
+
+ - False and nil are *falsy* in conditional expressions. All else is true.
+
+ ```lua
+ local str = ''
+
+ if str then
+ -- true
+ end
+ ```
+
+ - Use shortcuts when you can, unless you need to know the difference between
+ false and nil.
+
+ ```lua
+ -- bad
+ if name ~= nil then
+ -- ...stuff...
+ end
+
+ -- good
+ if name then
+ -- ...stuff...
+ end
+ ```
+
+ - Prefer *true* statements over *false* statements where it makes sense.
+ Prioritize truthy conditions when writing multiple conditions.
+
+ ```lua
+ --bad
+ if not thing then
+ -- ...stuff...
+ else
+ -- ...stuff...
+ end
+
+ --good
+ if thing then
+ -- ...stuff...
+ else
+ -- ...stuff...
+ end
+ ```
+
+ - Prefer defaults to `else` statements where it makes sense. This results in
+ less complex and safer code at the expense of variable reassignment, so
+ situations may differ.
+
+ ```lua
+ --bad
+ local function full_name(first, last)
+ local name
+
+ if first and last then
+ name = first .. ' ' .. last
+ else
+ name = 'John Smith'
+ end
+
+ return name
+ end
+
+ --good
+ local function full_name(first, last)
+ local name = 'John Smith'
+
+ if first and last then
+ name = first .. ' ' .. last
+ end
+
+ return name
+ end
+ ```
+
+ - Short ternaries are okay.
+
+ ```lua
+ local function default_name(name)
+ -- return the default 'Waldo' if name is nil
+ return name or 'Waldo'
+ end
+
+ local function brew_coffee(machine)
+ return machine and machine.is_loaded and 'coffee brewing' or 'fill your water'
+ end
+ ```
+
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='blocks'>Blocks</a>
+
+ - Single line blocks are okay for *small* statements. Try to keep lines to 80 characters.
+ Indent lines if they overflow past the limit.
+
+ ```lua
+ -- good
+ if test then return false end
+
+ -- good
+ if test then
+ return false
+ end
+
+ -- bad
+ if test < 1 and do_complicated_function(test) == false or seven == 8 and nine == 10 then do_other_complicated_function()end
+
+ -- good
+ if test < 1 and do_complicated_function(test) == false or
+ seven == 8 and nine == 10 then
+
+ do_other_complicated_function()
+ return false
+ end
+ ```
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='whitespace'>Whitespace</a>
+
+ - Use soft tabs set to 2 spaces.
+
+ ```lua
+ -- bad
+ function()
+ ∙∙∙∙local name
+ end
+
+ -- bad
+ function()
+ ∙local name
+ end
+
+ -- good
+ function()
+ ∙∙local name
+ end
+ ```
+
+ - Place 1 space before opening and closing braces. Place no spaces around parens.
+
+ ```lua
+ -- bad
+ local test = {one=1}
+
+ -- good
+ local test = { one = 1 }
+
+ -- bad
+ dog.set('attr',{
+ age = '1 year',
+ breed = 'Bernese Mountain Dog'
+ })
+
+ -- good
+ dog.set('attr', {
+ age = '1 year',
+ breed = 'Bernese Mountain Dog'
+ })
+ ```
+
+ - Place an empty newline at the end of the file.
+
+ ```lua
+ -- bad
+ (function(global)
+ -- ...stuff...
+ end)(self)
+ ```
+
+ ```lua
+ -- good
+ (function(global)
+ -- ...stuff...
+ end)(self)
+
+ ```
+
+ - Surround operators with spaces.
+
+ ```lua
+ -- bad
+ local thing=1
+ thing = thing-1
+ thing = thing*1
+ thing = 'string'..'s'
+
+ -- good
+ local thing = 1
+ thing = thing - 1
+ thing = thing * 1
+ thing = 'string' .. 's'
+ ```
+
+ - Use one space after commas.
+
+ ```lua
+ --bad
+ local thing = {1,2,3}
+ thing = {1 , 2 , 3}
+ thing = {1 ,2 ,3}
+
+ --good
+ local thing = {1, 2, 3}
+ ```
+
+ - Add a line break after multiline blocks.
+
+ ```lua
+ --bad
+ if thing then
+ -- ...stuff...
+ end
+ function derp()
+ -- ...stuff...
+ end
+ local wat = 7
+
+ --good
+ if thing then
+ -- ...stuff...
+ end
+
+ function derp()
+ -- ...stuff...
+ end
+
+ local wat = 7
+ ```
+
+ - Delete unnecessary whitespace at the end of lines.
+
+ **[[⬆]](#TOC)**
+
+## <a name='commas'>Commas</a>
+
+ - Leading commas aren't okay. An ending comma on the last item is okay but discouraged.
+
+ ```lua
+ -- bad
+ local thing = {
+ once = 1
+ , upon = 2
+ , aTime = 3
+ }
+
+ -- good
+ local thing = {
+ once = 1,
+ upon = 2,
+ aTime = 3
+ }
+
+ -- okay
+ local thing = {
+ once = 1,
+ upon = 2,
+ aTime = 3,
+ }
+ ```
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='semicolons'>Semicolons</a>
+
+ - **Nope.** Separate statements onto multiple lines.
+
+ ```lua
+ -- bad
+ local whatever = 'sure';
+ a = 1; b = 2
+
+ -- good
+ local whatever = 'sure'
+ a = 1
+ b = 2
+ ```
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='type-coercion'>Type Casting & Coercion</a>
+
+ - Perform type coercion at the beginning of the statement. Use the built-in functions. (`tostring`, `tonumber`, etc.)
+
+ - Use `tostring` for strings if you need to cast without string concatenation.
+
+ ```lua
+ -- bad
+ local totalScore = reviewScore .. ''
+
+ -- good
+ local totalScore = tostring(reviewScore)
+ ```
+
+ - Use `tonumber` for Numbers.
+
+ ```lua
+ local inputValue = '4'
+
+ -- bad
+ local val = inputValue * 1
+
+ -- good
+ local val = tonumber(inputValue)
+ ```
+
+ **[[⬆]](#TOC)**
+
+
+## <a name='naming-conventions'>Naming Conventions</a>
+
+ - Avoid single letter names. Be descriptive with your naming. You can get
+ away with single-letter names when they are variables in loops.
+
+ ```lua
+ -- bad
+ local function q()
+ -- ...stuff...
+ end
+
+ -- good
+ local function query()
+ -- ..stuff..
+ end
+ ```
+
+ - Use underscores for ignored variables in loops.
+
+ ```lua
+ --good
+ for _, name in pairs(names) do
+ -- ...stuff...
+ end
+ ```
+
+ - Use snake_case when naming objects, functions, and instances. Tend towards
+ verbosity if unsure about naming.
+
+ ```lua
+ -- bad
+ local OBJEcttsssss = {}
+ local thisIsMyObject = {}
+
+ local c = function()
+ -- ...stuff...
+ end
+
+ -- good
+ local this_is_my_object = {}
+
+ local function do_that_thing()
+ -- ...stuff...
+ end
+ ```
+
+ - Use PascalCase for factories.
+
+ ```lua
+ -- bad
+ local player = require('player')
+
+ -- good
+ local Player = require('player')
+ local me = Player({ name = 'Jack' })
+ ```
+
+ **[[⬆]](#TOC)**
+
+ - Use `is` or `has` for boolean-returning functions that are part of tables.
+
+ ```lua
+ --bad
+ local function evil(alignment)
+ return alignment < 100
+ end
+
+ --good
+ local function is_evil(alignment)
+ return alignment < 100
+ end
+ ```
+
+## <a name='modules'>Modules</a>
+
+ - The module should return a table or function.
+ - The module should not use the global namespace for anything ever. The
+ module should be a closure.
+ - The file should be named like the module.
+
+ ```lua
+ -- thing.lua
+ local thing = { }
+
+ local meta = {
+ __call = function(self, key, vars)
+ print key
+ end
+ }
+
+
+ return setmetatable(thing, meta)
+ ```
+
+ - Note that modules are [loaded as singletons](http://lua-users.org/wiki/TheEssenceOfLoadingCode)
+ and therefore should usually be factories (a function returning a new instance of a table)
+ unless static (like utility libraries.)
+
+ **[[⬆]](#TOC)**
+
+## <a name='testing'>Testing</a>
+
+ - Use [telescope](https://github.com/norman/telescope) for unit tests and Robot framework for functional testing.
+ Unit tests can rely on LuaJIT ffi module if C function testing is required.
+
+ **[[⬆]](#TOC)**
+
+## <a name='license'>License</a>
+
+ - Released under CC0 (Public Domain).
+ Information can be found at [http://creativecommons.org/publicdomain/zero/1.0/](http://creativecommons.org/publicdomain/zero/1.0/).
+
+**[[⬆]](#TOC)** \ No newline at end of file