This manual is a pre-release preview. Please follow Moonstalk on Twitter or Facebook to be notified of a public release.
These data-structures are universal providing access to the Moonstalk environment, the request, the response, and in many cases also database queries and their results (see next). You may check and change the values in these tables to determine how to handle a request—and to generate a page for response.
All tables exist in either a global or ephemeral (per-request) scope. The latter are discarded after each request is completed and may be modified freely, whilst the prior exist across all requests and should not be modified. Additional reference (pointer) tables are provided in the ephemeral scope for convenient access to some global tables.
Global tables should not be changed during a request, and are intended to only be modified by application load-time functions. Failure to adhere to this will result in random results when using more than a single backend instance. Whilst reference tables change with each request, they are dynamic pointers, or aliases, to the contents of global tables and therefore should also not be changed. No tables are persisted (use a database such as the Teller).
Typically if a controller has many references to, or a view consumes many instances of values in these tables, it is advisable for both cleaner code and better performance to assign them to a local at the top of these files. e.g. local data = page.data
local form = request.form
This predominantly contains details of the HTTP headers, with parsing into a more Lua-friendly format where appropriate. Use of this table is discouraged as its format and structure is dependant upon the web server API. See form
and page
instead.
request.get
and request.post
are not conditional, instead use request.method=="GET"
(case sensisitive).
path = "/My/Account"
browser
client
= {ip="address", token=, id=, timezone=, languages=, locale=}user.ip
is advised instead.
Contains attributes that lead to authentication, and some present irrespective, providing a place to find both browser and user dervived values.
Contains values encoded in the request.body such as in an HTML form; is always present regardless of method
.
In OpenResty when not urlencoded post={maxsize=bytes}
must be set on an address for the form to be populated, with maxsize generally only being required for files.
name = "value" or {name="name", size=bytes, ['content-type']="mime/type", contents="data"}
files = { {name="name", size=bytes, ['content-type']="mime/type", content="data"}, }
If a multi-part POST this will contain an array of the parts, this is primarily useful for multi-file uploads in particular using an HTML5 file input
with the multiple
attribute.
redirect = false or url
; default behaviour is to redirect all non-primary domains to the primary, false will disable this, or if set to a URL any domain without a redirect will then use it; the requested path will be appended to the URL (if undesired, terminate the URL with a question mark to transform the path in query string).Defines the page to be rendered as a response.
Do not set new keys in this table — use only the data key.
address
= "my/account"
request.path
used for matching addresses. (Lowercased and transliterated for Latin accents, with leading and trailing slashes removed.)
transliterated = true
paths = { my=true, account=true }
authenticator = "generic.Authenticator", }
site.authenticator or node.authenticator
, or false to disable.
locks = { "name", }
locale = "us"
user.locales
and site.locale
.
language
= "en"
user.languages
and site.vocabulary
. Defines the Language header.
localise = true
controller = "name"
view = "name"
template = "name"
editors = { function() end, myapp.editor, }
modified = 1290172118
error
scribe.Error()
rather than modifying directly.
headers = { ['Header-Name']="value", }
status = 200
page.data.foo = bar
or replace with a database query page.data = db.name.field
. Where any contents of the table will be referenced repetitively, create local data = page.data
to use them, likewise subtables.
Always declare variables as local
else use page.data
.
The global Moonstalk environment for each server (node). Settings are configured in the data/configuration/Host.lua file, and may also be changed at launch time by the .Elevator
servers = {"application"}
The server ids that need to be running. This defines the webserver (e.g. "openresty" for nginx or "lighttpd") and databases (e.g. "teller", "tarantool"). If not specified these are automatically detected, and will also be added from the database configuration for which "teller" is the default.
roles = {"name"}
When running multiple nodes, allows an individual node to only handle specific roles, such as specific servers and only the correspondingly associaated database tables.
scribe = {server="application"}
As above if not specified this will be detected or defaulted.
curators = {"application", …}
The curators and order in which they should be run for each request.
applications = {"application", …}
Default applications that must be enabled for all sites.
logging = 2
geoip = false
Each database application provides its own interface and behaviours. The OpenResty application bundles drivers for MySQL, Redis, and Memcached. Some Moonstalk database applications (e.g. Databin and Tarantool) offer a standardised interface and configuration using a schema.lua
file in the application as follows.
schema files can also define enum.name = keyed{…}
constants the same as in settings files.
To define a new database table (as opposed extend another application's tables, e.g. users
or tenants
), declare it in an application's schema.
The recommended naming is a plural for a table e.g. users = {…}
.
The following keys in each database table declaration are supported for each table. See the database application documentation for additional configuration such as field declarations.
system = "tarantool"
server = {…}
role = "name"
node.databases.role
and providing the server parameters. If node.roles contains the role name and the system is specified in node.servers then the server is assumed to be local. See Node.
For application-specific functions see the applications page.
(expression)
[[string]]
Appends the passed value to the current output section. Values may only be strings, numbers or nil (which is ignored).
Expressions evaluated by write
or ?(expression)
tags may only provide string or number values, nil and false values are ignored.
A table or function value will invoke an error as they cannot be represented as text in a response, for debugging you may however output them with tostring()
.
In views, do not call write
instead use an ?(expression)
tag.
Returns the given language key's value from a translated vocabulary, in its original case. Use L.key
for an initial capital.
This is a proxy table—a function with table.key syntax. (Achieved using a metatable __call
overload.)
{ [[text]], key="value", }
Replace defined ?(key)
macros in the specified text, with their correspondingly specified values.
Whilst macros in views (expression tags) accept expressions (thus, function calls) for replacement, text macros do not, and where no corresponding key is specified a macro is not replaced.
Text macros are typically used for customising strings, such as translated language keys from vocabularies, where pre-defined grammar is customised using embedded variables.
"name"
Changes the current output to a new container, as named. (The default page response output is named content
.) See also Cut
.
"name"
Marks the current position in a view to later Insert
a section or other value.
"section_name"
"mark_name"
Inserts a previously defined section into the current view/section. Takes an optional mark_name parameter to insert at the specified Mark instead of the current location. See also Paste
.
"name"
Run a specified controller and/or associated view and append it's output to the current page. Name is normalised (lower-case).
"name"
Run a specified view only and append it's output to the page. If the view provides versions for different cultures a matching translation/localisation will be served. Name is normalised (lower-case).
"name"
Run a specified controller only. Name is normalised (lower-case).
("name", value)
{name="", value="", expires=, httpOnly=true, path="", domain=""}
Sets a cookie in the user's browser.
The following functions abandon page processing in some way, to display an alternate view or response.
The current function from which these functions are called, and any chaining from it, will continue to run but their output will be surpressed. No further functions will run except for the default template (if any). To prevent the current function from continuing, follow the function call with a return
termination.
"address"
Abandons the page processing to instead returns a 302 redirect response to the user's browser. The address may be a path or URL.
{ "name", }
Checks that the current user has one of the specified keys, otherwise abandons the page processing. If not signed-in the generic/signin
view is displayed, or if signed-in but without a specified key, the generic/unauthorised
view is displayed.
For a conditional termination use if not scribe.Authorised{"name"} then return end
. For conditional content check user.keychain
directly.
"synopsis"
{title="synopsis", detail="explanation", realm="page"}
Abandons the page processing with the generic/error
view using the specified details.
()
("name")
Abandons the page processing with the generic/not-found
view. Accepts an optional user-friendly name to be displayed instead of the page.address
.
The following functions abandon page processing in some way, to display an alternate view or response.
All standard Lua libraries are available, with the following, and some other less commonly used libraries that are documented on the credits page.
You may use load other libraries (.lua or .so) saved in the same folder as a view or controller, or within an application folder by using the full path.
require "libraryfile"
require "appname/libraryfile"
Moonstalk uses its own package.path
and cpath
, therefore if you wish to use other libraries elsewhere on your system (such as from LuaRocks), you must append those paths to Moonstalk's values. Applications may do this in the settings or functions files.
package.path = package.path .. ";/my/lib/dir/?.lua"
Moonstalk uses the data/configuration/lighttpd.conf as its base configuration, with which its own configuration is combined at launch-time, and then saved to the temporary/lighttpd.conf file to launch Lighttpd.
Contains named and translated terms, phrases and blocks by language. These values are used in the Scribe by referencing l.term_name
(or L. to capitalise) for the language corresponding to request.client.language
. Using the macro
function these terms may contain placeholders for other values.
en.term_name = "Value to use in English"
Language and term IDs/names should not contain special characters nor be reserved words, if they are they must be declared as ['special-name']
.
With the exception of page vocabularies (see below), all application vocabularies are merged, therefore if more than one application declares a term, the value may not be as expected. Term names should be unique and prefoxed with a suitable identifier, unless for generic use..
view-name.vocab.lua
file, which is populated the same for bundle vocabularies, but is only accessible to the corresponding page (controller, view, template and editors).
view
defined in an address contains no dynamic markup, then the page.modified
value (and thus Last-modified
header) is automatically set using the last modification date of the corresponding view's file.
template=false
on page or address, or set it to the name of another template-view.
<html>
tag, page.template=false
. If you wish to run a template controller with such a view, specify the template
using an address, or use a custom Collator
.
application.id
of an application, will override its views and controllers with those having the same name in this folder.
A bundle whose table exists in the global environment, and having handlers that run at launch-time.
elevator.lua
Elevator ()
Loader ()
Enabler ()
Site ()
Starter ()
Reader (data,format)
data
.
ready
Curator ()
node.sites
(e.g. matched by site domains). This provides an opportunity to define sites and behaviours that may vary with every request on every site, such as ephemeral sites stored in a database (as in the Tenants application).Collator ()
Editor ()
output
response value.
application.id
and a slash e.g. appname/name
.
If prefixed as ~/name
the view or controller is assumed to exist in the current site only.application.id
and application.name
. Any part of the name before a . (period) is ignored.
Enabling applications has no per-request performance overhead except where the following are defined by the application, where the overhead will be specific to the functionality provided. Memory overhead for applications with multiple sites is minimal (sites are populated only with references to the application).
Sites are defined by the sites()
function of applications. See the Sites Folder or Tenants applications, or any other application with a sites function, for details on how sites may be defined and configured outside the Scribe environment.
Within the Scribe environment, sites are stored in the node.sites
table, with references from the global domains
table (used to lookup a site for each request) and ephemeral (per-request) site
table.
redirect=false
). This cannot be disabled without hacking as it is considered a best practice.
These values may be specified in the site's settings.lua file.
name = "My Site"
request.domain
is used.
domain = "example.org"
id = "example.org"
this is used internally and not normally consumed, it is the default domain, and may be used to reference site bundles with moonstalk.sites[id]
.
domains = { ['example.org']={} }
domains
table as a reference to the site.
redirects = { ['www.example.org']=true, }
language = "en"
applications = { "[application", }
locks = { "[Name", }
collators = { "application", "application.Function", application=false }
false
, ending with the default scribe.Collator which provides a page from addresses. If no collator provides a page, the scribe falls back to the generic found page.
token = { expires=seconds }
?(record.field or "")
, in this case the empty string default would cause the value to simply not appear if missing instead of generating an error.
Last-Modified
HTTP header when serving the response. This enables search engines to keep track of your site's changes to reindex pages, and improve their relevancy in search results. If your view displays content that changes in a database you should specify an appropriate timestamp for page.modified
in your controller or view accordingly.
scribe.View"name"
.Functions
#!/usr/bin/env lua
(for a CLI command) or contain a line starting module
(for a library) are ignored and not loaded as controllers.
autoaddresses
.
autoaddresses=false
.)
Defines how to match a request. Only one may be used.
matches = "path" or { "path1", "path2", }
starts = "prefix/"
ends = "/suffix"
contains = "value"
pattern = "value"
Defines what to do with the request. One or more may be specified per address. You need only specify both controller
and view
if they are different.
view = "name"
false
to prevent a view from being displayed.
controller = "name"
May be set to false
to prevent a controller from running.
template = "name"
false
collators = {"name"}
The named functions will be run in the specified order, and may set or change the controller and view or perform other functions prior to their execution. Names may reference functions in the current application or site, or other applications. May be set to false
to prevent inheritance and modification by applications, otherwise will inherit site.collator or node.collator as the first. These functions are run after the Binder(), prior to page rendering, and may used for preferences, authentication, and content retrieval.
redirect = "address"
Must be used exclusively (without any other handler). May be a URL or address. Optionally set redirect_preserve=true
to append the path and query_string to the given URL.
locks = { "key", }
post = {maxsize=bytes, …}
mazsize
permits uploads (which are otherwise disabled). Setting form=false
will prevent the Scribe from preprocessing the request body so that a controller may do so instead (e.g. using moonstalk.Resume with Nginx methods).Addresses may specify any of the attributes of a page, such as template
or title
or type
.
Represents the anonymous visitor that made the request, a profiled visitor (cookie-tracked but anonymous), or a registered user. Managed by the built-in authentication mechanism but may be extended by applications.
token
token
cookie, if signed-in or tracked.
id
To learn how you can extend moonstalk, and make use of pre-packaged functionality, read the applications page, or to understand the Moonstalk's design jump to the architecture page.