Erector - User Guide
Make sure to check out the RDoc Documentation for more details on the API.
- The Basics
- Mixin
- API Cheatsheet
- Pretty-printing
- Using Erector from Ruby on Rails
- Erector tool: Command-line conversion to and from HTML
- Layout Inheritance
- Inline Widgets
- Needs
- Externals
- Blocks
The Basics
The basic way to construct some HTML/XML with erector is to subclass Erector::Widget and implement a content method:
class Hello < Erector::Widget
def content
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>
|
Once you have a widget class, you can instantiate it and then call its
to_s
method. If you want to pass in 'locals' (aka 'assigns'), then do so in the constructor's default hash. This will make instance variables of the same name, with Ruby's '@' sign.
class Email < Erector::Widget
def content
a @address, :href => "mailto:"
end
end
>> Email.new(:address => "foo@example.com").to_s
=> "<a href="mailto:foo@example.com">foo@example.com</a>"
(If you want control over which locals are valid to be passed in to a widget, use the needs macro.)
Mixin
If all this widget stuff is too complicated, just do
include Erector::Mixin
and then call
erector { }
from anywhere in your code. It will make an inline widget for you, pass in the block, and call
to_s
on it. And if you pass any options to
erector
, like
:prettyprint => true
, it'll pass them along to
to_s
!
Examples:
erector { a "lols", :href => "http://icanhascheezburger.com/" }
=> "<a href=\"http://icanhascheezburger.com/\">lols</a>"
erector(:prettyprint => true) do
ol do
li "bacon"
li "lettuce"
li "tomato"
end
end
=> "<ol>\n <li>bacon</li>\n <li>lettuce</li>\n <li>tomato</li>\n</ol>\n"
API Cheatsheet
| code | output |
|---|---|
element('foo')
|
<foo></foo>
|
empty_element('foo')
|
<foo />
|
html
|
<html></html>
…and likewise for all non-deprecated elements from the HTML 4.0.1 spec |
b 'foo'
|
<b>foo</b>
|
div { b 'foo' }
|
<div><b>foo</b></div>
|
text 'foo'
|
foo
|
text '&<>'
|
&<>
…all normal text is HTML escaped, which is what you generally want, especially if the text came from the user or a database |
text raw('&<>')
|
&<>
…raw text escapes being escaped |
rawtext('&<>')
|
&<>
…alias for text(raw()) |
text!('&<>')
|
&<>
…another alias for text(raw()) |
div { text 'foo' }
|
<div>foo</div>
|
div 'foo'
|
<div>foo</div>
|
foo = 'bar'
|
<div>bar</div>
|
a(:href => 'foo.div')
|
<a href="foo.div"></a>
|
a(:href => 'q?a&b')
|
<a href="q?a&b"></a>
…attributes are escaped like text is |
a(:href => raw('&'))
|
<a href="&"></a>
…raw strings are never escaped, even in attributes |
a 'foo', :href => "bar"
|
<a href="bar">foo</a>
|
text nbsp('Save Doc')
|
Save Doc
…turns spaces into non-breaking spaces |
text nbsp
|
 
…a single non-breaking space |
text character(160)
|
 
