Wrapt

Wrapt is a rack middleware that provides facilities to any rack application for wrapping the content in a consistent layout.

Wrapt is intended to sit out the front of a rack graph (but doesn’t have to), and is only activated when a downstream application asks to be wrapped in a layout. This means that disparate rack applications that have been mounted, may share the same layout and appear to belong to a single application.

Wrapt injects a layout object into the Rack environment which is idle until activated. To activeate, just assign it some content, and return it as the body portion of your response.

Example

Declare your middleware stack

<code lang='ruby'>use Wrapt
run MyApp

Get your application using the wrapt layout

class MyApp
  def self.call(env)
    new.call(env)
  end</p>
def call(env)
layout = env[‘layout’]
layout.content = “This is the content of my app”
Rack::Response.new(layout).finish
end
<p>end

Say our layout file looked like this:

%html
  %body
    = yield

We would end up with:

<code lang='html'><html>
  <body>
    This is the content of my app
  </body>
</html>

Layout Files

Wrapt is built on Tilt Any tilt compatible template is usable.

A layout file is one that is constructed for use with a layout. To fill in the content into the layout template, you ‘yield’.

Template Location

By default, wrapt will look in the following palces for your layout templates:

  • layouts
  • views/layouts
  • app/views/layouts

You can customize this by passing an array of directory locations to wrapt when declaring the middleware.

use Wrapt do |wrapt|
  wrapt.layout_dirs = ["/path/to/my/layouts", "/fallback/path/to/layouts"]
end

Directories are checked in order and the first template found is used.

Template Naming

Templates follow a simple naming convention.

<template_name>.<format>.<template_type>

For example. The default template name is “application” with a default format of “html”. This will match anything then of the form “application.html”

And will render the layout, inserting the content in the right spot.

You can define the name of the default template when declaring the middleware

use Wrapt do |wrapt|
  wrapt.default_template = "my_default_layout"
end

You can also select the layout to use inside a request. The following sets the layout for all downstream applications as ‘special’ (unless it’s changed downstream’)

def call(env)
  layout = env['layout']

  layout.template_name = "special"
  app.call(env)
end

Ask the layout to check if a given layout is available. You can then decide if you want to use that, or leave it as is.

def call(env)
  content = generate_content_somehow
  layout = env['layout']
  layout.template_name = 'error' if layout.template_name?('error')

  layout.content = content
  Rack::Response.new(layout).finish
end

Format

Layouts are associated with a format. By default the format is html. Tempaltes are selected by their name with ...

You can select a default format when declaring the middleware

use Wrapt do |wrapt|
  wrapt.default_format = :json
end

Or in the request

def call(env)
  layout = env['layout']
  layout.format = :json
  layout.content = {:my => "hash"}.to_json
end

Content Sections

Wrapt allows you to have different sections of content. To declare this:

layout.content = "foo" # main content
layout.set_content_for(:nav, "some navigation")

Once that content is set, you may then use it in the layout by yielding to the layout.

%h1 My Layout
= yield # yields the main content

%nav
  = yield :nav # yields the content with the label :nav

Preventing Layouts from the Client

Wrapt allows you to prevent layouts from being applied when requested by the client.

This means that when using ajax, or esi, you can request the content without layout. There is no magic formula for this, because there could be any number of ways to provide this facility. Instead of prescribing a method to allow requests to not include the layout, you may configure wrapt with a block which checks the request. From there you can choose to ignore the layout request.

Example


use Wrapt do |wrapt|
  wrapt.ignore_layout do |env|
    request = Rack::Request.new(env)
    request.params['ignore_layout'] == 'true'
  end
end

run MyApp

Now when you make a request to any url, you may tell it to ignore the layout by including an ‘ignore_layout=true’ on the url.

GET ‘/foo?ignore_layout=true’

Helpers

You can include any helpers you need into the layout by including them into

Wrapt::Helpers

Wrapping content on demand

You can use the layouter object to wrap content on demand

def call(env)
  layout = env['layout']
  wrapped_content = layout.wrap("my content", :layout => :inner_layout, :format => :json)
  layout.content = wrapped_content
  Rack::Response(layout).finish
end

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don’t break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Copyright

Copyright © 2010 Daniel Neighman. See LICENSE for details.