Fork me on GitHub

This page describes integrating Erector into Ruby on Rails apps. Read the User Guide for details on Erector itself.

Erector On Rails

1. Install

To install as a gem, add gem 'erector' to your Gemfile, then add require 'erector' to environment.rb.

To install as a Rails plugin, copy the erector source to vendor/plugins/erector in your project directory. When installing this way, erector is automatically available to your Rails code (no require directive is needed).

2. 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 {
      head {
        title "Welcome page"
      }
      body {
        p "Hello, world"
      }
    }
  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.

You must also add app to the class load path. Put this line into config/application.rb

config.autoload_paths += %W(#{config.root}/app)

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.

3. 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:

# create a toy Rails app
rails foo
cd foo
script/generate scaffold post title:string body:text published:boolean

# convert all the "posts" views
erector app/views/posts

# remove the old ERB views
rm app/views/posts/*.erb

# a little configuration step
(echo ""; echo "require 'erector'") >> config/environment.rb

# launch the app and make sure it works
rake db:migrate
script/server
open http://localhost:3000/posts

On the erector-to-html side, pass in the --to-htmloption 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.

4. Page Layout Inheritance

Erector replaces the typical Rails layout mechanism with a more natural construct, the use of inheritance. Want a common layout? Implement a layout superclass and have your page class inherit from it and override methods as needed.

For example:

class Views::Layouts::Page < Erector::Widget
  def content
    html {
      head {
        title "MyApp - #{@page_title}"
        css "myapp.css"
      }
      body {
        div.navbar {
          navbar
        }
        div.main {
          main
        }
        div.footer {
          footer
        }
      }
    }
  end

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

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

  def 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 main
    p "Q: Why is the sky blue?"
    p "A: To get to the other side"
  end

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

Notice how this mechanism allows you to...

  • Set instance variables (e.g. page_title)
  • Override sections completely (e.g. render_body)
  • Append to standard content (e.g. render_navbar)
  • 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!)

Check out Erector::Widgets::Page for a widget that does a lot of this for you, including rendering externals in the HEAD element.

To use layout inheritance in Rails, declare layout nil in app/controllers/application.rb (or in an individual controller class) and then define your Page parent class as class Views::Layouts::Page in app/views/layouts as usual.

5. Erector Widgets as Rails Layouts

To use an Erector widget as a regular Rails layout, you'll have to set things up a bit differently.
app/views/layouts/application.rb:

class Views::Layouts::Application < Erector::Widget
  def content
    html {
      head {
        title "MyApp - #{page_title}"
        css "myapp.css"
      }
      body {
        div.navbar {
          navbar
        }
        div.main {
          content_for :layout
        }
        div.footer {
          footer
        }
      }
    }
  end

  def navbar
    ul {
      li { a "MyApp Home", :href => "/" }
      content_for :navbar if content_for? :navbar
    }
  end

  def footer
    p "Copyright (c) 2112, Rush Enterprises Inc."
    content_for :footer if content_for? :footer
  end

end

app/views/faq/index.rb:
class Views::Faq::Index < Erector::Widget
  def content
    content_for :navbar do
      li { a "More FAQs", :href => "http://faqs.org" }
    end

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

[TODO: more explanation]

6. Instance Variables

Controller instance variables (sometimes called "assigns") are available to the view, and also to any partial that gets rendered by the view, no matter how deeply-nested. This effectively makes controller instance variables "globals". In small view hierarchies this probably isn't an issue, but in large ones it can make debugging and reasoning about the code very difficult.

Often, large Rails applications will assign many controller instance variables. Sometimes these aren't used by a view: ApplicationController might assign variables that are used by many, but not all, views; and various other things may accumulate, especially if you've been using templating systems that are more forgiving than Erector. Erector's "needs" mechanism helps enforce stricter encapsulation. But if you migrate from a promiscuous Rails app to Erector, you're stuck using no "needs" declaration at all, because it needs to contain every assigned variable, or Erector will raise an exception.

Two widget-class-level settings can help you with these problems.

controller_assigns_propagate_to_partials

If you set this to true (and it's inherited through to subclasses), then any widget that's getting rendered as a partial will only have access to locals explicitly passed to it (render :partial => ..., :locals => ...). (This doesn't change the behavior of widgets that are explicitly rendered, as they don't have this issue.) This can allow for cleaner encapsulation of partials, as they must be passed everything they use and can't rely on controller instance variables.

ignore_extra_controller_assigns

If you set this to true (and it's inherited through to subclasses), however, then "needs" declarations on the widget will cause excess controller variables to be ignored -- they'll be unavailable to the widget (so 'needs' still means something), but they won't cause widget instantiation to fail, either. This can let a large Rails project transition to Erector more smoothly.

7. More about Rails

#capture_content is now an alias for #capture, so we can call it in a Rails 3.1 app