1
1
mirror of https://github.com/srid/ema.git synced 2024-11-29 09:25:14 +03:00

FileSystem -> unionmount

This commit is contained in:
Sridhar Ratnakumar 2022-02-04 19:13:00 -05:00
parent 32b51222d6
commit cabae54a23
7 changed files with 22 additions and 46 deletions

View File

@ -5,6 +5,7 @@
- Websocket API: Add `ema.switchRoute` to switch to other routes in live server.
- Helpers
- `Ema.Helpers.PathTree` moved to separate package *pathtree*.
- `Ema.Helpers.FileSystem` moved to separate package *unionmount*.
## 0.4.0.0 -- 2022-01-19

View File

@ -17,13 +17,13 @@ When switching to a new route or when receiving the new HTML, Ema uses [morphdom
### Haskell reload
Finally, hot reload on _code_ changes are supported via [ghcid](https://github.com/ndmitchell/ghcid). The [template repo](https://github.com/srid/ema-template)'s `bin/run` script uses ghcid underneath. Any HTML DSL (like blaze-html -- as used by the [Blaze helper](guide/helpers/blaze.md)) or CSS DSL automatically gets supported for hot-reload. If you choose to use a file-based HTML template language, you can enable hot-reload on template change using the [FileSystem helper](guide/helpers/filesystem.md).
Finally, hot reload on _code_ changes are supported via [ghcid](https://github.com/ndmitchell/ghcid). The [template repo](https://github.com/srid/ema-template)'s `bin/run` script uses ghcid underneath. Any HTML DSL (like blaze-html -- as used by the [Blaze helper](guide/helpers/blaze.md)) or CSS DSL automatically gets supported for hot-reload. If you choose to use a file-based HTML template language, you can enable hot-reload on template change using the [`unionmount` library](guide/helpers/filesystem.md).
Note that if your application makes use of threads, it is important to setup cleanup handlers so that `ghcid` doesn't leave [ghost](https://stackoverflow.com/q/24999636/55246) processes behind. Helpers like [`race_`](https://hackage.haskell.org/package/async-2.2.3/docs/Control-Concurrent-Async.html#v:race_) will do this automatically (incidentally it is used by `runEma` for running the user IO action).
### Data reload
For anything outside of the Haskell code, your code becomes responsible for monitoring and updating the model [LVar](concepts/lvar.md). The [filesystem helper](guide/helpers/filesystem.md) already provides utilities to facilitate this for monitoring changes to files and directories.
For anything outside of the Haskell code, your code becomes responsible for monitoring and updating the model [LVar](concepts/lvar.md). The [`unionmount` library](guide/helpers/filesystem.md) already provides utilities to facilitate this for monitoring changes to files and directories.
## Handling errors

View File

@ -6,5 +6,5 @@ order: 5
Beyond the model and route types, Ema leaves it up to you as to how to develop your site. The following are not *required* when using Ema; nevertheless they are useful inasmuch as they capture common patterns in writing a good static site:
* [Blaze](guide/helpers/blaze.md) -- [We recommend--but not mandate--Tailwind for CSS and blaze-html as HTML DSL]{.item-intro}
* [Working with files](guide/helpers/filesystem.md) -- [Ema provides a helper to support hot-reload on files]{.item-intro}
* [Working with files](guide/helpers/filesystem.md) -- [Use `unionmount` to support hot-reload on files]{.item-intro}
* [Converting Markdown](guide/helpers/markdown.md) -- [Pointers on how to work with Markdown files]{.item-intro}

View File

@ -17,44 +17,27 @@ runEma render $ \model -> do
For monitoring local files on disk you would typically use something like [fsnotify](https://hackage.haskell.org/package/fsnotify) in place of `observeFileSystem`. What is the point of doing this? To support [hot reload](concepts/hot-reload.md) 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](guide/model.md) and using [LVar](concepts/lvar.md) to update it *over time*.
For filesystem changes, Ema provides a helper based on `fsnotify` in the `Ema.Helper.FileSystem` module. You can use it as follows
Much of this is provided by the `System.UnionMount` module from [unionmount](https://hackage.haskell.org/package/unionmount) package. You should use it:
```haskell
import qualified Ema.Helper.FileSystem as FileSystem
type Model = Map FilePath Text
import qualified System.UnionMount as UnionMount
Ema.runEma render $ \model -> do
LVar.set model =<< do
mdFiles <- FileSystem.filesMatching "." ["**/*.md"]
forM mdFiles readFileText
<&> Map.fromList
FileSystem.onChange "." $ \fp -> \case
FileSystem.Update ->
when (takeExtension fp == ".md") $ do
log $ "Update: " <> fp
s <- readFileText fp
LVar.modify model $ Map.insert fp s
FileSystem.Delete ->
whenJust (takeExtension fp == ".md") $ do
log $ "Delete: " <> fp
LVar.modify model $ Map.delete fp
let pats = [((), "**/*.md")]
ignorePats = [".*"]
void . UnionMount.mountOnLVar "." pats ignorePats model model0 $ \() fp action -> do
case action of
UnionMount.Refresh _ () -> do
when (takeExtension fp == ".md") $ do
log $ "Update: " <> fp
s <- readFileText fp
pure $ Map.insert fp s
UnionMount.Delete ->
whenJust (takeExtension fp == ".md") $ do
log $ "Delete: " <> fp
pure $ Map.delete fp
```
In most cases, however, you probably want to use the higher level function `mountOnLVar`. It "mounts" the files you specify onto the [model LVar](concepts/lvar.md) such that any changes to them are *automatically* reflected in your [model](guide/model.md) value.
```haskell
Ema.runEma render $ \model -> do
FileSystem.mountOnLVar "." ["**/*.md"] [] model def $ \() fp -> \case
FileSystem.Update () -> do
s <- readFileText fp
pure $ Map.insert fp s
FileSystem.Delete ->
pure $ Map.delete fp
```
`mountOnLVar` "mounts" the files you specify onto the [model LVar](concepts/lvar.md) such that any changes to them are *automatically* reflected in your [model](guide/model.md) value.
[Full example here](https://github.com/srid/ema-template/blob/master/src/Main.hs).
This functionality is mostly provided by the [unionmount] library.
[unionmount]: https://hackage.haskell.org/package/unionmount

View File

@ -26,7 +26,7 @@ runEma render $ \_act model ->
liftIO $ threadDelay $ 1 * 1000000
```
In this contrived example ([full code here](https://github.com/srid/ema/blob/master/src/Ema/Example/Ex02_Clock.hs)), 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](concepts/hot-reload.md) to display the up to date value. For the `BlogPosts` model, you would typically use [fsnotify](https://hackage.haskell.org/package/fsnotify) to monitor changes to the underlying Markdown files, but note that Ema provides [a helper](guide/helpers/filesystem.md) for that.
In this contrived example ([full code here](https://github.com/srid/ema/blob/master/src/Ema/Example/Ex02_Clock.hs)), 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](concepts/hot-reload.md) to display the up to date value. For the `BlogPosts` model, you would typically use [fsnotify](https://hackage.haskell.org/package/fsnotify) to monitor changes to the underlying Markdown files, or even better use [the unionmount library](guide/helpers/filesystem.md).
## Advanced tips

View File

@ -1,6 +1,6 @@
cabal-version: 2.4
name: ema
version: 0.5.1.0
version: 0.5.2.0
license: AGPL-3.0-only
copyright: 2021 Sridhar Ratnakumar
maintainer: srid@srid.ca
@ -139,7 +139,6 @@ library
if (flag(with-helpers) || flag(with-examples))
exposed-modules:
Ema.Helper.Blaze
Ema.Helper.FileSystem
Ema.Helper.Markdown
other-modules:

View File

@ -1,7 +0,0 @@
-- | TODO: Deprecate this module!
module Ema.Helper.FileSystem
( module X,
)
where
import System.UnionMount as X