.. _lua-api:
.. default-domain:: lua
.. meta::
:description: CFG configuration format Lua API
:keywords: CFG, configuration, Lua
The CFG API for Lua
-------------------
The CFG reference implementation for the Lua language requires a Lua version of 5.3 or later. This documentation
assumes that you will be using the `LuaRocks `__ package manager to work with packages such as
this (using the name ``cfg-lib``).
Installation
++++++++++++
You can install the library for use by adding `cfg_lib` to your list of dependencies in
your LuaRocks ``rockspec`` file:
.. code-block:: lua
dependencies = {
'lua >= 5.3',
'cfg-lib >= 0.1.0'
}
There's a minimal example of a program that uses CFG `here
`__.
Exploration
+++++++++++
You can use the Lua interpreter's Read-Eval-Print-Loop (REPL) functionality, invoked by running the interpreter with
no arguments. You can run ``luarocks install cfg-lib`` to install the latest version of this package for your
exploration.
Getting Started with CFG in Lua
+++++++++++++++++++++++++++++++
A configuration is accessed through the :lua:class:`Config` class. This has methods
which can be passed a filename or a string which contains the text for the
configuration. The text is read in, parsed and converted to an object that you can
then query. A simple example:
.. include:: go-test0.cfg.txt
Loading a configuration
~~~~~~~~~~~~~~~~~~~~~~~
The configuration above can be loaded as shown below:
.. code-block:: lua
config = require('config') -- this line will be inferred in the snippets below
cfg = config.Config:from_file('test0.cfg')
The configuration is now available through the object referenced by ``cfg``.
Access elements with keys
~~~~~~~~~~~~~~~~~~~~~~~~~
Accessing elements of the configuration with a simple key is just like using a Lua table:
.. code-block:: lua
print(string.format('a is "%s"', cfg.a))
print(string.format('b is "%s"', cfg.b))
which prints:
.. code-block:: console
a is "Hello, "
b is "world!"
As an alternative to using attribute notation such as ``cfg.a``, you can also use indexing notation such as
``cfg['a']``. The attribute notation can only be used if the key is a valid identifier.
Access elements with paths
~~~~~~~~~~~~~~~~~~~~~~~~~~
As well as simple keys, elements can also be accessed using :term:`path` strings:
.. code-block:: lua
print(string.format('c.d is "%s"', cfg['c.d']))
which prints:
.. code-block:: console
c.d is "e"
.. include:: api-snip-02.inc
.. code-block:: lua
print(string.format('f.g is "%s"', cfg['f.g']))
which prints:
.. code-block:: console
f.g is "h"
Note that in the first case, you could access the value ``"e"`` using ``cfg.c.d``, but you can't access the value
``"h"``using attribute notation ``cfg.f.g`` -- you must use the indexing notation. That's because ``cfg.f`` isn't a
thing in the configuration.
.. include:: api-snip-03.inc
Access to date/time objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can also get date/time objects from a configuration, by using an ISO
date/time pattern in a :term:`backtick-string`:
.. code-block:: lua
print(string.format('Christmas morning is %s', cfg.christmas_morning))
which prints:
.. code-block:: console
Christmas morning is 2019-12-25 08:39:49
The returned date is a Lua table with the following keys:
* ``year`` (integer)
* ``month`` (integer)
* ``day`` (integer)
* ``hour`` (integer)
* ``minute`` (integer)
* ``second`` (integer or float)
* ``offset_sign`` (defaults to ``"+"``)
* ``offset_hour`` (integer)
* ``offset_minute`` (integer)
Access to environment variables
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To access an environment variable, use a :term:`backtick-string` of the form
```$VARNAME```:
.. code-block:: lua
print(cfg['home'] == os.getenv('HOME'))
which prints:
.. code-block:: console
true
.. include:: api-snip-07.inc
.. code-block:: lua
print(string.format('foo is "%s"', cfg.foo))
which prints:
.. code-block:: console
foo is "bar"
Access to computed values
~~~~~~~~~~~~~~~~~~~~~~~~~
.. include:: api-snip-04.inc
.. code-block:: cfg-body
:caption: test0a.cfg
total_period : 100
header_time: 0.3 * ${total_period}
steady_time: 0.5 * ${total_period}
trailer_time: 0.2 * ${total_period}
base_prefix: '/my/app/'
log_file: ${base_prefix} + 'test.log'
When this file is read in, the computed values can be accessed directly.
.. code-block:: lua
cfg = config.Config:from_file('test0a.cfg')
for _, key in ipairs({'header_time', 'steady_time', 'trailer_time', 'log_file'}) do
print(string.format('%s is %s', key, cfg[key]))
end
which prints:
.. code-block:: console
header_time is 30
steady_time is 50
trailer_time is 20
log_file is /my/app/test.log
Including one configuration inside another
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are times when it's useful to include one configuration inside another.
For example, consider the following configuration files:
.. code-block:: cfg-body
:caption: logging.cfg
layouts: {
brief: {
pattern: '%d [%t] %p %c - %m%n'
}
},
appenders: {
file: {
level: 'INFO',
layout: 'brief',
filename: 'run/server.log',
append: false,
charset: 'UTF-8'
},
error: {
level: 'ERROR',
layout: 'brief',
filename: 'run/server-errors.log',
append: false,
charset: 'UTF-8'
},
debug: {
level: 'DEBUG',
layout: 'brief',
filename: 'run/server-debug.log',
append: false,
charset: 'UTF-8'
}
},
loggers: {
mylib: {
level: 'INFO'
}
'mylib.detail': {
level: 'DEBUG'
}
},
root: {
handlers: ['file', 'error', 'debug'],
level: 'WARNING'
}
.. code-block:: cfg-body
:caption: redirects.cfg
cookies: {
url: 'http://www.allaboutcookies.org/',
permanent: false
},
freeotp: {
url: 'https://freeotp.github.io/',
permanent: false
},
'google-auth': {
url: 'https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2',
permanent: false
}
.. code-block:: cfg-body
:caption: main.cfg
secret: 'some random application secret',
port: 8000,
sitename: 'My Test Site',
default_access: 'public',
ignore_trailing_slashes: true,
site_options: {
want_ipinfo: false,
show_form: true,
cookie_bar: true
},
debug: true,
captcha_length: 4,
captcha_timeout: 5,
session_timeout: 7 * 24 * 60 * 60, # 7 days in seconds
redirects: @'redirects.cfg',
email: {
sender: 'no-reply@my-domain.com',
host: 'smtp.my-domain.com:587',
user: 'smtp-user',
password: 'smtp-pwd'
}
logging: @'logging.cfg'
.. include:: api-snip-05.inc
.. code-block:: lua
cfg = config.Config:from_file('main.cfg')
for _, key in ipairs({'logging.layouts.brief.pattern', 'redirects.freeotp.url', 'redirects.freeotp.permanent'}) do
print(string.format('%s is %s', key, cfg[key]))
end
which prints:
.. code-block:: console
logging.layouts.brief.pattern is %d [%t] %p %c - %m%n
redirects.freeotp.url is https://freeotp.github.io/
redirects.freeotp.permanent is false
Avoiding unnecessary repetition
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. include:: api-snip-08.inc
.. code-block:: cfg-body
:caption: logging.cfg (partial)
appenders: {
file: {
level: 'INFO',
layout: 'brief',
filename: 'run/server.log',
append: true,
charset: 'UTF-8'
},
error: {
level: 'ERROR',
layout: 'brief',
filename: 'run/server-errors.log',
append: false,
charset: 'UTF-8'
},
debug: {
level: 'DEBUG',
layout: 'brief',
filename: 'run/server-debug.log',
append: false,
charset: 'UTF-8'
}
},
This portion could be rewritten as:
.. code-block:: cfg-body
:caption: logging.cfg (partial)
defs: {
base_appender: {
layout: 'brief',
append: false,
charset: 'UTF-8'
}
},
appenders: {
file: ${defs.base_appender} + {
level: 'INFO',
filename: 'run/server.log',
append: true,
},
error: ${defs.base_appender} + {
level: 'ERROR',
filename: 'run/server-errors.log',
},
debug: ${defs.base_appender} + {
level: 'DEBUG',
filename: 'run/server-debug.log',
}
},
.. include:: api-snip-06.inc
.. code-block:: lua
cfg = config.Config:from_file('main.cfg')
for _, key in ipairs({'logging.appenders.file.append', 'logging.appenders.file.filename', 'logging.appenders.file.level',
'logging.appenders.debug.level', 'logging.appenders.debug.filename',
'logging.appenders.debug.append'}) do
print(string.format('%s is %s', key, cfg[key]))
end
which prints:
.. code-block:: console
logging.appenders.file.append is false
logging.appenders.file.filename is run/server.log
logging.appenders.file.level is INFO
logging.appenders.debug.level is DEBUG
logging.appenders.debug.filename is run/server-debug.log
logging.appenders.debug.append is false
.. include:: api-snip-09.inc
.. code-block:: cfg-body
:caption: logging.cfg (partial)
defs: {
base_appender: {
layout: 'brief',
append: false,
charset: 'UTF-8'
}
log_prefix: 'run/',
},
appenders: {
file: ${defs.base_appender} + {
level: 'INFO',
filename: ${defs.log_prefix} + 'server.log',
append: true,
},
error: ${defs.base_appender} + {
level: 'ERROR',
filename: ${defs.log_prefix} + 'server-errors.log',
},
debug: ${defs.base_appender} + {
level: 'DEBUG',
filename: ${defs.log_prefix} + 'server-debug.log',
}
}
with the same result as before. It *is* slightly more verbose than before, but
the location of all files can be changed in just one place now, as opposed to
three, as it was before.
Classes
+++++++
Classes are just Lua tables with metatables that determine their behaviour.
The ``Location`` class
~~~~~~~~~~~~~~~~~~~~~~
This represents a source location and has two integer attributes, ``line`` and
``column``. The line must be positive and the column non-negative (newlines have an
ending column of zero, as column 1 would have the first character of the next line).
The ``ConfigError`` class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This has an error message in the ``message`` attribute and a ``Location`` in the ``pos`` attribute indicating the
position in the source of the error, where appropriate. (Some errors don't have a location, and in such cases the
``location`` attribute of the instance will be set to ``nil``.)
The ``Config`` class
~~~~~~~~~~~~~~~~~~~~
This class implements access to a CFG configuration. You'll generally interface to CFG
files using this class. When you pass in a string or file path to a constructor the
CFG source in the string or file is parsed and converted into an internal form which
can then be accessed in a manner analogous to a standard Lua table.
.. index::
single: Config; Lua class
.. lua:class:: Config
.. cssclass:: class-members-heading
Attributes
.. lua:attribute:: path
The path to the file from which the configuration has been read. You won't
usually need to set this, unless you want to load a configuration from text that
references included files. In that case, set the path to a value whose parent
directory contains the included files, or specify relevant directories using
:lua:attr:`~Config.include_path`. When a configuration is loaded from a file,
this attribute is automatically set.
.. lua:attribute:: include_path
A list of directories which is searched for included sub-configurations if the
parent directory of :lua:attr:`~Config.path` (or the current directory, if
``path`` isn't set) doesn't contain them.
.. lua:attribute:: context
A mapping of strings to objects which is used whenever an identifier
is encountered when evaluating an expression in a configuration file.
.. lua:attribute:: no_duplicates = true
Whether keys are allowed to be duplicated in mappings. Usually, a
:class:`ConfigError` is raised if a duplicate key is seen. If ``false``,
then if a duplicate key is seen, its value silently replaces the value
associated with the earlier appearance of the same key.
.. lua:attribute:: strict_conversions = true
If ``true``, a :class:`ConfigEerro` is thrown if a string can't be
converted. It's intended to help catch typos in backtick-strings.
.. cssclass:: class-members-heading
Methods
.. index::
single: Config; Config class
.. lua:method:: new()
:noindex:
Returns a new instance.
.. lua:method:: Config.from_source(source)
Returns a new instance initialized with a configuration provided in ``source``.
.. lua:method:: Config.from_file(filepath)
Returns a new instance initialized with a configuration provided in the file
specified by ``filepath``.
.. lua:method:: load_file(filepath)
Overwrite any configuration with a configuration provided in the file specified
by ``filepath``.
.. lua:method:: as_dict()
Return's this instance's data as a standard Lua table. Amy expressions in the configuration are evaluated, so
that the contents are standard Lua values.
.. lua:method:: get(key, default_value)
Retrieve a value from the instance whose key or path is represented by ``key``. If
the key or path is absent from the configuration and ``default_value`` is provided, it is
returned. Otherwise, a ``ConfigError`` is thrown.
.. lua:method:: operator [](key)
Retrieve a value from the instance whose key or path is represented by ``key``, using notation like
``cfg[key]``. If the key or path is absent from the configuration, a ``ConfigError`` is thrown.
.. raw:: html