Les langages / Lua / Mini-projet final
Projets réels et best practices
Synthèse finale: comment structurer un projet Lua professionnel, conventions de code, documentation, déploiement, et exemples de projets complets. De débutant à expert!
Structure de projet professionnelle
-- Structure recommandée:
--[[
mon_projet/
├── src/ -- Code source
│ ├── init.lua -- Point d'entrée
│ ├── core/ -- Logique métier
│ ├── utils/ -- Utilitaires
│ └── models/ -- Modèles de données
├── tests/ -- Tests unitaires
│ ├── test_core.lua
│ └── test_utils.lua
├── docs/ -- Documentation
│ └── api.md
├── config/ -- Configuration
│ └── config.lua
├── lib/ -- Dépendances externes
├── .luarc.json -- Config LSP
└── README.md
]]
-- init.lua - Point d'entrée
local MyApp = {
_VERSION = "1.0.0",
_DESCRIPTION = "Mon application Lua",
_LICENSE = "MIT"
}
-- Charger les modules
MyApp.Core = require("src.core.engine")
MyApp.Utils = require("src.utils.helpers")
MyApp.Config = require("config.config")
-- Initialisation
function MyApp.init(options)
options = options or {}
print("Initialisation de " .. MyApp._VERSION)
-- Configuration...
end
return MyApp
Conventions et documentation
--- Module de gestion des utilisateurs
-- @module UserManager
-- @author Shadow
-- @license MIT
-- @copyright 2024
local UserManager = {}
--- Crée un nouveau gestionnaire d'utilisateurs
-- @return UserManager instance
function UserManager.new()
local self = setmetatable({}, {__index = UserManager})
self.users = {}
return self
end
--- Ajoute un utilisateur
-- @param name string Le nom de l'utilisateur
-- @param email string L'email de l'utilisateur
-- @return table L'utilisateur créé
-- @raise Erreur si l'email est invalide
function UserManager:addUser(name, email)
assert(type(name) == "string", "name doit être une string")
assert(type(email) == "string", "email doit être une string")
if not email:match("^[%w%.]+@[%w%.]+%.[%a]+$") then
error("Email invalide: " .. email)
end
local user = {
id = #self.users + 1,
name = name,
email = email,
createdAt = os.time()
}
table.insert(self.users, user)
return user
end
--- Trouve un utilisateur par ID
-- @param id number L'ID de l'utilisateur
-- @return table|nil L'utilisateur trouvé ou nil
function UserManager:findById(id)
for _, user in ipairs(self.users) do
if user.id == id then
return user
end
end
return nil
end
return UserManager
Configuration et environnements
-- config/config.lua
local Config = {}
-- Configuration par défaut
local defaults = {
env = "development",
debug = true,
database = {
host = "localhost",
port = 5432,
name = "myapp_dev"
},
logging = {
level = "debug",
file = "logs/app.log"
},
cache = {
enabled = true,
ttl = 3600
}
}
-- Configuration par environnement
local environments = {
production = {
debug = false,
database = {
host = "prod-db.example.com",
port = 5432,
name = "myapp_prod"
},
logging = {
level = "info",
file = "/var/log/myapp.log"
}
},
test = {
database = {
name = "myapp_test"
},
logging = {
level = "warn"
}
}
}
-- Charger la config
function Config.load(env)
env = env or os.getenv("LUA_ENV") or "development"
-- Copier les defaults
local config = {}
for k, v in pairs(defaults) do
if type(v) == "table" then
config[k] = {}
for k2, v2 in pairs(v) do
config[k][k2] = v2
end
else
config[k] = v
end
end
-- Merger avec l'environnement
if environments[env] then
for k, v in pairs(environments[env]) do
if type(v) == "table" then
config[k] = config[k] or {}
for k2, v2 in pairs(v) do
config[k][k2] = v2
end
else
config[k] = v
end
end
end
config.env = env
return config
end
return Config
Projet complet: API REST simple
-- Exemple de mini API REST
local API = {}
local users = {}
local nextId = 1
-- Router simple
local Router = {
routes = {GET = {}, POST = {}, PUT = {}, DELETE = {}}
}
function Router.add(method, path, handler)
Router.routes[method][path] = handler
end
function Router.handle(method, path, params)
local handler = Router.routes[method][path]
if handler then
return handler(params)
else
return {error = "Route not found", code = 404}
end
end
-- Définir les routes
Router.add("GET", "/users", function(params)
return {data = users, code = 200}
end)
Router.add("GET", "/users/:id", function(params)
local user = users[tonumber(params.id)]
if user then
return {data = user, code = 200}
else
return {error = "User not found", code = 404}
end
end)
Router.add("POST", "/users", function(params)
local user = {
id = nextId,
name = params.name,
email = params.email,
createdAt = os.time()
}
users[nextId] = user
nextId = nextId + 1
return {data = user, code = 201}
end)
Router.add("DELETE", "/users/:id", function(params)
local id = tonumber(params.id)
if users[id] then
users[id] = nil
return {message = "User deleted", code = 200}
else
return {error = "User not found", code = 404}
end
end)
-- Utilisation
print("=== API REST Demo ===")
-- POST /users
local response = Router.handle("POST", "/users",
{name = "Shadow", email = "shadow@example.com"})
print("Créé:", response.data.id, response.data.name)
-- GET /users
response = Router.handle("GET", "/users", {})
print("Total users:", #response.data)
-- GET /users/1
response = Router.handle("GET", "/users/:id", {id = 1})
print("User:", response.data.name)
1 exercice pour cette lecon
Les exercices demandent un compte : il faut bien enregistrer votre progression quelque part. La lecture, elle, reste libre. Creer un compte
- Générateur de nombres premiers difficile 35 XP