coding-conventions

In order to facilitate a coherent development cycle across multiple authors, and to allow Socialist to be easily understood by new authors, it is mandatory that the following conventions be followed for all commits to the Socialist code base.

Except where the following deviates from that document, please refer to and follow the Lua Style Guide.

Format

  • Semicolons are not to be used to end statements. Because of this, there will be not more than one statement per line.
  • Code level indentation will be accomplished with tab characters, not with spaces. For overflow lines, tab up to the current code level indentation and then use spaces (not tabs) to provide a visual margin differentiating the overflow from the normal code level. This is non-negotiable.
  • Table keys declared in the initial table declaration will be written in bracket-form. For instance (note that tabs are not available in WikiCreole markup):
-- Good
local mytable = {
    ["name"] = "Socialist example table.",
    ["version number"] = "v1.0",
}

-- Bad
local mytable = {
    name = "Socialist example table.",
    ["version number"] = "v1.0", -- Note that this field can't even be written
                          --in the 'bad' syntax (hence why we avoid it)
}
  • Table keys used after the declaration may be written in whatever form best suits the programmer.
  • UNIX-style line endings should be used when possible. However, SVN should take care of this conversion using the svn:eol-style = native property.
  • The code level indentation should not exceed three levels of indentation. By that time it is clear that the function or method has grown too large and should be refactored in to smaller component functions.
  • Similarly, no function or method should exceed fifty lines in length unless a large portion of that length is used to comment.
  • Please attempt to keep lines beneath 80 characters in length. I often violate this rule my self, but this is a goal you should strive for. Overflow lines are absolutely fine, please see the above rule on indenting overflow lines.
  • A function should not require any empty lines. If the function looks cluttered or hard to follow without empty lines, than it has grown too large and needs to be refactored into smaller component functions.

Naming

  • Functions and methods will be named in CamelBack notation. For instance, "Object:CreateNewObjectOfType(type)".
  • All variables of any type must be in the local scope, or attached to a pre-existing global object (most often the table named 'Socialist'). If for some reason a new global variable must be declared, please give it a name that prevents confusion or pollution of the global scope. A common exception to this rule would be for metasyntactic temporary variables which do not need to hold a meaning after the function has exited, such as "x", "i", "j", "k", etc.
  • Use of the above mentioned metasyntactic variables is, in fact, encouraged, so long as they are used over a short vertical space in the function (not more than, say, twenty lines) nor used too often (not more than, say, ten times). It is absolutely forbidden to use metasyntactic variables that require any sort of persistence between function executions. The only time a variable named "x" should be stored should be as part of a positioning attribute to a table representing a graphical element, referring to an x-axis offset.
  • Functionally speaking, "function MyTable.foo(args)" is equivalent to "MyTable.foo = function(args)". Use whichever seems to convey the better meaning in the given case. In general, use the first format when declaring functions pre-initialization (IE at the root of the document), and use the second format when modifying existing tables such as when registering callbacks.
  • Never use the "do ... end" control flow syntax. Simply place the code in the naturally occurring flow body. (If you don't understand what this means, you can safely ignore it.)

Functions vs Methods

  • Methods are considered a subclass of functions. Methods are defined as any functions which are declared using the table-colon syntax (e.g. Object:Create() and as opposed to the equivalent Object.Create(self)).
  • Methods are NOT to be used unless the following criteria are met:
    1. The method will only be called as a method. In other words, "self" should ALWAYS refer to the parent object, and never to anything else. (There is no way to ensure this, but you can use it as a design guideline.)
    2. The method manipulates or is directly dependent on the parent object. In other words, the method should always reference 'self' in the function body, and it should not be possible to write the method in another way.
  • Functions as attributes of a table are perfectly fine, and highly encouraged in place of methods that don't meet the above criteria. For instance, it is common for there to be event-related functions which one would seem to want to have be methods, but for which the first argument (which becomes "self" in a method) is actually a frame. For these, simply use the syntax "function Object.OnMyEvent(frame, key)"
  • Only attach functions to objects when that function is related to that object. Otherwise, it is safe and good to use local-scoped functions that are not members of objects.

Code example

Please remember that tabs are not possible in WikiCreole, and as such the below example does not follow the guideline on indentation. (Yes, there are better ways to do these functions, but this shows the proper style usage.)

IMPORTANT: This example does not yet use proper documentation. Documentation guidelines are still forthcoming.

local FIVE_WHOLE_APPLES = 5      -- a constant, using Lua Style Guidelines

FruitBasket = {
    ["apples"] = 5,
    ["oranges"] = 8,
}

function FruitBasket:AddApples(num)
    self.apples = self.apples + num
end

function FruitBasket.OnBasketChange(fruit_type,num)
    if not fruit_type then return end
    FruitBasket[fruit_type] ?
        FruitBasket[fruit_type] = FruitBasket[fruit_type] + num :
        FruitBasket[fruit_type] = num 
end

local function FruitBasket_EmptyMyBasket()
    for k,v in pairs(FruitBasket) do
        FruitBasket[k] = nil
    end
end

Comments

Posts Quoted:
Reply
Clear All Quotes