Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

599 lines (565 sloc) 29.331 kB
<html>
<head>
<title>Erector - User Guide</title>
<style>body, td, a, p {
font-family: Lucida Grande, Lucida Sans, arial, sans-serif;
font-size: 11pt; }
body {
margin: 0;
padding: 0; }
img {
border: none; }
.clear {
clear: both; }
ul {
list-style-position: inside; }
li {
margin-left: 1.5em;
padding-bottom: .5em; }
li > p {
-webkit-margin-before: 0;
-webkit-margin-after: 0; }
h1 {
/* font-variant: small-caps;*/
border-bottom: 1px solid gray;
/* background: #EEE; */
padding: .25em;
padding-left: 0px;
margin-left: 0px;
text-shadow: #999 1px 1px 1px; }
h2 {
margin-top: 2em;
background: #FEFEEB;
padding: .25em;
border: 1px solid #CCC;
text-shadow: #999 1px 1px 1px; }
pre {
background-color: #f4f4FF;
border: 1px solid gray;
padding: .5em 1em;
overflow: auto;
font-family: Inconsolata, Consolas, "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; }
pre.sh_html {
background-color: #FFFADE; }
code {
background-color: #F4F4FF;
font-size: 12pt; }
/* cheatsheet */
table.cheatsheet {
border-style: outset;
border-collapse: collapse;
font-size: 10pt; }
table.cheatsheet code {
background-color: #EEEEFF; }
table.cheatsheet i {
font-size: 11pt; }
table.cheatsheet th {
background-color: #EDEDED; }
table.cheatsheet td, table.cheatsheet th {
border-width: 1px;
border-style: solid;
border-color: lightgray;
padding: .5em;
font-size: 10pt; }
table.cheatsheet td {
vertical-align: top; }
.separator {
margin-right: 10px;
font-size: 36pt; }
/* top */
div.top {
background-color: #FEFDCD;
/* background-color: #FDF909;
*/
text-align: bottom;
margin: 0;
padding: 8px 8px 4px; }
div.top .logo {
margin: 2px auto; }
div.top .logo img {
opacity: 0.6; }
/* navbar */
div.navbar {
width: 100%;
margin: 0 0 2px;
padding: 8px 0px 11px;
/* no idea why, but this centers the buttons*/
overflow-vertical: auto;
text-align: left;
background-color: #A3D3D1;
border-top: 1px solid #6881BA;
border-bottom: 1px solid #6881BA; }
div.navbar h3 {
margin-bottom: .25em; }
div.navbar ul {
list-style-type: none;
display: inline;
margin: -1px 0px 0px;
padding: 8px 0px;
list-style-type: none; }
div.navbar li.clickable {
display: inline;
color: #ccdcea;
background-color: #3470a2;
list-style-type: none;
margin: 8px;
padding: 4px 6px;
/* font-variant: small-caps;*/
border: 2px solid #6881BA; }
div.navbar li.clickable a, div.navbar li.clickable a:visited {
text-decoration: none;
/*font-weight: bold;*/
color: white;
margin: 0;
font-size: 10pt; }
div.navbar li.clickable:hover {
background-color: blue;
cursor: pointer;
cursor: hand; }
div.navbar li.clickable.current {
font-weight: bold;
border: 2px solid black;
background-color: #3875D7; }
div.navbar li.clickable.current a {
color: white; }
/* main */
.main {
margin: 0 auto; }
.footer {
font-size: 10pt;
border-top: 1px solid black;
padding: 1em; }
.body {
max-width: 60em;
margin-left: 100px;
margin-right: 100px; }
.body a {
color: #6881BA;
text-decoration: none;
border-bottom: 1px dotted; }
.main h1.title {
margin: 0 200px;
text-align: center; }
/* article */
div.toc {
margin: 1em .25em;
display: inline-block;
border: 1px solid black; }
div.toc h2 {
margin: 0; }
div.toc ul, div.toc ol {
padding: .25em 1em; }
.promo_wrapper {
float: right; }
div.promo_wrapper {
text-align: center;
margin: 4px;
padding: 4px; }
div.promo {
display: inline-block;
border: 2px solid #6881BA;
text-align: center;
background: white; }
div.promo img {
border: 2px solid black;
margin: 2px; }
div.promo p {
padding: .25em .5em; }
/* example */
div.example {
position: relative;
border: 2px solid darkblue;
margin: 1em;
padding: .5em; }
div.example .before, div.example .after {
margin: 1em;
text-align: top;
vertical-align: top; }
div.example .before pre, div.example .after pre {
margin: 0;
width: 100%; }
</style>
<script src="js/sh_main.min.js"></script>
<script src="js/sh_lang/sh_ruby.min.js"></script>
<script src="js/sh_lang/sh_html.min.js"></script>
<script src="js/sh_lang/sh_sh.min.js"></script>
<link href="css/sh_style.css" rel="stylesheet" type="text/css" />
</head>
<body onload="sh_highlightDocument();"> <a href="http://github.com/erector/erector"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://a248.e.akamai.net/assets.github.com/img/30f550e0d38ceb6ef5b81500c64d970b7fb0f028/687474703a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6f72616e67655f6666373630302e706e67" alt="Fork me on GitHub"></a>
<div class="top"> <div class="logo"><a href="index.html"><img src="erector-logo.png" /></a></div>
</div>
<div class="navbar"> <ul class="clickable">
<li class="clickable" onclick="document.location=&#39;index.html&#39;"><a href="index.html">Home</a></li>
<li class="clickable current" onclick="document.location=&#39;userguide.html&#39;"><a href="userguide.html">User Guide</a></li>
<li class="clickable" onclick="document.location=&#39;rails.html&#39;"><a href="rails.html">Erector On Rails</a></li>
<li class="clickable" onclick="document.location=&#39;faq.html&#39;"><a href="faq.html">FAQ</a></li>
<li class="clickable" onclick="document.location=&#39;cheatsheet.html&#39;"><a href="cheatsheet.html">Cheatsheet</a></li>
<li class="clickable" onclick="document.location=&#39;rdoc&#39;"><a href="rdoc">RDoc API</a></li>
<li class="clickable" onclick="document.location=&#39;developers.html&#39;"><a href="developers.html">For Developers</a></li>
<li class="clickable" onclick="document.location=&#39;release_notes.html&#39;"><a href="release_notes.html">Release Notes</a></li>
<li class="clickable" onclick="document.location=&#39;community.html&#39;"><a href="community.html">Community</a></li>
</ul>
</div>
<div class="promo_wrapper"> <div class="promo"><img src="images/1959erector.jpeg" /></div>
</div>
<div class="main"> <div class="body">
<p>Make sure to check out the <a href="rdoc">RDoc Documentation</a> for more details on the API.</p>
<div class="article">
<h1 class="name">Erector User Guide</h1>
<div class="toc">
<h2>Table of Contents</h2>
<ol class="toc">
<li><a href="#thebasics">The Basics</a></li>
<li><a href="#mixin">Mixin</a></li>
<li><a href="#prettyprinting">Pretty-printing</a></li>
<li><a href="#classesandids">Classes and IDs</a></li>
<li><a href="#tool">Erector tool: Command-line conversion to and from HTML</a></li>
<li><a href="#pagelayoutinheritance">Page Layout Inheritance</a></li>
<li><a href="#inlinewidgets">Inline Widgets</a></li>
<li><a href="#needs">Needs</a></li>
<li><a href="#externals">Externals</a></li>
<li><a href="#blocks">Blocks</a></li>
</ol>
</div>
<div class="clear"></div>
<div class="sections">
<a name="thebasics"></a>
<h2>1. The Basics</h2>
<p>The basic way to construct some HTML/XML with erector is to subclass <code>Erector::Widget</code> and implement a <code>content</code> method:</p>
<table>
<tr>
<td valign="top">
<pre class="sh_ruby">class Hello &lt; Erector::Widget
def content
html {
head {
title &quot;Hello&quot;
}
body {
text &quot;Hello, &quot;
b &quot;world!&quot;
}
}
end
end
</pre>
</td>
<td><span class="separator">&#x2192;</span></td>
<td valign="top">
<pre class="sh_html">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Hello&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
Hello, &lt;b&gt;world!&lt;/b&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</td>
</tr>
</table>
<p>Once you have a widget class, you can instantiate it and then call its <code>to_html</code> method. If you want to pass in parameters (aka &#39;assigns&#39; or &#39;locals&#39; in Rails parlance), then do so in the constructor&#39;s default hash. This will make instance variables of the same name, with Ruby&#39;s &#39;@&#39; sign.</p>
<pre class="sh_ruby">class Email &lt; Erector::Widget
def content
a @address, :href =&gt; &quot;mailto:#{@address}&quot;
end
end
&gt;&gt; Email.new(:address =&gt; &quot;foo@example.com&quot;).to_html
=&gt; &quot;&lt;a href=\&quot;mailto:foo@example.com\&quot;&gt;foo@example.com&lt;/a&gt;&quot;
</pre>
<p>(If you want control over which locals are valid to be passed in to a widget, use the <a href="#needs">needs</a> macro.)</p>
<a name="mixin"></a>
<h2>2. Mixin</h2>
<p>If all this widget stuff is too complicated, just do </p>
<pre>include Erector::Mixin</pre>
<p>and then call <code>erector { }</code> from anywhere in your code. It will make an <a href="#inline">inline widget</a> for you, pass in the block, and call <code>to_html</code> on it. And if you pass any options to <code>erector</code>, like <code>:prettyprint =&gt; true</code>, it&#39;ll pass them along to <code>to_html</code>!</p>
<h3>Examples:</h3>
<pre class="sh_ruby">erector { a &quot;lols&quot;, :href =&gt; &quot;http://icanhascheezburger.com/&quot; }
=&gt; &quot;&lt;a href=\&quot;http://icanhascheezburger.com/\&quot;&gt;lols&lt;/a&gt;&quot;
erector(:prettyprint =&gt; true) do
ol {
li &quot;bacon&quot;
li &quot;lettuce&quot;
li &quot;tomato&quot;
}
end
=&gt; &quot;&lt;ol&gt;\n &lt;li&gt;bacon&lt;/li&gt;\n &lt;li&gt;lettuce&lt;/li&gt;\n &lt;li&gt;tomato&lt;/li&gt;\n&lt;/ol&gt;\n&quot;
</pre>
<a name="prettyprinting"></a>
<h2>3. Pretty-printing</h2>
<p>Erector has the ability to insert newlines and indentation to make the generated HTML more readable. Newlines are inserted before and after certain tags.</p>
<p>To enable pretty-printing (insertion of newlines and indentation) of Erector&#39;s output, do one of the following:</p>
<ul>
<li>call <code>to_pretty</code> instead of <code>to_html</code> on your Erector::Widget</li>
<li>pass <code>:prettyprint =&gt; true</code> to <code>to_html</code></li>
<li>call <code>enable_prettyprint(true)</code> on your Erector::Widget. Then subsequent calls to <code>to_html</code> will prettyprint</li>
<li>call <code>Erector::Widget.prettyprint_default = true</code> (for example, in environments/development.rb in a rails application, or anywhere which is convenient)</li>
</ul>
<a name="classesandids"></a>
<h2>4. Classes and IDs</h2>
<p>Because HTML tends to heavily use the <code>class</code> and <code>id</code> attributes, it is convenient to have a special syntax to specify them.</p>
<table>
<tr>
<td valign="top">
<pre class="sh_ruby">body.sample!.helpful &quot;Hello, world!&quot;
</pre>
</td>
<td><span class="separator">&#x2192;</span></td>
<td valign="top">
<pre class="sh_html">body class=&quot;helpful&quot; id=&quot;sample&quot;
</pre>
</td>
</tr>
</table>
<p>Most CSS and javascript tends to write classes and IDs with hyphens (for example <code>nav-bar</code> instead of <code>nav_bar</code>). Therefore, erector has a setting to convert underscores to hyphens.</p>
<table>
<tr>
<td valign="top">
<pre class="sh_ruby">Erector::Widget.hyphenize_underscores = true
body.my_id!.nav_bar &quot;Hello, world!&quot;
</pre>
</td>
<td><span class="separator">&#x2192;</span></td>
<td valign="top">
<pre class="sh_html">body class=&quot;nav-bar&quot; id=&quot;my-id&quot;
</pre>
</td>
</tr>
</table>
<p>You can put the setting of <code>hyphenize_underscores</code> anywhere it is convenient, for example <code>config/application.rb</code> in a rails application. For compatibility with erector 0.9.0, the default is false, but this is likely to change to true in a future version of erector, so explicitly set it to false if you are relying on the underscores.</p>
<a name="tool"></a>
<h2>5. Erector tool: Command-line conversion to and from HTML</h2>
<p>We&#39;ve written a little tool that will help you
erect your existing HTML app. The &#39;erector&#39; 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</p>
<pre>erector app/views/foos/*.html.erb</pre>
<p>or just</p>
<pre>erector app/views</pre>
<p>and then delete the original files when you&#39;re satisfied.</p>
<p>See the <a href="rails.html">Erector on Rails Guide</a> for more details on converting a Rails app.</p>
<p>On the erector-to-html side, pass in the <code>--to-html</code>option and some file names and it will render the erector widgets to appropriately-named HTML files. We&#39;re actually using <code>erector</code> to build this Erector documentation web site that you&#39;re reading <b>right now.</b> Check out the <a href="https://github.com/erector/erector/tree/master/web">&#39;web&#39; directory</a> and the <a href="https://github.com/erector/erector/blob/77738d13fbbb3e1b8d24653ff2950dbb88b756ed/Rakefile#L74-84">&#39;web&#39; task in the Rakefile</a> to see how it&#39;s done.</p>
<a name="pagelayoutinheritance"></a>
<h2>6. Page Layout Inheritance</h2>
<p>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.</p>
<p>For example:
<pre class="sh_ruby">class MyAppPage &lt; Erector::Widget
def content
html {
head {
title &quot;MyApp - #{@page_title}&quot;
css &quot;myapp.css&quot;
}
body {
div.navbar {
navbar
}
div.main {
main
}
div.footer {
footer
}
}
}
end
def navbar
a &quot;MyApp Home&quot;, :href =&gt; &quot;/&quot;
end
def main
p &quot;This page intentionally left blank.&quot;
end
def footer
p &quot;Copyright (c) 2112, Rush Enterprises Inc.&quot;
end
end
</pre>
<pre class="sh_ruby">class Faq &lt; MyAppPage
def initialize
super(:page_title =&gt; &quot;FAQ&quot;)
end
def main
p &quot;Q: Why is the sky blue?&quot;
p &quot;A: To get to the other side&quot;
end
def navbar
super
a &quot;More FAQs&quot;, :href =&gt; &quot;http://faqs.org&quot;
end
end
</pre>
</p>
<p>Notice how this mechanism allows you to...</p>
<ul>
<li>Set instance variables (e.g. title)</li>
<li>Override sections completely (e.g. render_body)</li>
<li>Append to standard content (e.g. render_navbar)</li>
<li>Use standard content unchanged (e.g. render_footer)</li>
</ul>
<p>all in a straightforward, easily understood paradigm (OO inheritance). (No more weird yielding to invisible, undocumented closures!)</p>
<p>Check out <a href="/rdoc/Erector/Widgets/Page.html">Erector::Widgets::Page</a> for a widget that does a lot of this for you, including rendering <a href="#externals">externals</a> in the HEAD element.</p>
<a name="inlinewidgets"></a>
<h2>7. Inline Widgets</h2>
<p>Instead of subclassing <code>Erector::Widget</code> and implementing a <code>content</code> method, you can pass a block to <code>Erector.inline</code> and get back a widget instance you can call <code>to_html</code> on. For example:
<pre class="sh_ruby">hello = Erector.inline do
p &quot;Hello, world!&quot;
end
hello.to_html #=&gt; &lt;p&gt;Hello, world!&lt;/p&gt;
</pre>
This lets you define mini-widgets on the fly.</p>
<p>If you&#39;re in Rails, your inline block has access to Rails helpers if you pass a helpers object to <code>to_html</code>:
<pre class="sh_ruby">image = Erector.inline do
image_tag(&quot;/foo&quot;)
end
image.to_html(:helpers =&gt; controller) #=&gt; &lt;img alt=&quot;Foo&quot; src=&quot;/foo&quot; /&gt;
</pre>
</p>
<p>Note that inline widgets are usually redundant if you&#39;re already inside an Erector content method. You can just use a normal <code>do</code> block and the Erector methods will work as usual when called back from <code>yield</code>. Inline widgets get evaluated with <code>instance_eval</code> which may or may not be what you want. See the section on <a href="#blocks">blocks</a> in this user guide for more detail.</p>
<p>One extra bonus feature of inline widgets is that they can call methods defined on the parent class, even though they&#39;re out of scope. How do they do this? Through method_missing magic. (But isn&#39;t method_missing magic against the design goals of Erector? Yes, some would say so, and that&#39;s why we&#39;re reserving it for a special subclass and method. For Erector::Widget and subclasses, if you pass in a block, it&#39;s a plain old block with normal semantics.) But they can&#39;t directly access instance variables on the parent, so watch it.</p>
<a name="needs"></a>
<h2>8. Needs</h2>
<p>Named parameters in Ruby are fun, but one frustrating aspect of the &#39;options hash&#39; technique is that the code is less self-documenting and doesn&#39;t &#39;fail fast&#39; 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.</p>
<p>To help this, we&#39;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:
<pre class="sh_ruby">class Car &lt; Erector::Widget
needs :engine, :wheels =&gt; 4
def content
text &quot;My #{@wheels} wheels go round and round; my #{@engine} goes vroom!&quot;
end
end
</pre>
This widget will throw an exception if you fail to pass <code>:engine =&gt; &#39;V-8&#39;</code> into its constructor. (Actually, it will work with any engine, but a V-8 is the baddest.)</p>
<p>See the <a href="rdoc/classes/Erector/Widget.html#M000053">rdoc for Widget#needs</a> for more details. Note that as of version 0.7.0, using <code>needs</code> no longer automatically declares accessor methods.</p>
<a name="externals"></a>
<h2>9. Externals</h2>
<p>Erector&#39;s got some nice tags, like <code>script</code> and <code>style</code>, 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 <code>head</code> section?</p>
<p><a href="rdoc/classes/Erector/Externals.html">Externals</a> are a way for your widget to announce to the world that it has an external dependency. It&#39;s then up to <a href="rdoc/classes/Erector/Widgets/Page.html">another widget</a> to emit that dependency while it&#39;s rendering the <code>head</code>.</p>
<p>Here&#39;s an example:
<pre class="sh_ruby">class HotSauce &lt; Erector::Widget
depends_on :css, &quot;/css/tapatio.css&quot;
depends_on :css, &quot;/css/salsa_picante.css&quot;, :media =&gt; &quot;print&quot;
depends_on :js, &quot;/lib/jquery.js&quot;
depends_on :js, &quot;/lib/picante.js&quot;
def content
p.hot_sauce {
text &quot;esta salsa es muy picante!&quot;
}
end
end
</pre>
Then when <code>Page</code> emits the <code>head</code> it&#39;ll look like this:
<pre class="sh_ruby">&lt;head&gt;
&lt;meta content=&quot;text/html;charset=UTF-8&quot; http-equiv=&quot;content-type&quot; /&gt;
&lt;title&gt;HotPage&lt;/title&gt;
&lt;link href=&quot;/css/tapatio.css&quot; media=&quot;all&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
&lt;link href=&quot;/css/salsa_picante.css&quot; media=&quot;print&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
&lt;script src=&quot;/lib/jquery.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;/lib/picante.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
</pre>
</p>
<p>It also collapses redundant externals, so if lots of your widgets declare the same thing (say, &#39;jquery.js&#39;), it&#39;ll only get included once.</p>
<p><a href="rdoc/classes/Erector/Widgets/Page.html">Page</a> looks for the following externals:
<table>
<tr>
<th>:js</th>
<td>included JavaScript file</td>
</tr>
<tr>
<th>:css</th>
<td>included CSS stylesheet</td>
</tr>
<tr>
<th>:script</th>
<td>inline JavaScript</td>
</tr>
<tr>
<th>:style</th>
<td>inline CSS style</td>
</tr>
</table>
It might be a little difficult to remember the difference between :js and :script, and between :css and :style, so I&#39;m thinking of maybe unifying them and looking at the content to determine whether it&#39;s inline or not. (Something simple like, if it includes a space, then it&#39;s inline.) Good idea? Let us know on <a href="http://googlegroups.com/group/erector">the erector mailing list</a>!</p>
<p>Instead of a string, you can also specify a File object; the file&#39;s contents get read and used as text. This allows you to inline files instead of referring to them, for potential performance benefits. Example:
<pre class="sh_ruby"> depends_on :style, File.new(&quot;#{File.dirname(__FILE__)}/../public/sample.css&quot;)
</pre>
</p>
<a name="blocks"></a>
<h2>10. Blocks</h2>
<p>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 &#39;undefined method&#39; or a nil instance variable, at least you&#39;ll have some context to help debug it.</p>
<p>There are basically three cases where you can pass a block to Erector:</p>
<h3>1. To an element method</h3>
<p>This is the normal case that provides the slick HTML DSL. In the following code:</p>
<pre class="sh_ruby">class Person &lt; Erector::Widget
def content
div {
h3 @name
p {
b &quot;Birthday: &quot;
span @birthday
}
}
end
end
</pre>
<p>the blocks passed in to <code>div</code> and <code>p</code> are evaluated using normal <code>yield</code> semantics, and the <code>@name</code> and <code>@birthday</code> instance variables are evaluated in the context of the Person instance being rendered.</p>
<p>So far, so good.</p>
<h3>2. To the constructor of an Erector::Widget</h3>
<p>In this case you can build a widget &quot;on the fly&quot; and have it render whatever it wants, then call your block. This is useful for widgets like <code>Form</code> which want to wrap your HTML in some of their own tags.</p>
<pre class="sh_ruby">class PersonActions &lt; Erector::Widget
needs :user
def content
div {
widget(Form.new(:action =&gt; &quot;/person/#{@user.id}&quot;, :method =&gt; &quot;delete&quot;) {
input :type =&gt; &quot;submit&quot;, :value =&gt; &quot;Remove #{@user.name}&quot;
})
widget(Form.new(:action =&gt; &quot;/person/#{@user.id}/email&quot;, :method =&gt; &quot;post&quot;) {
b &quot;Send message: &quot;
input :type =&gt; &quot;text&quot;, :name =&gt; &quot;message&quot;
input :type =&gt; &quot;submit&quot;, :value =&gt; &quot;Email #{@user.name}&quot;
})
}
end
end
</pre>
<p>In this case, you will get two <code>form</code> elements, each of which has some boilerplate HTML for emitting the form element, emitting the hidden <code>_method</code> 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 <code>@user</code> instance variable will be sought inside the <b>calling</b> widget<code>(PersonActions)</code>, not the <b>called </b> widget<code>(Form)</code>.</p>
<p>A quirk of this technique is that methods inside the block will be called on the calling widget, not the called widget. This doesn&#39;t cause any problems for element methods (<code>b</code> and <code>input</code> 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.
<pre class="sh_ruby">widget(Form.new(:action =&gt; &quot;/person/#{@user.id}&quot;, :method =&gt; &quot;delete&quot;) do |f|
span &quot;This form&#39;s method is #{f.method}&quot;
input :type =&gt; &quot;submit&quot;, :value =&gt; &quot;Remove #{@user.name}&quot;
end)
</pre>
</p>
<p>(As a variant of this case, note that the<code>widget</code> method can accept a widget class, hash and block, instead of an instance; in this case it will set the widget&#39;s block and this code:
<pre class="sh_ruby">widget Form, :action =&gt; &quot;/person/#{@user.id}&quot;, :method =&gt; &quot;delete&quot; do
input :type =&gt; &quot;submit&quot;, :value =&gt; &quot;Remove #{@user.name}&quot;
end
</pre>
will work the same as the version above.)</p>
<h3>3. To the constructor of an Erector::InlineWidget</h3>
<p>This is where things get hairy. Sometimes we want to construct a widget on the fly, but we&#39;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 <code>Erector::InlineWidget</code> which uses two magic tricks: <code>instance_eval</code> and <code>method_missing</code> to accomplish the following:
<ul>
<li>inside the block, <code>self</code> points to the widget, not the caller.</li>
<li>methods will be looked for first on the inline widget, and then on the caller.</li>
<li>instance variables will be looked for on the inline widget <b>only</b>. 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...</li>
<li><b>Bound</b> local variables will still be in scope. This means you can &quot;smuggle in&quot; instance variables via local variables. For example:
<pre class="sh_ruby">local_name = @name
Page.new do
div local_name
end.to_html
</pre>
</li>
</ul>
<p>When using the <a href="#mixin">mixin</a>, you get an inline widget, so the above list of tricks applies.</p>
<hr />
<p>One note for developers: when creating a widget like <code>Form</code> that needs to call back to its block, use the method <code>call_block</code>, which calls the block and passes in self as appropriate for both inline and normal widgets.</p>
</p>
</div>
</div>
</div>
<div class="footer"><a href="http://www.pivotallabs.com"><img alt="Pivotal Labs" height="57" src="pivotal.gif" style="float:right; padding: 8px;" width="158" /></a>
<center>Erector is an open source project released under the MIT license.<br />
Its initial development was sponsored by <a href="http://pivotallabs.com">Pivotal Labs</a>.<br />
Not affiliated with or sponsored by the makers of Erector or Meccano toys.</center>
</div>
</div>
</body>
</html>
Jump to Line
Something went wrong with that request. Please try again.