Lua - Libraries & LuaRocks
Libraries & LuaRocks
Time: ~30 minutes
Explore the Lua library ecosystem, learn to use LuaRocks package manager, discover essential libraries for various domains, and understand how to create and distribute your own Lua libraries.
Learning Objectives
- Understand the Lua library ecosystem and package management
- Install and use LuaRocks package manager
- Work with essential Lua libraries for different domains
- Create and structure your own Lua libraries
- Publish packages to LuaRocks repository
- Manage dependencies and version compatibility
LuaRocks Package Manager
Installation and Setup
# Ubuntu/Debian
sudo apt-get install luarocks
# macOS with Homebrew
brew install luarocks
# Windows - Download from luarocks.org
# Or install via scoop: scoop install luarocks
# Verify installation
luarocks --version
Basic LuaRocks Commands
# Search for packages
luarocks search json
# Install a package
luarocks install lua-cjson
# Install specific version
luarocks install penlight 1.13.1
# List installed packages
luarocks list
# Show package information
luarocks show lua-cjson
# Remove a package
luarocks remove lua-cjson
# Update package index
luarocks update
Using Installed Libraries
-- After installing with: luarocks install lua-cjson
local cjson = require("cjson")
-- JSON encoding
local data = {
name = "John Doe",
age = 30,
skills = {"Lua", "Python", "JavaScript"},
active = true
}
local json_string = cjson.encode(data)
print("JSON:", json_string)
-- JSON decoding
local decoded = cjson.decode(json_string)
print("Name:", decoded.name)
print("Skills:", table.concat(decoded.skills, ", "))
Expected Output:
JSON: {"active":true,"age":30,"name":"John Doe","skills":["Lua","Python","JavaScript"]}
Name: John Doe
Skills: Lua, Python, JavaScript
Essential Libraries by Domain
Web Development Libraries
# HTTP client library
luarocks install http
# Web framework
luarocks install lapis
# URL parsing
luarocks install net-url
-- HTTP Client Example
local http_request = require("http.request")
local cjson = require("cjson")
-- Make HTTP GET request
local request = http_request.new_from_uri("https://api.github.com/users/octocat")
local headers, stream = request:go()
if headers:get(":status") == "200" then
local body = stream:get_body_as_string()
local user_data = cjson.decode(body)
print("GitHub User Info:")
print("Login:", user_data.login)
print("Name:", user_data.name)
print("Public Repos:", user_data.public_repos)
print("Followers:", user_data.followers)
else
print("HTTP Error:", headers:get(":status"))
end
-- URL parsing example
local url = require("net.url")
local parsed = url.parse("https://example.com:8080/path?query=value#fragment")
print("Host:", parsed.host)
print("Port:", parsed.port)
print("Path:", parsed.path)
print("Query:", parsed.query)
File System and OS Libraries
# File system operations
luarocks install luafilesystem
# System utilities
luarocks install lua-system
# Path manipulation
luarocks install luapath
local lfs = require("lfs")
local path = require("path")
-- Directory operations
print("Current directory:", lfs.currentdir())
-- Create directory if it doesn't exist
local test_dir = "test_directory"
if not lfs.attributes(test_dir) then
lfs.mkdir(test_dir)
print("Created directory:", test_dir)
end
-- List directory contents
print("\nDirectory contents:")
for file in lfs.dir(".") do
if file ~= "." and file ~= ".." then
local attr = lfs.attributes(file)
local file_type = attr.mode == "directory" and "DIR" or "FILE"
print(string.format("%-4s %s", file_type, file))
end
end
-- Path manipulation
local file_path = path.join("data", "config", "settings.json")
print("\nPath operations:")
print("Joined path:", file_path)
print("Directory:", path.dirname(file_path))
print("Filename:", path.basename(file_path))
print("Extension:", path.extension(file_path))
Data Processing Libraries
# Penlight utilities
luarocks install penlight
# CSV processing
luarocks install lua-csv
# Template engine
luarocks install lustache
-- Penlight - Swiss Army Knife for Lua
local pl = require("pl.import_into")()
-- String utilities
local text = " Hello, World! "
print("Trimmed:", pl.stringx.strip(text))
print("Split:", pl.stringx.split("a,b,c,d", ","))
-- List operations
local numbers = {1, 2, 3, 4, 5}
local doubled = pl.tablex.map(function(x) return x * 2 end, numbers)
print("Doubled:", pl.pretty.write(doubled, ""))
local sum = pl.tablex.reduce("+", numbers)
print("Sum:", sum)
-- CSV processing
local csv = require("csv")
-- Write CSV data
local csv_data = {
{"Name", "Age", "City"},
{"Alice", "25", "New York"},
{"Bob", "30", "London"},
{"Charlie", "35", "Tokyo"}
}
local csv_file = csv.open("people.csv", "w")
for i, row in ipairs(csv_data) do
csv_file:write(row)
end
csv_file:close()
-- Read CSV data
print("\nReading CSV:")
csv_file = csv.open("people.csv", "r")
for fields in csv_file:lines() do
print(string.format("%-10s %-5s %s", fields[1], fields[2], fields[3]))
end
csv_file:close()
-- Template processing with Lustache
local lustache = require("lustache")
local template = "Hello {{name}}! You have {{count}} {{#plural}}messages{{/plural}}{{^plural}}message{{/plural}}."
local data = {
name = "Alice",
count = 3,
plural = true
}
local result = lustache:render(template, data)
print("\nTemplate result:", result)
Creating Your Own Library
Library Structure and Organization
-- mathutils.lua - A mathematical utilities library
local mathutils = {}
-- Module version
mathutils._VERSION = "1.0.0"
mathutils._DESCRIPTION = "Mathematical utilities for Lua"
mathutils._LICENSE = "MIT"
-- Private helper function
local function is_integer(n)
return type(n) == "number" and n == math.floor(n)
end
-- Public functions
function mathutils.factorial(n)
if not is_integer(n) or n < 0 then
error("factorial requires non-negative integer")
end
if n <= 1 then
return 1
else
return n * mathutils.factorial(n - 1)
end
end
function mathutils.gcd(a, b)
if not (is_integer(a) and is_integer(b)) then
error("gcd requires integers")
end
while b ~= 0 do
a, b = b, a % b
end
return math.abs(a)
end
function mathutils.lcm(a, b)
if a == 0 or b == 0 then
return 0
end
return math.abs(a * b) / mathutils.gcd(a, b)
end
function mathutils.is_prime(n)
if not is_integer(n) or n < 2 then
return false
end
if n == 2 then
return true
end
if n % 2 == 0 then
return false
end
for i = 3, math.sqrt(n), 2 do
if n % i == 0 then
return false
end
end
return true
end
function mathutils.prime_factors(n)
if not is_integer(n) or n < 2 then
return {}
end
local factors = {}
local d = 2
while d * d <= n do
while n % d == 0 do
table.insert(factors, d)
n = n / d
end
d = d + 1
end
if n > 1 then
table.insert(factors, n)
end
return factors
end
-- Statistics functions
mathutils.stats = {}
function mathutils.stats.mean(numbers)
if #numbers == 0 then
return nil
end
local sum = 0
for _, v in ipairs(numbers) do
sum = sum + v
end
return sum / #numbers
end
function mathutils.stats.median(numbers)
if #numbers == 0 then
return nil
end
local sorted = {}
for _, v in ipairs(numbers) do
table.insert(sorted, v)
end
table.sort(sorted)
local len = #sorted
if len % 2 == 0 then
return (sorted[len/2] + sorted[len/2 + 1]) / 2
else
return sorted[math.ceil(len/2)]
end
end
function mathutils.stats.mode(numbers)
if #numbers == 0 then
return nil
end
local counts = {}
for _, v in ipairs(numbers) do
counts[v] = (counts[v] or 0) + 1
end
local max_count = 0
local mode_val = nil
for val, count in pairs(counts) do
if count > max_count then
max_count = count
mode_val = val
end
end
return mode_val
end
return mathutils
Testing Your Library
-- test_mathutils.lua
local mathutils = require("mathutils")
print("Testing mathutils library v" .. mathutils._VERSION)
print()
-- Test basic functions
print("Factorial Tests:")
print("5! =", mathutils.factorial(5))
print("0! =", mathutils.factorial(0))
print("\nGCD/LCM Tests:")
print("gcd(48, 18) =", mathutils.gcd(48, 18))
print("lcm(12, 15) =", mathutils.lcm(12, 15))
print("\nPrime Tests:")
print("is_prime(17) =", mathutils.is_prime(17))
print("is_prime(15) =", mathutils.is_prime(15))
print("prime_factors(60) =", table.concat(mathutils.prime_factors(60), " × "))
-- Test statistics
local data = {1, 2, 2, 3, 4, 4, 4, 5}
print("\nStatistics for {1, 2, 2, 3, 4, 4, 4, 5}:")
print("Mean:", mathutils.stats.mean(data))
print("Median:", mathutils.stats.median(data))
print("Mode:", mathutils.stats.mode(data))
-- Error handling test
print("\nError Handling:")
local success, result = pcall(mathutils.factorial, -1)
if not success then
print("Caught error:", result)
end
Expected Output:
Testing mathutils library v1.0.0
Factorial Tests:
5! = 120
0! = 1
GCD/LCM Tests:
gcd(48, 18) = 6
lcm(12, 15) = 60
Prime Tests:
is_prime(17) = true
is_prime(15) = false
prime_factors(60) = 2 × 2 × 3 × 5
Statistics for {1, 2, 2, 3, 4, 4, 4, 5}:
Mean: 3.125
Median: 3.5
Mode: 4
Error Handling:
Caught error: factorial requires non-negative integer
Publishing to LuaRocks
Creating a Rockspec File
-- mathutils-1.0.0-1.rockspec
package = "mathutils"
version = "1.0.0-1"
source = {
url = "git://github.com/yourusername/mathutils",
tag = "v1.0.0"
}
description = {
summary = "Mathematical utilities for Lua",
detailed = [[
A collection of mathematical functions including
factorial, GCD/LCM, prime testing, and basic statistics.
]],
homepage = "https://github.com/yourusername/mathutils",
license = "MIT"
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "builtin",
modules = {
mathutils = "mathutils.lua"
}
}
Local Testing and Installation
# Test rockspec locally
luarocks make mathutils-1.0.0-1.rockspec
# Install from local rockspec
luarocks install mathutils-1.0.0-1.rockspec
# Upload to LuaRocks (requires account)
luarocks upload mathutils-1.0.0-1.rockspec
Popular Lua Libraries Overview
Domain | Library | Description |
---|---|---|
JSON | lua-cjson, dkjson | Fast JSON encoding/decoding |
HTTP | http, lua-resty-http | HTTP client libraries |
Web Frameworks | Lapis, OpenResty | Web application frameworks |
Database | luasql, lua-resty-mysql | Database connectivity |
File System | luafilesystem, luapath | File and directory operations |
Utilities | penlight, lua-std | General utility functions |
Testing | busted, luaunit | Unit testing frameworks |
Crypto | luacrypto, lua-resty-openssl | Cryptographic functions |
XML/HTML | lua-expat, htmlparser | XML/HTML parsing |
Logging | lualogging, lua-resty-logger | Logging frameworks |
Dependency Management Best Practices
Version Constraints
-- In rockspec dependencies
dependencies = {
"lua >= 5.1, < 5.5", -- Version range
"penlight >= 1.13.0", -- Minimum version
"lua-cjson ~> 2.1" -- Compatible version
}
Managing Multiple Environments
# Create project-specific package environment
mkdir myproject && cd myproject
luarocks init
# Install dependencies locally
./luarocks install penlight
./luarocks install lua-cjson
# Run with local packages
./lua myscript.lua
Creating Dependency Lists
# Generate current package list
luarocks list --porcelain > packages.txt
# Install from package list
cat packages.txt | while read pkg; do
luarocks install $pkg
done
Library Development Tips
Best Practices
- Module Structure: Return a table with functions and metadata
- Error Handling: Use
error()
for programming errors, return nil/error for runtime issues - Documentation: Include usage examples and API documentation
- Testing: Write comprehensive tests before publishing
- Versioning: Follow semantic versioning (major.minor.patch)
Common Pitfalls
Global Pollution: Always return a table, don't create globals
Missing Dependencies: Specify all dependencies in rockspec
Platform Issues: Test on multiple platforms before publishing
Version Conflicts: Be careful with dependency version constraints
Checks for Understanding
- What is LuaRocks and how do you install packages with it?
- How should you structure a Lua library for distribution?
- What information is required in a rockspec file?
- How do you manage project-specific dependencies?
Show Answers
- LuaRocks is Lua's package manager. Install packages with
luarocks install packagename
. - Return a table with functions and metadata, include version info, handle errors properly, provide clear API.
- Package name, version, source location, description, dependencies, and build instructions.
- Use
luarocks init
to create project-local package environment, or maintain dependency lists.
Exercises
- Create a utility library for string manipulation functions and publish it locally.
- Install and use three different web-related libraries to build a simple HTTP client.
- Write a rockspec for an existing Lua script and test the installation process.