node package manager
Loosely couple your services. Use Orgs to version and reuse your code. Create a free org »

ryver

Ryver

Ryver is the most powerful, extensible web site generator available. It's blazing fast, written in NodeJS, and using Yaml to configure how it works. You can use it to create powerful template-based static websites easily, and extend it adding simple hooks and filters.

Ryver has book-style documentation. Its usage is fully documented; I will work on the last bit of documentation (how to make plugins and its API) once it's production-ready (sometime this month, February 2016).

Ryver has a very solid filter-based architecture, it's extremely easy to understand and extend, and it's being deployed for two small sites and a large and established web site.

Historical notes

Ryver started as something that was meant to be a "one-week project", and it ended up taking me 6 weeks (initial version), + 1 week (initial fixups and documentation). It is being prepped to be used on three major production sites, which are going to be the testing bed for it to be declared "production ready".

Install Ryver

To install Ryver, simply type:

npm install -g ryver

And you should be good to go. After installation, you will end up with a "ryver" executable ready to work its magic.

Using Ryver

This guide will use you through every single possible way of using Ryver. It will start with its simple usage using its default plugins, up to the most advanced uses.

Hello world

First of all, create two directories called src and _site by running mkdir src and mkdir _site.

At this point, you are good to go. Run:

ryver src

The result (which is... nothing) will be placed in _site.

_site happens to be the default destination directory: you can change it to whatever you like by providing a second parameter:

ryver src somewhere

In this case, somewhere will contain the result of your hard work.

Note that you can set, as a destination directory, somewhere within src like so:

ryver src src/somewhere

This won't bother Ryver -- that is, Ryver won't scan src/somewhere as part of its input to generate your site.

In this guide, I will assume that you simply run ryver src and see your results in _site.

Since this chapter is about "Hello world", it's about time to create a file called hello_world.md in your source directory, and run ryver src.

You will see that an identical copy of hello_world.md will be placed in _site.

Since this guide is meant to show what Ryver is capable of, let's automatically transform hello_world.md into hello_world.html.

Place a file called _info.yaml into your src folder, so that it contains:

filters: markup-markdown

This info directive will apply to every file in that directory, as well as any file in nested directories.

By running ryver src again, you will see that a new file, hello_world.html, was created and contained:

<h1 id="hello-world-">Hello world!</h1>

The Markdown processing obviously worked. You probably noticed that _info.yaml wasn't copied over: that is because all files and directories starting with an underscore are not filtered, and therefore not copied over.

The markup-markdown filter will make sure that any file with the extension .md will be processed as Markdown.

The markup-markdown filter is provided by the plugin ryver-markup-markdown, which will filter every file with extension .md into HTML, as well as renaming them to .html

What you learned in this chapter:

  • ryver src is the same as ryver src _site: it will process all files in src recursively, and will use _site as output
  • ryver src src/_site is allowed (files in src/_site will not be considered part of the input)
  • Without any filtering instructions, files are simply copied over -- except the ones starting with _ (underscore), which are ignored
  • The file _info.yaml is important in Ryver since it tells Ryver how what to do with files in that directory as well as any nested directory.
  • By having filters: markup-markdown in your _info.yaml, you are telling Ryver that any file with the extenion .md will need to be filtered as Markdown and renamed to .html.

The frontmatter

Ryver allows you to define a frontmatter for every file. The frontmatter allows you to define some variables which will only apply to that page.

The variables you set might be for your own use (to be displayed in the templates) or could be useful to Ryver's plugins to know how to do their jobs.

For example, you might decide to turn off filters for a specific file; create a file called not_filtered.md like so:

---
filters:
---
# Hello boring world

