Filters

Filters are used for transforming an item’s content.

One commonly used filter is :erb, which interprets the item’s content as text with embedded Ruby. Another commonly used filter is :kramdown, which takes Markdown as its input and produces an HTML fragment.

Filters can be applied to both textual and binary items. An example of a binary filter is an image thumbnail filter. Filters can also convert textual content into binary files and vice versa; text-to-speech filters and OCR filters are therefore possible (but perhaps not very useful).

Filters are called within compilation rules. Here is an example rule that applies the :kramdown filter with the :auto_ids option set to false:

compile '/**/*.md' do
  filter :kramdown, auto_ids: false
  layout '/default.*'
end

For details on compilation rules, see the Rules page.

Nanoc comes with a handful of filters. See the Filters page for a list of filters bundled with Nanoc.

Writing filters

Filters are classes that inherit from Nanoc::Filter. Writing custom filters is done by subclassing this class and overriding the run() method, which is responsible for transforming the content.

Here is an example (textual) filter that replaces any occurrences of “Nanoc sucks” by “Nanoc rocks”:

class CensorFilter < Nanoc::Filter
  identifier :censor

  def run(content, params = {})
    content.gsub('Nanoc sucks', 'Nanoc rocks')
  end
end

Alternatively, there is a shorthand using Nanoc::Filter.define:

Nanoc::Filter.define(:censor) do |content, params|
  content.gsub('Nanoc sucks', 'Nanoc rocks')
end

Each filter needs an identifier, so that it can be used in a call to filter() in a compilation rule. A filter identifier is set using identifier(). In the example above, the identifier is set to :censor.

The content argument to the run() method is a string that contains the content to be transformed. The params argument is taken from the filter() call in the compilation rule.

Filters that output textual content should return the filtered content at the end of the method.

Filters have access to @item, @item_rep, @items, @layouts, and @config. For details, see the Variables page.

Binary filters

A filter is marked as a binary filter using the type() method. For example:

class SampleBinaryFilter < Nanoc::Filter
  identifier :sample_binary
  type :binary

  # (other code here)
end

The run() method for binary filters takes a filename argument, rather than a content argument. This filename argument contains the path to the file to be filtered. For example:

class SampleBinaryFilter < Nanoc::Filter
  identifier :sample_binary
  type :binary

  def run(filename, params = {})
    # (filter code here)
  end
end

Filters that output binary content should not return content, but rather write it to the location returned by the output_filename() method.

When writing filters that apply to binary data, make sure that you check the exit code and any errors generated by the sub-process that you are invoking (if any). If the sub-process exits with an error, you should raise an error in the filter.

Here is an example of a filter that resizes images to a given width:

class ResizeFilter < Nanoc::Filter
  identifier :resize
  type :binary

  def run(filename, params = {})
    system(
      'sips',
      '--resampleWidth', params[:width],
      '--out', output_filename,
      filename
    )
  end
end

Text-to-binary and binary-to-text filters

Filters that convert textual content to binary content or vice versa have the type declaration type :text => :binary or type :binary => :text, respectively. For example:

class SampleTextualToBinaryFilter < Nanoc::Filter
  identifier :sample_textual_to_binary
  type :text => :binary

  # (other code here)
end

A text-to-binary filter will receive a content string as its first argument, and should write the generated binary content to output_filename(). A binary-to-text filter will receive a filename string argument, and should return the filtered content.

Here is an example of an audio synthesis filter:

class SynthesiseAudio < Nanoc::Filter
  identifier :synthesize_audio
  type :text => :binary

  def run(content, params = {})
    system('say', content, '-o', output_filename)
  end
end