The Mongrel2 Config DSL

The Mongrel2::Config::DSL module is a mixin that will add functions to your namespace that can create and replace configuration items in the current Mongrel2 config database.

If you're creating a config that will be run standalone, you'll need to point the config classes to the right database before using the DSL:

#!/usr/bin/env ruby

require 'mongrel2'
require 'mongrel2/config'
require 'mongrel2/config/dsl'

Mongrel2::Config.configure( configdb: 'myconfig.sqlite' )
include Mongrel2::Config::DSL

server 'myserver' do
    # ...
end

If you're creating a config to be loaded via m2sh.rb, you don't need any of that, as m2sh.rb provides its own prelude before loading the config.

DSL Syntax

There is basically one directive for each configuration item, and the layout follows the basic structure described in the Mongrel2 manual.

server

server <uuid> { <server config block> }

This creates or replaces the server associated with the specified uuid. The server config block has directives for each of the server attributes, each of which corresponds to one of the columns in the configuration database's server table (descriptions largely borrowed from the manual):

chroot directory

The directory that Mongrel2 should chroot to at startup. Defaults to /var/www.

access_log path

The path to the access log file relative to the chroot. Usually starts with a ‘/’. Defaults to /logs/access.log.

error_log path

The error log file, just like access_log. Defaults to /logs/error.log.

pid_file path

The path to the PID file, relative to the chroot. Defaults to /run/mongrel2.pid.

default_host name

Which host in the server to use as the default if the Host header doesn't match any host's matching attribute. Defaults to localhost.

bind_addr ipaddr

The IP address to bind to; default is 0.0.0.0.

port int

The port the server should listen on for new connections; defaults to 8888.

The server will be saved immediately upon exiting the block, and will return the saved Mongrel2::Config::Server object.

host

host <name> { <host config block> }

This creates or replaces the named Host within a server block. Inside the host config block, there are directives for further configuring the Host, adding Routes, and setting up Handler, Proxy, and Directory targets for Routes.

matching pattern

This is a pattern that’s used to match incoming Host headers for routing purposes.

maintenance boolean

This is a (currently unused) setting that will display a “down for maintenance” page.

The rest of the block will likely be concerned with setting up the routes for the host using the route, handler, directory, and proxy directives, e.g.:

host 'main' do
  matching 'example.com'
  maintenance false

  # Hello world application
  route '/hello', handler('tcp://127.0.0.1:9999', 'helloworld-handler')
  # Contact form handler
  route '/contact', handler('tcp://127.0.0.1:9995', 'contact-form')

  # reverse proxy for a non-mongrel application listening on 8080
  route '/api', proxy('127.0.0.1', 8080)
end
# => #<Mongrel2::Config::Host ...>

Since the block is just a Ruby block, and each directive returns the configured object, you can use conditionals, assign targets to variables to be reused later, etc:

require 'socket'

host 'test' do
  matching 'testing.example.com'

  # Decide which address to listen to, and which app to use based on which
  # host the config is running on.
  testhandler =
    if Socket.gethostname.include?( 'example.com' )
      handler 'tcp://0.0.0.0:31218', 'ci-builder'
    else
      handler 'tcp://127.0.0.1', 'unittest-builder'
    end

  route '', testhandler
end

route

route <pattern>, <target>, [<opts>]

Create a route in the current host that will match the given pattern and pass the request to the specified target, which should be a Handler, a Directory, or a Proxy.

The only current option is reversed, which (if true) means that the pattern is bound to the end rather than the beginning of the path.

It returns the configured Route object.

route '/demo', handler('tcp://localhost:9400', 'demo-handler')
# => #<Mongrel2::Config::Route ...>
route '.css', directory('/public/css', 'base.css', 'text/css'), reverse: true

# Use the same image-factory handler for each image type
image_handler = handler('tcp://localhost:9300', 'image-factory')
route '.jpg', image_handler, reverse: true
route '.gif', image_handler, reverse: true
route '.png', image_handler, reverse: true
route '.ico', image_handler, reverse: true

handler

handler <send_spec>, <send_ident>, [<recv_spec>[, <recv_ident>]], [<options>]

Create a Handler that will send and receive on send_spec and recv_spec 0mq sockets, respectively. The application's send_ident is an identifier (usually a UUID) that will be used to register the send socket so messages persist through crashes.

