rheo

2.2.0 • Public • Published

Rheo

Goddes Rhea probably from the word rheo

Rheo is a template library built on top of html-tokenize and html-select it is heavily inspired by hyperstream and hyperspace.

Usage

Rheo uses streams everywhere, and where it can't use streams it uses callbacks that recieves and returns streams, with few exceptions.

Simple example

The simplest and not very interesting you can do is to just parse and then render html, like so:

rheo('<h1>Hello Rheo!</h1>').render().pipe(req)
 
\\ or
 
html_stream.pipe(rheo()).render().pipe(req)

Using CSS selectors

That was not really exciting, right? Let's do something more fun and change the message from the last "exercise".

html_stream.pipe(rheo())
  .inner('h1', function (inner_template) {
    return rheo('Hello Template')
  })
  .render()
  .pipe(req)

or the short hand:

html_stream.pipe(rheo())
  .inner('h1', rheo('Hello Template'))
  .render()
  .pipe(req)

Outer and inner both return a new rheo stream and you could chain multiple of these calls and shape your template.

Changing attributes

A lot of the things that happen in a webpage are defined by attributes. This is the only place where the stream/callback pattern breaks in Rheo. But don't worry it's still not complicated. And in theory we are only a pull request away from unifying the interface.

html_stream.pipe(rheo())
  .attribute('h1', 'class', function (old_value) {
    return old_value + ' rainbow'
  })
  .render()
  .pipe(req)

Or shorthand if there is no other classes.

html_stream.pipe(rheo())
  .attribute('h1', 'class', 'rainbow')
  .render()
  .pipe(req)

A little rainbow in your headings could never hurt, could it?

If you would like to change multiple attributes on the same element there is a shorthand for that to.

html_stream.pipe(rheo())
  .attributes('a', {
    'class': 'rainbow',
    'href': '#rainbow_power'
  })
  .render()
  .pipe(req)

Design by Example

Sometimes hardcoded is just plain better then handcrafting a machinery that does the same thing. Therefore its easy to pick one of many options when replacing a content with one of its elements.

<div class="fortune">
  <span class="fortune-good">Your code will read like well writen prose.</span>
  <span class="fortune-bad">You will be stuck in the debugger all day.</span>
</div>
html_stream.pipe(rheo())
  .inner('fortune', function (fortunes) {
    return fortunes.find('.fortuen-good')
  })
  .render()
  .pipe(req)
 

Iteration, map

HTML is a tree structure writen in a serial format. Sooner or later we want map some stream of data into a stream of html.

Take a look at this view controller.

function render_pet(template, data) {
  return template
    .inner('pet-name', function () {return rheo(data.name)})
    .inner('pet-age', function () {return rheo(data.age.toString())})
    .inner('pet-type', function () {return rheo(data.type)})
}

This function binds data to one pet template. This could be used for a pet profile, with the correct HTML. But it can also be reused for building list of pets.

It works fine now to render one, but how do we render all list items?

html_stream.pipe(rheo())
  .inner('.pet-list', function (pet_template) {
    var pet_template_stream = pet_template.map(render_pet)
    return pet_data_stream.pipe(pet_template_stream)
  })
  .render()
  .pipe(req)

Assuming that pet_data_stream is a stream of pet data objects and that html_stream is a text stream of html with a list with the class pet-list and that that list contains one example of a pet list item, then this will render a list of pets in that list.

Super Modularity

As you can see there is enormus potential to decouple your layout and templating from logic. And that is both good and dangerous so please be careful! But one more neat trick needs to be shared.

How many times have a templating librarie let you down when doing something as trivial as setting the title of your page. I have seen realy nasty ways to deal with that and selecting the current page in a menu.

Changing things that have already been renderd in a layout template is normaly frustrating and hackish. With Rheo? No, it's simple!

Rheo templates can be piped together like so:

function select_menu(menu_name) {
  var selector = '.menu .menu-item.' + menu_name
  return rheo.template(function (stream) {
    return stream
      .attribute(selector, 'class', function (classes) {
        return classes + ' selected'
      })
      .attribute(selector + ' a', 'href', function () {
        return '#'
      })
  })
}
 
function set_title(title) {
  return rheo.template(function (s) {
    return s.inner('title', rheo(title))
  })
}
 
page_template
  .pipe(select_menu('profile'))
  .pipe(set_title('My fluffy dog'))
  .render()
  .pipe(req)

As long as the element with the selector that you want to alter is put into the stream it doesn't matter when you add the menu or change the title, except for performence. Neat huh!?

Advanced example

<html>
  <head></head>
  <body>
    <h1>Todo</h1>
    <ul class="things">
      <li class="done">
        <img src="smily.png">
        <span class="title"></span>
      </li>
      <li class="pending">
        <img src="sadface.png">
        <span class="title"></span>
      </li>
    </uĺ>
  </body>
<html>
var rheo = require('rheo')
html_stream.pipe(rheo())
  .inner('.things', function (items) {
    var item_template_stream = items.map(function (template, data) {
      return template
        .find(data.done? ".done": ".pending")
        .inner(".title", function () {
          return rheo(data,title)
        })
    })
    return item_stream.pipe(item_template_stream)
  })
  .render()
  .pipe(process.stdout)

Package Sidebar

Install

npm i rheo

Weekly Downloads

7

Version

2.2.0

License

MIT

Last publish

Collaborators

  • neppord