Defining your model

A “model” in Ema represents the state to use to generate your site. It could be as simple as a variable, or it could be a list of parsed Markdown files (as in the case of a weblog). Ema’s model is also conceptually similar to Elm's model, in that - changing the model automatically changes the view.

Here’s an example model:

newtype BlogPosts = BlogPosts (Map Slug Text}

Here BlogPosts is the model type. If we are generating a weblog site, then all the “data” we need is loaded into memory as a value of BlogPosts.

Modifying the model

Ema’s dev server supports hot reload; it will observe changes to your model, in addition to code. To facilitate this you will manage your model as a LVar. The runEma function (described here) takes an IO action that gets LVar model as an argument.

For example,

runEma render $ \model ->
  forever $ do
    LVar.set model =<< liftIO getCurrentTime
    liftIO $ threadDelay $ 1 * 1000000

In this contrived example (full code here), we are using UTCTime as the model. We set the initial value using LVar.set, and then continually update the current time every second. Every time the model gets updated, the web browser will hot reload to display the up to date value. For the BlogPosts model, you would typically use fsnotify to monitor changes to the underlying Markdown files, but note that Ema provides a helper for that.

Next, we will talk about routes.

Links to this page
  • Working with files

    For monitoring local files on disk you would typically use something like fsnotify in place of observeFileSystem. What is the point of doing this? To support hot reload on data change. Imagine that your static site is generated based on Markdown files as well as HTML templates on disk. If either the Markdown file, or a HTML template file is modified, we want the web browser to hot reload the updated HTML instantly. This is enabled by storing both these kinds of files in the application model and using LVar to update it over time.

  • Working with files

    In most cases, however, you probably want to use the higher level function mountOnLVar. It “mounts” the files you specify onto the model LVar such that any changes to them are automatically reflected in your model value.

  • Tutorial

    Next, let’s define a model. A model will hold the state of our website used to render its HTML. Let’s put the speaker variable in it, as that’s all we are using:

  • Rendering HTML

    Once you have model and routes in place and constrained, the last piece of the puzzle is to write a function that takes both as arguments and returns file content (lazy bytestring, to be exact) to generate. This function can be as simple as the following:

  • LVar

    If you are familiar with Haskell’s stm package, a LVar is essentially a TMVar but with an extra ability for other threads to observe changes. Ema uses it for hot reload, and your application code is expected to set and update its model through the LVar.

  • Guide
    Defining your modelDefine your site model such that it supports hot reload
  • Defining Ema instance
    an IO action that takes LVar model as an argument.
  • Defining Ema instance

    Once you have model and route types in place, we must tell the Haskell compiler that they are suitable for generating static sites. We do this by creating an instance of the Ema typeclass.