Custom Extensions

Middleman extensions are Ruby classes which can hook into various points of the Middleman system, add new features and manipulate content. This guide explains some of what's available, but you should read the middleman source and the source of plugins like middleman-blog to discover all the hooks and extension points.

Bootstrapping a new extension

To bootstrap a new extension you can use the extension-command. This will create all needed files.

middleman extension middleman-my_extension

# create  middleman-my_extension/.gitignore
# create  middleman-my_extension/Rakefile
# create  middleman-my_extension/middleman-my_extension.gemspec
# create  middleman-my_extension/Gemfile
# create  middleman-my_extension/lib/middleman_extension.rb
# create  middleman-my_extension/lib/middleman-my_extension.rb
# create  middleman-my_extension/features/support/env.rb
# create  middleman-my_extension/fixtures

Basic Extension

The most basic extension looks like:

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super
  end
  alias :included :registered
end

::Middleman::Extensions.register(:my_feature, MyFeature)

This module must be accessible to your config.rb file. Either define it directly in that file, or define it in another ruby file and require it in config.rb

Finally, once your module is included, you must activate it in config.rb:

activate :my_feature

The register method lets you choose the name your extension is activated with. It can also take a block if you want to require files only when your extension is activated.

In the MyFeature extension, the initialize method will be called as soon as the activate command is run. The app variable is a Middleman::Application class.

activate can also take an options hash (which are passed to register) or a block which can be used to configure your extension. You define options with the options class method and then access them with options:

class MyFeature < Middleman::Extension
  # All the options for this extension
  option :foo, false, 'Controls whether we foo'

  def initialize(app, options_hash={}, &block)
    super

    puts options.foo
  end
end

## Two ways to configure this extension
activate :my_feature, :foo => 'whatever'
activate :my_feature do |f|
  f.foo = 'whatever'
  f.bar = 'something else'
end

Passing options to activate is generally preferred to setting global variables via set to configure your extension (see the next section).

Setting variables

The Middleman::Application class can be used to change global settings (variables using the set command) that can be used in your extension.

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super

    app.set :css_dir, "lib/my/css"
  end
end

You can also use this ability to create new settings which can be accessed later in your extension.

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super

    app.set :my_feature_setting, %w(one two three)
  end

  helpers do
    def my_helper
      my_feature_setting.to_sentence
    end
  end
end

set adds a new method to Middleman::Application, meaning you can read the value of your variable via my_feature_setting elsewhere. However, consider using activate options instead of global settings when only your extension needs a particular value.

Adding Methods to config.rb

Methods available inside config.rb are simply class methods of Middleman::Application. Let's add a new method to be used in the config.rb:

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super
    app.extend ClassMethods
  end

  module ClassMethods
    def say_hello
      puts "Hello"
    end
  end
end

By extending the Middleman::Application class, available as app, we've added a say_hello method to the environment which simply prints "Hello". Internally, these methods are used to build lists of paths and requests which will be processed later in the app.

Adding Helpers

Helpers are methods available inside your template. To add helper methods, we do the following:

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super
  end

  helpers do
    def make_a_link(url, text)
      "<a href='#{url}'>#{text}</a>"
    end
  end
end

Now, inside your templates, you will have access to a make_a_link method. Here's an example using an ERb template:

<h1><%= make_a_link("http://example.com", "Click me") %></h1>

Sitemap Manipulators

You can modify or add pages in the sitemap by creating a Sitemap extension. The :directory_indexes extension uses this feature to reroute normal pages to their directory-index version, and the blog extension uses several plugins to generate tag and calendar pages. See the Sitemap::Store class for more details.

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super
  end

  def manipulate_resource_list(resources)
    resources.each do |resource|
      resource.destination_path.gsub!("original", "new")
    end
  end
end

Callbacks

There are many parts of the Middleman lifecycle that can be hooked into by extensions. These are some examples, but there are many more.

after_configuration

Sometimes you will want to wait until the config.rb has been executed to run code. For example, if you rely on the :css_dir variable, you should wait until it has been set. For this, we'll use a callback:

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super
  end

  def after_configuration
    the_users_setting = app.settings.css_dir
    app.set :my_setting, "#{the_users_setting}_with_my_suffix"
  end
end

before

The before callback allows you to do processing right before Middleman renders the page. This can be useful for returning data from another source, or failing early.

Here's an example:

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super
    app.before do
      app.set :currently_requested_path, request.path_info
      true
    end
  end
end

The above sets the :currently_requested_path value at the beginning of each request. Notice the return value of "true." All blocks using before must return either true or false.

after_build

This callback is used to execute code after the build process has finished. The middleman-smusher extension uses this feature to compress all the images in the build folder after it has been built. It's also conceivable to integrate a deployment script after build.

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super
    app.after_build do |builder|
      builder.run './my_deploy_script.sh'
    end
  end
end

The builder parameter is the class that runs the build CLI, and you can use Thor actions from it.

compass_config

Similarly, if your extension relies on variable and settings within Compass to be ready, use the compass_config callback.

class MyFeature < Middleman::Extension
  def initialize(app, options_hash={}, &block)
    super

    app.compass_config do |config|
      # config is the Compass.configuration object
      config.output_style = :compact
    end
  end
end