After running ryver src, you will see that there is an extra file in the _site directory, but it's called not_filtered.md -- and it's still plain boring Markdown (it didn't get filtered). Notice however that the frontmatter did get filtered out of the end result.

NOTE: Remember to wipe out the _site directory as running ryver src doesn't delete old files left over by previous runs.

The variables in the frontmatter have priority over the ones defined in _info.yaml. Since filters is set to empty in the frontmatter, the file will not get filtered.

The frontmatter ability is provided by the plugin ryver-frontmatter.

What you learned in this chapter:

  • Files can have a frontmatter, which can be used to change user variables, as well as module variables which will change the way a file will get filtered.
  • Variables in the frontmatter have priority over variables set in _info.yaml.

Liquid template and more variables

One of Ryver's filters is template-liquid, which allows you to process Liquid directives within your file. You can activate Liquid for a specific file using its frontmatter. Imagine you have:

---
title: Hello world!
population: 6000000
postProcessFilters: template-liquid
---
# {{info.title}}

Your population is {{info.population}}

Running ryver src will output the following file:

<h1 id="hello-world-">Hello world!</h1>
<p>Your population is 6000000</p>

Note that the file was filtered by Markdown because of filters: markup-markdown in the _info.yaml file in the directory.

The template-liquid filter is provided by the plugin ryver-template-liquid. It may seem silly to add variables and then reference them in the templates directly like I did. However, their importance is more evident when you add more plugin to the mix, since plugins can (and will) create interesting (and useful!) variables.

What you learned in this chapter:

  • You can enable the template-liquid filter by adding it to the postProcessFilters variable, enabling powerful templating based on variables.
  • Variables in the frontmatter are accessible with in the info. namespace (or the info object)
  • One of the things plugins do is provide extra variables for you to use

Nested directories and variables

It's important now to take a deep breath and realise the power of Ryver and its variable system. When you have a _info.yaml file in a directory, the variables set there will be available to every filtered file in that directory, as well as every filtered file in any nested directory.

This means that for every directory you can have a "master" _info.yaml file, where you set variables to sane values, but then you can redifine some (of all) of the values in sub-directories creating a _info.yaml file inside each one. And then, again, you can have files defining variables themselves using their own frontmatter.

This implies that you will want the generic settings for variables in the site's "main" _info.yaml file, and then get more and more specific as you go deeper into the file system.

What you learned in this chapter:

  • Variables set in _info.yaml files will influence variables for every filtered file in that directory and in any subsirectory. Frontmatter in files will also redefine variables on a per-file basis.

Filtering lifecycle

At this point, you saw two cases where filtering was added: setting the filters variable, and setting the postProcessFilters variable.

The basics of Ryver are simple: every file is filtered by the specified filters. Filters are made available by plugins. By default, all plugins available in stock Ryver are loaded (although this can be changed).

You can decide what filters will apply to a file by setting the following phases:

  • preProcessFilters.
  • preFilters.
  • filters.
  • postFilters.
  • postProcessFilters.

Generally speaking, you can place a filter anywhere here. However, some filters might need to be placed in specific spots. For example, you want template-liquid to have some action only when all variables have been set by other plugins. The only time when you are guaranteed that that will have happen is at postProcessfilters time.

The stages are there mainly for grouping convenience. You can use all of them, or only a subset depending on how you want to organise your site. For example, it's common to only really use three of them, and set something like this in your _info.yaml file:

preFilters:
filters: markup-markdown
postFilters: layout

This will set a general behaviour you want nearly every page to have. I haven't yet explained the layout filter, but its meaning is pretty straightforward (it will place the contents of each file into a specified template). Generally speaking, every HTML page will have a layout, and it will be filtered as Markdown first (before being placed in the layout, obviously).

For some inner pages, you might want to add more filters but still have layout there. So, in an inner directory, you might set:

filters: markup-markdown,pager

This will only affect pages within that subdirectory, and the postFilters variable will be untouched. So, layout will still be applied, along with markup-markdown and pager -- which is what you want.

Basically, the five different stages are there to help you group what filters apply where. The scenario above is the most common one, but having five different groups five you the freedom to deal with the most complex scenarios.

The only special phase is postProcessFilters: filters will be able to run once all of the variables set by other plugins are fully set (which is why template-liquid, which is often used for variable substitution, is placed there).

What you learned in this chapter:

  • You can plce filters in whichever phase you like, depending on how you decide to group them
  • All phases are equal, except postProcessFilters which guarantees that all variables set by plugins are set.
  • You should use grouping to simplify how you redefine filters according to filter definition in _info.yaml. You will usually put the most common case in the root _info.yaml, and then redefine specific groups in inner directories.

Ryver's _config.yaml file

Some of Ryver's plugins are configurable. If you place a file called _config.yaml in Ryver, you will be able to change some of the default configuration settings.

Ryver uses the config file to those settings that are 1) Global in scope 2) Affecting how a plugin will work.