If no recv_spec is given, the port immediately below the send_spec is used.

The recv_ident is another UUID if you want the receive socket to subscribe to its messages. Handlers properly mention the send_ident on all returned messages, so you should either set this to nothing and don’t subscribe, or set it to the same as send_ident.

Valid options for Handlers are:

raw_payload boolean

?

protocol name

The protocol used to communicate with the handler. Should be either 'tnetstring' or 'json' (the default).

As with the other directives, handler returns the newly-saved Handler object. This means that you can either assign it to a variable for later inclusion in one or more Route declarations, or you can define it in the route itself:

route '/gravatar/:email', handler('tcp://localhost:1480', 'gravatar-service')
# => #<Mongrel2::Config::Route ...>

See the documentation for @route@ for more examples of this.

directory

directory <base>, [<index_file>[, <default_ctype>[, <options>]]]

Create a Dir target for a route that will serve static content from the specified base directory. Returns the saved Dir object.

There aren't currently any supported options, but the Mongrel2 manual says that “eventually you’ll be able to tweak more and more of the settings to control how Dirs work.”

proxy

proxy <addr>[, <port>]

Create a Proxy target for a route that will proxy another server listening on the given addr and port. If not specified, port defaults to 80. Returns the saved Proxy object.

filter

filter <path>[, <settings>]

Set up a Filter for the containing Server by loading the object from the specified path and passing it the specified options as a TNetstring.

setting/settings

setting <name>, <value>
settings <name1> => <value1>[, <name2> => <value2>, ...]

Set one or more of Mongrel2's internal settings. See the tweakable expert settings section of the manual for valid values and more details on what these do.

An example (from the manual):

settings "zeromq.threads" => 1,
    "upload.temp_store" => "/home/zedshaw/projects/mongrel2/tmp/upload.XXXXXX",
    "upload.temp_store_mode" => "0666"
# => [ #<Mongrel2::Config::Setting ...>, #<Mongrel2::Config::Setting ...>, ... ]

mimetype/mimetypes

mimetype <extension>, <mimetype>
mimetypes <extension1> => <mimetype1>[, <extension2> => <mimetype2>]

Map one or more file +extension+s to a mimetype.

An example:

mimetypes '.ttf' => 'application/x-font-truetype',
          '.otf' => 'application/x-font-opentype'
# => [#<Mongrel2::Config::Mimetype ...>, ... ]

Example

This is the mongrel2.org config re-expressed in the Ruby DSL:

# the server to run them all
server '2f62bd5-9e59-49cd-993c-3b6013c28f05' do

    access_log   "/logs/access.log"
    error_log    "/logs/error.log"
    chroot       "./"
    pid_file     "/run/mongrel2.pid"
    default_host "mongrel2.org"
    name         "main"
    port         6767

    # your main host
    host "mongrel2.org" do

        # a sample of doing some handlers
        route '@chat', handler(
            'tcp://127.0.0.1:9999',
            '54c6755b-9628-40a4-9a2d-cc82a816345e',
            'tcp://127.0.0.1:9998'
        )

        route '/handlertest', handler(
            'tcp://127.0.0.1:9997',
            '34f9ceee-cd52-4b7f-b197-88bf2f0ec378',
            'tcp://127.0.0.1:9996'
        )

        # a sample proxy route
        web_app_proxy = proxy( '127.0.0.1', 8080 )

        route '/chat/', web_app_proxy
        route '/', web_app_proxy

        # here's a sample directory
        test_directory = directory(
            'tests/',
            :index_file => 'index.html',
            :default_ctype => 'text/plain'
        )

        route '/tests/', test_directory
        route '/testsmulti/(.*.json)', test_directory

        chat_demo_dir = directory(
            'examples/chat/static/',
            :index_file => 'index.html',
            :default_ctype => 'text/plain'
        )

        route '/chatdemo/', chat_demo_dir
        route '/static/', chat_demo_dir

        route '/mp3stream', handler(
            'tcp://127.0.0.1:9995',
            '53f9f1d1-1116-4751-b6ff-4fbe3e43d142',
            'tcp://127.0.0.1:9994'
        )
    end

end

settings(
    "zeromq.threads"         => 1,
    "upload.temp_store"      => "/home/zedshaw/projects/mongrel2/tmp/upload.XXXXXX",
    "upload.temp_store_mode" => "0666"
)