…output a character given its unicode code point |
text character(:right-arrow)
|
→
…output a character given its unicode name |
instruct
|
<?xml version="1.0" encoding="UTF-8"?>
|
comment 'foo'
|
<!--foo-->
|
url 'http://example.com'
|
<a href="http://example.com">http://example.com</a>
|
capture { div }
|
<div></div>
…returns the block as a string, doesn't add it to the current output stream |
div :class => ['a', 'b']
|
<div class="a b"></div>
|
javascript(
|
<script type="text/javascript">
|
jquery '$("p").wrap("<div></div>");'
|
<script type="text/javascript">
|
join([widget1, widget2],
|
…See examples/join.rb for more explanation |
Lots more documentation is at the RDoc API pages especially for Erector::Widget so don't go saying we never wrote you nothin'.
Pretty-printing
Erector has the ability to insert newlines and indentation to make the generated HTML more readable. Newlines are inserted before and after certain tags.
To enable pretty-printing (insertion of newlines and indentation) of Erector's output, do one of the following:
- call
to_prettyinstead ofto_son your Erector::Widget - pass
:prettyprint => truetoto_s - call
enable_prettyprint(true)on your Erector::Widget. Then subsequent calls to to_s will prettyprint - call
Erector::Widget.prettyprint_default = true(for example, in environments/development.rb in a rails application, or anywhere which is convenient)
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 content 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 content
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 Google Groups mailing list for status updates on these features.
Erector tool: 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 'erector' 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
erector app/views/foos/*.html.erb
or just
erector 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 erector 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
On the erector-to-html side, pass in the
--to-html
option and some file names and it will render the erector widgets to appropriately-named HTML files. We're actually using
erector
to build this Erector documentation web site that you're reading right now. Check out the 'web' directory and the 'web' task in the Rakefile to see how it's done.
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 content in the superclass and implement template methods in its subclasses.
For example:
class Views::Layouts::Page < Erector::Widget
def content
html do
head do
title "MyApp - #{page_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 Views::Faq::Index < Views::Layouts::Page
def initialize
super(:page_title => "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 content
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
content
method, you can pass a block to
Erector.inline
and get back a widget instance you can call to_s on. For example:
html = Erector.inline do p "Hello, world!" end html.to_s #=> <p>Hello, world!</p>This lets you define mini-widgets on the fly.
If you're in Rails, your inline block has access to Rails helpers if you pass a helpers object to to_s:
html = Erector.inline do
image_tag("/foo")
end
html.to_s(:helpers => controller) #=> <img alt="Foo" src="/foo" />
Note that inline widgets are usually redundant if you're already inside an Erector content method. You can just use a normal
do
block and the Erector methods will work as usual when called back from
yield
. Inline widgets get evaluated with
instance_eval
which may or may not be what you want. See the section on blocks in this user guide for more detail.
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 that's why we're reserving it for a special subclass and method. For Erector::Widget and subclasses, if you pass in a block, it's a plain old block with normal semantics.) But they can't directly access instance variables on the parent, so watch it.
Needs
Named parameters in Ruby are fun, but one frustrating aspect of the 'options hash' technique is that the code is less self-documenting and doesn't 'fail fast' if you pass in the wrong parameters, or fail to pass in the right ones. Even simple typos can lead to very annoying debugging problems.
To help this, we've added an optional feature by which your widget can declare that it needs a certain set of named parameters to be passed in. For example:
class Car < Erector::Widget
needs :engine, :wheels => 4
def content
text "My #{@wheels} wheels go round and round; my #{@engine} goes vroom!"
end
end
This widget will throw an exception if you fail to pass
:engine => 'V-8'
into its constructor. (Actually, it will work with any engine, but a V-8 is the baddest.)
See the rdoc for Widget#needs for more details. Note that as of version 0.7.0, using
needs
no longer automatically declares accessor methods.
Externals
Erector's got some nice tags, like
script
and
style
, that you can emit in the content method of your widget. But what if your widget needs something, say a JavaScript library, that should be included not in the main page, but inside the
head
section?
Externals are a way for your widget to announce to the world that it has an external dependency. It's then up to another widget to emit that dependency while it's rendering the
head
.
Here's an example:
class HotSauce < Erector::Widget
external :css, "/css/tapatio.css"
external :css, "/css/salsa_picante.css", :media => "print"
external :js, "/lib/jquery.js"
external :js, "/lib/picante.js"
def content
p :class => "tapatio" do
text "esta salsa es muy picante!"
end
end
end
Then when
Page
emits the
head
it'll look like this:
<head> <meta content="text/html;charset=UTF-8" http-equiv="content-type" /> <title>HotPage</title> <link href="/css/tapatio.css" media="all" rel="stylesheet" type="text/css" /> <link href="/css/salsa_picante.css" media="print" rel="stylesheet" type="text/css" /> <script src="/lib/jquery.js" type="text/javascript"></script> <script src="/lib/picante.js" type="text/javascript"></script> </head>
It also collapses redundant externals, so if lots of your widgets declare the same thing (say, 'jquery.js'), it'll only get included once.
There's at least one drawback to this technique: since the externals get declared at class load time, then every external will be emitted on every page, even if that widget isn't on the page at all. This is not usually a problem, but it may lead to namespace collision, so be careful out there.
Page looks for the following externals:
| :js | included JavaScript file |
|---|---|
| :css | included CSS stylesheet |
| :script | inline JavaScript |
| :style | inline CSS style |
Blocks
Erector is all about blocks (otherwise known as closures). Unfortunately, there are some confusing aspects to working with blocks; this section aims to clarify the issues so if you find yourself stuck on an 'undefined method' or a nil instance variable, at least you'll have some context to help debug it.
There are basically three cases where you can pass a block to Erector:
1. To an element method
This is the normal case that provides the slick HTML DSL. In the following code:
class Person < Erector::Widget
def content
div do
h3 @name
p do
b "Birthday: "
span @birthday
end
end
end
end
the blocks passed in to
div
and
p
are evaluated using normal
yield
semantics, and the
@name
and
@birthday
instance variables are evaluated in the context of the Person instance being rendered.
So far, so good.
2. To the constructor of an Erector::Widget
In this case you can build a widget "on the fly" and have it render whatever it wants, then call your block. This is useful for widgets like
Form
which want to wrap your HTML in some of their own tags.
class PersonActions < Erector::Widget
needs :user
def content
div do
widget(Form.new(:action => "/person/#{@user.id}", :method => "delete") do
input :type => "submit", :value => "Remove #{@user.name}"
end)
widget(Form.new(:action => "/person/#{@user.id}/email", :method => "post") do
b "Send message: "
input :type => "text", :name => "message"
input :type => "submit", :value => "Email #{@user.name}"
end)
end
end
end
In this case, you will get two
form
elements, each of which has some boilerplate HTML for emitting the form element, emitting the hidden
_method
input tag in the case of the delete method, then calling back into your widget to emit the contents of the form. In this case, as above, the
@user
instance variable will be sought inside the calling widget
(PersonActions)
, not the called widget
(Form)
.
A quirk of this technique is that methods inside the block will be called on the calling widget, not the called widget. This doesn't cause any problems for element methods (
b
and
input
above), but may be confusing if you want the block to be able to call methods on the target widget. In that case the caller can declare the block to take a parameter; this parameter will point to the nested widget instance.
widget(Form.new(:action => "/person/#{@user.id}", :method => "delete") do |f|
span "This form's method is #{f.method}"
input :type => "submit", :value => "Remove #{@user.name}"
end)
(As a variant of this case, note that the
widget
method can accept a widget class, hash and block, instead of an instance; in this case it will set the widget's block and this code:
widget Form, :action => "/person/#{@user.id}", :method => "delete" do
input :type => "submit", :value => "Remove #{@user.name}"
end
will work the same as the version above.)
3. To the constructor of an Erector::InlineWidget
This is where things get hairy. Sometimes we want to construct a widget on the fly, but we're not inside a widget already. So any block we pass in will not have access to Erector methods. In this case we have a special subclass called
Erector::InlineWidget
which uses two magic tricks:
instance_eval
and
method_missing
to accomplish the following:
- inside the block,
selfpoints to the widget, not the caller. - methods will be looked for first on the inline widget, and then on the caller.
- instance variables will be looked for on the inline widget only. This can be the source of many a nil! As a general rule, you should probably stay away from instance variables when using inline widgets. However...
- Bound local variables will still be in scope. This means you can "smuggle in" instance variables via local variables. For example:
local_name = @name Page.new do div local_name end.to_s
When using the mixin, you get an inline widget, so the above list of tricks applies.
One note for developers: when creating a widget like
Form
that needs to call back to its block, use the method
call_block
, which calls the block and passes in self as appropriate for both inline and normal widgets.

