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).
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
user.ip
is advised instead.
get = { name="value", }
method="GET"
. Use of form
is advised instead.
post = { name="value", post_data="data", }
method="POST"
. Use of form
is advised instead.
Contains merged values from both request.get
and request.post
(latter has precedence), but is always present regardless of method
.
name = "value" or {name="name", size=bytes, ['content-type']="mime/type", contents="data"}
uploads = { {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.
Defines the page to be rendered as a response.
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 }
locks = { "name", }
locale = "us"
user.locales
and site.locale
.
language
= "en"
user.languages
and site.vocabulary
. Defines the Language header.
polyglot = false
collators = { function() end, }
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
Always declare variables as local
or use the temp
table.
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.
database = {default="application"}
As above if not specified this will be detected or defaulted.
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.
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.
table_name = { system="name" }
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"
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).
A table or function value will invoke an error as they cannot be respresented as text in a response, for debugging you may however output them with tostring()
.
In views, do not call write
from a ?()
expression tag, instead call it from a <? ?>
processing 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"
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"
Run a specified controller only.
("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.
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.false
<html>
tag, page.template
is not set to the default template. If you wish to run a template controler with such a view, specify the template
using an address, or 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
. This provides an opportunity to define sites and behaviours that may vary with every request, such as ephemeral sites stored in a database but that may be arbitrarily deleted (as in the Tenants application).
The curators of all installed applications are called in succession until one provides a site. The Generic application provides a curator that runs last, to handle the unknown domain condition. When using multiple applications with curators (e.g. both Tenant and Sites Folder) the curator order should be specified with node.curators.
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.
id = "example.org"
name = "My Site"
request.domain
is used.
domain = "example.org"
domains = { ['example.org']={} }
domains
table as a reference to the site.
redirects = { ['www.example.org']=true, }
language = "en"
applications = { [application.id]=application, }
?(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 or referenced 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. If specified the default site or application's collator function will not be added. May be set to false
or an empty table to prevent inheritance of a default collator function.
redirect = "address"
Must be used exclusively (without any other handler). May be a URL or address.
locks = { "key", }
form = {"…"}
action = "name"
Addresses may specify any of the attributes of a page, such as template
or title
.
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.