For example, in _config.yaml you can define the list of plugins installed by default by Ryver, or where the layout files are (for the layout plugin). This sort of setting is global in scope, meaning that it wouldn't make sense to have it in _info.yaml (which has a per-directory scope) or in the file's frontmatter.

I will now explain several filters; for each one, I will explain what confguration options you have in _config.yaml (which, I remind you, should be at the root of your source directory).

What you learned in this chapter:

  • You can have a global config file called _config.yaml in the root of your source directory called
  • The _config.yaml file has global confguration options for plugins and filters

Layout

This is probably one of the most important plugins in Ryver. It allows you to place the contents of a file into a template.

For example, create a file called templateHelloWorld.md with the following contents:

---
layout: page.html
postFilters: layout
---
# Hello world!
Mind you, it's a templated world!

Running Ryver will result in an error:

`Ryver · ENOENT, open 'src/_layouts/page.html'``

This error is there because Ryver expects, by default, to find page.html (the template file) in the _layouts directory. (NOTE: the _layouts directory will not be in the resulting site since its name starts with an underscore).

So, create a _layouts directory in the root of your source directory, and place a page.html file as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>The title!</title>
  </head>
  <body>

<!--contents-->

  </body>
</html>

Note that this is a minimalistic HTML file, with a placeholder -- <!--contents--> that will instruct Ryver where to place the filtered page.

If you run ryver src, you will see that the resulting templateHelloWorld.md will be created like so:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>The title!</title>
  </head>
  <body>

<h1 id="hello-world-">Hello world!</h1>
<p>Mind you, it’s a templated world!</p>

  </body>
</html>

Note that this is the first time we do something actually useful using Ryver: you had a simple Markdown file called templateHelloWorld.md and produced, as a result, a functioning and well formatted HTML file called templateHelloWorld.html.

Don't forget that the markup-markdown filter was applied because you have a _info.yaml file in the root of your source directory, with the following:

filters: markup-markdown

Finally, you can configure the layout plugin to change the name of the _layouts directory, by placing something like this in your _config.yaml file:

layoutsFolder: _alternativeLayoutsFolder

It's important for the directory name to start with an underscore, so that it won't be copied over to the final result.

What you learned in this chapter:

  • You can filter contents though the layout filter, which will place the contents within a template with postFilters: layout in your frontmatter
  • In order for filtering to do anything, you need to have a layout variable set (on a per-directory bases, in a _info.yaml file, or in your frontmatter). E.g. layout: page.html will use the template in _layouts/page.html
  • The template file will be just text, with the special placeholder <!--contents--> which will be replaced with the contents of the file

Layout and liquid together

If you worked with HTML generators before, you probably noticed an eye-sore in the page.html file:

<title>The title!</title>

This implies tht every single page using page.html as a template will have The title! as their HTML title. This is hardly ideal.

To make things really shine, you can -- and in fact -- use the layout and the liquid filter together.

Change page.html like so:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>{{info.title}}</title>
  </head>
  <body>

<!--contents-->

  </body>
</html>

Also, change templateHelloWorld.md like so:

---
layout: page.html
postFilters: layout
postProcessFilters: template-liquid

title: It's a template title!
---
# Hello world!
Mind you, it's a templated world!

The result will me much better:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>It's a template title!</title>
  </head>
  <body>

<h1 id="hello-world-">Hello world!</h1>
<p>Mind you, it’s a templated world!</p>

  </body>
</html>

This is what happened in terms of filtering. The file had the following fiters set:

  • filters: markup-markdown (from _info.yaml)
  • postFilters: layout (from the file's frontmatter)
  • postProcessFilters: template-liquid (from the file's frontmatter)

So, the filters will be applied in the following order: markup-markdown, layout, template-liquid.

After markup-markdown, the contents will go from:

# Hello world!
Mind you, it's a templated world!

To:

<h1 id="hello-world-">Hello world!</h1>
<p>Mind you, it’s a templated world!</p>

Note that the file's name will also change to templateHelloWorld.html (the filter will actually rename the file).

After layout, the contents will be placed accodding to the placeholder in page.html, and the contents will be transformed into:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>{{info.title}}</title>
  </head>
  <body>

<h1 id="hello-world-">Hello world!</h1>
<p>Mind you, it’s a templated world!</p>

  </body>
</html>

After template-liquid, all of the liquid directives will be executed, including {{info.title}}, and the contents will be transformed into:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>It's a template title!</title>
  </head>
  <body>

<h1 id="hello-world-">Hello world!</h1>
<p>Mind you, it’s a templated world!</p>

  </body>
</html>

As you can see, the order in which the filters are executed actually matters. If you ran the template-liquid filter before layout, the variable {{info.title}} would never be resolved. If you ran markup-markdown after layout, the markdown filter would not work since it wouldn't compile anything within a <div> (which would be the case, since the contents would already be placed in the layout).

The order in which you should execute the filters is the natural order in which you would process the contents. This will become more evident with the next two filters I will explain.

What you learned in this chapter:

  • The order in which filters are applied is important, as it needs to make sense
  • Variable substitution (with template-liquid) should happen after layout, so that any liquid tags in the template file will actually be compiled
  • The filter template-liquid (or any filter that does variable-substitution) should be called in postProcessFilters, which is the only special stage

Pager

You sometimes want to split your contents into separate pages. Ryver majes this very simple, by using the pager plugin.

Create a file like so:

---
layout: page.html
preFilters: pager
postFilters: layout
postProcessFilters: template-liquid

title: It's a template title!
---

# Hello there

This is a file that will end up split into several ones.
The way it works is really simple, and yet very powerful.

This is the end of the first page.

<!--pagebreak-->

This is the second page.

<!--pagebreak-->

This is the third page.

<!--pagebreak-->

This is the fourth (and last) page.

Once you run ryver src, you will see that four files were actually created:

  • paged.html
  • paged_2.html
  • paged_3.html
  • paged_4.html

So, the final result, rather than being a whole file, is four different slices where each slice is marked by <!--pagebreak-->.

You can decide both the separator and the pattern of the "paged" file names in your _config.yaml file. For example you could have:

pageFileName: 'another_page{{originalName}}_{{number}}'
pageSeparator: '<!--anotherPagebreak-->'

Variables available in the pages

While having split files is very useful, what's really crucial is being able to know, within the page, enough information to create a "pager" and actually navigate to the next page. Crucial information is for example:

  • The page number
  • The total number of pages
  • The name of the "next" page
  • The name of the "previous" page
  • An array with all the available pages, where each element has a page number, a page name, and a flag set to true if the page in the pager is the same as the displayed one

Luckily, all of this information is made available by the pager plugin to every single page, through the followng variables (in info.pagerData):

  • pageNumber
  • totalPages
  • prevPageName
  • nextPageName
  • pager[] (an array where each element has the following attributes)
    • pageNumber
    • pageName
    • thisPage

Using liquid, you can easily create a powerful pager with something like this to show a working, themable pager:

<!-- This loops through the paginated posts -->
<div class="pager">
  <ul>
  {% for page in info.pagerData.pager %}
    {% if page.thisPage %}
    <li><p class="this_page">{{page.pageNumber}}</p></li>
    {% else %}
    <li><a href="{{page.pageName}}{{system.fileExt}}" class="this_page">{{page.pageNumber}}</a></li>
    {% endif %}
  {% endfor %}
  </ul>
</div>

The code is very basic: it goes through all of the elements in info.pagerData.pager, and provides either a link to the page, or simply a page number (in case you are viewing that very page).

Note the use of {{system.fileExt}} to make up the link with the page's name plus the page's extenson. Making previous and next links is just as easy:

<!-- Prev and Next links -->
<div class="pagination">

  <!-- Prev link -->
  {% if info.pagerData.prevPageName %}
  <a href="{{ info.pagerData.prevPageName }}{{system.fileExt}}" class="previous">Previous</a>
  {% else %}
  <span class="previous">Previous</span>
  {% endif %}

  <!-- page number -->
  <span class="page_number ">Page: {{ info.pagerData.pageNumber }} of {{ info.pagerData.totalPages }}</span>

  <!-- Next link -->
  {% if info.pagerData.nextPageName %}
  <a href="{{ info.pagerData.nextPageName }}{{system.fileExt}}" class="previous">Next</a>
  {% else %}
  <span class="next">Next</span>
  {% endif %}
</div>

Since you only want to provide the pager and navigation to pages that are actually sliced up, you should surround any pger code with:

{% if info.pagerData %}
...
{% endif %}

Earlier in this guide I mentioned how some filters created important variables, as well as filtering files. This is the prime example of this mechanism at work: the pager plugin splits files in slices, and also (more importantly) sets several variables on those files so that the users know which slice they are a looking at.

What you learned in this chapter:

  • You can split files up using the pager plugin
  • You can configure both the separator (pageSeparator) and the pattern used to create pages (pageFileName) setting those values in _config.yaml. The default values are <!--pagebreak--> and {{originalName}}_{{number}}.
  • The sliced up pages have a pagerData variable that you should use to make up navigation and pager with

Landing

In some cases, you might want to create a "landing" page: a page that users will see, and that will then link to the real contents.

You can do this with the landing filter.

Create a file called article.md that contains the following:

---
layout: page.html
landing: `_landing.md`
postFilters: landing, layout
postProcessFilters: template-liquid

title: It's a page behind a landing one!
---
# You got here!

Mind you, you probably had a landing page first!

You will also need to create a file called _landing.md (referenced in the frontmatter) in the same directory. For example:

---
layout: page.html
postFilters: layout
postProcessFilters: template-liquid

title: It's a page behind a landing one!
---
# Landing page!

Hello, this is the landing page!
Now you should go to your [Final destination]({{info.originalDestination}})

The landing file (in this case _landing.md) will be filtered providing it an extra variable, called info.originalDestination, which can be used to make a link to the original generating file.

The landing page will inherit the same variables as the originating file.

If you would like to use a generic landing page, you can place the _landing.md file in a directory called _includes in the root of the source directory. This will allow you to use the same landing page for different files, regardless of where they are stored on the file system. If a file called _landing.md isn't in the same directory as the processed file, it will be searched in _includes.

Keep in mind that the landing page will inherit the same variables as the originating file, even if it's placed in the _includes directory.

You can easily change the includes directory by setting the includesFolder variable in your config.yaml file:

includesFolder: `_someOtherIncludes`

What you learned in this chapter:

TODO

Lister

The most powerful (and complex) plugin in Ryver is lister, which allows you to create a number of categories, and then "attach" any post to any categories amongst the available ones.

The most common example is the possibility of wanting tags and category for your posts. This means that a post could have something like this it its frontmatter:

There are two main tasks the lister accomplishes:

  • Generates template-based, paged pages which list all documents belonging to a tag/category. This allows you to have comprehensive lists of all articles with the tag linux.
  • Create template variables with the latest N documents belonging to a specific tag/category. This allows you to have, in a page, something like "Latest 10 articles about Linux".

There is no limit on the number of categories you can have.

Configuring the lister

Unlike other plugins, lister needs to be configured in order to work (since it's not really possible to set it with sane defaults).

This is what you could have in your _config.yaml file:

# File generation section
listTemplate: _listTemplate.md
listMainPageName: index
listNumberPageName: index_{{number}}

# Variable to define grouping variables and placement of generated list files
listVars:
  tags:
    indexFolder: tags/{{name}}
    perPage: 5
    sortBy: date
  categories:
    indexFolder: categories/{{name}}
    perPage: 5
    sortBy: date

listAll:
  indexFolder: articles
  perPage: 10
  sortBy: date

The first part, listTemplate: _listTemplate.md, defines which file will be used as basic template to generate a list of articles. The file will need to be placed in the _includes directory (or wherever you set includesFolder to be in your _config.yaml file).

listMainPageName and listNumberPageName set the names of the main index file and subsequent pager files generated (since there could be 10000 articles on linux, and listing them all in one page might prove difficult). In listNumberPageName, {{number}} is obviously resolved to the page number.

The listVars variable defines which variables in the frontmatter of a file will be considered by lister. In this case, tags and categories. This means that when a file has in its frontmatter something like this:

----
listed: true
categories: hacking
tags: programming,security,linux
date: 2002-12-14
...the rest of the frontmatter...
----

The page will be assigned to the category hacking and to the tags programming, security and linux. Note that in order to make sure the artcle is considered by lister you also need to set listed: true. As you can see, in lister's configuration for each key in listVars you need to set indexFolder (which is the directory in which the paginated files will be placed, where {{name}} is resolved to the category/tag name), perPage (the number of articles placed in each page) and sortBy (the field that will be used to sort the documents).

The last part of the configuration file is the listAll key, which configures how the full list of documents is created, paginated and sorted.

The file generaton side of things

When you run ryver src, the following directories will be created in the destination folder:

  • tags. Will contain one subdirectory for each tag defined at least once; each subdirectory will contain the listing files, called index.html, index_1.html and so on.
  • categories. Will contain one subsirectory for each category defined at least once; each subdirectory will contain the listing files, called index.html, index_1.html and so on
  • articles. Will contain the listing files directly (called index.html, index_1.html and so on.)

Each listing page will be generated using _listTemplate.md as a template. Note that in terms of _info.yaml, it's as if the template file were placed into tags or categories. So, the _info.yaml from the root directory and the _info.yaml in tags or categories will be able to set variables referenced as info in _listTemplate.md. Also note that any file in tags/ in the source folder will be copied over. So, you will be able to populate the destination directories with any extra CSS, icon files, or whatever else you need.

The file _listTemplate.md will have the same variables available for the pager. Specifically:

  • pageNumber
  • totalPages
  • prevPageName
  • nextPageName
  • pager[] (an array where each element has the following attributes)
    • pageNumber
    • pageName
    • thisPage

However, these extra values will also be available:

  • pageName The current page name displaying the elements.
  • listName The name of the list. In this case it could be articles or tags.
  • valueName The value within that list. In this case it could be linux, or security
  • totalElemens The total number of documents across all pages
  • elementsPerPage The maximum number of documents a page can list
  • elementsInThisPage An array containing all elements listed this very page. Each element will be in fileData format.

These extra variables only make sense in the context of paginating a list of articles within the pager.

While the pager code is the exact same as the one shown in the pager chapter of this guide, the code to list the articles will simply need to cycle through elementsInThisPage and display formatted information about each entry:

<!-- This loops through the paginated posts -->
<div class="article-list article-{{info.listName}}">
  <ul>
  {% for article in info.elementsInThisPage %}
    <p><a href="{{article.system.filePath}}{{article.system.fileName}}{{article.system.fileExt}}">{{article.info.title}}</a></p>
  {% endfor %}
  </ul>
</div>
TODO: check and improve this code

Since you also know the list name and the value name. This means that you can theme the site accordingly.

Since creating a working example of list generation is complex, a full setup with tags and categories will be provided in the next chapter, along with a full example web site that covers everything explained in this guide.

What you learned in this chapter:

TODO

A complete Ryver site

If you followed this guide, you ended up with a bunched of test files in your src folder, a good understanding of Ryver, but no real starting point for a real site.

The example directory in Ryver provides exactly that: a simil-real life example of how to use Ryver in a site.

Ryver plugins

When you use Ryver, you are actually using it with

TODO

Ryver watcher

Ryver allows you to work on file and only regenerate what's strictly necessary when you save a file. This is a common feature in static site generators since it allows you to make small changes and see them immediately. This feature is especially important when

TODO

Developing with Ryver

Ryver is based on plugins. In fact, Ryver itself is a small core, which deals almost exclusively with loading plugins and setting up -- as well as calling -- the hooks for those plugins.

Before getting into development, you should read Ryver's user guide and use Ryver at least for a little while, so that you are at least familiar with Ryver's "way" of doing things.

Modules as plugins

TODO

Life cycle of filtering: hooks and filters

TODO

API

TODO

Functions signatures

TODO

Watching API

TODO

Analisys of every plugin in Ryver

TODO

Looking at existing plugins: techniques and tips

TODO

An example plugin

TODO