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