Skip to content

Commit

Permalink
Merge branch 'release/0.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominic Barnes committed Oct 11, 2014
2 parents 15208b2 + 7b3b99e commit 4cdfe38
Show file tree
Hide file tree
Showing 16 changed files with 394 additions and 289 deletions.
8 changes: 8 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@

# 0.3.0 / 10-10-2014
* massive internal overhaul
* many more configuration options, no longer encouraging custom `Renderer` (although it's still available)
* no longer injecting data to `locals` (using handlebars' `options.data` instead)
* default `options.partialId` now camel-cases by default
* 100% test coverage :)
* probably more... just read current readme

# 0.2.0 / 10-02-2014
* adding custom error message
* adding `options.beforeRender` fn to make last-minute adjustments before rendering
Expand Down
170 changes: 129 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ app.listen(3000);
<title>{{title}}</title>
</head>
<body>
{{{body}}}
{{{@body}}}
</body>
</html>
```
Expand All @@ -66,7 +66,7 @@ Hello, {{name}}!

The entry point for rendering is known as a view. The view usually contains the
content specific to a single page. It has access to all loaded partials, helpers
and is injected into the `{{{body}}}` of the corresponding layout. (if there is
and is injected into the `{{{@body}}}` of the corresponding layout. (if there is
one)

The simplest usage is to have a flat `views/` directory that contains `.hbs`
Expand All @@ -78,7 +78,7 @@ In "development mode", views are not cached.
### Layouts

Layouts are the content that is shared between many views. Like views, it has
access to all loaded partials and helpers. The `{{{body}}}` local is set as the
access to all loaded partials and helpers. The `{{{@body}}}` local is set as the
content of the corresponding view.

The simplest usage is to have a flat `layouts/` directory that contains `.hbs`
Expand All @@ -92,28 +92,24 @@ In "development mode", layouts are not cached.
[Partials](https://github.com/wycats/handlebars.js/#partials) are sub-templates
that you can use to render smaller bits within layouts/views.

Due to how Handlebars deals with partials, they must all be registered before
they can be used. This is unfortunate, particularly during development, as you
would need to restart your server each time you change a partial. (unlike views
and layouts, which are easy to deal with) Thankfully, koa-handlebars alleviates
this problem for you.
There are 2 main types of partials. First, global partials are registered
during init. (see `options.partials`) These will not be dynamically updated,
not even during "development mode". (thus, you will need to restart your server
when these globals change)

On the first `render` call, the available partials are loaded and compiled. In
"development mode", the partials directory is monitored for changes, keeping
handlebars aware of all changes to partials. (including updates, additions and
removals)
Secondly, partials residing in the directory specified by `options.partialsDir`
will be dynamically loaded on each render call. When caching is enabled, that
overhead is reduced substantially, and further optimization will be done in the
future.

### Helpers

[Helpers](http://handlebarsjs.com/#helpers) are functions that any of your
templates can call upon.

The primary way to register helpers is during init with the `helpers` option.
This is a hash of functions (where each key is the helper id that will be used
in templates)

Currently, these are not very automated, so any changes here will require your
server to be restarted.
Currently, helpers can only be defined globally and must be declared during
initialization. (see `options.helpers`) This requires a server restart after
any changes, but this will be improved upon in the future.

### Development

Expand All @@ -124,26 +120,118 @@ In addition, this library uses
[visionmedia/debug](https://github.com/visionmedia/debug), so you can enable
debug output via `DEBUG=koa-handlebars` in the terminal.

### Configuration Options
* `root`: the root directory to operate with (defaults to `process.cwd()`)
* `viewsPath`: the path (relative to `root`) to find views (defaults to
`views/`)
* `partialsPath`: the path (relative to `root`) to find partials (defaults
to `partials/`)
* `layoutsPath`: the path (relative to `root`) to find layouts (defaults to
`layouts/`)
* `extension`: the file extension to use for your templates (default: `.hbs`)
* `defaultLayout`: if you are using layouts, enter your main one here
(otherwise each call to `render` will need to specify `layout` manually)
* `helpers`: a hash of helpers to load handlebars with (you can always add
more after init)
* `cache`: enables/disables the view cache (default: `true`)
* `beforeRender()`: function that is called using the koa context before
merging locals and rendering. (useful to make last-minute adjustments before
rendering)

## Advanced Usage

If you have an app structure that's different than the norm, you are able to
accomodate quite a bit by creating a custom instance of the `Renderer`. See
the advanced example for real code demonstrating what I mean.
### Special Variables

When rendering templates, koa-handlebars will add 3 special private variables
to your templates:

* `@body`: in layouts, this is the contents of the rendererd view
* `@view`: the name of the view that is being rendered
* `@layout`: the name of the layout that is being rendered
* `@koa`: the koa `ctx` of the current request

You can add more variables of your own via the `beforeRender` option. (see
configuration options section for more details)

Generally speaking, avoid injecting data directly into `locals` from middleware,
instead focus on adding things to `options.data` or using the koa context to
grab data from there. (eg: `{{@koa.request.length}}`)

## Configuration Options

### root

The base directory to use when resolving paths.

**Default:** `process.cwd()`

### cache

Enables or disables the view cache. This is basically the flag for "development
mode".

**Default:**: `true`

```js
app.use(handlebars({
cache: app.env !== "development"
}));
```

### extension

The file extension used by templates. Your files must be named consistently
throughout the project at this time.

**Default:** `".hbs"`

### viewsDir

The location of your view templates (relative to `root`)

**Default:** "views"

### viewPath(id)

Translates an `id` passed to `render()` and returns an absolute path to the
template. For example: `"home" => "/path/to/root/views/home.hbs"`

This function is run with the renderer as it's context (ie: `this`) so you can
access `this.options` within your custom functions.

### defaultLayout

If you are using layouts, then this can be used to bypass requiring each call
to `render()` to specify a layout manually. Otherwise, leaving it empty will
not render a layout at all unless otherwise specified.

### layoutsDir

The location of your layout templates (relative to `root`)

**Default:** "layouts"

### layoutPath(id)

Translates an `id` passed to `render()` and returns an absolute path to the
template. For example: `"main" => "/path/to/root/layouts/main.hbs"`

This function is run with the renderer as it's context (ie: `this`) so you can
access `this.options` within your custom functions.

### partialsDir

The location of your non-global partial templates (relative to `root`)

**Default:** "partials"

### partialId(file)

This function is a little backwards compared to layouts and views, but it takes
a path for a partial template file. (relative to `partialsDir`) and converts it
into a handlebars-friendly identifier.

For example: `"navigation.hbs" => "navigation"`

By default, it will camel-case your partial if it is in a nested directory.

For example: `"nav/main.hbs" => "navMain"`

### helpers

Allows you to define global helpers during initialization, this should be a
shallow object where each key is a helper name and the value is a function.

### partials

Allows you to define global partials during initialization, this should be a
shallow object where each key is a partial name and the value is a function.

### beforeRender(locals, options)

This function is around to give you a hook in before rendering is performed
to make last-minute modifications to either the view `locals` or the handlebars
`options` (see [docs](http://handlebarsjs.com/execution.html) for more info)

*Generally-speaking*, you should avoid modifying `locals`. If you have further
data you want your templates to access, use `options.data` instead.
38 changes: 35 additions & 3 deletions example/advanced/app.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,54 @@
// dependencies
var camel = require("to-camel-case");
var koa = require("koa");
var handlebars = require("./handlebars.js");
var handlebars = require("../../index.js");
var path = require("path");

// locals
var app = koa();

// middleware
app.use(require("koa-favi")());
app.use(require("koa-logger")());

// handlebars config
app.use(handlebars({
// setting up cache based on the env
cache: app.env !== "development",

// setting up a default layout
defaultLayout: "main",
viewPath: "pages",

// custom views dir
viewsDir: "pages",

// expects pages/:name/template.hbs
viewPath: function (id) {
var o = this.options;
return path.resolve(o.root, o.viewsDir, id, "template" + o.extension);
},

// expects layouts/:name/template.hbs
layoutPath: function (id) {
var o = this.options;
return path.resolve(o.root, o.layoutsDir, id, "template" + o.extension);
},

// expects partials/:name/template.hbs
partialId: function (file) {
return camel(file.split(path.sep).slice(0, -1).join("-"));
},

// loading some global helpers
helpers: require("./lib/helpers.js")
}).middleware());
}));

// render example
app.use(function *() {
yield this.render("test", {
user: { name: "World" }
});
});

// start server
app.listen(3000);
38 changes: 0 additions & 38 deletions example/advanced/handlebars.js

This file was deleted.

4 changes: 2 additions & 2 deletions example/advanced/layouts/main/template.hbs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Layout: {{layout}}<br>
<b>{{{body}}}</b>
Layout: {{@layout}}<br>
<b>{{{@body}}}</b>
1 change: 1 addition & 0 deletions example/advanced/pages/test/template.hbs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Page: {{@view}}
{{> hello user}}
4 changes: 2 additions & 2 deletions example/simple/layouts/main.hbs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Layout: {{layout}}<br>
<b>{{{body}}}</b>
Layout: {{@layout}}<br>
<b>{{{@body}}}</b>
1 change: 1 addition & 0 deletions example/simple/views/test.hbs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
View: {{@view}}
{{> hello user}}
53 changes: 53 additions & 0 deletions lib/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// dependencies
var camel = require("to-camel-case");
var path = require("path");


// root directory to search for templates
exports.root = process.cwd();

// whether or not to cache templates
exports.cache = true;

// what extension to use for templates
exports.extension = ".hbs";

// what dir contains views
exports.viewsDir = "views";

// turn a view id into a absolute path
exports.viewPath = function (id) {
var o = this.options;
return path.resolve(o.root, o.viewsDir, id + o.extension);
};

// what layout (if any) should be used by default
exports.defaultLayout = null;

// what dir contains layouts
exports.layoutsDir = "layouts";

// turn a layout id into an absolute path
exports.layoutPath = function (id) {
var o = this.options;
return path.resolve(o.root, o.layoutsDir, id + o.extension);
};

// what dir contains partials
exports.partialsDir = "partials";

// turn a partial relative path into an id
exports.partialId = function (file) {
var dir = path.dirname(file);
var base = path.basename(file, this.options.extension)
return camel(path.join(dir, base));
};

// predefined global helpers
exports.helpers = null;

// predefined global partials
exports.partials = null;

// allow last-second modifications before render
exports.beforeRender = null;

0 comments on commit 4cdfe38

Please sign in to comment.