Erector - User Guide


Make sure to check out the RDoc Documentation for more details on the API.

The Basics

The basic way to construct some HTML/XML with erector is to subclass Erector::Widget and implement a render method:

class Hello < Erector::Widget
  def render
    html do
      head do
        title "Hello"
      end
      body do
        text "Hello, "
        b "world!"
      end
    end
  end
end
=>
<html>
  <head>
    <title>Hello</title>
  </head>
  <body>
  Hello, <b>world!</b>
  </body>
</html>

API Cheatsheet

element('foo')           # <foo></foo>
empty_element('foo')     # <foo />
html                     # <html></html> (likewise for other common html tags)
b 'foo'                  # <b>foo</b>
text 'foo'               # foo
text '&<>'               # &amp;&lt;&gt; (what you generally want, especially
                         # if the text came from the user or a database)
text raw('&<>')          # &<> (back door for raw html)
rawtext('&<>')           # &<> (alias for text(raw()))
html { text 'foo' }      # <html>foo</html>
html 'foo'               # <html>foo</html>
html foo                 # <html>bar</html> (if the method foo returns the string "bar")
a(:href => 'foo.html')   # <a href="foo.html"></a>
a(:href => 'q?a&b')      # <a href="q?a&amp;b"></a>  (quotes as for text)
a(:href => raw('&amp;')) # <a href="&amp;"></a>
a 'foo', :href => "bar"  # <a href="bar">foo</a>
text nbsp('Save Doc')    # Save&#160;Doc (turns spaces into non-breaking spaces)
text nbsp()              # &#160; (a single non-breaking space)
text character(160)      # &#xa0; (output a character given its unicode code point)
text character(:right-arrow)    # &#x2192; (output a character given its unicode name)
instruct                 # <?xml version="1.0" encoding="UTF-8"?>

javascript('if (x < y && x > z) alert("don\'t stop");') #=>
<script type="text/javascript">
// <![CDATA[
if (x < y && x > z) alert("don't stop");
// ]]>
</script>
TODO: document more obscure features like capture, Table, :class => ['one', 'two']

Using Erector from Ruby on Rails

Your views are just ruby classes. Your controller can either call Rails' render :template method as usual, or directly instantiate the view class and call its render method.

For example:

app/controllers/welcome_controller.rb:
class WelcomeController < ApplicationController
  def index
    render :template => 'welcome/show'
  end
end
app/views/welcome/show.rb:
class Views::Welcome::Show < Erector::Widget
  def render
    html do
      head do
        title "Welcome page"
      end
      body do
        p "Hello, world"
      end
    end
  end
end

For Rails to find these .rb files during render :template, you must first either copy the erector source to vendor/plugins/erector, or add require 'erector' to config/environment.rb. You also should delete (or rename) any other view files with the same base name that might be getting in the way.

Currently there is only partial support for some standard Rails features like partials, layouts, assigns, and helpers. Check the erector-devel mailing list for status updates on these features.

Erect: Command-line conversion to and from HTML

To make Rails integration as smooth as possible, we've written a little tool that will help you erect your existing Rails app. The 'erect' tool will convert HTML or HTML/ERB into an Erector class. It ships as part of the Erector gem, so to try it out, install the gem, then run

erect app/views/foos/*.html.erb

or just

erect app/views

and then delete the original files when you're satisfied.

Here's a little command-line howto for erecting a scaffold Rails app:

rails foo
cd foo
script/generate scaffold post title:string body:text published:boolean

erect app/views/posts

mate app/views/posts
sleep 30 # this should be enough time for you to stop drooling

rm app/views/posts/*.erb
(echo ""; echo "require 'erector'") >> config/environment.rb
rake db:migrate
script/server
open http://localhost:3000/posts

Layout Inheritance

Erector replaces the typical Rails layout mechanism with a more natural construct, the use of inheritance. Want a common layout? Just implement a layout superclass and inherit from it. Implement render in the superclass and implement template methods in its subclasses.

For example:

class Page < Erector::Widget
  def initialize(title = self.class.name)
    super
    @title = title
  end

  def render
    html do
      head do
        title "MyApp - #{@title}"
        css "myapp.css"
      end
      body do
        div :class => 'sidebar' do
          render_sidebar
        end
        div :class => 'body' do
          render_body
        end
        div :class => 'footer' do
          render_footer
        end
      end
    end
  end

  def render_sidebar
    a "MyApp Home", :href => "/"
  end

  def render_body
    p "This page intentionally left blank."
  end

  def render_footer
    p "Copyright (c) 2112, Rush Enterprises Inc."
  end
end
class Faq < Page
  def initialize
    super("FAQ")
  end

  def render_body
    p "Q: Why is the sky blue?"
    p "A: To get to the other side"
  end

  def render_sidebar
    super
    a "More FAQs", :href => "http://faqs.org"
  end
end

Notice how this mechanism allows you to...

  • Set instance variables (e.g. title)
  • Override sections completely (e.g. render_body)
  • Append to standard content (e.g. render_sidebar)
  • Use standard content unchanged (e.g. render_footer)

all in a straightforward, easily understood paradigm (OO inheritance). (No more weird yielding to invisible, undocumented closures!)

To use this in Rails, declare layout nil in app/controllers/application.rb and then define your Page parent class as class Views::Layouts::Page in app/views/layouts as usual.

There's one trick you'll need to use this layout for non-erector view templates. Here's an example.

application.rb - The Erector layout superclass

class Views::Layouts::Application < Erector::Widget
  attr_accessor :content

  def render
    html do
      head { } # head content here
      # body content here
      body do
        text content
      end
    end
  end
end

application.mab - The markaby template (adjust for other appropriately templating technologies)

widget = Views::Layouts::Application.new(self)
widget.content = content_for_layout
self << widget.to_s

Here the abstract layout widget is used in a concrete fashion by the template-based layout. Normally, the content method would be implemented by subclassing widgets, but the layout template sets it directly and then calls to_s on the layout widget. This allows the same layout to be shared in a backward compatible way.

Inline Widgets

Instead of subclassing Erector::Widget and implementing a render method, you can pass a block to Erector::Widget.new. For example:

html = Erector::Widget.new do
  p "Hello, world!"
end
html.to_s          #=> <p>Hello, world!</p>
This lets you define mini-widgets on the fly.

One extra bonus feature of inline widgets is that they can call methods defined on the parent class, even though they're out of scope. How do they do this? Through method_missing magic. (But isn't method_missing magic against the design goals of Erector? Yes, some would say so, and we're probably going to discuss this feature on the mailing list before long.)