Basics
At it's core the upmk tool is a sytem of conventions for building a static
site.
upmk has the notion of a Model. Models have a schema definition, and have
supporting instances of that model. Schema definitions are written into
app/config/db/<name>.json as json files. Instances are written into
app/models/<name>/<instance-name>.md as markdown files (with a toml front
matter section for data attributes).
upmk has the notion of a View, which are templates for
generating views. Views are located in app/views/<name>/<action>.html.njk.
It has the notion of a Controller. Controllers are js files that expose actions (functions) that pull data from the DataStore and expose it to the approprite View.
Controller Actions build Pages
Unlike some other approaches to static page generation, upmk builds pages based
on exposed controller actions. For example, if you have a controller at
app/controllers/event_controller.js that exposes a testing action:
function testing(ds) {
return {context: {}, settings: {}}
}
module.exports = {
testing
}
The build would look for a template view in app/views/event/testing.html.njk
and would create a static page in static/event/testing.html
Controller Actions Interface
Controller Actions have a specific interface. Actions get passed an instance of
the datastore (often called ds). They are expected to return an ActionResult
object.
ActionResult objects have the following shape:
{
context: {
this: "is",
some: "data that the view uses to build the template"
},
settings: {
route: "/testing",
model: "blog"
}
}
The context object is generally used to pass data along to your view template
for buidling the page view.
There are a few available, optional keys for the settings object. Each are described here:
route
The route attribute defines the route at which a generated file will
be written. By default it is the name of the controller without the
_controller.js.
model
The model attribute is used in show actions to determine what type of
models the show action should iterate over to build the show action (see
section on Show Actions for more details). It also defaults to the name of the
controller.
layoutTemplate
The layoutTemplate defaults to "layouts/application". In your views, you'll
see it used as a variable to choose the appropriate template to extend.
onlyIn
The optional onlyIn attribute is used to only build views in a specific mode.
For example setting it to development means that these views will only be
created when building for development. (i.e. `upmk build --mode
Here's what a default event_controller.js would look like with an index
action:
const defaults = {
route: 'event',
model: 'event'
}
function index(ds) {
return responds({items: ds[defaults.model].instances})
}
function responds(context, settings) {
let finalSettings = Object.assign({}, defaults, (settings || {}))
return {
settings: finalSettings,
context: context
}
}
module.exports = {
index: index
}
See the generate command for more details on generating pages (controllers, actions, views) from the cli.
DataStore Structure
The DataStore is created during an upmk build and is the cobmination of data
schema for your models, and the collection of instances of that specific type.
Given a project with a single event model type, it is structured like this:
{
event: {
typeDef: {},
instances: []
}
}
In this above example we have a single model type called event. The typeDef
key is an object containing the schema for the model type. The instances
attribute is an array of instances of the event type.
The DataStore is passed to every controller action during a build.
Index Actions
Controller actions named index are typically used to show the listing of the
various models of a given type ... but they don't need to be. They can be any
type of page that you'd like. By default they'll be written to an index.html
file. With the default server settings in nginx, they'll be
served at the controller name (without the index.html being necessary)
Show Actions
Controller actions named show are considered special in upmk. They are used
to generate individual pages for each model instance of a given model type. By
default upmk chooses the model based on the name of the controller. This can be
over written by supplying a settings.model in your ActionResult object.
As an example, assume we have an event_controller.js that exposes a show.
When it processes the show action for our event controller, it will call
the show action once for each event model instance we have in our project.
It will write these files to event/<slug>.html (where slug is an attribute of
the model, or the model file name)
A show action's interface is slightly different in that it expects an instance of the DataStore, and an instance of the model to be passed. Note that the ActionResult object it returns is the same.
function show(ds, item) {
return responds({item: item})
}
Be aware that prior to building your actual show action views, the upmk tool
will call your show action one time to determine which model type it should
be itterating over. (Due to the fact that the model may be set on the
ActionResults.settings). This generally means that your show action should be
resilient to getting called with a datastore, and a blank item, ie:
show(dataStore, {})