This page describes integrating Erector into Ruby on Rails apps. Read the User Guide for details on Erector itself.
Erector On Rails
Table of Contents
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-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.
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