Report abuse

Making the Jump from nanoc 2.2 to nanoc 3.0
===========================================

nanoc 3.0 is quite different from nanoc 2.2 in several ways. It is not
backward compatible, which means that it is not possible to compile a nanoc
2.2 site out of the box. This document is meant to be a guide through the
steps necessary to convert a nanoc 2.2 site to a nanoc 3.0 one.

Getting nanoc 3.0
-----------------

At the moment, there is no package for nanoc 3.0 yet. To get nanoc, first
clone the repository (http://projects.stoneship.org/hg/nanoc/) using hg, like
this:

    hg clone http://projects.stoneship.org/hg/nanoc/

Now cd into the new `nanoc` directory and execute the 'gem:install' rake task
as root in order to install nanoc, like this:

    sudo rake gem:install

Name changes
------------

The nanoc 3.0 commandline tool is called `nanoc3` and not `nanoc`. Therefore,
e.g. compiling a nanoc 3.0 site is done using `nanoc3 co`. The old `nanoc`
commandline tool cannot be used with nanoc 3.0 sites.

nanoc 3.0 uses a new namespace, `Nanoc3`, instead of the old `Nanoc`
namespace. Any custom code that lives in the `Nanoc` namespace should
therefore be moved into the new `Nanoc3` namespace.

Dropped Features
----------------

Several features present in nanoc 2.2 are no longer supported in nanoc 3.0.
These include:

* Support for binary assets
* Support for drafts
* Support for templates
* `save_*`, `delete_*`, `move_*` methods in data sources
* Old-style routers (replaced with routing rules---see below for details)
* Page and asset defaults

Items versus Pages and Assets
-----------------------------

Pages and textual assets have been merged and are now known simply as "items."
This means that all operations on pages can now be applied to textual assets
as well (such as laying out an asset, which was not possible before). Binary
assets are no longer supported.

Paths versus identifiers
------------------------

The _identifier_ of an item is a path starting with a slash and ending with a
slash. It is not necessarily the path that will be used when linking to this
item.

The _path_ of an item representation is the path that will be used when
linking to the item. It can be identical to the identifier, but it does not
have to be. The path does not include any trailing index file names (as
specified in the site configuration).

The _raw path_ of an item representation is the path, relative to the current
working directory, to the output file. It includes the path to the output
directory and it also includes the trailing index filename (if any).

Since items themselves are not written (their representations are), items do
not have a path. It is not possible to link to an item, but it is possible to
link to a representation; i.e. do not use `@item.path` but use
`@item.reps[0].path`.

Accessing attributes
--------------------

Accessing item attributes is now done using the #[] method (e.g.
`@item[:title]`). It is no longer possible to use "dot notation" to request
attributes (e.g. `@item.title` will not work).

Site Configuration versus Page Defaults
---------------------------------------

Some helper, such as the `XMLSitemap` and the `Blogging` one, will now take
their configuration from the site configuration file (`config.yaml`) instead
of the page defaults. Consult the documentation for these helpers for details.

Item Content
------------

It is not possible to get the compiled content of an item; only the compiled
content of an item _representation_ can be fetched. `Nanoc3::ItemRep` has a
method `#content_at_snapshot` which returns the content at the given snapshot.
If you want the raw, uncompiled content, pass `:raw` as an argument for
`#content_at_snapshot`; if you want the compiled but not yet laid out content,
pass `:pre`; if you want the full compiled and laid out content, pass `:post`.

nanoc 3.0 layouts should use `yield` to insert the content of the item. It is
possible to use `@item_rep.content_at_snapshot(:pre)` instead of yielding, but
this is not recommended.

Dates and Times
---------------

nanoc 3.0 does not automatically convert attributes ending with `_on` or `_at`
to `Date` and `Time` instances, respectively.

Filter Arguments
----------------

Filters can now take a hash of arguments. The signature of a filter's `#run`
method has changed: instead of taking only one option (the content), it takes
two: the content and a hash of arguments. For example:

    class MyNanoc3Filter < Nanoc3::Filter
      def run(content, arguments={})
        # ... code goes here ...
      end
    end

LinkTo Helper and Reps
----------------------

The `#link_to` function now expects an item rep, not an item.

Data Sources
------------

nanoc 3.0 supports multiple data sources, and the configuration for data
sources has changed to reflect this new feature. The site configuration now
has an array named `data_sources` which contains a list of hashes describing
the data source.

This hash has a `type` attribute containing the identifier of the data source
to use (e.g. `filesystem`). It also has a `root` attribute, which contains the
root at which items should be mounted (similar to mount points in Unix-like
OSes). Finally, the `config` attribute is a hash containing custom
configuration attributes (such as usernames for a data source that requires
authentication).

Example #1: This data source configuration is the default configuration which
contains only a single data source (the filesystem one) mounted at /:

    data_sources:
      - type: filesystem
        root: '/'

Example #2: This data source configuration contains multiple data sources
(filesystem, twitter, delicious) mounted at different paths:

    data_sources:
      - type: filesystem
        root: '/'
      - type: twitter
        root: '/tweets'
        config:
          username: 'ddfreyne'
      - type: delicious
        root: '/links'
        config:
          username: 'ddfreyne'

Rules
-----

nanoc 3.0 does not use attributes in metadata files (the `.yaml` files) in
order to figure out how to compile an item. Instead, processing instructions
are located in a new file called `Rules` which lives at the top level of the
nanoc site directory.

The file contains three different kinds of rules: routing rules, compilation
rules and layouting rules. Compilation rules determine the actions that should
be executed during compilation (filtering, layouting), while routing rules
determine the path where the compiled files should be written to.

A rule consists of a selector, which identifies which items the rule should be
applicable to, and an action block, which contains the actions to perform.

nanoc will first attempt to build a route for each item. To do so, it will run
through all items and find the first matching rule for each item. Only the
first matching rule will be used; matching rules that come later will be
ignored. Once all items are routed, nanoc will run through all items and find
the first matching compilation rule and apply that to the relevant item.

### Routing Rules

A routing rule looks like this:

    route '/foo/' do |rep|
      # ... routing code here ...
    end

The argument for the `#route` method is the identifier of the item that should
be compiled. It can also be a string that contains the `*` wildcard, which
matches zero or more characters. Additionally, it can be a regular expression.

The block argument is the item representation that is currently being routed
(not the item).

The code block should return the routed path for the relevant item. The code
block can return nil, in which case the item will not be written.

Example #1: The following rule will give the item with identifier `/404/` the
path `/error/404.php` (in nanoc 2.2, this would have been done using the
`custom_path` attribute):

    route '/404/' do |rep|
      '/errors/404.php'
    end

Example #2: The following rule will give all identifiers for which no prior
matching rule exists a path based directly on its identifier (for example, the
item `/foo/bar/` would get the path `/foo/bar/index.html`):

    route '*' do |rep|
      rep.item.identifier + 'index.html'
    end

Example #3: The following rule will prevent all items with identifiers
starting with '/links/' from being written (the items will still be compiled
so that they can be included in other items, however):

    route '/link/*' do |rep|
      nil
    end

### Compilation Rules

A compilation rule looks like this:

    compile '/foo/' do |rep|
      # ... compilation code here ...
    end

The argument for the `#compile` command is exactly the same as the argument
for `#route`; see above for details. Similarly, the block argument is the item
representation that is being compiled.

The code block should execute the necessary actions for compiling the item. It
does not have to return anything.

A compilation action can either be a filter action or a layout action. To
filter an item representation, call `#filter` on the rep and pass the name of
the filter as argument, along with any other filter arguments. To lay out an
item representation, call `#layout` on the rep and pass the layout identifier
as argument.

Example #1: The following rule will not perform any actions, i.e. the item
will not be filtered nor laid out:

    compile '/sample/one/' do |rep|
    end

Example #2: The following rule will filter the rep using the `erb` filter, but
not lay out the rep:

    compile '/samples/two/' do |rep|
      rep.filter :erb
    end

Example #3: The following rule will filter the rep using the `erb` filter, lay
out the rep using the `shiny` layout, and finally run the laid out rep through
the `rubypants` filter:

    compile '/samples/three/' do |rep|
      rep.filter :erb
      rep.layout '/shiny/'
      rep.filter :rubypants
    end

Example #4: The following rule will filter the rep using the
`relativize_paths` filter with the filter argument `type` equal to `css`:

    compile '/assets/style/' do |rep|
      rep.filter :relativize_paths, :type => :css
    end

### Layouting Rules

To specify the filter used for a layout, use the `#layout` method. This method
takes a hash with one key and one pair.

The key should be a string containing the identifier of the layout. It can
also be a string that contains the `*` wildcard, which matches zero or more
characters. Additionally, it can be a regular expression.

The value should be the identifier of the filter to use.

Example: The following rule would make all layouts use the `:erb` filter:

    layout '*' => :erb