<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Building Web Apps</title>
    <link>http://www.buildingwebapps.com</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description></description>
        
        <item>
          <title>Web 2.0 Expo: Social Media</title>
          <description>&lt;p&gt;The conference sessions at Web 2.0 Expo had a major emphasis on social media. I only attended a couple of these talks, about which I have brief comments below; at the end of the article, I have links to several others.&lt;/p&gt;

&lt;h2&gt;Social Media Marketing - why it fails and how to fix it&lt;/h2&gt;

&lt;p&gt;This one felt somewhat remedial to me, with the essential points being:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;Be authentic. You need to be a person, not just a representative of a company.&lt;/li&gt;
	&lt;li&gt;It's hard to change the marketing culture in an organization to deal well with social media. (A point that I was glad to gloss over, being in my tiny organization that doesn't have this sort of problem.)&lt;/li&gt;
	&lt;li&gt;It's a conversation. (If in doubt, go read The Cluetrain Manifesto.)&lt;/li&gt;
&lt;/ul&gt;
	
&lt;h2&gt;The Whuffie Factor&lt;/h2&gt;

&lt;p&gt;Taking a completely different approach to similar material, Tara Hunt gave what I found to be the most inspiring and engaging talk of the conference: The Whuffie Factor.&lt;/p&gt;

&lt;p&gt;Since both the slides and the video have been posted, I'll let her speak for herself.&lt;/p&gt;

&lt;h3&gt;Slides&lt;/h3&gt;
&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_1242483&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/missrogue/whuffie-at-web-20-expo?type=powerpoint&quot; title=&quot;Whuffie at Web 2.0 Expo&quot;&gt;Whuffie at Web 2.0 Expo&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=whuffiepresentation-090402205538-phpapp01&amp;stripped_title=whuffie-at-web-20-expo&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=whuffiepresentation-090402205538-phpapp01&amp;stripped_title=whuffie-at-web-20-expo&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/missrogue&quot;&gt;Tara Hunt&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;

&lt;h3&gt;Video&lt;/h3&gt;
&lt;object width=&quot;480&quot; height=&quot;360&quot;&gt;&lt;param name=&quot;allowfullscreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot; /&gt;&lt;param name=&quot;movie&quot; value=&quot;http://vimeo.com/moogaloop.swf?clip_id=3983571&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1&quot; /&gt;&lt;embed src=&quot;http://vimeo.com/moogaloop.swf?clip_id=3983571&amp;server=vimeo.com&amp;show_title=0&amp;show_byline=0&amp;show_portrait=0&amp;color=00ADEF&amp;fullscreen=1&quot; type=&quot;application/x-shockwave-flash&quot; allowfullscreen=&quot;true&quot; allowscriptaccess=&quot;always&quot; width=&quot;480&quot; height=&quot;360&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;a href=&quot;http://vimeo.com/3983571&quot;&gt;The Whuffie Factor: The 5 Keys for Maxing Social Capital and Winning with Online Communities (Tara Hunt)&lt;/a&gt; from &lt;a href=&quot;http://vimeo.com/steffan&quot;&gt;Steffan Antonas&lt;/a&gt; on &lt;a href=&quot;http://vimeo.com&quot;&gt;Vimeo&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
&lt;h2&gt;Other Social Media Talks&lt;/h2&gt;

&lt;h3&gt;Designing Social Interfaces&lt;/h3&gt;

&lt;img style=&quot;visibility:hidden;width:0px;height:0px;&quot; border=0 width=0 height=0 src=&quot;http://counters.gigya.com/wildfire/IMP/CXNID=2000002.0NXC/bT*xJmx*PTEyMzkxNjMwNDk1MjkmcHQ9MTIzOTI1MDk2MTA5NiZwPTEwMTkxJmQ9Jmc9MiZ*PSZvPWJmNmQ*NjE2YzQ*NDQ4YTdiNDk3ZjU*MDkzZmM1NDk*.gif&quot; /&gt;&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_1229890&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/emalone/social-patterns-talk-web-20-version?type=powerpoint&quot; title=&quot;Social Patterns Talk - Web 2.0 version&quot;&gt;Social Patterns Talk - Web 2.0 version&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=socialpatternstalk-web2-090331165811-phpapp02&amp;stripped_title=social-patterns-talk-web-20-version&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=socialpatternstalk-web2-090331165811-phpapp02&amp;stripped_title=social-patterns-talk-web-20-version&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/emalone&quot;&gt;Erin Malone&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;

&lt;h3&gt;Designing Social Websites&lt;/h3&gt;

&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_1236224&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/cwodtke/designing-the-social-web-for-web20-expo?type=powerpoint&quot; title=&quot;Designing the Social Web (for Web2.0 expo)&quot;&gt;Designing the Social Web (for Web2.0 expo)&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=designingsocialweb20low-090401174528-phpapp02&amp;stripped_title=designing-the-social-web-for-web20-expo&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=designingsocialweb20low-090401174528-phpapp02&amp;stripped_title=designing-the-social-web-for-web20-expo&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/cwodtke&quot;&gt;cwodtke&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;

&lt;h3&gt;Building Sites Around Social Objects&lt;/h3&gt;

&lt;object width=&quot;400&quot; height=&quot;300&quot;&gt;&lt;param name=&quot;allowfullscreen&quot; value=&quot;true&quot; /&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot; /&gt;&lt;param name=&quot;movie&quot; value=&quot;http://vimeo.com/moogaloop.swf?clip_id=4071624&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1&quot; /&gt;&lt;embed src=&quot;http://vimeo.com/moogaloop.swf?clip_id=4071624&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1&quot; type=&quot;application/x-shockwave-flash&quot; allowfullscreen=&quot;true&quot; allowscriptaccess=&quot;always&quot; width=&quot;400&quot; height=&quot;300&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;a href=&quot;http://vimeo.com/4071624&quot;&gt;Building Sites Around Social Objects (Web 2.0 Expo - Jyri Engestrom, Google)&lt;/a&gt; from &lt;a href=&quot;http://vimeo.com/steffan&quot;&gt;Steffan Antonas&lt;/a&gt; on &lt;a href=&quot;http://vimeo.com&quot;&gt;Vimeo&lt;/a&gt;.

&lt;h3&gt;Social Media Buyer's Guide&lt;/h3&gt;

&lt;img style=&quot;visibility:hidden;width:0px;height:0px;&quot; border=0 width=0 height=0 src=&quot;http://counters.gigya.com/wildfire/IMP/CXNID=2000002.0NXC/bT*xJmx*PTEyMzkyNTEwOTgxMjImcHQ9MTIzOTI1MTE4MTEzMyZwPTEwMTkxJmQ9Jmc9MiZ*PSZvPWJmNmQ*NjE2YzQ*NDQ4YTdiNDk3ZjU*MDkzZmM1NDk*.gif&quot; /&gt;&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_1245456&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/socialmedia/social-media-buyers-guide-preview?type=powerpoint&quot; title=&quot;Social Media Buyers Guide Preview&quot;&gt;Social Media Buyers Guide Preview&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=smbgfinw2e-090403122628-phpapp01&amp;stripped_title=social-media-buyers-guide-preview&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=smbgfinw2e-090403122628-phpapp01&amp;stripped_title=social-media-buyers-guide-preview&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/socialmedia&quot;&gt;socialmedia&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;

&lt;h3&gt;Beyond Buzz: Measuring a Conversation&lt;/h3&gt;

&lt;img style=&quot;visibility:hidden;width:0px;height:0px;&quot; border=0 width=0 height=0 src=&quot;http://counters.gigya.com/wildfire/IMP/CXNID=2000002.0NXC/bT*xJmx*PTEyMzkzODkzMDQ*MjMmcHQ9MTIzOTM4OTM4MjMyNCZwPTEwMTkxJmQ9Jmc9MiZ*PSZvPTBkZDRjNzZmYWEyNDQzOTFhODEyZTE3MzE2NWNkZDhj.gif&quot; /&gt;&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_1242672&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/kategn/beyond-buzz-web-20-expo-kniederhoffer-msmith-1242672?type=powerpoint&quot; title=&quot;Beyond Buzz - Web 2.0 Expo - K.Niederhoffer &amp;amp; M.Smith&quot;&gt;Beyond Buzz - Web 2.0 Expo - K.Niederhoffer &amp;amp; M.Smith&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=web2-kn-ms-final-3-30-090402221335-phpapp02&amp;stripped_title=beyond-buzz-web-20-expo-kniederhoffer-msmith-1242672&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=web2-kn-ms-final-3-30-090402221335-phpapp02&amp;stripped_title=beyond-buzz-web-20-expo-kniederhoffer-msmith-1242672&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/kategn&quot;&gt;kategn&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;</description>
          <pubDate>Thu, 09 Apr 2009 04:32:09 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79318-web-2-0-expo-social-media</guid>
          <link>http://www.buildingwebapps.com/item/79318-web-2-0-expo-social-media</link>
        </item>
        
        <item>
          <title>Web 2.0 Expo: Web Developer Tools</title>
          <description>&lt;p&gt;Ben Galbraith and Dio Almaer of &lt;a href='http://ajaxian.com'&gt;Ajaxian&lt;/a&gt; and the &lt;a href='http://labs.mozilla.com/2008/10/developer-tools-and-the-open-web/'&gt;developer tools group at Mozilla&lt;/a&gt; gave a wide-ranging talk on web developer tools. They noted that the web often seems more like a hack than a platform &amp;mdash; it's amazing that developers are able to make web apps do the things they do. This is especially true with Ajax raising the bar for interactivity, and sites turning into applications instead of documents. They're out to make the web a better platform.&lt;/p&gt;

&lt;p&gt;Some future technologies that have them excited:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;HTML5 canvas&lt;/li&gt;
  &lt;li&gt;Fast JavaScript &amp;mdash; kicked off by Chrome, Firefox's TraceMonkey&lt;/li&gt;
  &lt;li&gt;Web workers, giving web apps the ability to have background threads&lt;/li&gt;
  &lt;li&gt;Desktop integration, such as &lt;a href='http://fluidapp.com/'&gt;Fluid&lt;/a&gt;, &lt;a href='http://labs.mozilla.com/projects/prism/'&gt;Prism&lt;/a&gt;, &lt;a href='http://www.adobe.com/products/air/'&gt;Adobe Air&lt;/a&gt;, and &lt;a href='http://titaniumapp.com/'&gt;Titanium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As part of its efforts to improve the web as a platform, Mozilla is working on an open web tools directory, as well as a variety of development projects.&lt;/p&gt;

&lt;p&gt;Ben and Dio noted that Firebug blew away existing tools and set a new standard for web development tools, providing a comprehensive set of tools in the browser itself. Other browsers have followed with IE8 and Safari 3.0 both providing good tools.&lt;/p&gt;

&lt;p&gt;Mozilla Labs has a relatively new project called &lt;a href='http://labs.mozilla.com/projects/bespin/'&gt;Bespin&lt;/a&gt;, a web-based code editor that is intended to be a showcase for HTML 5. This project is still young, and it won't run on an flavor of IE for a while, but it looks very promising.&lt;/p&gt;

&lt;p&gt;Mozilla Labs is also working on a grid-based layout system using JavaScript and HTML5 canvas; so far, there's not much on the web site about this project, but an announcement is due soon.&lt;/p&gt;

</description>
          <pubDate>Thu, 09 Apr 2009 05:39:08 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79319-web-2-0-expo-web-developer-tools</guid>
          <link>http://www.buildingwebapps.com/item/79319-web-2-0-expo-web-developer-tools</link>
        </item>
        
        <item>
          <title>Web 2.0 Expo: TV and Radio with an API</title>
          <description>&lt;p&gt;National Public Radio (NPR) is changing what it means to be a news organization. While traditional news organizations want to keep their content close, NPR has taken the approach of making content widely available via an API &amp;ndash; an approach they call &quot;brand and release.&quot; They've made 250,000 stories, going back 13 years, available through their API.&lt;/p&gt;

&lt;p&gt;Zack Brand from NPR said that the &lt;a href='http://npr.org/api'&gt;NPR API&lt;/a&gt; is getting about 2 million requests per month, and there are 1,300 registrants (you have to register to get access to the API). Content is available in a variety of formats; so far, 51% of requests use XML, 20% RSS, and 23% widgets.&lt;/p&gt;

&lt;p&gt;(Another notable new organization that has been making some content available via an API is the New York Times, which has an &lt;a href='http://open.blogs.nytimes.com/2009/02/04/announcing-the-article-search-api/'&gt;article search API&lt;/a&gt;, among others.)
	
&lt;p&gt;Also participating in this session was Robin Sloan from &lt;a href='http://current.tv'&gt;Current.tv&lt;/a&gt;. Current made interesting use of Twitter streams as part of its presidential debate coverage. The hash tag they created for people to tag their posts, #current, was the third most popular Twitter tag during the debates, bested only by the tags for the candidates themselves.&lt;/p&gt;

&lt;p&gt;Current built an application to provide a mash up of various data sources, including Digg, on election day. In true agile fashion, they made multiple software updates even after the show began broadcasting!&lt;/p&gt;</description>
          <pubDate>Thu, 09 Apr 2009 05:41:55 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79320-web-2-0-expo-tv-and-radio</guid>
          <link>http://www.buildingwebapps.com/item/79320-web-2-0-expo-tv-and-radio</link>
        </item>
        
        <item>
          <title>Web 2.0 Expo San Francisco Spring 2009</title>
          <description>&lt;h2&gt;March 31 - April 3, San Francisco&lt;/h2&gt;

&lt;p&gt;Last week's Web 2.0 Expo seemed like a successful event, with a good-sized crowd despite the economic malaise – due in part, no doubt, to unusually aggressive discounting and promotion. With multiple tracks across four days (one day of workshops and three of conference sessions) there was much more to see than any one person could possibly attend.&lt;/p&gt;

&lt;p&gt;Here's a few articles that we've written about talks at the conference:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href='http://www.buildingwebapps.com/articles/17896-web-2-0-expo-social-media'&gt;Web 2.0 Expo: Social Media&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href='http://www.buildingwebapps.com/articles/17897-web-2-0-expo-web-developer-tools'&gt;Web 2.0 Expo: Web Developer Tools&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href='http://www.buildingwebapps.com/articles/17898-web-2-0-expo-tv-and-radio'&gt;TV and Radio with an API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many of the presentations are available online, as slides and/or videos:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;a href='http://www.web2expo.com/webexsf2009/public/schedule/proceedings'&gt;Official download site for Web 2.0 Presentations&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href='http://www.slideshare.net/tag/w2e'&gt;Presentation slides on SlideShare&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href='http://vimeo.com/videos/search:w2e'&gt;Videos on Vimeo&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href='http://www.youtube.com/results?search_type=&amp;search_query=web+2.0+expo+sf+2009&amp;aq=f'&gt;Videos on YouTube&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And to go beyond the presentations and get more flavor of the event:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href='http://search.twitter.com/search?q=%23w2e'&gt;Twitter Stream&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href='http://www.flickr.com/search/?q=w2e'&gt;Photos on Twitter&lt;/a&gt;&lt;/li&gt;</description>
          <pubDate>Mon, 06 Apr 2009 22:02:46 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79317-web-2-0-expo-san-francisco-spring</guid>
          <link>http://www.buildingwebapps.com/item/79317-web-2-0-expo-san-francisco-spring</link>
        </item>
        
        <item>
          <title>Enhancing Conditional Routing in Rails</title>
          <description>&lt;p&gt;Rails&amp;#8217; routing infrastructure supports the concept of conditional routes: preconditions that must be satisfied before a particular route will trigger. Rails 2.1 supports one built-in condition, &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; method checking, which is of some use but rather limited. What I needed was to be able to limit certain routes to only trigger when a particular host-name was used to access the application.&lt;/p&gt;
&lt;p&gt;I thought I&amp;#8217;d have to write messy additional logic until a little comment tucked away in ActionController::Routing::RouteSet and ActionController::Routing::Routing caught my eye. Here I briefly show you how to leverage this functionality for your own purposes.&lt;/p&gt;
&lt;h2&gt;The Goal &amp;#8212; Conditional Routes in routes.rb&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s work backwards and see the result I was aiming for. I wanted to expand the existing capabilities of the routing engine and be able to restrict routes to specific hosts. The conditional routing option works by adding a parameter to your route specifications. Here are some examples:&lt;/p&gt;
&lt;pre&gt;
map.with_options(:controller =&amp;gt; 'feeds', :conditions =&amp;gt; {:hosts =&amp;gt; MY_HOSTS}) do |feed|
  feed.feeds_articles '/feeds/articles', :action =&amp;gt; 'articles'
  feed.feeds_podcast '/feeds/podcast', :action =&amp;gt; 'podcast'
end
&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;
map.resources :podcasts, :conditions =&amp;gt; {:hosts =&amp;gt; MY_HOSTS}, 
   :member =&amp;gt; {:show_notes =&amp;gt; :get, :transcript =&amp;gt; :get},
   :collection =&amp;gt; {:admin =&amp;gt; :get} do |podcast|
     podcast.resources :comments, :member =&amp;gt; {:report_as_ham =&amp;gt; :get, :report_as_spam =&amp;gt; :get}
   end
&lt;/pre&gt;
&lt;p&gt;or even&lt;/p&gt;
&lt;pre&gt;
map.connect ':controller/:action/:id', :conditions =&amp;gt; {:hosts =&amp;gt; MY_HOSTS}
&lt;/pre&gt;
&lt;p&gt;In Rails 2.1, however, no such option &lt;code&gt;:hosts&lt;/code&gt; exists, only an option to check the &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; method via &lt;code&gt;:method&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The Implementation&lt;/h2&gt;
&lt;p&gt;I haven&amp;#8217;t really ever needed to use the conditional routing support before, and didn&amp;#8217;t really think about it due to it only supporting the &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; method check. For that reason, I originally thought I&amp;#8217;d have to write my own logic, either patching existing Routing routines (nearly right!) or by writing new stuff that could get messy (bad idea).&lt;/p&gt;
&lt;p&gt;During a last scan through the code for the keyword &amp;#8220;conditions&amp;#8221;, I saw this comment:&lt;/p&gt;
&lt;pre&gt;
# Plugins may override this method to add other conditions, like checks on
# host, subdomain, and so forth. Note that changes here only affect route
# recognition, not generation.
&lt;/pre&gt;
&lt;p&gt;Good, a place to start afterall! The solution is elegant as it only requires overriding two simple routines. You can do this in your own app by writing code that gets loaded at startup. Here is one implementation in its entirety:&lt;/p&gt;
&lt;pre&gt;
require 'action_controller'

module ActionController
  module Routing
    class RouteSet
      def extract_request_environment(request)
        { :method =&amp;gt; request.method, :host =&amp;gt; request.host }
      end
    end

    class Route
      def recognition_conditions
        result = [&quot;(match = #{Regexp.new(recognition_pattern).inspect}.match(path))&quot;]
        result &amp;lt;&amp;lt; &quot;conditions[:method] === env[:method]&quot; if conditions[:method]
        result &amp;lt;&amp;lt; &quot;conditions[:hosts].include?(env[:host])&quot; if conditions[:hosts]
        result
      end
    end
  end
end	
&lt;/pre&gt;
&lt;p&gt;My code is very simplistic and tuned for my needs, but gives you an example of where to patch in. Here, I simply supply a list of host names I care about, and check the incoming host against that list.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;extract_request_environment&lt;/code&gt; to parse out and store any data you will want to use in your conditional checks. This data will be available in the &lt;code&gt;env&lt;/code&gt; hash later on.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;recognition_conditions&lt;/code&gt; generates an Array of String objects that contain the Ruby code that will be used to build dynamic conditional test methods when the routing engine compiles the routes data in &lt;code&gt;routes.rb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I drop the source file into my project&amp;#8217;s pre-existing &lt;code&gt;lib/plugins/action_controller_extensions/lib&lt;/code&gt; directory as &lt;code&gt;action_controller_extensions.rb&lt;/code&gt; and include an &lt;code&gt;init.rb&lt;/code&gt; loader stub in my &lt;code&gt;lib/plugins/action_controller_extensions&lt;/code&gt; directory:&lt;/p&gt;
&lt;pre&gt;
require 'action_controller_extensions'	
&lt;/pre&gt;
&lt;p&gt;My app deals with loading up such &amp;#8220;plugins&amp;#8221; at startup. You may have a different set-up. You can get the same effect by putting a &lt;code&gt;require&lt;/code&gt; for the main source file in your startup code.&lt;/p&gt;
&lt;p&gt;It would be great to see other generally useful conditionals contributed by the community.&lt;/p&gt;</description>
          <pubDate>Wed, 27 Aug 2008 20:21:08 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79223-enhancing-conditional-routing-in-rails</guid>
          <link>http://www.buildingwebapps.com/item/79223-enhancing-conditional-routing-in-rails</link>
        </item>
        
        <item>
          <title>Lab 8 (Bonus): A Page Model and Cleanup</title>
          <description>&lt;p&gt;This final RailsQuickStart lab write-up describes changes we&amp;#8217;ve made to the sample application. Some changes are just code clean-up, while others implement some of the additional features suggested at the end of each lab.&lt;/p&gt;
&lt;p&gt;To use the new code, check it out from our shared Subversion repository (see the &lt;a href=&quot;/labs/7&quot;&gt;Lab 7&lt;/a&gt; writeup if you need a refresher on how to do this) and migrate to the current database version (&lt;code&gt;rake db:migrate&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Page model&lt;/h2&gt;
&lt;p&gt;The most dramatic change is the addition of a Page model, and its associated pages_controller and views. This replaces the old static_controller, which we&amp;#8217;ve purged. By using a model to store the content and metadata for each page in the database, an administrator can update page titles and set page description and keyword metadata without having to edit code.&lt;/p&gt;
&lt;h3&gt;Controller and view code&lt;/h3&gt;
&lt;p&gt;The page model is administered just like any other, with the usual create and edit views.&lt;/p&gt;
&lt;p&gt;To render each page, we added an action called simply &amp;#8220;view&amp;#8221;. The method in pages_controller is:&lt;/p&gt;
&lt;pre&gt;
  def view
    unless @page = Page.find_by_name(params[:name])
      render :action =&amp;gt; 'error' and return
    else     
      login_required if @page.protect
      @current_tab = @page.navtab
    end
  end
&lt;/pre&gt;
&lt;p&gt;We find the page by its name, and if there is no page of a matching name, we render the error page. Assuming it is found, we check its &amp;#8216;protect&amp;#8217; attribute, and if that is set, we require login to view the page. Finally, we set the &lt;code&gt;@current_tab&lt;/code&gt; value, which is used by application layout to highlight the appropriate tab.&lt;/p&gt;
&lt;p&gt;The &amp;#8220;view&amp;#8221; template is very simple, since all it is doing is rendering the contents of the page body attribute:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;%= render_to_string :inline =&amp;gt; @page.body %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Note that we are not just using &lt;code&gt;&amp;lt;%= @page.body %&amp;gt;&lt;/code&gt; here, because we want the body to be intepreted as ERb. Because the &lt;code&gt;render_to_string&lt;/code&gt; method is not normally available in views, we need to add this line to pages_controller to make it available as a view helper:&lt;/p&gt;
&lt;pre&gt;
	helper_method :render_to_string
&lt;/pre&gt;
&lt;h3&gt;The joys and dangers of ERb&lt;/h3&gt;
&lt;p&gt;The body for each page, as read from the database, is interpreted as ERb, so you can use helpers and other code. This gives you complete control over what goes on each page; you can use the helpers we wrote for content blocks and images to include items from those database tables, and you can use the usual ERb code to display data from other models.&lt;/p&gt;
&lt;p&gt;There is a danger to this, however, which you should be aware of: there is no restriction on what code can go in an ERb block, so it could, for example, delete everything from the database. It can even issue system commands and wipe the file system. With great power comes great responsibility. Presumably, the only people who would be allowed to log in and edit the pages would be people you could trust.&lt;/p&gt;
&lt;p&gt;If you want to use a similar approach but in a safer way, take a look at the &lt;a href=&quot;http://www.liquidmarkup.org/&quot;&gt;Liquid&lt;/a&gt; templating language. It is similar to ERb for displaying data but does not allow destructive operations or access to system commands.&lt;/p&gt;
&lt;h3&gt;Creating pages&lt;/h3&gt;
&lt;p&gt;In addition to the migration that creates the Page model, we added a migration that populates the three pages that used to be handled by the static controller. You can edit the content of these pages by going to the main admin page, clicking on Pages Admin, and then clicking the Edit link for the appropriate page.&lt;/p&gt;
&lt;p&gt;If you add new pages, you&amp;#8217;ll need to add them to the navigation bar, or put a link on another page somewhere, so users can access them. You could take this design a step further by creating the nav bar from the Page model, so that each new page would automatically get a nav button.&lt;/p&gt;
&lt;p&gt;Note that pages that are created from other models, such as the resources page, are independent of the Page model, and for those pages, the page title and metadata are set via instance variables in the controller, just as we did in the static controller. We&amp;#8217;ve made changes to the application layout so the description and keyword metadata can be set, in addition to the page title.&lt;/p&gt;
&lt;h3&gt;Putting metadata in the layout&lt;/h3&gt;
&lt;p&gt;We want to take the metadata (title, description, and keywords) for each page and put it in the page header, so we need to do that in the layout. These lines go in the head section of views/layouts/application.html.erb:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;% if @page %&amp;gt;
      &amp;lt;title&amp;gt;&amp;lt;%= @page.title %&amp;gt;&amp;lt;/title&amp;gt; 
      &amp;lt;meta name=&quot;keywords&quot; content=&quot;&amp;lt;%= @page.keywords %&amp;gt;&quot; /&amp;gt;
      &amp;lt;meta name=&quot;description&quot; content=&quot;&amp;lt;%= @page.description %&amp;gt;&quot; /&amp;gt;
   &amp;lt;% end %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;If there is a page object (remember that some pages, like the resources page, are not generated from page objects), then we render the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; required for each piece of metadata, pulling the contents from the appropriate attribute of the &lt;code&gt;@page&lt;/code&gt; object.&lt;/p&gt;
&lt;h3&gt;Supporting routes&lt;/h3&gt;
&lt;p&gt;We added a couple of routes to support the Page model:&lt;/p&gt;
&lt;pre&gt;
   map.root :controller =&amp;gt; &quot;pages&quot;, :action =&amp;gt; &quot;view&quot;, :name =&amp;gt; 'home'
   map.connect ':name', :controller =&amp;gt; &quot;pages&quot;, :action =&amp;gt; 'view'
&lt;/pre&gt;
&lt;p&gt;The first route makes the page named &amp;#8220;home&amp;#8221; function as the home page of the site.&lt;/p&gt;
&lt;p&gt;The second route causes any &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; that consists of a single word to invoke the &lt;code&gt;view&lt;/code&gt; action in the &lt;code&gt;pages&lt;/code&gt; controller, and pass that word as the &lt;code&gt;:name&lt;/code&gt; parameter.&lt;/p&gt;
&lt;h2&gt;Resource page enhancements&lt;/h2&gt;
&lt;p&gt;We modified the view for the resources page so it doesn&amp;#8217;t show a category heading if there are no links for that category. This is just a one-line change in links/resources_page.html.erb:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;% if category.links.size &amp;gt; 0 %&amp;gt;&amp;lt;h2&amp;gt;&amp;lt;%= category.name %&amp;gt;&amp;lt;/h2&amp;gt;&amp;lt;% end %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;We also added a route for the resources page so the simple &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; &amp;#8220;/resources&amp;#8221; would work:&lt;/p&gt;
&lt;pre&gt;
   map.resources_page '/resources', :controller =&amp;gt; 'links', :action =&amp;gt; 'resources_page'
&lt;/pre&gt;
&lt;p&gt;Since we gave the route a name (after &amp;#8220;map.&amp;#8221;), we can refer to it in other places, such as in the nav link in the application layout, as &lt;code&gt;resources_page_path&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;User management&lt;/h2&gt;
&lt;p&gt;We added an admin page that lists all the users, and allows you to delete users and to change their passwords. Take a look at &lt;code&gt;users_controller&lt;/code&gt; and &lt;code&gt;views/users/index.html.erb&lt;/code&gt; to see how this works. Here&amp;#8217;s the heart of the index view:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;table&amp;gt;
&amp;lt;% @users.each do |user| %&amp;gt;
   &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= user.login %&amp;gt;&amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= user.email %&amp;gt;&amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= link_to &quot;Delete&quot;, user, :confirm =&amp;gt; 'Are you sure?', :method =&amp;gt; :delete %&amp;gt;&amp;lt;/td&amp;gt;
      &amp;lt;td&amp;gt;&amp;lt;%= link_to &quot;Change Password&quot;, :controller =&amp;gt; 'users', :action =&amp;gt; 'change_password', :id =&amp;gt; user %&amp;gt;&amp;lt;/td&amp;gt;
   &amp;lt;/tr&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/table&amp;gt;
&lt;/pre&gt;
&lt;p&gt;On each row of the table, we show one user&amp;#8217;s login and email, and provide links to delete that user or change their password.&lt;/p&gt;
&lt;h3&gt;Creating the first user&lt;/h3&gt;
&lt;p&gt;We had disabled the requirement for login to access the users controller so you could create your first user, but obviously this makes the entire system completely insecure. So we&amp;#8217;ve re-enabled the login requirement.&lt;/p&gt;
&lt;p&gt;This creates a quandary if you&amp;#8217;re starting with an empty database: you can&amp;#8217;t log in to create your first user if there are no users. You could use the Rails console to create a user, but to make this easier, we added a migration that creates a user &amp;#8220;admin&amp;#8221; with the password &amp;#8220;seminar&amp;#8221; (in the file db/migrate/013_create_admin_user.rb):&lt;/p&gt;
&lt;pre&gt;
   def self.up
      User.create(:login =&amp;gt; 'admin', 
                  :email =&amp;gt; 'noone@anydomain.com', 
                  :password =&amp;gt; 'seminar', 
                  :password_confirmation =&amp;gt; 'seminar')
   end
&lt;/pre&gt;
&lt;p&gt;You should either change the password (which you can now do via the Users Admin page) or delete this user if you&amp;#8217;re using this code in a production environment.&lt;/p&gt;
&lt;h2&gt;Making things a little prettier&lt;/h2&gt;
&lt;p&gt;We&amp;#8217;ve added some styling to the forms, so they&amp;#8217;re a little more respectable. This includes changes in the html pages, as well as some additional &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; styles. We&amp;#8217;ve also linked the standard Rails &lt;code&gt;scaffold.css&lt;/code&gt; file in the application layout, which provides styling for validation error messages and highlights fields with errors.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s one thing we don&amp;#8217;t like about the way the standard Rails error display works. Fields that have errors are wrapped in a div that has an ID of &amp;#8220;fieldWithErrors&amp;#8221;. This lets the stylesheet apply different styling to fields that have errors.&lt;/p&gt;
&lt;p&gt;But a div is the wrong element to use for this; for one thing, it messes up the layout of the forms when there is an error. We added a little code to environment.rb that makes Rails use a span, instead of a div, to avoid this problem:&lt;/p&gt;
&lt;pre&gt;
   ActionView::Base.field_error_proc = Proc.new{ |html_tag, instance| &quot;&amp;lt;span class=\&quot;fieldWithErrors\&quot;&amp;gt;#{html_tag}&amp;lt;/span&amp;gt;&quot; }
&lt;/pre&gt;
&lt;p&gt;This is a little obscure, but we didn&amp;#8217;t have to figure it out ourselves; a little bit of Google searching with a description of the problem revealed this solution.&lt;/p&gt;
&lt;p&gt;On the new subscription page, we combined all the error messages into one block by listing all of the models on one line:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;%= error_messages_for :subscription, :customer, :creditcard %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Finally, we added some styles to our &lt;code&gt;quickstart.css&lt;/code&gt; style sheet that override some of the styles in the standard Rails &lt;code&gt;scaffold.css&lt;/code&gt;. We also added a little default styling for the &lt;code&gt;table&lt;/code&gt; element so the admin pages would look a little better.&lt;/p&gt;
&lt;h2&gt;Testing Purchases Through Mock ActiveMerchant Gateway&lt;/h2&gt;
&lt;p&gt;In the code we developed previously, we weren&amp;#8217;t testing the creation of a subscription (where ActiveMerchant gets called). In this bonus lab, we refactored &lt;code&gt;SubscriptionsController.create&lt;/code&gt; to lend itself to testing. We needed to make a few changes:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;change the &lt;code&gt;create&lt;/code&gt; action itself to allow swapping in a test gateway object;&lt;/li&gt;
	&lt;li&gt;change the environment setup files to use the proper gateway depending on whether you were in test, development, or production;&lt;/li&gt;
	&lt;li&gt;and add some tests.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Fortunately, ActiveMerchant provides many tools to help with testing. The &lt;a href=&quot;http://peepcode.com/products/activemerchant-pdf&quot;&gt;PeepCode Active Merchant &lt;span class=&quot;caps&quot;&gt;PDF&lt;/span&gt;&lt;/a&gt; is an excellent resource that you should check out.&lt;/p&gt;
&lt;h3&gt;Refactored SubscriptionsController.create&lt;/h3&gt;
&lt;p&gt;We refactored the code to look like this:&lt;/p&gt;
&lt;pre&gt;
def create
  # render :inline =&amp;gt; &quot;&amp;lt;%= debug params %&amp;gt;&quot; and return
  @current_tab = &quot;subscribe_tab&quot;
  @page_title = &quot;Please correct the errors below&quot;
  @subscription = Subscription.new(params[:subscription])
  @subscription.amount = Subscription.cost_for_period(@subscription.period)
  @customer = Customer.new(params[:customer])
  # create the creditcard object and get name from customer object
  @creditcard = ActiveMerchant::Billing::CreditCard.new(params[:creditcard])    
  @creditcard.first_name = @customer.first_name
  @creditcard.last_name = @customer.last_name
  # make sure all three models are valid
  @customer.valid?
  @subscription.valid?    
  #
  # Our test code uses bogus credit cards for signaling various conditions to the 
  # bogus gateway. In test mode, the creditcard is never &quot;valid&quot;, so work around that
  # here. The better thing to do if you are going to do creditcard stuff in the future
  # is to refactor this logic into separate classes. See PeepCode's ActiveMerchant PDF
  #
  unless (ENV['RAILS_ENV'] == 'test' or @creditcard.valid?) and @customer.errors.empty? and @subscription.errors.empty?
    render :action =&amp;gt; :new and return
  end
  # if everything looks ok, send the purchase request to the gateway
  # set up options hash with billing address and ip
  options = {
    :ip =&amp;gt; request.remote_ip,
    :billing_address =&amp;gt; { 
      :name     =&amp;gt; @customer.full_name,
      :address1 =&amp;gt; @customer.address,
      :address2 =&amp;gt; '',
      :city     =&amp;gt; @customer.city,
      :state    =&amp;gt; @customer.state,
      :country  =&amp;gt; 'US',
      :zip      =&amp;gt; @customer.zip,
      :phone    =&amp;gt; @customer.phone
    }
  }  
  begin
    response = Subscription.gateway.purchase(@subscription.amount, @creditcard, options)
    # redisplay form if not successful
    unless response.success?
      flash[:notice] = &quot;Transaction failed: #{response.message}&quot;
      render :action =&amp;gt; :new
    else
      # save the responses from the transaction in the subscription record
      @subscription.message = response.message
      @subscription.reference = response.authorization
      @subscription.test = response.test?
      @subscription.params = response.params.to_yaml
      @subscription.customer = @customer
      @subscription.save!
      flash[:notice] = &quot;Thanks for subscribing to our site!&quot;
      redirect_to root_path
    end
  rescue ActiveMerchant::ActiveMerchantError =&amp;gt; e
    # Consider much better error handling (of ActiveMerchant specific exceptions)
    flash[:notice] = &quot;Transaction failed: #{e}&quot;
    render :action =&amp;gt; :new
  end        
end
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;unless&lt;/code&gt; clause that tests for valid objects changed. Our tests will use a bogus credit card object that ActiveMerchant&amp;#8217;s test code knows how to use. The fake credit card encodes whether the mocked merchant gateway should succeed or not. This means we can&amp;#8217;t use the validation check when we are in test mode.&lt;/p&gt;
&lt;p&gt;We also rewrote the gateway usage itself. Now, we use a gateway that is stored in a class variable of Subscription. The gateway is created at initialization time, and this allows us to swap in different gateways if desired. ActiveMerchant supplies a mocked gateway and we use that when testing.&lt;/p&gt;
&lt;p&gt;Note we also put the gateway usage between exception handling code (the begin/rescue/end blocks). If something goes wrong in the gateway during testing, or during real use, we catch that problem and send the user back to the data entry form.&lt;/p&gt;
&lt;h3&gt;Environment File Change and Subscription Model Tweak&lt;/h3&gt;
&lt;p&gt;Since we are creating the appropriate gateway at startup, we need to do that in our environment files. The code for test.rb looks like:&lt;/p&gt;
&lt;pre&gt;
config.after_initialize do
  ActiveMerchant::Billing::Base.mode = :test
  Subscription.gateway =
    ActiveMerchant::Billing::BogusGateway.new
end
&lt;/pre&gt;
&lt;p&gt;We are simply putting ActiveMerchant into test mode and setting the class variable in Subscription to hold an instance of the mock gateway object called &lt;code&gt;ActiveMerchant::Billing::BogusGateway&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Subscription&lt;/code&gt; class needed a small change to add the variable:&lt;/p&gt;
&lt;pre&gt;
class Subscription &amp;lt; ActiveRecord::Base

  belongs_to :customer  
  validates_numericality_of :amount, :message =&amp;gt; &quot;Choose a subscription amount&quot;
  validates_numericality_of :period, :message =&amp;gt; &quot;Choose a subscription amount&quot;
  
  # The payment gateway we want to use is set in our environments, so we can
  # swap it out for testing
  cattr_accessor :gateway
  
  def self.cost_for_period period
    if period == 1
      995     # $9.95 in cents
    elsif period == 12
      9995    # $99.95 in cents
    else
      &quot;invalid&quot;
    end
  end
end
&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;cattr_accessor&lt;/code&gt; is a method that creates a class level set of data accessors for a variable, in this case called &lt;code&gt;gateway&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Some Tests&lt;/h3&gt;
&lt;p&gt;We tweaked the test_helper.rb file to add a helper that makes a creditcard hash object for us:&lt;/p&gt;
&lt;pre&gt;
def credit_card(options = {})
  { :number =&amp;gt; '1',
    :first_name =&amp;gt; 'Bob',
    :last_name =&amp;gt; 'Sample',
    :month =&amp;gt; '8',
    :year =&amp;gt; &quot;#{ Time.now.year + 1 }&quot;,
    :verification_value =&amp;gt; '123',
    :type =&amp;gt; 'visa'
  }.update(options)
end
&lt;/pre&gt;
&lt;p&gt;and we use it in several functional tests in our subscriptions_controller_test.rb file:&lt;/p&gt;
&lt;pre&gt;
def test_should_create_subscription
  assert_difference('Subscription.count') do
    post :create, :subscription =&amp;gt; { :amount =&amp;gt; 1, :period =&amp;gt; 1 }, 
                  :customer =&amp;gt; {:first_name =&amp;gt; 'bob', :last_name =&amp;gt; 'Sample', :address =&amp;gt; '123 main', :city =&amp;gt; 'San Jose', :state =&amp;gt; 'CA', :zip =&amp;gt; '95000'},
                  :creditcard =&amp;gt; credit_card({:number =&amp;gt; '1'})
  end

  assert_redirected_to root_path
end

def test_should_not_create_subscription_failed_auth
  assert_no_difference('Subscription.count') do
    post :create, :subscription =&amp;gt; { :amount =&amp;gt; 1, :period =&amp;gt; 1 }, 
                  :customer =&amp;gt; {:first_name =&amp;gt; 'bob', :last_name =&amp;gt; 'Sample', :address =&amp;gt; '123 main', :city =&amp;gt; 'San Jose', :state =&amp;gt; 'CA', :zip =&amp;gt; '95000'},
                  :creditcard =&amp;gt; credit_card({:number =&amp;gt; '2'})
  end
  
  assert_response :success
end

def test_should_not_create_subscription_activemerchant_exception
  assert_no_difference('Subscription.count') do
    post :create, :subscription =&amp;gt; { :amount =&amp;gt; 1, :period =&amp;gt; 1 }, 
                  :customer =&amp;gt; {:first_name =&amp;gt; 'bob', :last_name =&amp;gt; 'Sample', :address =&amp;gt; '123 main', :city =&amp;gt; 'San Jose', :state =&amp;gt; 'CA', :zip =&amp;gt; '95000'},
                  :creditcard =&amp;gt; credit_card({:number =&amp;gt; '3'})
  end
  
  assert_response :success
end
&lt;/pre&gt;
&lt;p&gt;The only difference between these three tests is setting the number of the creditcard to 1, 2, or 3. These magic numbers are used by the mock object to signal three cases: 1) good validation; 2) bad credit card validation; and 3) a fault in the gateway. See the ActiveMerchant documentation for more info.&lt;/p&gt;
&lt;h2&gt;But wait, there&amp;#8217;s more!&lt;/h2&gt;
&lt;p&gt;There&amp;#8217;s an assortment of other changes throughout the code: adding validations for most model fields, making the defensio code a little more robust, and fixing some tests.&lt;/p&gt;
&lt;h2&gt;What&amp;#8217;s next?&lt;/h2&gt;
&lt;p&gt;That&amp;#8217;s all, folks. If you find any issues with the code, or want to make any suggestions, please post to the &lt;a href=&quot;http://groups.google.com/group/railsquickstart&quot;&gt;Google group&lt;/a&gt;.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:46 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79181-lab-8-bonus-a-page-model</guid>
          <link>http://www.buildingwebapps.com/item/79181-lab-8-bonus-a-page-model</link>
        </item>
        
        <item>
          <title>Lab 1. Set-Up and Static Pages</title>
          <description>&lt;p&gt;In this lab, you&amp;#8217;ll create a new Rails site from scratch, building it up step by step until you have a four-page site with navigation. This creates the basis upon which all the other labs build.&lt;/p&gt;
&lt;h2&gt;1. Set-up development environment&lt;/h2&gt;
&lt;p&gt;The instructions below assume you have the following software installed:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Ruby 1.8.6&lt;/li&gt;
	&lt;li&gt;Rails 2.0.2&lt;/li&gt;
	&lt;li&gt;MySQL database server&lt;/li&gt;
	&lt;li&gt;NetBeans 6.0.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can use another editor or &lt;span class=&quot;caps&quot;&gt;IDE&lt;/span&gt;, but then you&amp;#8217;re on your own for the specifics of how to accomplish these tasks. We will, however, provide the command-line alternative to each of the NetBeans Rails commands.&lt;/p&gt;
&lt;p&gt;If you are using NetBeans, you must have it configured to use your native Ruby interpreter, and not JRuby.&lt;/p&gt;
&lt;h2&gt;2. Create your Rails project and its associated databases&lt;/h2&gt;
&lt;h3&gt;a. Create your local database&lt;/h3&gt;
&lt;p&gt;To create your database using the MySQL command-line client, type&lt;/p&gt;
&lt;pre&gt;
	mysql --u root
&lt;/pre&gt;
&lt;p&gt;in a terminal window. (Note: if you installed MySQL using macports, you&amp;#8217;ll probably have to type mysql5 instead of mysql.) This assumes this is a new database server installation and you haven&amp;#8217;t set a root password. If you have set a root password, enter&lt;/p&gt;
&lt;pre&gt;
	mysql --u root --p
&lt;/pre&gt;
&lt;p&gt;and it will prompt you for the password.&lt;/p&gt;
&lt;p&gt;You should now have a &amp;gt;&amp;gt; prompt, which indicates that you&amp;#8217;re in the MySQL monitor. Enter the following commands at this prompt:&lt;/p&gt;
&lt;pre&gt;
	create database labs_development;
	grant all on labs_development.* to 'seminar'@'localhost' identified by 'seminar';
&lt;/pre&gt;
&lt;p&gt;The first command creates the database, and the second command creates the user &amp;#8220;seminar&amp;#8221; with the password &amp;#8220;seminar&amp;#8221;.&lt;/p&gt;
&lt;p&gt;You don&amp;#8217;t need this quite yet, but while you&amp;#8217;re at it you might as well create the test database, which is used for running tests:&lt;/p&gt;
&lt;pre&gt;
	create database labs_test;
	grant all on labs_test.* to 'seminar'@'localhost' identified by 'seminar';
&lt;/pre&gt;
&lt;p&gt;Finally, you can create a production database if you&amp;#8217;d like, in case you later want to try running in production mode on your development machine:&lt;/p&gt;
&lt;pre&gt;
	create database labs_production;
	grant all on labs_production.* to 'seminar'@'localhost' identified by 'seminar';
&lt;/pre&gt;
&lt;h3&gt;b. Create a Rails project&lt;/h3&gt;
&lt;p&gt;If you&amp;#8217;re using NetBeans, Choose File &amp;gt; New Project &amp;gt; Ruby on Rails Project and enter &amp;#8220;labs&amp;#8221; as the name of the project. By default, NetBeans will create a folder called NetBeans Projects in your home folder, and then within that it will create a folder that matches the name of the project. You can change this if you want, but these defaults should be fine. Make sure that the MySQL database is selected.&lt;/p&gt;
&lt;p&gt;When you click Next, unless you&amp;#8217;ve updated to a newer version of NetBeans, it is likely to tell you that you have Rails 1.2.6, and that your gems are unavailable. Don&amp;#8217;t worry, this is a NetBeans bug.&lt;/p&gt;
&lt;p&gt;When you click Finish, you should see a bunch of file creation commands scroll by in the Output window (if that window isn&amp;#8217;t visible, choose Window &amp;gt; Output &amp;gt; Output), and now in the Project pane on the left you should see your new project with all of its components.&lt;/p&gt;
&lt;p&gt;Note that the NetBeans Projects tab does not show the literal folder structure, but rather a conceptual structure that is somewhat flattened relative to the actual folder structure and has more explicit names (e.g., Configuration instead of config). Switch to the Files tab to see the actual folder structure.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re working from the command line instead, make a folder for your Rails project, change directory (cd) into that folder, and type:&lt;/p&gt;
&lt;pre&gt;
	rails -d mysql labs
&lt;/pre&gt;
&lt;h3&gt;c. Configure your Rails project to use your database&lt;/h3&gt;
&lt;p&gt;Open the file database.yml in the config folder (Configuration if you&amp;#8217;re in the NetBeans project view).&lt;/p&gt;
&lt;p&gt;In the section that starts with &amp;#8220;development:&amp;#8221;, change the username from root to seminar, and the password from blank to seminar. Make the same changes in the test and projection sections.&lt;/p&gt;
&lt;p&gt;We suggest also deleting the socket: line from the database.yml file from each of the three sections.&lt;/p&gt;
&lt;h3&gt;d. Run the application&lt;/h3&gt;
&lt;p&gt;You now have a new, empty Rails application. To run the application, if you&amp;#8217;re using NetBeans, right-click on the project name in the Projects pane and choose Run. If you&amp;#8217;re working from the command line, make sure you&amp;#8217;re in the project&amp;#8217;s folder, and enter &amp;#8220;ruby script/server&amp;#8221;. (If you&amp;#8217;re using a Mac or Linux system, you can omit the word &amp;#8220;ruby&amp;#8221; in all such commands, but it won&amp;#8217;t hurt, and is required on Windows.)&lt;/p&gt;
&lt;p&gt;Now open your browser and enter http://localhost:3000 (that&amp;#8217;s port number 3000 on the local server, which goes by the alias localhost). You should see the standard Rails &amp;#8220;Welcome Aboard&amp;#8221; page. (If not, look at the log in the output window to see what the error messages are.)&lt;/p&gt;
&lt;p&gt;In the Welcome Aboard page in your browser, click on the link &amp;#8220;About Your Application&amp;#8217;s Environment&amp;#8221;. You should see a list of versions that shows that you have Ruby 1.8.6 and version 2.0.2 of the various Rails components.&lt;/p&gt;
&lt;p&gt;If you see an error message, it probably means that either your database server is not running, or you didn&amp;#8217;t create the labs_development database, or you didn&amp;#8217;t create the seminar user with the seminar password, or you didn&amp;#8217;t edit the database.yml file properly.&lt;/p&gt;
&lt;p&gt;Assuming all is well, now you want to get rid of this welcome page, because from this point forward it will only get in your way. Delete the file index.hmtl in your application&amp;#8217;s &amp;#8220;public&amp;#8221; folder. (Or rename it to something like index.html.old if you want to be able to restore it for old time&amp;#8217;s sake.)&lt;/p&gt;
&lt;p&gt;Now refresh the page in your browser and you should see:&lt;/p&gt;
&lt;pre&gt;
	Routing Error

	No route matches &quot;/&quot; with {:method=&amp;gt;:get}
&lt;/pre&gt;
&lt;p&gt;Congratulations! You&amp;#8217;ve gone to a lot of trouble to create an application that does absolutely nothing except display an error message.&lt;/p&gt;
&lt;p&gt;But actually you&amp;#8217;ve done a great deal. You now have all the infrastructure in place for your Rails application. Now the fun starts, and you&amp;#8217;ll be able to start adding real pages very quickly.&lt;/p&gt;
&lt;p&gt;This error message is Rails&amp;#8217; way of saying that it doesn&amp;#8217;t know what it is supposed to do when you access the root page of your site, because we haven&amp;#8217;t defined anything yet and we&amp;#8217;ve taken away the default index.html page.&lt;/p&gt;
&lt;h2&gt;3. Creating your first pages&lt;/h2&gt;
&lt;h3&gt;a. Creating your first controller and views&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;re going to start by doing the absolute minimum of work required to create a few static web pages within the Rails structure. As we progress through the lessons, we&amp;#8217;ll evolve these to more sophisticated versions that are more representative of good Rails design practices.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re going to create a site with four pages:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Home&lt;/li&gt;
	&lt;li&gt;About Us&lt;/li&gt;
	&lt;li&gt;Contact Us&lt;/li&gt;
	&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To do so, we need a controller, which we&amp;#8217;re going to call &amp;#8220;static&amp;#8221; because we&amp;#8217;re using it to access these static pages. Each of the four pages will be created by one action in this controller and its corresponding view.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re using NetBeans, right-click on the name of the project in the Projects pane and choose Generate. The Rails Generator dialog opens, and it defaults to the &amp;#8220;controller&amp;#8221;, option, which is what we want. Enter &amp;#8220;static&amp;#8221; for the name, and in the Views field enter &amp;#8220;home about contact resources&amp;#8221;. Click OK, and the generator will create the controller and open it in the editor.&lt;/p&gt;
&lt;p&gt;(If you&amp;#8217;re not using NetBeans, enter &amp;#8220;ruby script/generate controller static home about contact resources&amp;#8221; in a terminal window, at the root of your application.)&lt;/p&gt;
&lt;p&gt;In the file static_controller.rb, you&amp;#8217;ll see four empty methods, one for each action. We can leave them empty for now.&lt;/p&gt;
&lt;p&gt;Now look in the Views folder (inside the apps folder if you&amp;#8217;re not in the NetBeans project view), and you&amp;#8217;ll find a new folder called &amp;#8220;static&amp;#8221;, and inside that you&amp;#8217;ll find the four view files for the pages we&amp;#8217;ve defined.&lt;/p&gt;
&lt;p&gt;To see the changes in the browser, you need to first stop and then restart the server. Using NetBeans, click the red X in the the output window labeled &amp;#8220;Mongrel for labs on 3000&amp;#8221; and then click the green double arrow. (If you&amp;#8217;re using the console. type control-C to stop the server and then reissue the script/server command.)&lt;/p&gt;
&lt;p&gt;Now click the refresh button in the browser. Still an error message! That&amp;#8217;s because Rails doesn&amp;#8217;t know which of these pages is our home page. We&amp;#8217;ll fix that in a minute.&lt;/p&gt;
&lt;p&gt;To see the home page, enter this &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;:&lt;/p&gt;
&lt;pre&gt;
	http://localhost:3000/static/home
&lt;/pre&gt;
&lt;p&gt;That tells Rails we want it to run the home action in the static controller. You should now see a page with this exciting content:&lt;/p&gt;
&lt;pre&gt;
	Static#home
	Find me in app/views/static/home.html.erb
&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s the default text Rails puts in the empty view file. Open the file views/static/home.html.erb, and you&amp;#8217;ll see the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; that created this page.&lt;/p&gt;
&lt;p&gt;You can replace that &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; with whatever you&amp;#8217;d like, perhaps:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;h1&amp;gt;Welcome to RailsQuickStart&amp;lt;/h1&amp;gt;
	&amp;lt;p&amp;gt;This is the home page&amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now you can refresh the page and you should see the new text. You can do the same for the other three pages, editing their view files, and entering the corresponding action name in the &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; to access them.&lt;/p&gt;
&lt;h3&gt;b. Adding a layout&lt;/h3&gt;
&lt;p&gt;If you view the source of one of these pages in your browser, you&amp;#8217;ll notice that they aren&amp;#8217;t well-formed &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;. There&amp;#8217;s no doctype, no head section, and so forth &amp;#8212; just the raw &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; for the page. We could add that in each view, but that would involve a lot of repetition.&lt;/p&gt;
&lt;p&gt;A layout file is the answer. Every view gets wrapped in a layout, so the view only needs to provide the information that is unique to that page.&lt;/p&gt;
&lt;p&gt;In your Views folder, you&amp;#8217;ll see an empty folder called &amp;#8220;layouts&amp;#8221;. In the NetBeans projects pane, right-click on layouts, choose New &amp;gt; &lt;span class=&quot;caps&quot;&gt;ERB&lt;/span&gt; file, and name the file application.html. NetBeans will append the .erb suffix. (If you&amp;#8217;re using another editor, you&amp;#8217;ll probably need to specify the full filename, application.html.erb.) This layout file will be used by default for all views, unless there is another layout specific to that controller.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a minimal &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; wrapper you can paste into that file (first delete the useless boilerplate code that NetBeans inserts):&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
	&amp;lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&amp;gt;
	&amp;lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&amp;gt;
	    &amp;lt;head&amp;gt;
	        &amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=UTF-8&quot;/&amp;gt;
	        &amp;lt;title&amp;gt;Page Title Goes Here&amp;lt;/title&amp;gt;
	    &amp;lt;/head&amp;gt;
	    &amp;lt;body&amp;gt;
	        &amp;lt;p&amp;gt;
	            &amp;lt;%= yield %&amp;gt;
	        &amp;lt;/p&amp;gt;
	    &amp;lt;/body&amp;gt;
	&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This is just standard &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;, with the exception of a single line of ERb code:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;%= yield %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This line tells Rails that it should insert the code for the current view at this point.&lt;/p&gt;
&lt;p&gt;Now you can refresh the browser page, and it should look just the same, except that &amp;#8220;Page Title Goes Here&amp;#8221; appears in the browser&amp;#8217;s title bar. If you view the source, you&amp;#8217;ll see that there&amp;#8217;s now a proper &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; structure, courtesy of our layout.&lt;/p&gt;
&lt;h3&gt;c. Add basic navigation links&lt;/h3&gt;
&lt;p&gt;Now we need a way to navigate to each of our pages without having to enter the full &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;. First, let&amp;#8217;s add some simple navigation links, using an &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; list (which we&amp;#8217;ll style a little later).&lt;/p&gt;
&lt;p&gt;Insert the following code in layouts/application.html.erb, immediately after the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag.
	
&lt;pre&gt;
	&lt;ul&gt;
		&lt;li&gt;&amp;lt;%= link_to &amp;#8216;Home&amp;#8217;, {:controller =&amp;gt; &amp;#8216;static&amp;#8217;, :action =&amp;gt; &amp;#8217;home&amp;#8217;} &lt;span&gt;&amp;gt;&lt;/li&gt;
    	&lt;li&gt;&amp;lt;&lt;/span&gt;= link_to &amp;#8216;About Us&amp;#8217;, {:controller =&amp;gt; &amp;#8216;static&amp;#8217;, :action =&amp;gt; &amp;#8217;about&amp;#8217;} &lt;span&gt;&amp;gt;&lt;/li&gt;
    	&lt;li&gt;&amp;lt;&lt;/span&gt;= link_to &amp;#8216;Contact Us&amp;#8217;, {:controller =&amp;gt; &amp;#8216;static&amp;#8217;, :action =&amp;gt; &amp;#8217;contact&amp;#8217;} &lt;span&gt;&amp;gt;&lt;/li&gt;
    	&lt;li&gt;&amp;lt;&lt;/span&gt;= link_to &amp;#8216;Resources&amp;#8217;, {:controller =&amp;gt; &amp;#8216;static&amp;#8217;, :action =&amp;gt; &amp;#8217;resources&amp;#8217;} %&amp;gt;&lt;/li&gt;&lt;/p&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Now refresh the page in the browser, and you&amp;#8217;ll see four ugly links, which at least allow us to easily navigate to each of the four pages.&lt;/p&gt;
&lt;h3&gt;d. Make the root &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; work, and clean up the others&lt;/h3&gt;
&lt;p&gt;Now we can get to each of the pages, but we still get an error if we try to just browse to the root of the site. Let&amp;#8217;s fix this.&lt;/p&gt;
&lt;p&gt;Open the file config/routes.rb (in the Configuration folder in the NetBeans project pane). This default file is mostly full of comments that give you some hints about how to add routes, plus two default routes at the end. The first of those default routes is what has been enabling us to navigate to our pages.&lt;/p&gt;
&lt;p&gt;To make our home view work as the site&amp;#8217;s home page, add the following line immediately above the first default route (so your new line is now the first uncommented line):&lt;/p&gt;
&lt;pre&gt;
     map.root :controller =&amp;gt; &quot;static&quot;, :action =&amp;gt; &quot;home&quot;
&lt;/pre&gt;
&lt;p&gt;This tells Rails that the root, or home, page of our site is accessed via the home action in the static controller. Save the routes.rb file, and now you should be able to browse to //localhost:3000 and see the home page.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s one more bit of cleanup we&amp;#8217;ll do while we&amp;#8217;re in the routes file. Note that when you click on the links to browse to the other pages, you still see /static/ before the page name. This is ugly, and there&amp;#8217;s no need for us to expose the name of our controller to our visitors. Add this line immediately below the map.root line you just added:&lt;/p&gt;
&lt;pre&gt;
     map.connect &quot;:action&quot;, :controller =&amp;gt; &quot;static&quot;
&lt;/pre&gt;
&lt;p&gt;This tells Rails that if there is only a single word after the host name, then it should use that word as the name of the action, and assume that the controller is &amp;#8220;static&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Save the file, and now when you click on the links for each of the pages, you should see the simpler &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; without the word &amp;#8220;static&amp;#8221;.&lt;/p&gt;
&lt;h3&gt;e. Add a stylesheet&lt;/h3&gt;
&lt;p&gt;Now it&amp;#8217;s time to make this all a little prettier by adding a &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; stylesheet. You can use any stylesheet you&amp;#8217;d like, but to follow along with our example, grab the file &amp;#8220;quickstart.css&amp;#8221; (it&amp;#8217;s in the example code for any of the labs, or right-click &lt;a href=&quot;/assets/seminar/labs/quickstart.css&quot;&gt;here&lt;/a&gt; and choose Save Link As&amp;#8230;) and save it in the folder /public/stylesheets.&lt;/p&gt;
&lt;p&gt;Now you need to add a tag to tell the browser to use this stylesheet. Insert the following line in the head section of layouts/application.html.erb:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;%= stylesheet_link_tag 'quickstart.css' %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Note that you don&amp;#8217;t need to specify that this file is in the stylesheets folder; Rails assumes that by default.&lt;/p&gt;
&lt;p&gt;If you refresh the browser now, you should see that the fonts have changed, but it&amp;#8217;s still just as ugly. We need to add some divs in the layout to tell the browser what styles to apply. This is just regular &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;/&lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; stuff, nothing to do with Rails.&lt;/p&gt;
&lt;p&gt;Just grab the updated layout file from the &amp;#8220;labs2_end&amp;#8221; sample application code (or right-click &lt;a href=&quot;/assets/seminar/labs/application.html.erb&quot;&gt;here&lt;/a&gt; and choose Save Link As&amp;#8230;). This layout provides a &amp;#8220;pagewrapper&amp;#8221; div to give the content a defined width and center it; &amp;#8220;header&amp;#8221; and &amp;#8220;navbar&amp;#8221; divs for the top of the page; and a &amp;#8220;footer&amp;#8221; div for the bottom of the page.&lt;/p&gt;
&lt;p&gt;The updated layout also adds an id tag to the navigation list, which we&amp;#8217;ll explain in the next section.&lt;/p&gt;
&lt;p&gt;With the new application layout in place, refresh the browser, and you should finally have something that looks almost like a respectable, if extraordinarily simple, web site.&lt;/p&gt;
&lt;h3&gt;f. Adding in page titles&lt;/h3&gt;
&lt;p&gt;All of our pages have the same &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; title (which appears in the browser&amp;#8217;s title bar), which isn&amp;#8217;t good. Let&amp;#8217;s fix that.&lt;/p&gt;
&lt;p&gt;When we slipped in the new layout file in the previous section, the title tag in the head section was changed to this:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;title&amp;gt;RailsQuickStart: &amp;lt;%= @page_title %&amp;gt;&amp;lt;/title&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This provides the text &amp;#8220;RailsQuickStart:&amp;#8221; as the start of the title for every page, follow by a bit of ERb code that simply inserts the value of the @page_title instance variable. Now we need to set that variable somewhere.&lt;/p&gt;
&lt;p&gt;You might think that the natural place to do this would be in the view files, but that won&amp;#8217;t work; the layout has already rendered the head section of the page by the time it gets to the view file. So we need to set it in the controller.&lt;/p&gt;
&lt;p&gt;So far, all of our controller actions are empty. Now add one line to the home action so it looks like this:&lt;/p&gt;
&lt;pre&gt;
	def home
		@page_title = &quot;Welcome&quot;
	end
&lt;/pre&gt;
&lt;p&gt;and make similar changes to the other three actions. Save the file, and now browse to each of the pages. You&amp;#8217;ll see the appropriate titles now appear in the browser&amp;#8217;s title bar.&lt;/p&gt;
&lt;h3&gt;g. Make the navigation buttons highlight&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;re almost done. There&amp;#8217;s just one little improvement to make. It would be nice if the navigation buttons highlighted to show which page we&amp;#8217;re on.&lt;/p&gt;
&lt;p&gt;A standard &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; technique to make this happen is to use a descendant selector. We apply an id to the navigation ul element, with the value of the id indicating the current page. We&amp;#8217;ve also added an id to each of the navigation links. A &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; rule highlights the navigation link if the navigation link&amp;#8217;s parent has an id that corresponds to the link:&lt;/p&gt;
&lt;pre&gt;
	#home_tab a#home_nav, #about_tab a#about_nav, #subscribe_tab a#subscribe_nav, #resources_tab a#resources_nav, #contact_tab a#contact_nav {
	    background-color: #333;
	}
&lt;/pre&gt;
&lt;p&gt;This is just standard &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt;, nothing to do with Rails. But we need to somehow change the id for the ul tag that wraps the navigation list, depending on which page we&amp;#8217;re on.&lt;/p&gt;
&lt;p&gt;This change has already been slipped in to the layout file we swapped in a couple steps ago. The tag that starts the list looks like this:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;ul id=&quot;&amp;lt;%= @current_tab %&amp;gt;&quot;&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This sets the id for the ul element to the value of the instance variable @current_tab. Now we just need to set that in static_controller, just as we did for the page title. So the controller now looks like this:&lt;/p&gt;
&lt;pre&gt;
	def home
    @page_title = &quot;Home&quot;
    @current_tab = &quot;home_tab&quot;
  end

  def about
    @page_title = &quot;About Us&quot; 
    @current_tab = &quot;about_tab&quot;   
  end

  def contact
    @page_title = &quot;Contact Us&quot;
    @current_tab = &quot;contact_tab&quot;
  end

  def resources
    @page_title = &quot;Resources&quot;
    @current_tab = &quot;resources_tab&quot;
  end
&lt;/pre&gt;
&lt;p&gt;Now the tabs stay highlighted to indicate the page we&amp;#8217;re on.&lt;/p&gt;
&lt;p&gt;It may seem clumsy to have to set the tab and the page title in the controller like this, especially since they&amp;#8217;re really aspects of the view. In a more elaborate design, we could pull these values from a database, so they could be set by a non-technical site administrator and didn&amp;#8217;t clutter up the controller. But for now, this is the simplest way to do it.&lt;/p&gt;
&lt;h2&gt;Moving On&lt;/h2&gt;
&lt;p&gt;That concludes Lab 1. You now have a simple static site, which makes use of a few little bits of Ruby to set and use instance variables, and also uses a layout file to provide the parts that are common to all pages. This gives us a core structure that we can easily extend in the later labs.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:45 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79174-lab-1-set-up-and-static-pages</guid>
          <link>http://www.buildingwebapps.com/item/79174-lab-1-set-up-and-static-pages</link>
        </item>
        
        <item>
          <title>Lab 2. Building a Simple CMS</title>
          <description>&lt;p&gt;In this lab, we&amp;#8217;ll create a mini content management system to enable an administrative user to edit the site&amp;#8217;s content without having to modify the code.&lt;/p&gt;
&lt;p&gt;To start this lab, load the lab2_start folder from the &lt;span class=&quot;caps&quot;&gt;USB&lt;/span&gt; Flash drive. And if you have a question about how any of the code should look after completing these steps, take a look at the labs2_end folder.&lt;/p&gt;
&lt;h2&gt;1. Creating a text management system&lt;/h2&gt;
&lt;h3&gt;a. Using the scaffold generator&lt;/h3&gt;
&lt;p&gt;We can create the heart of the code we&amp;#8217;ll need using the scaffold generator, which creates a database migration, a controller, a model, views, and some test files, all with a single command.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s a graphical interface to the generators built in to NetBeans, but the current version of NetBeans doesn&amp;#8217;t provide the proper interface for the Rails 2 scaffold. So we need to use the terminal. Open a terminal window, change to the root directory of your application, and enter the following command:&lt;/p&gt;
&lt;pre&gt;
ruby script/generate scaffold content_block name:string body:text
&lt;/pre&gt;
&lt;p&gt;(If you&amp;#8217;re not using Windows, you can drop the &amp;#8220;ruby&amp;#8221; preface.)&lt;/p&gt;
&lt;p&gt;This command tells Rails that we want to generate a model called content_blocks, with two fields: one called &amp;#8220;name&amp;#8221;, of type string, and one called &amp;#8220;body&amp;#8221;, of type text. (Strings are limited to 255 characters, whereas text fields can be arbitrarily long.)&lt;/p&gt;
&lt;p&gt;Take a look at the files that the generator created:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;models/content_block.rb &amp;#8212; this is empty for now, but inherits a great deal of capability from ActiveRecord::Base&lt;/li&gt;
	&lt;li&gt;views/content_block/ &amp;#8212; there are four view files here that create the scaffolded admin interface&lt;/li&gt;
	&lt;li&gt;controllers/content_block_controller.rb &amp;#8212; the controller that implements the standard RESTful actions to create, edit, delete, and list the content blocks&lt;/li&gt;
	&lt;li&gt;db/migrate/001_create_content_blocks.rb &amp;#8212; the migration file (in the NetBeans project view, find this at Database Migrations/migrate/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;#8217;s more files in the test directory, but we&amp;#8217;ll ignore those for now.&lt;/p&gt;
&lt;h3&gt;b. Creating the database table&lt;/h3&gt;
&lt;p&gt;The migration file provides the specification for creating the database tables. The t.timestamps line is added automatically, and it creates two fields: created_at and updated_at, which are automatically maintained by Rails. The id field is automatically generated, so it isn&amp;#8217;t mentioned here.&lt;/p&gt;
&lt;p&gt;To actually create the database table, we need to run the migration. In the NetBeans project pane, right-click on the project name and choose Migrate Database &amp;gt; To Current Version. The output window will show the actions that Rails takes. (If you prefer to work from the command line, the command is &amp;#8220;rake db:migrate&amp;#8221;.)&lt;/p&gt;
&lt;p&gt;If you want, you can now examine the database (either with one of many MySQL graphical user interface programs, or by issuing commands with the mysql monitor) and see the table that was created and its fields.&lt;/p&gt;
&lt;h3&gt;c. Using the content blocks&lt;/h3&gt;
&lt;p&gt;Now you can browse to //localhost:3000/content_blocks and you&amp;#8217;ll see the list of content blocks, which isn&amp;#8217;t too interesting because there aren&amp;#8217;t any yet. Click the New Content Block link, and you can now create the first content block. Name it &amp;#8220;welcome&amp;#8221; and enter some text, and click Create. You&amp;#8217;ve now created your first database entry.&lt;/p&gt;
&lt;p&gt;Notice that these pages are not using our default layout. That&amp;#8217;s because the scaffold generator annoyingly creates a new layout for every model, and if there is a layout whose name matches the model, it is used instead of the default layout (which is named application.html.erb). So delete the automatically generated views/layouts/content_blocks.html.erb file, and access //localhost:3000/content_blocks again. Now the admin pages are using the layout.&lt;/p&gt;
&lt;p&gt;Now, how do we use the data from the content block? We could write the code to find a content block by name each time we want to use one, but that would create a lot of redundancy. To avoid this, we&amp;#8217;ll create a small view method, called a helper. There&amp;#8217;s a helper file for each controller.&lt;/p&gt;
&lt;p&gt;Open the file helpers/content_blocks_helper.rb, and add the following code between the two lines that are already there:&lt;/p&gt;
&lt;pre&gt;
	def show_content name
	    content = ContentBlock.find_by_name(name)
	    if content
	        content.body
	    else
	      &quot;No content for #{name}&quot;
	    end
	end
&lt;/pre&gt;
&lt;p&gt;This method first uses the find_by_name method to find the desired content block (Active Record automatically provides such a method for every attribute of every model). If the result is not nil, then the show_content_name method returns the body of the content block. If the result is nil, that means that there was no content block that matched the request name, so a message to that effect is displayed.&lt;/p&gt;
&lt;p&gt;Now open the home page (views/static/home.rhtml.erb) and add the following line:&lt;/p&gt;
&lt;pre&gt;&amp;lt;%= show_content 'welcome' %&amp;gt;&lt;/pre&gt;
&lt;p&gt;Make sure you&amp;#8217;ve saved all the files, and then browse to //localhost:3000. You should now see the content you entered when you created the &amp;#8220;welcome&amp;#8221; content block.&lt;/p&gt;
&lt;h3&gt;d. Using Textile markup&lt;/h3&gt;
&lt;p&gt;We don&amp;#8217;t want our administrative users to have to enter &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; code, especially since they could &amp;#8220;break&amp;#8221; the page if they made an error in the coding. So we&amp;#8217;ll use a simple markup language called &lt;a href=&quot;http://hobix.com/textile/&quot;&gt;Textile&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s two pieces of Ruby code we use to implement this. The first is the gem RedCloth, which you should already have installed. This is the code that translates Textile into &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;. The second is a Rails plugin called &amp;#8220;acts_as_textiled&amp;#8221;, which makes it trivially easy to add Textile markup to our content blocks.&lt;/p&gt;
&lt;p&gt;To add the plugin, open a terminal window at the root of your application and enter the following command:&lt;/p&gt;
&lt;pre&gt;script/plugin install svn://errtheblog.com/svn/plugins/acts_as_textiled&lt;/pre&gt;
&lt;p&gt;How did we know this obscure address to enter? Just do a google search for the name of a plugin, or go to www.agilewebdevelopment.com/plugins, and you can find most anything pretty quickly.&lt;/p&gt;
&lt;p&gt;If you look in the vendor/plugins folder (in NetBeans 6.0.1, you need to use the Files pane, not the Projects pane, to see this folder), you&amp;#8217;ll see a folder into which this software has been installed.&lt;/p&gt;
&lt;p&gt;Now, to turn our content_block body into textile markup, all you need to do is open the file models/content_block.rb, and add this line:&lt;/p&gt;
&lt;pre&gt;acts_as_textiled :body&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s it. This plugin is smart enough to automatically display the Textile markup source when you&amp;#8217;re displaying the body in a form field, as we are in the admin pages, but to render it into &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; anywhere else you use the body.&lt;/p&gt;
&lt;p&gt;You&amp;#8217;ll need to stop and restart the server before the plugin will function, though. Plugins add their own initialization code that is run when Rails starts.&lt;/p&gt;
&lt;p&gt;Now browse to //localhost:3000/content_blocks, click the Edit link for the welcome block you created earlier, and edit the body to include some Textile markup. For example, surround a word or phrase with asterisks to make it bold, or with underscores to make it italic. Click Update to save the modified content block.&lt;/p&gt;
&lt;p&gt;In the list of content blocks, you&amp;#8217;ll notice that the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; markup is now shown. That&amp;#8217;s because the code generated by the scaffold to generate the list uses the &amp;lt;%=h %&amp;gt; wrapper for the attributes it display. The &amp;#8220;h&amp;#8221; causes &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; elements to be rendered as &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; entities, so the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; text is displayed rather than interpreted by the browser. This is a security measure to avoid cross-site scripting attacks, but it has the side effect here of showing us the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; markup that acts_as_textiled, with help from RedCloth, generated from the text in our content block.&lt;/p&gt;
&lt;p&gt;Now refresh the home page, and you should see your welcome text rendered with bold, or italic, or whatever you&amp;#8217;ve specified in the content block&amp;#8217;s Textile markup.&lt;/p&gt;
&lt;h2&gt;2. Adding a File Uploader&lt;/h2&gt;
&lt;h3&gt;a. Creating the asset scaffold&lt;/h3&gt;
&lt;p&gt;Now we have the basis for a content management system for text, but what about images or other uploaded files? We need a file uploader.&lt;/p&gt;
&lt;p&gt;We can start by scaffolding another model, which we&amp;#8217;ll call asset:&lt;/p&gt;
&lt;pre&gt;script/generate scaffold asset name:string&lt;/pre&gt;
&lt;p&gt;Go ahead and run the scaffold generator, but don&amp;#8217;t run the migration yet, because we&amp;#8217;re going to need to add some fields to it.&lt;/p&gt;
&lt;p&gt;While we&amp;#8217;re thinking of it, go ahead and delete views/layouts/assets.html.erb, the pesky layout file that the scaffold generator created but that whose only effect will be to prevent our usual layout from being used.&lt;/p&gt;
&lt;h3&gt;b. Adding attachment_fu&lt;/h3&gt;
&lt;p&gt;Uploading files involves a little complexity but, as usual, there&amp;#8217;s a plugin that handles most of it for us: in this case, it&amp;#8217;s &amp;#8220;attachment_fu&amp;#8221;. First, install the plugin with the following command in your terminal:&lt;/p&gt;
&lt;pre&gt;script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu/&lt;/pre&gt;
&lt;p&gt;This is a very capable plugin, which will take care of generating thumbnails of uploaded images and lots of other things, but we&amp;#8217;re going to use it in a pretty basic way.&lt;/p&gt;
&lt;p&gt;You can look at the readme file in the vendor/plugins/attachment_fu folder to see what some of its requirements and capabilities are. From this, we can see that we need to provide a number of fields in our database model that is going to manage the uploaded files.&lt;/p&gt;
&lt;p&gt;Open the migration file that the scaffold command generated, db/migrate/002_create_assets.rb, and add the following two lines after the line &lt;code&gt;t.string :name&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;
  t.integer :parent_id, :size, :width, :height
  t.string :content_type, :filename, :thumbnail
&lt;/pre&gt;
&lt;p&gt;This adds four integer fields and three string fields that are required for attachment_fu to do its work. Now run the migration to create the table, and restart the server so the plugin can run its initialization code.&lt;/p&gt;
&lt;h3&gt;c. Updating the model&lt;/h3&gt;
&lt;p&gt;Now we need to tell the asset model that we want it to use attachment_fu. Add the following two lines in the file model/asset.rb:&lt;/p&gt;
&lt;pre&gt;
  has_attachment :storage =&amp;gt; :file_system
  validates_as_attachment
&lt;/pre&gt;
&lt;p&gt;The first line tells Rails to use attachment_fu for this model, and to store the uploaded assets in the file system (not in the database). The second line tells it to validate the entered data (such as the filename).&lt;/p&gt;
&lt;h3&gt;d. Updating the views&lt;/h3&gt;
&lt;p&gt;That&amp;#8217;s all it takes for our asset model to deal with uploaded files. Now we need to add a little code to the view files to allow us to choose the filename. Open the file views/new.html.erb. Add the following code after the name field so we&amp;#8217;ll have a place to enter the filename:&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Asset File&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
       &amp;lt;%= f.file_field :uploaded_data %&amp;gt;
    &amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The file_field helper will display a text field for the file name and a button that will bring up a file browser dialog so the user can easily pick a file.&lt;/p&gt;
&lt;p&gt;This won&amp;#8217;t actually work, however, without one more change. When we submit the form, it needs to use a special &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; format called multipart, so the form &lt;span class=&quot;caps&quot;&gt;POST&lt;/span&gt; can provide not only the filename but the actual file contents. Edit the form_for line so that it looks like this:&lt;/p&gt;
&lt;pre&gt;&amp;lt;% form_for(@asset, :html =&amp;gt; { :multipart =&amp;gt; true }) do |f| %&amp;gt;&lt;/pre&gt;
&lt;p&gt;Now repeat these changes in the file views/edit.html.erb, so you&amp;#8217;ll be able to edit assets after they have been created.&lt;/p&gt;
&lt;p&gt;Finally, we want to see the asset after we&amp;#8217;ve uploaded it, so open the file views/show.html.erb, and after the line &lt;code&gt;&amp;lt;%=h @asset.name %&amp;gt;&lt;/code&gt;, add this line:&lt;/p&gt;
&lt;pre&gt;&amp;lt;%= image_tag @asset.public_filename %&amp;gt;&lt;/pre&gt;
&lt;p&gt;This uses the image_tag helper, which generates a standard &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; img tag. We use the method public_filename, which is provided by attachment_fu to give us the externally accessible filename for the image.&lt;/p&gt;
&lt;p&gt;Now you can browse to //localhost:3000/assets, click New asset, enter a name (we&amp;#8217;ll use &amp;#8220;logo&amp;#8221; for our example), click the Browse button to locate an image file, and finally click Create. The file is uploaded, and the database record to associate it with the name (and store other information about the file) is created. The image should then be displayed on the &amp;#8220;show&amp;#8221; scaffold page.&lt;/p&gt;
&lt;h3&gt;e. Using the image&lt;/h3&gt;
&lt;p&gt;Now we&amp;#8217;ll add a helper, much like the one we created for the content blocks, to make it easy to display images. Add the following code to the file helpers/assets_helper.rb:&lt;/p&gt;
&lt;pre&gt;
   def show_asset name
      asset = Asset.find_by_name(name)
      if asset
         image_tag asset.public_filename
      else
         &quot;No content for #{name}&quot;
      end
   end
&lt;/pre&gt;
&lt;p&gt;Now, assuming you&amp;#8217;ve uploaded an image and named the asset &amp;#8220;logo&amp;#8221;, you can add the following code to the home page:&lt;/p&gt;
&lt;pre&gt;&amp;lt;%= show_asset 'logo' %&amp;gt;&lt;/pre&gt;
&lt;p&gt;And the image should be displayed there.&lt;/p&gt;
&lt;h2&gt;3. Adding an admin page and log-in&lt;/h2&gt;
&lt;h3&gt;a. Creating an admin page&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;ve been entering the full &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; to get to the admin pages for content blocks and assets, and we should have an easier way. Let&amp;#8217;s create an admin page.&lt;/p&gt;
&lt;p&gt;In the file static_controller.rb, add the empty method for an admin page:&lt;/p&gt;
&lt;pre&gt;
	def admin
	end
&lt;/pre&gt;
&lt;p&gt;And then create a file named admin.html.erb in the views/static folder. (In NetBeans, right-click on the views/static folder, choose New &amp;gt; &lt;span class=&quot;caps&quot;&gt;ERB&lt;/span&gt;, and enter the name &amp;#8220;admin.html&amp;#8221;. NetBeans will add the .erb suffix.)&lt;/p&gt;
&lt;p&gt;Open the file views/static/admin.html.erb, and add the following:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;h1&amp;gt;Site Admin Dashboard&amp;lt;/h1&amp;gt;
	&amp;lt;ul&amp;gt;
	  &amp;lt;li&amp;gt;&amp;lt;%= link_to 'Content Block Admin', content_blocks_path %&amp;gt;&amp;lt;/li&amp;gt;
	  &amp;lt;li&amp;gt;&amp;lt;%= link_to 'Asset Admin', assets_path %&amp;gt;&amp;lt;/li&amp;gt;  
	&amp;lt;/ul&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now add a link to this admin page to the layout, which you might put in the navigation bar, or in the footer:&lt;/p&gt;
&lt;pre&gt;&amp;lt;%= link_to 'Admin', :controller =&amp;gt; 'static', :action =&amp;gt; 'admin' %&amp;gt;&lt;/pre&gt;
&lt;p&gt;Now you can browse to the admin page when you want to access the content blocks and assets administration pages.&lt;/p&gt;
&lt;h3&gt;b. Adding authentication&lt;/h3&gt;
&lt;p&gt;We now have the heart of a content management system. But we&amp;#8217;ve also made it possible for any visitor to modify the content of our site! Clearly we need to require log-in (authentication) before accessing these functions.&lt;/p&gt;
&lt;p&gt;As usual, there&amp;#8217;s a plugin that takes care of all the dirty work for us. Let&amp;#8217;s install restful_authentication:&lt;/p&gt;
&lt;pre&gt;
  script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/
&lt;/pre&gt;
&lt;p&gt;You can view the readme in vendor/plugins/restful_authentication to learn more about it.&lt;/p&gt;
&lt;p&gt;This plugin provides a generator of its own to help us set things up, so let&amp;#8217;s run it:&lt;/p&gt;
&lt;pre&gt;
	script/generate authenticated user sessions
&lt;/pre&gt;
&lt;p&gt;In this command, &amp;#8220;authenticated&amp;#8221; is the name of the generator; &amp;#8220;user&amp;#8221; is the name of the model that will store user names and passwords; and &amp;#8220;sessions&amp;#8221; is the name of the controller that will manage user sessions.&lt;/p&gt;
&lt;p&gt;This generator creates a migration for us, but we need to run the migration, so go ahead and migrate the database to the current version.&lt;/p&gt;
&lt;p&gt;Finally, to make the methods provided by this plugin available in your controllers, you need to add the following line to the file controllers/application.rb:&lt;/p&gt;
&lt;pre&gt;include AuthenticatedSystem&lt;/pre&gt;
&lt;p&gt;The application controller is &amp;#8220;mixed in&amp;#8221; to all other controllers, so any code you put here is available to all controllers. So by putting the &amp;#8220;include&amp;#8221; statement for the authenticated system here, you&amp;#8217;re effectively adding that code to every controller.&lt;/p&gt;
&lt;h3&gt;c. Adding user management&lt;/h3&gt;
&lt;p&gt;Before we can have log-in, we need to have users, so let&amp;#8217;s add some user management.&lt;/p&gt;
&lt;p&gt;The authenticated generator we ran in the previous section gave us a users controller and a view for creating users, so browse to //localhost:3000/users/new and create a login for yourself.&lt;/p&gt;
&lt;p&gt;The generator also created a sessions controller, which is what handles the actual log in and log out operations. Logging in is creating a new session, and logging out is destroying a session. It&amp;#8217;s convenient to have shortcuts we can use to refer to these, so let&amp;#8217;s add some named routes. Add the following lines to the top of the config/routes.rb file:&lt;/p&gt;
&lt;pre&gt;
  map.logout '/logout', :controller =&amp;gt; 'sessions', :action =&amp;gt; 'destroy'
  map.login '/login', :controller =&amp;gt; 'sessions', :action =&amp;gt; 'new'
&lt;/pre&gt;
&lt;p&gt;This gives us two things. The text &amp;#8216;/logout&amp;#8217; and &amp;#8216;/login&amp;#8217; makes those simple URLs work. And the words after the &amp;#8220;map.&amp;#8221; provide shortcuts we can refer to anywhere in our code when we want to generate that &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;, using either login_path, for example, to generate a relative &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;, or login_url, to generate a full, absolute &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s add a log-in button to the navigation bar. Open views/layouts/application.html.erb, and add this code to the end of the list of links that creates the nav bar:&lt;/p&gt;
&lt;pre&gt;
    &amp;lt;li&amp;gt;&amp;lt;% if logged_in? %&amp;gt;&amp;lt;%= link_to &quot;Log Out&quot;, logout_path %&amp;gt;
    &amp;lt;% else %&amp;gt;&amp;lt;%= link_to &quot;Log In&quot;, login_path %&amp;gt;&amp;lt;% end %&amp;gt;&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The restful_authentication plugin provides us with a &amp;#8220;logged_in?&amp;#8221; method that we can use to determine if any user is logged in. If someone is logged in, then we display a log out link; if not, we display a log in link.&lt;/p&gt;
&lt;h3&gt;d. Using authentication to protect the admin pages&lt;/h3&gt;
&lt;p&gt;Now we have our entire authentication system in place, but we haven&amp;#8217;t actually used it to require login for any pages. We need to invoke the authentication feature for the pages for which we want to require log-in.&lt;/p&gt;
&lt;p&gt;Open the file controllers/static_controller.rb, and add the following line in the admin method:&lt;/p&gt;
&lt;pre&gt;
   login_required
&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s all it takes to protect that method. (&amp;#8220;login_required&amp;#8221; is another method provided for us by the restful_authentication plugin.) Try accessing //localhost:3000/admin, and you should be presented with a login screen. Go ahead and log in with the credentials you created in the previous step, and you should see the Log In button in the nav bar change to Log Out. Log out now so we can continue exercising the authentication.&lt;/p&gt;
&lt;p&gt;Note that while we&amp;#8217;ve protected the admin page, you can still browse directly to the content blocks and assets pages, as well as create users, if you know the URLs. Let&amp;#8217;s fix that.&lt;/p&gt;
&lt;p&gt;Open the file controllers/content_blocks_controller.rb, and add the following line right after the first line that defines the controller:&lt;/p&gt;
&lt;pre&gt;
	before_filter :login_required
&lt;/pre&gt;
&lt;p&gt;This invokes the same method that we added to static_controller/admin, but because we&amp;#8217;ve put it in a before_filter, it is executed as part of every method in the controller, protecting all of them.&lt;/p&gt;
&lt;p&gt;Make the same change to the assets controller and the users controller.&lt;/p&gt;
&lt;h2&gt;What&amp;#8217;s Next?&lt;/h2&gt;
&lt;p&gt;We&amp;#8217;re done! You now have a working, albeit somewhat primitive, content management system. To make this a more full-featured &lt;span class=&quot;caps&quot;&gt;CMS&lt;/span&gt;, some things you might want to add include:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;User admin pages, so you can list the users, delete users, and enable users to request new passwords&lt;/li&gt;
	&lt;li&gt;A page database, so the page title, metatags such as description and keywords, tab to be highlighted, and content blocks and images to be displayed, could be stored in a database.&lt;/li&gt;
	&lt;li&gt;An image processing library, such as Image Magick, so attachment_fu can generate thumbnails and other image sizes from uploaded images.&lt;/li&gt;
&lt;/ul&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:45 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79175-lab-2-building-a-simple-cms</guid>
          <link>http://www.buildingwebapps.com/item/79175-lab-2-building-a-simple-cms</link>
        </item>
        
        <item>
          <title>Lab 3. The Resources Page, with Ajax</title>
          <description>&lt;p&gt;In this lab, we&amp;#8217;ll create a database-driven Resources page for our site that presents a list of links. We&amp;#8217;ll also illustrate how you can use Ajax to allow the sorting of the list to be changed without reloading the page.&lt;/p&gt;
&lt;h2&gt;1. Creating the links and categories database&lt;/h2&gt;
&lt;h3&gt;a. Creating the models&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;re going to need two models: one for the links, and one for the categories. As in the previous lab, we&amp;#8217;ll use the scaffold command to create the models and the first cut at the admin pages for them.&lt;/p&gt;
&lt;p&gt;For the links, we need a name, a &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;, and a description, so issue the following command (in a terminal window at the root of your application):&lt;/p&gt;
&lt;pre&gt;
	ruby script/generate scaffold link name:string url:string description:text
&lt;/pre&gt;
&lt;p&gt;The categories model needs only a name, so issue this command:&lt;/p&gt;
&lt;pre&gt;
	ruby script/generate scaffold category name:string
&lt;/pre&gt;
&lt;h3&gt;b. Creating the join table&lt;/h3&gt;
&lt;p&gt;A link can have many categories, and a category can have many links, so we need a many-to-many relationship, which we&amp;#8217;ll implement with has_and_belongs_to_many. To do so, we need a join table, which records the relationships between the two models.&lt;/p&gt;
&lt;p&gt;Rails does not provide a generator for join tables, so we need to create a migration and then enter into the migration file the specifications for the join table. In NetBeans, you can right-click on the project name, choose Generate, and select Migration from the pop-up menu. Then enter the name for the migration; it doesn&amp;#8217;t matter what this is, but we&amp;#8217;d suggest something like &lt;code&gt;add_link_category_join&lt;/code&gt;. (If you want to do this from the terminal, enter the command &lt;code&gt;ruby script/generate migration add_link_category_join&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;Now open the migrations folder (Database Migrations in the NetBeans projects view, or db/migrate in the file system), and edit the empty migration file that the generator created. It should be named something like &lt;code&gt;006_add_link_category_join.rb&lt;/code&gt;. (If you&amp;#8217;re using NetBeans, this file will be automatically opened for you.)&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;self.up&lt;/code&gt; method, insert the following code:&lt;/p&gt;
&lt;pre&gt;
   create_table :categories_links, :id =&amp;gt; false do |t|
      t.integer :link_id
      t.integer :category_id
   end
&lt;/pre&gt;
&lt;p&gt;The first line of this migration code tells Rails the name of the table you want to create, and that you don&amp;#8217;t want it to automatically generate an ID field. Join tables don&amp;#8217;t have an ID of their own, just the two foreign IDs of the tables they are linking together. The &lt;code&gt;do |t|&lt;/code&gt; begins the code block that defines the table, and gives the arbitrary name &lt;code&gt;t&lt;/code&gt; to the object that is used to create the table (the internals of this are a little complex, and you can just think of this as a magic incantation that you can use in every migration).&lt;/p&gt;
&lt;p&gt;The names of the table and the fields are not arbitrary, but are required for Rails to be able to make all the connections. This is an example of convention over configuration. The table name is the two model names, joined by an underscore, in alphabetical order. The field names are the model names with &lt;code&gt;_id&lt;/code&gt; appended.&lt;/p&gt;
&lt;p&gt;Now add this line to the &lt;code&gt;self.down&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;
	drop_table :categories_links
&lt;/pre&gt;
&lt;p&gt;This allows you to migrate back down if desired.&lt;/p&gt;
&lt;p&gt;Now run all three migrations. (In NetBeans, right-click on the project name, choose Migrate Database &amp;gt; To Current Version, or in the terminal enter &lt;code&gt;rake db:migrate&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;c. Setting up the association&lt;/h3&gt;
&lt;p&gt;To make the association work, we need to tell Rails how we want these two models associated. To do so, add this line to models/link.rb:&lt;/p&gt;
&lt;pre&gt;
	has_and_belongs_to_many :categories
&lt;/pre&gt;
&lt;p&gt;and this line to models/category.rb:&lt;/p&gt;
&lt;pre&gt;
	has_and_belongs_to_many :links
&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s all you need to do for Rails to know to use the join table and associate the two models. Whenever you save a Link object for which one or more categories has been specified, the appropriate entries will automatically be written to the join table.&lt;/p&gt;
&lt;h2&gt;2. Fleshing out the link admin&lt;/h2&gt;
&lt;h3&gt;a. Adding a category picker to the link views&lt;/h3&gt;
&lt;p&gt;The scaffolded views that Rails produces for us don&amp;#8217;t deal with associations between models; they just work with one model at a time. This is fine for the category admin pages, which allow us to create and edit category names. But for the links admin pages, we need to add the ability to choose categories to associate with each link.&lt;/p&gt;
&lt;p&gt;We want to add a select element to the new link form that lists all the categories. Here&amp;#8217;s the line of code that generates the desired select, which you should add to the file views/links/new.html.erb:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Category&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
   &amp;lt;%= f.collection_select :category_ids, @categories, :id, :name, {}, {:name =&amp;gt; 'link[category_ids][]', :multiple =&amp;gt; true} %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This is perhaps the most obscure bit of code we&amp;#8217;ve seen yet, and it is something that is not as straightforward in Rails as it should be. The collection_select helper creates a select field, using an array of objects as the source for the select list. It&amp;#8217;s a powerful helper, but the parameters are a little obtuse.&lt;/p&gt;
&lt;p&gt;The first parameter, &amp;#8220;:category_ids&amp;#8221;, specifies the field that we want to set. In this case, since what we want to set is actually the id in a join table, the peculiar syntax of &amp;#8220;category_ids&amp;#8221; instead of &amp;#8220;categories&amp;#8221; is required. The second parameter, &amp;#8220;@categories&amp;#8221;, is the name of the array that has the objects used to populate the select list. We&amp;#8217;ll set this in the controller. The next two fields specify the attributes of the objects in this array to be used for the value returned by the select list, and for the text to be displayed in the select list. The empty hash is a placeholder for an options parameter that we&amp;#8217;re not using.&lt;/p&gt;
&lt;p&gt;The final parameter is the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; options hash. First, we set the name that will be used when posting the form data. This obscure syntax is what is required for the Rails &amp;#8220;save&amp;#8221; operation (&amp;#8220;@link.save&amp;#8221; in the controller) to properly update the join table. This is a peculiarity of using a has_and_belongs_to_many relationship with a select list. The final item is setting the standard &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; option to allow multiple items in the select list to be chosen, so a link can be assigned to multiple categories.&lt;/p&gt;
&lt;p&gt;How would you figure all this out? It&amp;#8217;s tricky. We couldn&amp;#8217;t figure it out from the documentation. But some google searching on terms such as &amp;#8220;habtm collection_select&amp;#8221; found some helpful notes. Sometimes that&amp;#8217;s what it takes. You can also ask questions about such things in the rubyonrails-talk Google group.&lt;/p&gt;
&lt;p&gt;Add the category selector to the edit view as well, so you can edit existing categories.&lt;/p&gt;
&lt;p&gt;Note: in the code on the &lt;span class=&quot;caps&quot;&gt;USB&lt;/span&gt; Flash drive, there is an error in this code in the &amp;#8220;new&amp;#8221; view, but it is correct in the &amp;#8220;edit&amp;#8221; view.&lt;/p&gt;
&lt;p&gt;If you try to use these views now, you&amp;#8217;ll get an error, because we haven&amp;#8217;t yet set the instance variable &amp;#8220;@categories&amp;#8221;.&lt;/p&gt;
&lt;h3&gt;b. Providing the list of categories in the links controller&lt;/h3&gt;
&lt;p&gt;In the links controller, we need to prepare the list of categories to be used in the select list. Just add this line of code to the &amp;#8220;new&amp;#8221; method in /controllers/links_controller.rb:&lt;/p&gt;
&lt;pre&gt;
   @categories = Category.find(:all, :order =&amp;gt; :name)
&lt;/pre&gt;
&lt;p&gt;This statement finds all the categories, sorts them by name, and puts them (as an array) in the instance variable &amp;#8220;@categories&amp;#8221;. The select list in the view then draws from this instance variable to populate the select.&lt;/p&gt;
&lt;p&gt;Since we use the same instance variable in the edit view, you need to add this line of code to the edit action in the links_controller as well. And there&amp;#8217;s two more places where we need it too: at the start of the &amp;#8220;else&amp;#8221; clause in the create action and the update action. That&amp;#8217;s because, if there&amp;#8217;s a validation failure when creating or editing the link, the form will be displayed again, and it will need the instance variable to populate the select list.&lt;/p&gt;
&lt;h3&gt;c. Adding links on the admin pages&lt;/h3&gt;
&lt;p&gt;To provide easy access to our new link and category admin, let&amp;#8217;s add a couple links to our admin page. Add to the list in /views/static/admin.html.erb:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;li&amp;gt;&amp;lt;%= link_to 'Links Admin', links_path %&amp;gt;&amp;lt;/li&amp;gt;
   &amp;lt;li&amp;gt;&amp;lt;%= link_to 'Categories Admin', categories_path %&amp;gt;&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To test all this, create a few categories, and then create a few links. You should see the list of categories in the select list.&lt;/p&gt;
&lt;h3&gt;d. Protecting the link and category admin pages&lt;/h3&gt;
&lt;p&gt;One last step for the link and category admin: we don&amp;#8217;t want site visitors to access these admin pages, so just as we did for the other administrative controllers, add this line at the top of the links and categories controllers:&lt;/p&gt;
&lt;pre&gt;
   before_filter :login_required
&lt;/pre&gt;
&lt;h2&gt;3. Creating the Resources page from the database&lt;/h2&gt;
&lt;h3&gt;a. Adding the resources_page view&lt;/h3&gt;
&lt;p&gt;Now our admin features for links are in place, and we need to generate a page that displays them. We&amp;#8217;d like to add this to our links controller and views, to keep the link-related functions together. But the view that shows a list of links, &amp;#8220;index&amp;#8221;, is an admin view. So we need to add a new view for the public part of the site, which we&amp;#8217;ll call &amp;#8220;resources_page&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Create a new file in the views/links folder, called &amp;#8220;resources_page.html.erb&amp;#8221; (remember that NetBeans adds the &amp;#8220;.erb&amp;#8221; for you when you create a new erb file). The simplest thing we can do is to just list out all the links, assuming we have an instance variable that the controller has prepared for us:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;h1&amp;gt;Resources&amp;lt;/h1&amp;gt;
   &amp;lt;ul&amp;gt;
      &amp;lt;% @resources.each do |resource| %&amp;gt;
         &amp;lt;li&amp;gt;&amp;lt;%= link_to resource.name, resource.url %&amp;gt;&amp;lt;br /&amp;gt;
             &amp;lt;%= resource.description %&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;% end %&amp;gt;
   &amp;lt;/ul&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This code uses the &amp;#8220;each&amp;#8221; iterator to step through each of the items in the @links array, and then uses the &amp;#8220;link_to&amp;#8221; helper to create a link to each one. We use the name attribute as the link text, and the url attribute as the actual link, and then show the description on the next line. (Note that this code assumes that your links, as entered into the database, include &amp;#8220;http://&amp;#8221;.)&lt;/p&gt;
&lt;p&gt;(An aside: We named the instance variable &amp;#8220;@resource&amp;#8221; so it didn&amp;#8217;t get confusing with too many things called &amp;#8220;link&amp;#8221;, but you could just as well use &amp;#8220;link&amp;#8221; everywhere we&amp;#8217;ve used &amp;#8220;resource&amp;#8221;, and in retrospect this might have been better. It is a Link object we&amp;#8217;re accessing.)&lt;/p&gt;
&lt;p&gt;Now, since we want the resources page to be visible to anyone, even though the other actions, in the resources page are for admin use only, modify the before_filter we added previously to the links_controller as follows:&lt;/p&gt;
&lt;pre&gt;
   before_filter :login_required, :except =&amp;gt; 'resources_page'
&lt;/pre&gt;
&lt;h3&gt;b. Updating the links controller&lt;/h3&gt;
&lt;p&gt;Our newly-created resources_page view expects an instance variable called @resources with an array of Link objects, so we need to create that in the controller. We&amp;#8217;ve added a new view, so we need to add a new method to the controller. Open the file controllers/links_controller.rb, and add the following method (it can go anywhere, but we&amp;#8217;d put it at the end).&lt;/p&gt;
&lt;pre&gt;
   def resources_page
      @page_title = &quot;Resources&quot;
      @current_tab = 'resources_tab'
      @resources = Link.find(:all)
   end
&lt;/pre&gt;
&lt;p&gt;In addition to setting the @resources instance variable, we&amp;#8217;ve set the variables that control the navigation tab highlighting and the page title.&lt;/p&gt;
&lt;h3&gt;c. Enabling the non-RESTful route&lt;/h3&gt;
&lt;p&gt;We need to do one more thing to make this all work. We&amp;#8217;ve added a non-RESTful action (that is, not one of the core group of standard &lt;span class=&quot;caps&quot;&gt;REST&lt;/span&gt; actions) to the links controller. The routing for this controller is set with the statement &lt;code&gt;map.resource :links&lt;/code&gt; in the routes.rb file, and this only supports the standard routes. So we need to tell the routing that we&amp;#8217;ve added another action. Modify that line in config/routes.rb so that it reads:&lt;/p&gt;
&lt;pre&gt;
	map.resources :links, :collection =&amp;gt; {:resources_page =&amp;gt; :get}
&lt;/pre&gt;
&lt;p&gt;This tells the routing system that we&amp;#8217;ve added another action, which acts on the collection of links (rather than on a single link), that the action is called &amp;#8220;resources_page&amp;#8221;, and that the &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; method used to access it is &amp;#8220;get&amp;#8221;. (Alas, our use of the instance variable name &amp;#8220;@resources&amp;#8221; might cause a little more confusion here; that use of the word resources has nothing to do with &amp;#8220;map.resources&amp;#8221;. In that context, resources is referring to the standard set of routes for RESTful resources.)&lt;/p&gt;
&lt;p&gt;With this change, you should now be able to browse to //localhost:3000/links/resources_page and see the list of links that you entered earlier.&lt;/p&gt;
&lt;h3&gt;d. Cleaning out the old static page&lt;/h3&gt;
&lt;p&gt;Our navigation button labeled &amp;#8220;Resources&amp;#8221; still points to the static page we created in Lab 1, so let&amp;#8217;s change that.&lt;/p&gt;
&lt;p&gt;Open views/layouts/application.html.erb, and find the &amp;#8220;link_to_&amp;#8221; statement that creates the &amp;#8220;Resources&amp;#8221; navigation button. Change that line to read:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;li&amp;gt;&amp;lt;%= link_to 'Resources', {:controller =&amp;gt; 'links', :action =&amp;gt; 'resources_page'}, :id =&amp;gt; 'resources_nav' %&amp;gt;&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Finally, delete the resources action from the static controller, since it is no longer used. Now you should be able to click on the Resources navigation button and see the dynamically generated list of links, complete with descriptions (assuming you entered some).&lt;/p&gt;
&lt;p&gt;(A debugging note: if you find that your resources links don&amp;#8217;t work when you click on them, check that you have prefaced them with http:// when you entered them in the database. Otherwise, they will be viewed as relative URLs on your site, not as external sites.)&lt;/p&gt;
&lt;h3&gt;3. Sorting the list by category&lt;/h3&gt;
&lt;p&gt;So far, we haven&amp;#8217;t done anything with the categories we have assigned to the links. Let&amp;#8217;s sort the links by category, and display them under a heading for each category.&lt;/p&gt;
&lt;p&gt;Replace the code in views/links/resources_page.html.erb with the following:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;% @categories.each do |category| %&amp;gt;
      &amp;lt;h2&amp;gt;&amp;lt;%= category.name %&amp;gt;&amp;lt;/h2&amp;gt;
      &amp;lt;ul&amp;gt;
         &amp;lt;% category.links.each do |resource| %&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;%= link_to resource.name, resource.url %&amp;gt;&amp;lt;br /&amp;gt;
                &amp;lt;%= resource.description %&amp;gt;&amp;lt;/li&amp;gt;
         &amp;lt;% end %&amp;gt;
      &amp;lt;/ul&amp;gt;
   &amp;lt;% end %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;ve added an outer loop that iterates through the categories. For each category, we display the category name, and then we iterate through each of the links, using &amp;#8220;category.links&amp;#8221; to find the links associated with the category. This method is added to the category object because we included the &lt;code&gt;has_and_belongs_to_many :links&lt;/code&gt; statement in the category model.&lt;/p&gt;
&lt;p&gt;This code needs the list of categories, so add this line to the resources_page action in the links controller:&lt;/p&gt;
&lt;pre&gt;
   @categories = Category.find(:all, :order =&amp;gt; :name)
&lt;/pre&gt;
&lt;p&gt;Now refresh the resources page, and you&amp;#8217;ll see the links listed by category. If a link is assigned to more than one category, it will appear in more than one place.&lt;/p&gt;
&lt;h2&gt;3. Adding a touch of Ajax&lt;/h2&gt;
&lt;p&gt;Suppose you wanted to give the viewer the option of dividing the list by category or not. We could create two different resources pages with the two different views. Or we could pass a query string parameter to the view that would choose one option or the other. But we&amp;#8217;ll take a more sophisticated approach, which may be overkill in this case but allows us to illustrate how Ajax works: updating how the list is displayed, based on a check box, without reloading the entire page.&lt;/p&gt;
&lt;h3&gt;a. Adding a sorting option to the view&lt;/h3&gt;
&lt;p&gt;As a first step, let&amp;#8217;s put in both forms of our view code, with a parameter choosing which is displayed. The new view (except for the H1 heading at the top) looks like this:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;% if params[:divide_by_category] == '1' %&amp;gt;
      &amp;lt;% @categories.each do |category| %&amp;gt;
         &amp;lt;h2&amp;gt;&amp;lt;%= category.name %&amp;gt;&amp;lt;/h2&amp;gt;
         &amp;lt;ul&amp;gt;
             &amp;lt;% category.links.each do |resource| %&amp;gt;
               &amp;lt;li&amp;gt;&amp;lt;%= link_to resource.name, resource.url %&amp;gt;&amp;lt;br /&amp;gt;
                   &amp;lt;%= resource.description %&amp;gt;&amp;lt;/li&amp;gt;
	         &amp;lt;% end %&amp;gt;
         &amp;lt;/ul&amp;gt;
      &amp;lt;% end %&amp;gt;
   &amp;lt;% else %&amp;gt;
      &amp;lt;ul&amp;gt;
         &amp;lt;% @resources.each do |resource| %&amp;gt;
            &amp;lt;li&amp;gt;&amp;lt;%= link_to resource.name, resource.url %&amp;gt;&amp;lt;br /&amp;gt;
                &amp;lt;%= resource.description %&amp;gt;&amp;lt;/li&amp;gt;
	     &amp;lt;% end %&amp;gt;
	  &amp;lt;/ul&amp;gt;
   &amp;lt;% end %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This is simply our two versions of the view code, with an if statement choosing which version is used.&lt;/p&gt;
&lt;p&gt;Now when you browse to the Resources page, you&amp;#8217;ll see the list as we first rendered it, without a division into categories. But if you append the parameter to the &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;, so the complete &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; is now:&lt;/p&gt;
&lt;pre&gt;
	//localhost:3000/links/resources_page?divide_by_category=1
&lt;/pre&gt;
&lt;p&gt;Then you&amp;#8217;ll see the page divided into categories. We haven&amp;#8217;t done any Ajax here &amp;#8212; we&amp;#8217;re just passing a query string parameter (the thing after the question mark in the &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;) to the view. Such parameters are provided in the params hash, as you can see in the first line of our updated view.&lt;/p&gt;
&lt;h3&gt;b. Moving the list into a partial&lt;/h3&gt;
&lt;p&gt;As a first step toward turning this into an Ajax function, let&amp;#8217;s move the generation of the list into a partial. Cut all the text that you just put into the view, so there&amp;#8217;s nothing there but the H1 heading. Create a new file in views/links, called &amp;#8220;_list.html.erb&amp;#8221;. The leading underscore is what identifies this file as a partial, rather than a full view. Now paste the text you just deleted from the links/resource_page view into the new _list view (or copy it from the listing above).&lt;/p&gt;
&lt;p&gt;To reference the partial from the resources_page view, add this line after the H1 heading:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;%= render :partial =&amp;gt; 'list' %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Note that you don&amp;#8217;t include the underscore, or the suffixes, when you reference a partial.&lt;/p&gt;
&lt;p&gt;Make sure the modified view files are saved, and then refresh the Resources page. It should behave just as before. We haven&amp;#8217;t changed the behavior yet, we&amp;#8217;ve just moved the rendering of the list into a partial, for reasons that will become apparent shortly.&lt;/p&gt;
&lt;h3&gt;c. Updating the view for Ajax&lt;/h3&gt;
&lt;p&gt;To turn this into an Ajax view, we need to do three things:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Add a mini-form with a check box to select the view&lt;/li&gt;
	&lt;li&gt;Wrap the partial in a div so we can apply an ID and thus be able to modify that div using JavaScript&lt;/li&gt;
	&lt;li&gt;Add an observer to monitor the checkbox, request an updated list from the server, and generate a new list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;#8217;s full code for the new view:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;% form_tag '#' do %&amp;gt;
      &amp;lt;%= check_box_tag 'divide_by_category' %&amp;gt;&amp;lt;label&amp;gt;Divide by Category&amp;lt;/label&amp;gt;
   &amp;lt;% end %&amp;gt;

   &amp;lt;div id='list'&amp;gt;
      &amp;lt;%= render :partial =&amp;gt; 'list' %&amp;gt;
   &amp;lt;/div&amp;gt;

   &amp;lt;%= observe_field 'divide_by_category',
       :on =&amp;gt; 'click',
       :url =&amp;gt; {:controller =&amp;gt; 'links', :action =&amp;gt; 'get_list' },
       :with =&amp;gt; 'divide_by_category',
       :update =&amp;gt; 'list' %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;ve created a form that has no action associated with it, because the form will never be submitted (it has no submit button). Inside this form, we have just the check box.&lt;/p&gt;
&lt;p&gt;Then we have the same partial we used in the previous step, just wrapped in a div so we can replace it using JavaScript.&lt;/p&gt;
&lt;p&gt;And finally, the observe_field helper provides all the Ajax magic. This is a very powerful helper, and it can take a lot of parameters. Here&amp;#8217;s the explanation for each of the parameters we&amp;#8217;ve used:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;pre&gt;&amp;#8216;divide_by_category&amp;#8217;&lt;/pre&gt; is the ID of the field that we want to observe&lt;/li&gt;
	&lt;li&gt;&lt;pre&gt;:on =&amp;gt; &amp;#8216;click&amp;#8217; &lt;/pre&gt; specifies that it is the click event on the check box that we want to have trigger the Ajax request.&lt;/li&gt;
	&lt;li&gt;&lt;pre&gt;:url =&amp;gt; {:controller =&amp;gt; &amp;#8216;links&amp;#8217;, :action =&amp;gt; &amp;#8216;get_list&amp;#8217; }&lt;/pre&gt; specifies the controller and action to which the request should be sent when the field changes&lt;/li&gt;
	&lt;li&gt;&lt;pre&gt;:with =&amp;gt; &amp;#8216;divide_by_category&amp;#8217;&lt;/pre&gt; indicates that we want the value of this field sent to the controller when the Ajax request is sent&lt;/li&gt;
	&lt;li&gt;&lt;pre&gt;:update =&amp;gt; &amp;#8216;list&amp;#8217;&lt;/pre&gt; indicates that the div with the ID &amp;#8216;list&amp;#8217; should be updated with the Ajax response&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This observe_field helper is our first bit of JavaScript, using the Prototype framework, and we haven&amp;#8217;t yet included that code in our &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;. So open up layouts/application.html.erb, and add the following line in the head section:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;%= javascript_include_tag :defaults %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This helper creates &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; &lt;code&gt;script&lt;/code&gt; tags to include all the standard Rails JavaScript files.&lt;/p&gt;
&lt;p&gt;You can try the new page, but the check box won&amp;#8217;t do anything. Why? We haven&amp;#8217;t yet provided the &lt;code&gt;get_list&lt;/code&gt; method that the Ajax action is invoking. If you are using Firefox and have Firebug installed, open the Firebug window and click the Console tab with the Resources page displayed, and then click the check box. You&amp;#8217;ll see that a &lt;span class=&quot;caps&quot;&gt;POST&lt;/span&gt; action occurred (that&amp;#8217;s that &lt;span class=&quot;caps&quot;&gt;XML&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; request generated by the observe_field helper) but the result was a 404 error, because there is no such action.&lt;/p&gt;
&lt;h3&gt;d. Adding the Ajax controller action&lt;/h3&gt;
&lt;p&gt;Now that we have everything in place, we just need an action that will return the list.&lt;/p&gt;
&lt;p&gt;Add the following method to the end of the links controller:&lt;/p&gt;
&lt;pre&gt;
   def get_list
      @resources = Link.find(:all)
      @categories = Category.find(:all)
      render :partial =&amp;gt; 'list', :layout =&amp;gt; false
   end
&lt;/pre&gt;
&lt;p&gt;This is the action that will be invoked by the Ajax request. It sets up the two instance variables required by the list partial, and then renders that partial. We need to add the option &lt;code&gt;:layout =&amp;gt; false&lt;/code&gt; because we want just the bare results from this view, not wrapped in any layout file, since we aren&amp;#8217;t using it to render a page; we&amp;#8217;re just returning the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; as the result of the Ajax request, and it will be used to modify the contents of the list div.&lt;/p&gt;
&lt;p&gt;Now try the check box in the Resources page again. It should control whether or not the view is sorted by category or not. Note that the &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; does not change, and there is no page refresh. When the &lt;code&gt;get_list&lt;/code&gt; action returns the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; for the list to the JavaScript code generated by the &lt;code&gt;observer_field&lt;/code&gt; helper, the JavaScript code replaces the contents of the &amp;#8220;list&amp;#8221; div with this new &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;. You can use the Firebug console to see the Ajax request and response each time you click on the check box.&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;You&amp;#8217;ve now created a database to manage a set of links, sorted by category, and a page that allows them to be displayed with or without category sorting using Ajax.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s some enhancements you might consider (and which we&amp;#8217;ll put into our final version of the sample application):&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Don&amp;#8217;t display the category name if there are no links in that category&lt;/li&gt;
	&lt;li&gt;Validate that there is at least a name and a &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; for each link&lt;/li&gt;
	&lt;li&gt;Validate that the category name is not blank&lt;/li&gt;
	&lt;li&gt;Validate the format of the &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;Make the simple &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; &amp;#8220;/resources&amp;#8221; work for the new resources page&lt;/li&gt;
&lt;/ul&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:45 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79176-lab-3-the-resources-page-with</guid>
          <link>http://www.buildingwebapps.com/item/79176-lab-3-the-resources-page-with</link>
        </item>
        
        <item>
          <title>Lab 4. Contact Form, Mailer, and an Anti-Spam Web Service</title>
          <description>&lt;p&gt;In this lab, we&amp;#8217;re going to create a contact form that visitors to our web site can complete. When they submit the contact form, we&amp;#8217;ll both store it in the database and generate an email to ourselves.&lt;/p&gt;
&lt;h2&gt;1. Creating the contact form&lt;/h2&gt;
&lt;h3&gt;a. Scaffolding the contact model&lt;/h3&gt;
&lt;p&gt;If we only wanted to send an email when the contact form was filled in, we wouldn&amp;#8217;t really need to use an Active Record model and save it to the database. But it&amp;#8217;s actually easier to use all the scaffolding and other support that Active Record provides, and it is handy to have the messages stored in the database in case any of them are lost in the email maze.&lt;/p&gt;
&lt;p&gt;So let&amp;#8217;s start by creating a scaffold for contacts:&lt;/p&gt;
&lt;pre&gt;
	ruby script/generate scaffold contact name:string email:string message:text subject:string
&lt;/pre&gt;
&lt;p&gt;As in the previous labs, this will generate the model, controller, views, and test files.&lt;/p&gt;
&lt;p&gt;Now run the migration, as in previous labs, by migrating the database to the current version (&lt;code&gt;rake db:migrate&lt;/code&gt; if you&amp;#8217;re working from the command line.)&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s add some simple validations, since we don&amp;#8217;t want any contact messages that don&amp;#8217;t have all the fields filled in. Add this line to the file models/contact.rb:&lt;/p&gt;
&lt;pre&gt;
   validates_presence_of :name, :email, :subject, :message
&lt;/pre&gt;
&lt;p&gt;You&amp;#8217;d probably want more sophisticated validations in the real world, but this is a start.&lt;/p&gt;
&lt;h3&gt;b. Hooking up the contact form&lt;/h3&gt;
&lt;p&gt;First delete the annoying layout file the scaffold generates (views/layouts/contact.html.erb), so the scaffolded views will use our standard layout.&lt;/p&gt;
&lt;p&gt;We already have a contact button in the navigation bar, but it is pointing to one of our initial static pages, and now we want it to point to the new contact form. So find the link to &amp;#8220;Contact&amp;#8221; in views/layouts/application.html.erb, and change it to:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;li&amp;gt;&amp;lt;%= link_to 'Contact', {:controller =&amp;gt; 'contacts', :action =&amp;gt; 'new'}, :id =&amp;gt; 'contact_nav' %&amp;gt;&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Or, to use the path shortcut that map.resources provides:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;li&amp;gt;&amp;lt;%= link_to 'Contact', new_contact_path, :id =&amp;gt; 'contact_nav' %&amp;gt;&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;These both do the same thing. The first is more explicit, the second is the &amp;#8220;modern&amp;#8221; Rails way of doing things.&lt;/p&gt;
&lt;p&gt;Now you should be able to click on the Contact button in the nav bar and see the new contact form. Go ahead and create a couple of messages so we can see them in a minute in the admin interface.&lt;/p&gt;
&lt;p&gt;Since we&amp;#8217;ve hijacked a form that was meant to be part of the admin interface and are using it for a user-facing form, we need to change some of the linkages. Delete this line from the end of views/contacts/new.html.erb, since we don&amp;#8217;t want visitors to try to get to the list of all messages:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;%= link_to 'Back', contacts_path %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Finally, when a contact form is successfully saved, we don&amp;#8217;t want to redirect to the normal &amp;#8220;show&amp;#8221; action, as the scaffolded pages are set up to do. Instead, let&amp;#8217;s redirect to the home page. Open the file controllers/contacts_controller.rb, and find this section of code in the create action:&lt;/p&gt;
&lt;pre&gt;
   if @contact.save
      flash[:notice] = 'Contact was successfully created.'
      format.html { redirect_to(@contact) }
&lt;/pre&gt;
&lt;p&gt;Let&amp;#8217;s provide a more appropriate flash message, and change the redirect, so the code now reads as follows:&lt;/p&gt;
&lt;pre&gt;
   if @contact.save
      flash[:notice] = 'Thanks for your message. It has been delivered.'
      format.html { redirect_to :controller =&amp;gt; 'static', :action =&amp;gt; 'home' }
&lt;/pre&gt;
&lt;h3&gt;c. Setting up the contact admin&lt;/h3&gt;
&lt;p&gt;We don&amp;#8217;t want site visitors being able to view the list of contact messages, even if they guess the &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; for that page, so we need to add authentication to the contacts controller. But we do need the &amp;#8220;new&amp;#8221; action in this controller to be accessible to visitors, as well as the &amp;#8220;create&amp;#8221; action that is invoked by the form when it is submitted. So add the following line to the start of controllers/contacts_controller.rb:&lt;/p&gt;
&lt;pre&gt;
   before_filter :login_required, :except =&amp;gt; [:new, :create]
&lt;/pre&gt;
&lt;p&gt;We also want to be able to easily access the list of contacts from our admin page, so add this line to the file views/static/admin.html.erb:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;li&amp;gt;&amp;lt;%= link_to 'Contacts Admin', contacts_path %&amp;gt;&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now you can click the Admin button to access the admin page (you&amp;#8217;ll have to log in), and then click the Contacts Admin link to see the list of messages that have been submitted.&lt;/p&gt;
&lt;h2&gt;2. Sending the messages via email&lt;/h2&gt;
&lt;p&gt;Now we need to create the linkage to the mail system, so new contact messages will be sent to us via email as well as added to the database.&lt;/p&gt;
&lt;h3&gt;a. Generating the mailer&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;ve used the scaffold and migration generators in previous labs. There&amp;#8217;s another generator for mailers, which is what we&amp;#8217;re going to use here. Enter the following command in your terminal, at the root of your app:&lt;/p&gt;
&lt;pre&gt;
   ruby script/generate mailer contact_mailer contact_form
&lt;/pre&gt;
&lt;p&gt;(This generator actually works from within NetBeans, so you can use its &lt;span class=&quot;caps&quot;&gt;GUI&lt;/span&gt; version of you prefer.)&lt;/p&gt;
&lt;p&gt;This creates a mailer model, called contact_mailer and its associated view, which we&amp;#8217;ve called contact_form. You can enter multiple views here, for example if you had different kinds of contact forms and wanted to format each message differently.&lt;/p&gt;
&lt;h3&gt;b. Configuring the mailer model&lt;/h3&gt;
&lt;p&gt;Once you&amp;#8217;ve run the generator, you&amp;#8217;ll find a file contact_mailer.rb in your models folder. Open that file, and this is what you should find there:&lt;/p&gt;
&lt;pre&gt;
   def contact_form(sent_at = Time.now)
      @subject    = 'ContactMailer#contact_form'
      @body       = {}
      @recipients = ''
      @from       = ''
      @sent_on    = sent_at
      @headers    = {}
   end
&lt;/pre&gt;
&lt;p&gt;Note that this method is in a class that inherits from ActionMailer::Base, rather than ActiveRecord::Base like all your other models, so it has a different set of characteristics and capabilities.&lt;/p&gt;
&lt;p&gt;We need to fill in some of the blanks in this method. First, we need to pass to this method the parameters from our form, so modify the first line to read:&lt;/p&gt;
&lt;pre&gt;
	def contact_form(name, email, subject, message, sent_at = Time.now)
&lt;/pre&gt;
&lt;p&gt;Note that the one parameter the default method has is &lt;code&gt;sent_at = Time.now&lt;/code&gt;. When a parameter has an assignment to a value like this, it makes it optional to actually pass that parameter. So this is set up so we can pass a sent_at value if we want, but if we don&amp;#8217;t, the current time will be used (which is just fine in most cases).&lt;/p&gt;
&lt;p&gt;Now we need to use these parameters appropriately in the body of the method. First, the subject:&lt;/p&gt;
&lt;pre&gt;
   @subject = subject
&lt;/pre&gt;
&lt;p&gt;Simple enough, we&amp;#8217;re just taking the subject from the variable that we passed in as a parameter, and assigning it to the instance variable that will be passed to the view. Mailer views are special, so this subject text will automatically become the actual subject line of the email message.&lt;/p&gt;
&lt;p&gt;Now for the body, which is a little more obscure. This line should read:&lt;/p&gt;
&lt;pre&gt;
    @body = {:name =&amp;gt; name, :message =&amp;gt; message}
&lt;/pre&gt;
&lt;p&gt;Mailers are odd, and this is one of the odd bits of syntax. The @body instance variable is set to a hash, and each key in the hash is turned into an instance variable of that name. So we&amp;#8217;re creating two instance variables, for use in the body of the message. You might think that we should be able to just add lines of text to explicitly set the name and message instance variables, just as you would in a controller, but remember this is a mailer model, not a controller. The only variables that will be available to our mailer view for explicit use are the ones we define in this body hash. All the other variables are used to create the message headers.&lt;/p&gt;
&lt;p&gt;Now we need to set the recipient:&lt;/p&gt;
&lt;pre&gt;
    @recipients = CONTACT_RECIPIENT
&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;ve chosen here to set it to a constant, rather than a literal value, because we don&amp;#8217;t want a specific email address, which may change in time, deep in our code files. Add this line in your config/environments/development.rb file:&lt;/p&gt;
&lt;pre&gt;
   CONTACT_RECIPIENT = 'yourname@yourdomain.com'
&lt;/pre&gt;
&lt;p&gt;Another benefit of this approach is that you can set a different address in your production.rb environment file. For example, in development, you&amp;#8217;ll probably want contact messages to go to the developer, but in production, they should go to an administrative or sales person.&lt;/p&gt;
&lt;p&gt;Note that if you want messages to go to more than one person, you can provide an array of email addresses:&lt;/p&gt;
&lt;pre&gt;
	CONTACT_RECIPIENT = ['email1@domain.com', 'email2@domain.com', 'email3@domain.com']
&lt;/pre&gt;
&lt;p&gt;And all of these recipients will be included on the &amp;#8220;to&amp;#8221; line. You can also set &amp;#8220;cc&amp;#8221; recipients by adding &amp;#8220;@cc = COPY_RECIPIENT&amp;#8221; in the model.&lt;/p&gt;
&lt;p&gt;Finally, we need to set the &amp;#8220;from&amp;#8221; address. That&amp;#8217;s been passed in as a parameter from our form, so this line should simply read:&lt;/p&gt;
&lt;pre&gt;
	@from = email
&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s it for the mailer model. All it is really doing it taking parameters that we&amp;#8217;ll pass to it from the contact form (when we get to modifying the controller), puts those parameters into the right places for the mailer view, and sets the recipient address. Now on to the view.&lt;/p&gt;
&lt;h3&gt;c. Creating the mailer view&lt;/h3&gt;
&lt;p&gt;You&amp;#8217;ll find an empty (well, with a couple lines of useless boilerplate) view in views/contact_mailer/contact_form.erb. This is the view that will get invoked when we use the mailer model to request delivery of a message.&lt;/p&gt;
&lt;p&gt;The instance variables passed to this view are those we defined in the &amp;#8220;@body&amp;#8221; variable in the model. Now we just use those however we want, much like in a regular view, but remember that we&amp;#8217;re generating a plain-text email here, so there&amp;#8217;s no &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; code required. Here&amp;#8217;s a simple view:&lt;/p&gt;
&lt;pre&gt;
   Email from your web site

   From: &amp;lt;%= @name %&amp;gt;

   Message: &amp;lt;%= @message %&amp;gt;
&lt;/pre&gt;
&lt;h3&gt;c. Configuring how mail is sent&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;re almost ready to generate email, but first we need to tell Rails how it supposed to access the mail system. You can configure it to use an &lt;span class=&quot;caps&quot;&gt;SMTP&lt;/span&gt; server, or you can tell it to use sendmail, which is the more common approach if you&amp;#8217;re on a unix-type system. The details can be a little tricky, but you should be able to get help from your hosting company or system administrator. Here&amp;#8217;s the configuration that works on the Joyent server that you have:&lt;/p&gt;
&lt;pre&gt;
   ActionMailer::Base.delivery_method = :sendmail
   ActionMailer::Base.sendmail_settings = {
      :location  =&amp;gt; '/opt/csw/sbin/sendmail',
      :arguments     =&amp;gt; '-t'
   }
   ActionMailer::Base.default_charset = 'utf-8'
&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;re providing two parameters to make sendmail work. The first is the path to the executable file, which is specific to the way the Joyent servers are set up. The second, which you&amp;#8217;d probably want for any system configuration, is the &lt;code&gt;-t&lt;/code&gt; parameter, which tells sendmail to extract the recipient addresses from the message headers.&lt;/p&gt;
&lt;p&gt;In the sample app, we put this in a new file, called mail.rb, which we put in config/initializers. You might want to to put it in environments/production.rb instead, so you could then put a different configuration in environments/development.rb. For example, here&amp;#8217;s the configuration for sending email via &lt;span class=&quot;caps&quot;&gt;SMTP&lt;/span&gt; over a Comcast connection:&lt;/p&gt;
&lt;pre&gt;
	ActionMailer::Base.delivery_method = :smtp
	ActionMailer::Base.smtp_settings = {
	  :address =&amp;gt; 'smtp.comcast.net',
	  :port =&amp;gt; 25,
	  :domain =&amp;gt; 'comcast.net'
	}
	ActionMailer::Base.default_charset = 'utf-8'
&lt;/pre&gt;
&lt;p&gt;&lt;span class=&quot;caps&quot;&gt;SMTP&lt;/span&gt; servers are picky about what mail they will accept, so you&amp;#8217;ll have to be on a 
Comcast internet connection for the above settings to work. If your &lt;span class=&quot;caps&quot;&gt;SMTP&lt;/span&gt; server requires authentication, you may need to add login and password settings to the smtp_settings hash.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re having trouble getting your mail to go out in development mode, you may want to change this line in your development environment:&lt;/p&gt;
&lt;pre&gt;
	config.action_mailer.raise_delivery_errors = false
&lt;/pre&gt;
&lt;p&gt;And set it to &lt;code&gt;true&lt;/code&gt; instead. If you don&amp;#8217;t do that, deliver failures are silently ignored in development mode.&lt;/p&gt;
&lt;h3&gt;d. Generating the email from the contact form&lt;/h3&gt;
&lt;p&gt;With all this setup behind us, actually delivering the mail is easy. Open the file controllers/contacts_controller.rb, and add this line immediately after &amp;#8220;if @contact.save&amp;#8221; (we only want to deliver the mail if the save was successful, indicating that the validations passed):&lt;/p&gt;
&lt;pre&gt;
   ContactMailer.deliver_contact_form(@contact.name, @contact.email, @contact.subject, @contact.message)
&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;re invoking the contact_form method in the ContactMailer class. This is the class we edited at the start of this lab. We pass to that method the data from the form; the order of these parameters is determined by the order in which we listed them in the method definition.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s just one strange thing here: the method we&amp;#8217;re invoking is &amp;#8220;deliver_contact_form&amp;#8221;, not &amp;#8220;contact_form&amp;#8221;. Rails creates this method name for us, and we have to use it. Just one of those oddities to get used to. (This odd syntax exists to support another option: you can call the create_contact_form method, which will produce the mail object, ready to be sent, but won&amp;#8217;t actually send it.)&lt;/p&gt;
&lt;p&gt;You can now try creating a contact message, and it should be sent. Depending on what system you&amp;#8217;re running on, and how your mail delivery is configured, it may not actually go out, but you can look at the Rails log, and it will show the email that was generated, even if it couldn&amp;#8217;t contact a mail system to actually send it.&lt;/p&gt;
&lt;h2&gt;3. Blocking spam&lt;/h2&gt;
&lt;p&gt;As an example of integrating a third-party web service, we&amp;#8217;ll use the Defensio service to check our contact messages to see if they are spam.&lt;/p&gt;
&lt;h3&gt;a. Installing the plugin&lt;/h3&gt;
&lt;p&gt;As with most web services, there&amp;#8217;s a Rails plugin available that simplifies integrating the Defensio service. Install it as follows:&lt;/p&gt;
&lt;pre&gt;
   ruby script/plugin install http://code.macournoyer.com/svn/plugins/defensio/
&lt;/pre&gt;
&lt;h3&gt;b. Setting up the contact model to use Defensio&lt;/h3&gt;
&lt;p&gt;We need to tell our comment model to use the Defensio plugin. The basic invocation, which you should paste into the file models/contact.rb, is:&lt;/p&gt;
&lt;pre&gt;
   acts_as_defensio_comment :fields =&amp;gt; {:content =&amp;gt; :message,
                                        :author =&amp;gt; :name,
                                        :author_email =&amp;gt; :email,
                                        :title =&amp;gt; :subject
                                       }
&lt;/pre&gt;
&lt;p&gt;This tells Defensio that the field with the content we want to check is the message, and also points to the other relevant fields.&lt;/p&gt;
&lt;p&gt;Because it is designed for use with a blog, there&amp;#8217;s a little trick we need to use to make Defensio work in our situation, in which there is no parent article to a message. Add the following code to the contact.rb. model:&lt;/p&gt;
&lt;pre&gt;
   class StubArticle
      attr_accessor :created_at

      def initialize(date = Time.now)
         @created_at = date
      end

      # stubbing acts_as_defensio_article methods 
      def self.defensio_fields(field)
         field
      end
   end

   def article
      StubArticle.new(self.created_at)
   end
&lt;/pre&gt;
&lt;p&gt;What we&amp;#8217;ve done here is to create a phony article, with no contents, that will act as the parent article that the Defensio plugin expects to find. We created a class, StubArticle, to provide the behaviors expected of an Article object, and then we defined an article method to create an instance of that class.&lt;/p&gt;
&lt;h3&gt;c. Using Defensio in the contacts controller&lt;/h3&gt;
&lt;p&gt;Now that this is in place, all we need to do to check for spam is to modify the section of code following &lt;code&gt;if @contact.save&lt;/code&gt; in the create method of the contacts_controller to test the spam? method of the contact object, which is provided by the Defensio plugin, to see if the message is believed to be spam:&lt;/p&gt;
&lt;pre&gt;
   if @contact.save
      if @contact.spam?
         flash[:notice] = 'Your message is Under Review.'
      else
         ContactMailer.deliver_contact_form(@contact.name, @contact.email, @contact.subject, @contact.message)
         flash[:notice] = 'Thanks for your message. It has been delivered.'      
      end
      format.html { redirect_to :controller =&amp;gt; 'static', :action =&amp;gt; 'home' }
   else ...
&lt;/pre&gt;
&lt;p&gt;If the save is successful, the we test to see if the message has been rated as spam. If so, we don&amp;#8217;t generate an email, and we display an appropriate message. If not, we proceed as before.&lt;/p&gt;
&lt;h3&gt;d. Setting up the Defensio &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; key&lt;/h3&gt;
&lt;p&gt;To use the Defensio service, you need to sign up for an &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; key. For testing, you can use ours. If you&amp;#8217;re going to use the service, sign up for your own key at http://defensio.com//, as ours will be deactivated in a month or two.&lt;/p&gt;
&lt;p&gt;The Defensio plugin looks for a file called defensio.yml in the config folder. The plugin installs a template version of this file, but the &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; keys in that version won&amp;#8217;t work. So open that file and paste the following information into it, overwriting what&amp;#8217;s there:&lt;/p&gt;
&lt;pre&gt;
   development:
      api_key: 47dcc80bc580d10b3c2b3eb4ed2841b1
      owner_url: http://lab.railsquickstart.com/

   test:
      api_key: 47dcc80bc580d10b3c2b3eb4ed2841b1
      owner_url: http://lab.railsquickstart.com/

   production:
      api_key: 47dcc80bc580d10b3c2b3eb4ed2841b1
      owner_url: http://lab.railsquickstart.com/
&lt;/pre&gt;
&lt;h3&gt;e. Adding the Defensio columns to the contact model&lt;/h3&gt;
&lt;p&gt;Defensio needs to store a few things in the model for each contact: a boolean value that indicates whether or not a message is spam, a &amp;#8220;spaminess&amp;#8221; rating that gives the estimated likelihood that a comment is spam, and a &amp;#8220;signature&amp;#8221; field that Defensio uses to identify the message on future calls to the service.&lt;/p&gt;
&lt;p&gt;To add these fields to your content model, create a new migration, called something like AddDefensioFields, and put the following in the self.up block in the new migration file:&lt;/p&gt;
&lt;pre&gt;
   add_column :contacts, :spam,      :boolean, :default =&amp;gt; false
   add_column :contacts, :spaminess, :float
   add_column :contacts, :signature, :string
&lt;/pre&gt;
&lt;p&gt;And to keep things tidy, add the following to the self.down block in the migration file:&lt;/p&gt;
&lt;pre&gt;
   remove_column :contacts, :spam
   remove_column :contacts, :spaminess
   remove_column :contacts, :signature
&lt;/pre&gt;
&lt;p&gt;Now run the migration, and restart your server so the plugin can initialize itself. You should now be able to submit contact messages just as before. You may not notice anything different yet, but when the contact message is saved, it is also sent to the Defensio service, and the service responds with the spam flag and the spamminess value, which are stored in the database. All this happens automatically because of the &lt;code&gt;acts_as_defensio_comment&lt;/code&gt; statement that we added to the contact model.&lt;/p&gt;
&lt;p&gt;If you get any error messages at this point, check all the Defensio-related code.&lt;/p&gt;
&lt;h3&gt;f. Adding Defensio support to the contact admin pages&lt;/h3&gt;
&lt;p&gt;The last piece of the puzzle is displaying the values created by Defensio in the contact admin list, and providing a couple links to send information to Defensio.&lt;/p&gt;
&lt;p&gt;Open the file views/contacts/index.html.erb, and add these two lines at the end of the list of headers (the last of which is &amp;#8220;Subject&amp;#8221; until you make this addition):&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;th&amp;gt;Spam?&amp;lt;/th&amp;gt;
    &amp;lt;th&amp;gt;Spam Score&amp;lt;/th&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now we need to add the columns that display this information. In the block that is displaying the information for each contact, add these two lines after the one that displays contact.subject:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;td&amp;gt;&amp;lt;%=h contact.spam? ? &quot;TRUE&quot; : &quot;FALSE&quot; %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%=h contact.spaminess %&amp;gt;&amp;lt;/td&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The first of these displays &lt;span class=&quot;caps&quot;&gt;TRUE&lt;/span&gt; or &lt;span class=&quot;caps&quot;&gt;FALSE&lt;/span&gt; depending on the state of the spam attribute. The second displays the &amp;#8220;spaminess&amp;#8221; value.&lt;/p&gt;
&lt;p&gt;Finally, all the follow lines just before the &lt;code&gt;&amp;lt;/tr&amp;gt;&lt;/code&gt; element, after the link to the Destroy action:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;td&amp;gt;&amp;lt;%= link_to 'Report Spam', {:action =&amp;gt; :report_as_spam, :id =&amp;gt; contact} %&amp;gt;&amp;lt;/td&amp;gt;
    &amp;lt;td&amp;gt;&amp;lt;%= link_to 'Report Ham', {:action =&amp;gt; :report_as_ham, :id =&amp;gt; contact} %&amp;gt;&amp;lt;/td&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The first link allows us to flag a message as spam; the second allows us to flag it as &amp;#8220;ham&amp;#8221; (i.e., not spam).&lt;/p&gt;
&lt;h3&gt;g. Adding the actions&lt;/h3&gt;
&lt;p&gt;Just one more step. We need to add the actions to the contacts controller to deal with reporting of messages as spam or ham from the admin page we just modified. Add these two methods to the end of controllers/contacts_controller.rb:&lt;/p&gt;
&lt;pre&gt;
   def report_as_spam
      @contact = Contact.find(params[:id])
      @contact.report_as_spam if @contact
      redirect_to(contacts_url) 
   end

   def report_as_ham
      @contact = Contact.find(params[:id])
      @contact.report_as_ham if @contact
      redirect_to(contacts_url)
   end
&lt;/pre&gt;
&lt;p&gt;These are both very simple methods, since all they need to do is invoke the corresponding method on the contact object. Those methods are provided by the Defensio plugin.&lt;/p&gt;
&lt;h3&gt;h. Testing it out&lt;/h3&gt;
&lt;p&gt;You&amp;#8217;re done! Go ahead and create a few contact messages. The go to the Contacts admin page, and you&amp;#8217;ll see how the messages were rates in terms of their spaminess. Click the Report Spam link for one of them, and you&amp;#8217;ll see its spam state change to &lt;span class=&quot;caps&quot;&gt;TRUE&lt;/span&gt;, and its spaminess score go to 1.0. Click the Report Ham link for another, and its spaminess score will go to zero.&lt;/p&gt;
&lt;p&gt;After some training, Defensio will begin recognizing messages as spam and marking them as such automatically when they are saved, which prevents them from being emailed to you.&lt;/p&gt;
&lt;h2&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;As you extend this code, there lots of things you can do to:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Improve the contact form validations. You might require a minimum length for the name, subject, and message, and validate the email address with a regular expression that checks its format.&lt;/li&gt;
	&lt;li&gt;The &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;/&lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; layout of the contact form could use a lot of help.&lt;/li&gt;
	&lt;li&gt;If Defensio marks a message as spam, and you later mark it as Ham in the admin interface, you might want to send it as an email, since the initial sending of the email was inhibited when it was first marked as spam.&lt;/li&gt;
	&lt;li&gt;If the spaminess value is high enough, you might want to not even store the message in your database, but simply discard it.&lt;/li&gt;
&lt;/ul&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:45 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79177-lab-4-contact-form-mailer-and</guid>
          <link>http://www.buildingwebapps.com/item/79177-lab-4-contact-form-mailer-and</link>
        </item>
        
        <item>
          <title>Lab 5. Subscriptions and Credit Card Processing</title>
          <description>&lt;p&gt;In this lab, we&amp;#8217;re going to enable visitors to our web site to subscribe and send us real money! Well, not quite real money, since this is using a test account at the credit card gateway, but all it would take is a real merchant account to make the money real.&lt;/p&gt;
&lt;h2&gt;1. Installing the Active Merchant plug-in&lt;/h2&gt;
&lt;p&gt;Because we&amp;#8217;re going to need some of its features, even before we get to the gateway interface itself, let&amp;#8217;s start by installing the Active Merchant plug-in. This is a mature and widely-used plug-in that supports more then 30 credit card gateways.&lt;/p&gt;
&lt;p&gt;The NetBeans plug-in interface seems to have trouble installing this plug-in, so let&amp;#8217;s do it from the command line:&lt;/p&gt;
&lt;pre&gt;
   ruby script/plugin install http://activemerchant.googlecode.com/svn/trunk/active_merchant
&lt;/pre&gt;
&lt;p&gt;This is a big plugin with lots of pieces, so the installation may take a minute or two.&lt;/p&gt;
&lt;h2&gt;2. The subscription and customer models and forms&lt;/h2&gt;
&lt;p&gt;First, we need a form for the subscriber to complete. This form needs to include information about the subscription, which we&amp;#8217;ll store in the subscription model, about the customer, which we&amp;#8217;ll store in the customer model, and about the credit card, for which we&amp;#8217;ll use a non-database model provided by the Active Merchant plug-in.&lt;/p&gt;
&lt;h3&gt;a. Creating the customer and subscription scaffolds&lt;/h3&gt;
&lt;p&gt;As in previous labs, we&amp;#8217;ll use the scaffold generator to get a quick start on our models, views, and controllers.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;ll create a customer model, which has information about the customer but not about their subscription, and a subscription model, which has only the information about the subscription and a foreign key to point to the customer.&lt;/p&gt;
&lt;p&gt;Run both of these scaffold commands:&lt;/p&gt;
&lt;pre&gt;
	ruby script/generate scaffold customer first_name:string last_name:string address:string city:string state:string zip:string phone:string
	ruby script/generate scaffold subscription customer_id:integer period:integer amount:integer reference:string message:string params:text test:boolean
&lt;/pre&gt;
&lt;p&gt;The customer model should be self-explanatory. Here&amp;#8217;s the description of the fields in the subscription model:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;customer_id: foreign key for the associated customer&lt;/li&gt;
	&lt;li&gt;period: length of the subscription, in months&lt;/li&gt;
	&lt;li&gt;amount: cost of the subscription, in cents&lt;/li&gt;
	&lt;li&gt;reference: an identifier for the transaction, provided by the gateway&lt;/li&gt;
	&lt;li&gt;message: the message provided by the gateway, describing the success or failure of the transaction&lt;/li&gt;
	&lt;li&gt;params: a holding tank for various parameters that the gateway may return&lt;/li&gt;
	&lt;li&gt;test: a flag indicating that the gateway was in test mode when the transaction was completed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now migrate the database to the current version. And delete the pesky layouts that we have no use for, views/layouts/customers.html.erb and views/layouts/subscriptions.html.erb.&lt;/p&gt;
&lt;h3&gt;b. Creating the subscription form&lt;/h3&gt;
&lt;p&gt;Now you can take a look at the automatically generated forms:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;New customer form: //localhost:3000/customers/new&lt;/li&gt;
	&lt;li&gt;New subscription form: //localhost:3000/subscriptions/new&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new customer form has most of the information we want for entering a new subscription. The new subscription form is rather worthless, however, since this information mostly comes from the gateway, or from our application logic.&lt;/p&gt;
&lt;p&gt;Nevertheless, the form we want is most naturally thought of as a new subscription form, so we&amp;#8217;re going to use that one, eviscerate most of the scaffolded content, copy the fields from the customer form, and add a few more details.&lt;/p&gt;
&lt;p&gt;Open the file view/subscriptions/new, and delete everything between the &lt;code&gt;form_for&lt;/code&gt; statement and the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tag that precedes the submit button helper, so the entire file now looks like this:
	
&lt;pre&gt;
   &lt;h1&gt;New subscription&lt;/h1&gt;&lt;/p&gt;
&amp;lt;%= error_messages_for :subscription %&amp;gt;
&amp;lt;% form_for(@subscription) do |f| %&amp;gt;
&lt;p&gt;
&amp;lt;%= f.submit &amp;#8220;Create&amp;#8221; %&amp;gt;
&lt;/p&gt;
&amp;lt;% end %&amp;gt;
&lt;p&gt;&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;While we have this nice simple view, change the header text (between the &lt;code&gt;h1&lt;/code&gt; tags) to &amp;#8220;Subscribe to Our Site&amp;#8221;, and change the submit button label from &amp;#8220;Create&amp;#8221; to &amp;#8220;Subscribe&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Now we&amp;#8217;re going to insert the customer fields, much like they exist in the customer form, in the new subscription form. You can do this with some judicious copy-and-paste and then some editing, but it&amp;#8217;s a lot of steps to write out (and to read), so we&amp;#8217;ll just show the final result, which you can copy and paste from here:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;h1&amp;gt;Subscribe to Our Site&amp;lt;/h1&amp;gt;

	&amp;lt;%= error_messages_for :subscription %&amp;gt;
	&amp;lt;%= error_messages_for :customer %&amp;gt;

	&amp;lt;% form_for(@subscription) do |f| %&amp;gt;

	&amp;lt;% fields_for(@customer) do |customer| %&amp;gt;
	  &amp;lt;p&amp;gt;
	    &amp;lt;b&amp;gt;First name&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
	    &amp;lt;%= customer.text_field :first_name %&amp;gt;
	  &amp;lt;/p&amp;gt;

	  &amp;lt;p&amp;gt;
	    &amp;lt;b&amp;gt;Last name&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
	    &amp;lt;%= customer.text_field :last_name %&amp;gt;
	  &amp;lt;/p&amp;gt;

	  &amp;lt;p&amp;gt;
	    &amp;lt;b&amp;gt;Address&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
	    &amp;lt;%= customer.text_field :address %&amp;gt;
	  &amp;lt;/p&amp;gt;

	  &amp;lt;p&amp;gt;
	    &amp;lt;b&amp;gt;City&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
	    &amp;lt;%= customer.text_field :city %&amp;gt;
	  &amp;lt;/p&amp;gt;

	  &amp;lt;p&amp;gt;
	    &amp;lt;b&amp;gt;State&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
	    &amp;lt;%= customer.text_field :state %&amp;gt;
	  &amp;lt;/p&amp;gt;

	  &amp;lt;p&amp;gt;
	    &amp;lt;b&amp;gt;Zip&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
	    &amp;lt;%= customer.text_field :zip %&amp;gt;
	  &amp;lt;/p&amp;gt;

	  &amp;lt;p&amp;gt;
	    &amp;lt;b&amp;gt;Phone&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
	    &amp;lt;%= customer.text_field :phone %&amp;gt;
	  &amp;lt;/p&amp;gt;

	&amp;lt;% end %&amp;gt;

	  &amp;lt;p&amp;gt;
	    &amp;lt;%= f.submit &quot;Subscribe&quot; %&amp;gt;
	  &amp;lt;/p&amp;gt;
	&amp;lt;% end %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Note that we&amp;#8217;ve changed the &lt;code&gt;form_for&lt;/code&gt; helper for the customer fields to &lt;code&gt;fields_for&lt;/code&gt;. This is necessary because you can&amp;#8217;t nest forms, and Rails provides a helper for just this situation. We also changed the block variable in the &lt;code&gt;fields_for&lt;/code&gt; helper, &lt;code&gt;f&lt;/code&gt; by default, to &lt;code&gt;customer&lt;/code&gt;, so we can associate the customer fields with the customer model, and subscription fields with the subscription model.&lt;/p&gt;
&lt;h3&gt;c. Adding in the subscription period&lt;/h3&gt;
&lt;p&gt;So far, we have no fields for our subscription model in our new subscription form. The one thing we need the user to choose is the subscription period. We could put in a text field for this, as in the scaffolded form, but we probably don&amp;#8217;t want arbitrary subscription lengths. So let&amp;#8217;s just provide two options, selected via radio buttons. Insert the following text between the form_for and the fields_for statements:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;p&amp;gt;
      &amp;lt;b&amp;gt;Subscription Period&amp;lt;/b&amp;gt;&amp;lt;br /&amp;gt;
      &amp;lt;%= f.radio_button :period, 1 %&amp;gt;&amp;lt;label&amp;gt;One Month: $9.95&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
      &amp;lt;%= f.radio_button :period, 12 %&amp;gt;&amp;lt;label&amp;gt;One Year: $99.95&amp;lt;/label&amp;gt;
   &amp;lt;/p&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Both radio buttons are associated with the attribute &lt;code&gt;:period&lt;/code&gt;. Since only one can be selected, that one will determine the value that is provided.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;ll take care of figuring the price based on the selected period when we get to the controller.&lt;/p&gt;
&lt;h3&gt;d. Entering the credit card information&lt;/h3&gt;
&lt;p&gt;The last chunk of information we need from the user is their credit card information. We&amp;#8217;ll use a fields_for helper, just as we did for the customer info. We don&amp;#8217;t have a model yet for the credit card, and we&amp;#8217;ll see when we get to the controller that this model comes from Active Merchant. For our purposes in the form, it acts just like a normal Active Record model.&lt;/p&gt;
&lt;p&gt;Insert the following code just before the &amp;#8220;Subscribe&amp;#8221; submit button (and its surrounding p tag):&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;% fields_for(:creditcard, @creditcard) do |cc| %&amp;gt;

      &amp;lt;p&amp;gt;&amp;lt;label&amp;gt;Card Number&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
         &amp;lt;%= cc.text_field :number %&amp;gt;&amp;lt;/p&amp;gt;

      &amp;lt;p&amp;gt;&amp;lt;label&amp;gt;Card Type&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
         &amp;lt;%= cc.select :type, &quot;Visa&quot;, &quot;visa&quot;], [&quot;MasterCard&quot;, &quot;master&quot;], [&quot;Discover&quot;, &quot;discover&quot;], [&quot;American Express&quot;, &quot;american_express&quot; (*ERROR - NO SUCH CATEGORY*) %&amp;gt;&amp;lt;/p&amp;gt;

      &amp;lt;p&amp;gt;&amp;lt;label&amp;gt;Expiration&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
         &amp;lt;%= cc.select :month, (1 .. 12) %&amp;gt;
         &amp;lt;%= cc.select :year, (Time.now.year .. 10.years.from_now.year) %&amp;gt;&amp;lt;/p&amp;gt;

      &amp;lt;p&amp;gt;&amp;lt;label&amp;gt;Verification code&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;
         &amp;lt;%= cc.text_field :verification_value %&amp;gt;&amp;lt;/p&amp;gt;

   &amp;lt;% end %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;You may notice that this &lt;code&gt;fields_for&lt;/code&gt; helper provides both the name of the object, as a symbol, and the instance variable used to hold it, whereas in our other examples we needed only the instance variable. We need this because this is not an Active Record model, even though it mostly behaves like one; Active Record provides some magic that eliminates the need for both versions, which Active Merchant doesn&amp;#8217;t provide.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s an explanation of the fields:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The &lt;code&gt;:number&lt;/code&gt; field is straightforward &amp;#8212; just a text field for the credit card number.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code&gt;:card_type&lt;/code&gt; is a select field, and we&amp;#8217;re providing an array of arrays for the list. The first element of each component array is the text to be displayed, and the second element is the data to be returned if that item is selected.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The &lt;code&gt;:month&lt;/code&gt; field is a select, for which we use the Ruby &amp;#8220;range&amp;#8221; syntax to easily create the list of month numbers, from 1 through 12.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The &lt;code&gt;:year&lt;/code&gt; field is similar, and we again use a range, with the start of the range being the current year, and the end of of the range being 10 years later.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;The &lt;code&gt;:verification_value&lt;/code&gt; field is for the 3- or 4-digit security code, and it is just a text field.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, at the top of the form, we need to report any validation errors on the creditcard model. So add this line just below the other two &lt;code&gt;error_messages_for&lt;/code&gt; helpers:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;%= error_messages_for :creditcard %&amp;gt;
&lt;/pre&gt;
&lt;h3&gt;e. Adding the subscription button&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s put a subscription button in the navigation bar so we can display our lovely new form easily. Add this code at whatever point you&amp;#8217;d like in the navbar list in views/layouts/application.html.erb:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;li&amp;gt;&amp;lt;%= link_to 'Subscribe!', new_subscription_path, :id =&amp;gt; 'subscription_nav' %&amp;gt; &amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;p&gt;To make the tab highlight work, we need to add the code for this new tab to the &lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt;. Add these lines to public/stylesheets/quickstart.css:&lt;/p&gt;
&lt;pre&gt;
   #subscribe_tab a#subscribe_nav {
      background-color: #333;
   }
&lt;/pre&gt;
&lt;p&gt;Refresh the page, and you&amp;#8217;ll see the Subscribe! button. Click on the button, and you&amp;#8217;ll see our nice new form. But it won&amp;#8217;t yet do much if you submit it, and the tab highlight won&amp;#8217;t work yet, because we have work to do in the controller.&lt;/p&gt;
&lt;h3&gt;f. Styling the validation errors&lt;/h3&gt;
&lt;p&gt;Rails includes a default style sheet that provides some styling for the validation error messages, as well as highlights for the fields that have errors. We haven&amp;#8217;t included that stylesheet in the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; until now, even though the stylesheet is in the public/stylesheets folder. So just add this line to the head section of views/layouts/application.html.erb:&lt;/p&gt;
&lt;pre&gt;
	&amp;lt;%= stylesheet_link_tag 'scaffold.css' %&amp;gt;
&lt;/pre&gt;
&lt;h3&gt;g. Adding links to the admin pages&lt;/h3&gt;
&lt;p&gt;The scaffolded customer and subscriber admin pages are useful for looking at the data that has been stored, so let&amp;#8217;s add links to them to our admin page. Add these lines to the list of links in views/static/admin.hmtl.erb:&lt;/p&gt;
&lt;pre&gt;
   &amp;lt;li&amp;gt;&amp;lt;%= link_to 'Customers Admin', customers_path %&amp;gt;&amp;lt;/li&amp;gt;
   &amp;lt;li&amp;gt;&amp;lt;%= link_to 'Subscriptions Admin', subscriptions_path %&amp;gt;&amp;lt;/li&amp;gt;
&lt;/pre&gt;
&lt;h2&gt;3. The subscriptions controller&lt;/h2&gt;
&lt;h3&gt;a. Setting up the instance variables&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s get to work on the file controllers/subscriptions_controller.rb.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;new&lt;/code&gt; method, you&amp;#8217;ll see that it sets only one instance variable, &lt;code&gt;@subscription&lt;/code&gt;. Since we are using three instance variables in the form, we need to set those too. So after the line that sets the &lt;code&gt;@subscription&lt;/code&gt; instance variable, add these two lines:&lt;/p&gt;
&lt;pre&gt;
   @customer = Customer.new
   @creditcard = ActiveMerchant::Billing::CreditCard.new
&lt;/pre&gt;
&lt;p&gt;The first line is just like all the others we&amp;#8217;ve used in the past. But the second is a little different. We could have set up a creditcard model, and generated a scaffold, but that would be bad, bad, bad! We don&amp;#8217;t want to store credit card information in our database. And we don&amp;#8217;t want to have to write the routines to validate the credit card information.&lt;/p&gt;
&lt;p&gt;Fortunately, Active Merchant provides us with a very nice solution. Active Merchant enables us to create a credit-card object, and to validate that object, without any possibility of it being stored in our database. Since the CreditCard class is part of the Billing module in the ActiveMerchant plugin, we need to create the creditcard object using the syntax shown above.&lt;/p&gt;
&lt;p&gt;While we&amp;#8217;re at it, add the two lines required for the tab highlighting and the page title:&lt;/p&gt;
&lt;pre&gt;
   @current_tab = &quot;subscribe_tab&quot;
   @page_title = &quot;Subscribe to Our Site&quot;
&lt;/pre&gt;
&lt;p&gt;Now we&amp;#8217;re done with the &lt;code&gt;new&lt;/code&gt; method.&lt;/p&gt;
&lt;h3&gt;b. Creating the new objects&lt;/h3&gt;
&lt;p&gt;The default &lt;code&gt;create&lt;/code&gt; method in the subscriptions controller creates a subscription object in the usual manner, since we started from a subscription scaffold. But it doesn&amp;#8217;t know anything about customers, or credit cards, no does it know how to figure out the subscription price from the subscription period, not to mention actually sending the credit card transaction to the gateway.&lt;/p&gt;
&lt;p&gt;We need to teach it these things. This requires the biggest chunk of Ruby code we&amp;#8217;ve written in the entire seminar, but there&amp;#8217;s nothing very complicated here. Let&amp;#8217;s take it step by step.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re not going to use the standard &lt;code&gt;respond_to&lt;/code&gt; block that the scaffolding provided, so go ahead and delete all that code now. This should leave you with an empty method, except for the creation of the subscription object.&lt;/p&gt;
&lt;p&gt;First, let&amp;#8217;s create the customer object, which works in the usual way:&lt;/p&gt;
&lt;pre&gt;
	@customer = Customer.new(params[:customer])
&lt;/pre&gt;
&lt;p&gt;Remember the &lt;code&gt;fields_for&lt;/code&gt; helper that we used to wrap the customer fields? That helper takes care of putting the customer fields into a hash within the params hash, named :customer, so we can easily pull those out and set up the customer object in the usual way.&lt;/p&gt;
&lt;p&gt;Now we need to figure the subscription cost. The translation from subscription period to subscription cost is business logic, so it belongs in the model, not in the controller. So we&amp;#8217;ll just assume that the model will provide us with a method to take care of this:&lt;/p&gt;
&lt;pre&gt;
   @subscription.amount = Subscription.cost_for_period(@subscription.period)
&lt;/pre&gt;
&lt;p&gt;We just need to remember to create this cost_for_period method in the subscription model.&lt;/p&gt;
&lt;p&gt;Now let&amp;#8217;s move on to setting up the credit card object. We do this in much the same way as the subscription and customer objects, except that this is not an Active Record model, but rather one provided by Active Merchant:&lt;/p&gt;
&lt;pre&gt;
   @creditcard = ActiveMerchant::Billing::CreditCard.new(params[:creditcard])    
&lt;/pre&gt;
&lt;p&gt;And then we need to set the first and last name of the cardholder, which we need to get from the customer object:&lt;/p&gt;
&lt;pre&gt;
   @creditcard.first_name = @customer.first_name
   @creditcard.last_name = @customer.last_name
&lt;/pre&gt;
&lt;p&gt;We now have our customer, subscriber, and creditcard objects set up.&lt;/p&gt;
&lt;h3&gt;c. Checking for a valid order&lt;/h3&gt;
&lt;p&gt;Now we need to make sure these objects are all valid. Normally, when saving a single object, we use something like this:&lt;/p&gt;
&lt;pre&gt;
   if customer.save
      # proceed, no validation errors
   else
      # redisplay the form with validation error messages
   end
&lt;/pre&gt;
&lt;p&gt;But in this case, we have three objects, and we don&amp;#8217;t want to save any of them unless all three are valid. There&amp;#8217;s a few ways to handle this, but here&amp;#8217;s one simple approach:&lt;/p&gt;
&lt;pre&gt;
   @customer.valid?
   @subscription.valid?    
   unless @creditcard.valid? and @customer.errors.empty? and @subscription.errors.empty?
      render :action =&amp;gt; :new and return
   end
&lt;/pre&gt;
&lt;p&gt;We take advantage of the &lt;code&gt;valid?&lt;/code&gt; method that is available on all Active Record objects. This performs the validations but doesn&amp;#8217;t save the object to the database. If there are any validation errors, they&amp;#8217;re stored in the &lt;code&gt;errors&lt;/code&gt; array for that object (which is where the &lt;code&gt;error_messages_for&lt;/code&gt; helper in the form gets the messages if there are validation errors).&lt;/p&gt;
&lt;p&gt;So we validate both the customer and subscription objects, and then we bail out and redisplay the form &lt;em&gt;unless&lt;/em&gt; the credit card is valid and the errors attribute on both the customer object and the subscription object are empty.&lt;/p&gt;
&lt;p&gt;If our code makes it past this &lt;code&gt;unless&lt;/code&gt; clause, we know that we have a valid subscription to process.&lt;/p&gt;
&lt;h3&gt;d. Setting up the gateway&lt;/h3&gt;
&lt;p&gt;Now we need to set up all the information needed by the gateway and perform the actual transaction.&lt;/p&gt;
&lt;p&gt;First, let&amp;#8217;s create the gateway object:&lt;/p&gt;
&lt;pre&gt;
	gateway = ActiveMerchant::Billing::BraintreeGateway.new(BRAINTREE_CREDENTIALS)
&lt;/pre&gt;
&lt;p&gt;We&amp;#8217;re just calling a method in ActiveMerchant that sets up the gateway. If we were using a different gateway, we&amp;#8217;d of course use the method for the appropriate gateway, but virtually all of the other code would remain the same regardless of the gateway being used (with the one caveat that some gateways require different parameters).&lt;/p&gt;
&lt;p&gt;Note that &lt;code&gt;BRAINTREE_CREDENTIALS&lt;/code&gt; is set up as a constant. We set this constant in our development environment (config/environments/development.rb):&lt;/p&gt;
&lt;pre&gt;
	BRAINTREE_CREDENTIALS = {:login =&amp;gt; 'demo', :password =&amp;gt; 'password'}
	ActiveMerchant::Billing::Base.mode = :test
&lt;/pre&gt;
&lt;p&gt;If you were to use this in production, you&amp;#8217;d need to get a real gateway account from Braintree (or another gateway provides), and you&amp;#8217;d put the production credentials in config/environments/production.rb.&lt;/p&gt;
&lt;p&gt;We also set the gateway mode to &lt;code&gt;:test&lt;/code&gt; for good measure, though it doesn&amp;#8217;t actually matter because the gateway we&amp;#8217;re accessing is &lt;em&gt;only&lt;/em&gt; a test gateway.&lt;/p&gt;
&lt;h3&gt;e. Setting the optional information&lt;/h3&gt;
&lt;p&gt;Different gateways require different amounts of information, and even within one gateway often some information is optional but may reduce your transaction fee if it is provided and is correct. All this optional information is assembled into on hash. So in preparation for running the purchase transaction, we&amp;#8217;ll set up this options hash:&lt;/p&gt;
&lt;pre&gt;
   options = {
      :ip =&amp;gt; request.remote_ip,
      :billing_address =&amp;gt; { 
         :name     =&amp;gt; @customer.full_name,
         :address1 =&amp;gt; @customer.address,
         :address2 =&amp;gt; '',
         :city     =&amp;gt; @customer.city,
         :state    =&amp;gt; @customer.state,
         :country  =&amp;gt; 'US',
         :zip      =&amp;gt; @customer.zip,
         :phone    =&amp;gt; @customer.phone
      }
   }
&lt;/pre&gt;
&lt;p&gt;The names of the hash keys are taken from the documentation for the Braintree gateway. First we set the customer&amp;#8217;s IP address, which we can get from the request object that is always available in any controller. Then we set the billing_address hash, taking the information from the customer object.&lt;/p&gt;
&lt;p&gt;Notice, however, that there is no &lt;code&gt;full_name&lt;/code&gt; attribute on the customer object &amp;#8212; we have first_name and last_name. We could just concatenate those here, but there&amp;#8217;s likely to be other places where we need the full name, so it&amp;#8217;s better to invent a method to provide us what we need, and then we&amp;#8217;ll add this method to the customer model.&lt;/p&gt;
&lt;h3&gt;f. Performing the transaction&lt;/h3&gt;
&lt;p&gt;With all this setup in place, actually performing the transaction is easy:&lt;/p&gt;
&lt;pre&gt;
   response = gateway.purchase(@subscription.amount, @creditcard, options)
&lt;/pre&gt;
&lt;p&gt;We simply call the purchase method on the gateway object that we previously created, passing in as parameters the amount of the subscription, the credit card object, and the options hash. We store the response in a new variable we&amp;#8217;ve called simply response.&lt;/p&gt;
&lt;p&gt;It is in this single line of code that all the web services interaction occurs. Remember that &lt;code&gt;gateway&lt;/code&gt; is an Active Merchant object, and when we invoke the &lt;code&gt;purchase&lt;/code&gt; method on that object, the Active Merchant code connects to the Braintree payment gateway web service, sends all the information about the transaction, waits for the response, and then returns the response as the result of executing the method.&lt;/p&gt;
&lt;h3&gt;g. Processing the response&lt;/h3&gt;
&lt;p&gt;Now that we&amp;#8217;ve attempted the transaction, we need to see if it was successful. If not, we want to redisplay the subscription form with appropriate error messages. If so, then we want to save the information from the gateway for later reference and save the subscription and customer information.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the code:&lt;/p&gt;
&lt;pre&gt;
   unless response.success?
      flash[:notice] = &quot;Transaction failed: #{response.message}&quot;
      render :action =&amp;gt; :new
   else
      # save the responses from the transaction in the subscription record
      @subscription.message = response.message
      @subscription.reference = response.authorization
      @subscription.test = response.test?
      @subscription.params = response.params.to_yaml
      @subscription.customer = @customer
      @subscription.save!
      flash[:notice] = &quot;Thanks for subscribing to our site!&quot;
      redirect_to :controller =&amp;gt; 'static', :action =&amp;gt; 'home'
   end
&lt;/pre&gt;
&lt;p&gt;First we use the &lt;code&gt;success?&lt;/code&gt; method on the &lt;code&gt;response&lt;/code&gt; object, which is provided by Active Merchant, to see if all went well. If not, we set an appropriate message and redisplay the subscription form.&lt;/p&gt;
&lt;p&gt;If all did go well, we save in the subscription object various pieces of information provided by the gateway response. One thing the response object returns is a params object, which is a hash of various parameters that may change from gateway to gateway. So rather than creating a model field for each of these parameters, we convert the params object to text form, using &lt;code&gt;.to_yaml&lt;/code&gt; (this is called serializing the data). This allows us to store any arbitrary set of parameters without requiring any code changes.&lt;/p&gt;
&lt;p&gt;Finally, we need to associate the customer with the subscription, and then save the subscription. Saving the subscription will also save the associated customer object.&lt;/p&gt;
&lt;p&gt;Note that we use the &lt;code&gt;save!&lt;/code&gt; method here, because we&amp;#8217;ve already validated all the objects, and the new information we&amp;#8217;ve added from the transaction doesn&amp;#8217;t need to be validated. So if this save fails, that means that there was a programming error, or something really unexpected (like a database failure) happened. If this save fails, an exception will be raised. Somewhere in our code, we should capture that exception and display a &amp;#8220;Sorry, something bad happened&amp;#8221; message, but there&amp;#8217;s not much else we can do.&lt;/p&gt;
&lt;p&gt;Finally, we set the flash message and redirect to the home page.&lt;/p&gt;
&lt;h3&gt;h. Handling the validation error cases&lt;/h3&gt;
&lt;p&gt;There&amp;#8217;s one problem with our code. Suppose that a validation or transaction error occurs, and we redisplay the form. If this happens, we need to have all the instance variable set up that the new subscription form expects. We&amp;#8217;ve already created the instance variables for the subscription, customer, and credit card, but we still need to set the page title and the current tab. Since there&amp;#8217;s two paths where this would be needed (validation failure, or transaction failure), it&amp;#8217;s easiest to just set these values at the top of the method, even though they won&amp;#8217;t be used if the transaction succeeds. So add these two lines at the top of the &lt;code&gt;create&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;
    @current_tab = &quot;subscribe_tab&quot;
    @page_title = &quot;Please correct the errors below&quot;
&lt;/pre&gt;
&lt;p&gt;Note that we&amp;#8217;ve set the page title to an error message, as one more hint to the customer that there&amp;#8217;s something they need to fix.&lt;/p&gt;
&lt;h3&gt;i. Avoiding credit card leaks&lt;/h3&gt;
&lt;p&gt;We need to be really careful to avoid credit card numbers leaking out. We don&amp;#8217;t save them, but we do process them in memory. And we have, inadvertently, stored credit card numbers: they&amp;#8217;re in our application log file for each transaction. We can stop this by adding the following line at the top of the subscriptions controller:&lt;/p&gt;
&lt;pre&gt;
	filter_parameter_logging :creditcard
&lt;/pre&gt;
&lt;p&gt;This tells the logger not to show the value of this parameter. The logs will instead show &amp;#8220;[&lt;span class=&quot;caps&quot;&gt;FILTERED&lt;/span&gt;]&amp;#8221; in place of the actual value.&lt;/p&gt;
&lt;h2&gt;4. Completing the models&lt;/h2&gt;
&lt;h3&gt;a. The subscription model&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;re done with the controller, but we need to complete the models before you can test the code.&lt;/p&gt;
&lt;p&gt;First we need to set the associations. A subscription belongs to a customer; that is, we included the customer ID in the subscription table. So add this line to the file models/subscription.rb:&lt;/p&gt;
&lt;pre&gt;
	belongs_to :customer
&lt;/pre&gt;
&lt;p&gt;Now we have to implement the method that we made up when we were writing the controller, that determines the subscription price based on the period. Add the following code:&lt;/p&gt;
&lt;pre&gt;
   def self.cost_for_period period
      if period == 1
         995     # $9.95 in cents
      elsif period == 12
         9995    # $99.95 in cents
      else
         &quot;invalid&quot;
      end
   end
&lt;/pre&gt;
&lt;p&gt;This method is a &lt;em&gt;class method&lt;/em&gt;: it acts not upon an instance of the class, but on the class in general. (In other words, the result is the same for all subscriptions.) We signal this fact by prefixing the class name with &lt;code&gt;self.&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The logic is quite simple. If the period is 1, the price is 995 (Active Merchant expects prices in cents, so this is $9.95). If the period is 12, the price is 9995 ($99.95).&lt;/p&gt;
&lt;p&gt;What should we do if the period is something else? It shouldn&amp;#8217;t be possible, since the value is set by a choice from one of two radio buttons. But suppose some trickster spoofed the &lt;span class=&quot;caps&quot;&gt;POST&lt;/span&gt; data from the form? They could put in any value they wanted. So we provide a final &lt;code&gt;else&lt;/code&gt; clause that returns the string &amp;#8220;invalid&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Then we need to add a line of validation code to the model:&lt;/p&gt;
&lt;pre&gt;
   validates_numericality_of :amount, :message =&amp;gt; &quot;Choose a subscription period&quot;
&lt;/pre&gt;
&lt;p&gt;This catches the situation in which neither radio button is selected, so the period is nil, and it also takes care of the situation in which the period is invalid, since the string &amp;#8220;invalid&amp;#8221; fails the &amp;#8220;numericality&amp;#8221; test.&lt;/p&gt;
&lt;h3&gt;b. The customer model&lt;/h3&gt;
&lt;p&gt;Now we just need to polish off the customer model. First, let&amp;#8217;s make all the fields except the phone number required:&lt;/p&gt;
&lt;pre&gt;
	validates_presence_of :first_name, :last_name, :address, :city, :state, :zip
&lt;/pre&gt;
&lt;p&gt;And we need to define the association with subscriptions:&lt;/p&gt;
&lt;pre&gt;
	has_many :subscriptions
&lt;/pre&gt;
&lt;p&gt;(We could use a has_one relationship here if we wanted to restrict a customer to having a single subscription.)&lt;/p&gt;
&lt;p&gt;And finally, we need to provide the &lt;code&gt;full_name&lt;/code&gt; method that we invented for use in the controller:&lt;/p&gt;
&lt;pre&gt;
   def full_name
      self.first_name + ' ' + self.last_name
   end
&lt;/pre&gt;
&lt;p&gt;Note that this method acts on an instance of the class, not on the class itself, so we do not use &lt;code&gt;self.&lt;/code&gt; in the method name, but we do use &lt;code&gt;self.&lt;/code&gt; when accessing the instance&amp;#8217;s attributes.&lt;/p&gt;
&lt;h2&gt;5. Ready to test!&lt;/h2&gt;
&lt;p&gt;This has been our most complex example yet. Let&amp;#8217;s make sure it all works.&lt;/p&gt;
&lt;p&gt;You&amp;#8217;ll need to restart the server, since we&amp;#8217;ve added a constant in the configuration file, which is only read on startup.&lt;/p&gt;
&lt;p&gt;Click the Subscribe! button, choose a subscription period, and enter a name and address. For the credit card information, the Braintree gateway documentation provides us with the following test card information:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Card type: &lt;span class=&quot;caps&quot;&gt;VISA&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;Card number: 4111 1111 1111 1111&lt;/li&gt;
	&lt;li&gt;Expiration: 10/2010&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can use any first and last name and address.&lt;/p&gt;
&lt;p&gt;Enter this information, click subscribe, and if all has gone well you&amp;#8217;ll see the home page, with &amp;#8220;Thanks for subscribing to our site!&amp;#8221; at the top. If you get a Rails error, check that you&amp;#8217;ve put in all the proper code in the subscriptions controller, the subscription and customer models, and the development environment file.&lt;/p&gt;
&lt;p&gt;Now click on the Admin nav button, and then on the Subscriptions Admin link. This will display the scaffolded subscription admin page, so we can see the information that has been written to the subscription database by our code. You can see the series of parameters that have been stuffed into the params field. You can also look at the Customers Admin page to see the customer information.&lt;/p&gt;
&lt;p&gt;To see how the parameters change with different levels of verification, enter a subscription with 77777 in the zip code field. The parameters should then show avsresponse=Z (zip code match) instead of N (no match). To simulate a verification code match, enter 999 in that field, and the parameters will show cvvresponse=M (match) instead of N (none). If you get a real gateway account, you&amp;#8217;ll find that you pay different fees depending on whether these items match or not, and you can configure your account to decline transactions that don&amp;#8217;t match.&lt;/p&gt;
&lt;p&gt;You can show what happens with a bad credit card number by entering any other number. The error message displayed doesn&amp;#8217;t come from the gateway; our validation of the creditcard object catches this before we get that far, and Active Merchant provides the error messages.&lt;/p&gt;
&lt;h2&gt;6. Next Steps&lt;/h2&gt;
&lt;p&gt;This was by far our most complicated lab, but even so, it falls short of a fully robust solution. A complete ecommerce solution has a lot of components and could easily be a two-day class of its own.&lt;/p&gt;
&lt;p&gt;Some things you&amp;#8217;d want to consider adding for a production environment:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Validate addresses more thoroughly than simply requiring that they not be blank&lt;/li&gt;
	&lt;li&gt;Set a default value for the subscription period&lt;/li&gt;
	&lt;li&gt;Process the exception that could occur if for some reason the final &lt;code&gt;save!&lt;/code&gt; failed&lt;/li&gt;
	&lt;li&gt;Create a user account for the customer, so they can log in and see their subscription status&lt;/li&gt;
	&lt;li&gt;Improve the layout of the subscription form&lt;/li&gt;
	&lt;li&gt;Process the error messages, rather than using the standard error_messages_for helper, to display them more cleanly&lt;/li&gt;
	&lt;li&gt;Improve the customer admin page by showing the subscription date and period&lt;/li&gt;
	&lt;li&gt;Refactor the subscription/create controller code to push more of the logic into the model&lt;/li&gt;
	&lt;li&gt;And, or course, we haven&amp;#8217;t provided any logic to deal with expiration of subscriptions or renewals&lt;/li&gt;
&lt;/ul&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:45 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79178-lab-5-subscriptions-and-credit-card</guid>
          <link>http://www.buildingwebapps.com/item/79178-lab-5-subscriptions-and-credit-card</link>
        </item>
        
        <item>
          <title>Lab 6. Testing</title>
          <description>&lt;p&gt;In this lab, we are going to look at some examples of unit and functional testing in Rails. Many of the tests that Rails generates for us automatically need some fixing up to track the changes we made in the code (such as adding authentication), and in this lab we&amp;#8217;ll walk through those changes. Then we&amp;#8217;ll take some first steps toward more complete tests.&lt;/p&gt;
&lt;p&gt;As we&amp;#8217;ve discussed, there are multiple approaches to writing tests. In this lab, we are writing tests after we&amp;#8217;ve created logic in our program. We encourage you to take a look at test-driven development as well.&lt;/p&gt;
&lt;p&gt;Creating automated tests for a Rails application is easier than with most other technologies, but it is still a relatively complex topic. If you&amp;#8217;re early in your Rails learning curve, you many want to skip this material and return to it later.&lt;/p&gt;
&lt;h2&gt;1. Running tests&lt;/h2&gt;
&lt;p&gt;Many generator scripts, such the scaffold generator, create placeholder test files at the same time they generate the associated application code. All of these test source files get placed in the test directory hierarchy of the application.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s start by seeing what happens when we run our tests:&lt;/p&gt;
&lt;pre&gt;
rake test
&lt;/pre&gt;
&lt;p&gt;You can run more focused sets of the tests by selecting &lt;code&gt;rake test:units&lt;/code&gt; or &lt;code&gt;rake test:functionals&lt;/code&gt; for instance.&lt;/p&gt;
&lt;p&gt;You can also trigger the tests within NetBeans too: just right-click on the project, select the Run Rake Task menu, and select one of the test options.&lt;/p&gt;
&lt;p&gt;Running our tests, we get a lot of feedback. Rake runs the tests in sequence, first the unit tests, then the functionals, and so on.&lt;/p&gt;
&lt;p&gt;It looks like we have some work to do.&lt;/p&gt;
&lt;h2&gt;2. Reading the test results&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s take apart the results a little at a time. For the unit tests, we scan down and see the lines:&lt;/p&gt;
&lt;pre&gt;
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -Ilib:test &quot;/Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake/rake_test_loader.rb&quot; &quot;test/unit/asset_test.rb&quot; &quot;test/unit/category_test.rb&quot; &quot;test/unit/contact_mailer_test.rb&quot; &quot;test/unit/contact_test.rb&quot; &quot;test/unit/content_block_test.rb&quot; &quot;test/unit/customer_test.rb&quot; &quot;test/unit/link_test.rb&quot; &quot;test/unit/subscription_test.rb&quot; &quot;test/unit/user_test.rb&quot; 
	Loaded suite /Library/Ruby/Gems/1.8/gems/rake-0.8.1/lib/rake/rake_test_loader
	Started
	..E..................
	Finished in 0.19429 seconds.	
&lt;/pre&gt;
&lt;p&gt;This block of information tells me what tests file were loaded and run, and using the simple command line result format, we see a series of periods and an &amp;#8220;E&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Recall that tests can have one of three states:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Success, indicated with a period (&lt;code&gt;.&lt;/code&gt;), sometimes also called &amp;#8220;green&amp;#8221;;&lt;/li&gt;
	&lt;li&gt;Failure, indicated with an &lt;code&gt;F&lt;/code&gt;, (called &amp;#8220;red&amp;#8221;), which means one of the assertions we wrote to test an assumption about our code did not work;&lt;/li&gt;
	&lt;li&gt;Error, indicated by an &lt;code&gt;E&lt;/code&gt;, (also called &amp;#8220;red&amp;#8221;), which means a program logic problem was detected in our test or application code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So, apparently, we have a number successful tests from seeing all of those periods, but one errored out.&lt;/p&gt;
&lt;p&gt;We look further down in the unit test results and see:&lt;/p&gt;
&lt;pre&gt;
   1) Error:
   test_contact_form(ContactMailerTest):
   ArgumentError: wrong number of arguments (1 for 4)
	    /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:410:in `contact_form'
	    /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:410:in `__send__'
	    /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:410:in `create!'
	    /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:403:in `initialize'
	    /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:351:in `new'
	    /Library/Ruby/Gems/1.8/gems/actionmailer-2.0.2/lib/action_mailer/base.rb:351:in `method_missing'
	    ./test/unit/contact_mailer_test.rb:10:in `test_contact_form'
	    /Library/Ruby/Gems/1.8/gems/activesupport-2.0.2/lib/active_support/testing/default.rb:7:in `run'

&lt;/pre&gt;
&lt;p&gt;This gives us a clue as to what went wrong and where. The &lt;code&gt;ContactMailerTest&lt;/code&gt; class&amp;#8217; &lt;code&gt;test_contact_form&lt;/code&gt; method had a programming error in it: an incorrect number of parameters were passed in. We&amp;#8217;ll look at that in a minute.&lt;/p&gt;
&lt;p&gt;Finally, just below the list of diagnostic messages for failing or error state tests, we get the summary:&lt;/p&gt;
&lt;pre&gt;
   21 tests, 33 assertions, 0 failures, 1 errors
&lt;/pre&gt;
&lt;p&gt;Wow, we had 21 tests with 33 assertion calls, and we didn&amp;#8217;t write any code. Before we look at the problem in &lt;code&gt;ContactMailerTest&lt;/code&gt;, what were all of the successful tests doing?&lt;/p&gt;
&lt;p&gt;Recall that unit tests check out our models. If we look in all but &lt;code&gt;ContactMailerTest&lt;/code&gt; and &lt;code&gt;UserTest&lt;/code&gt;, we see classes that contain this:&lt;/p&gt;
&lt;pre&gt;
   # Replace this with your real tests.
   def test_truth
      assert true
   end
&lt;/pre&gt;
&lt;p&gt;Alas, this is a do-nothing placeholder method, so we will ignore it for now. What we &lt;em&gt;should&lt;/em&gt; do is write some tests to exercise each model. We&amp;#8217;ll try that later.&lt;/p&gt;
&lt;p&gt;Peeking inside &lt;code&gt;UserModel&lt;/code&gt;, we see a different story. This class is full of tests. It turns out this test file was generated for us when we used the &lt;code&gt;restful_authentication&lt;/code&gt; plugin and associated generator to create our user and login system. &lt;code&gt;UserModel&lt;/code&gt; tests most of the functionality of our User model and is a good example of how a model should be tested.&lt;/p&gt;
&lt;h2&gt;3. Fixing the Unit Tests&lt;/h2&gt;
&lt;p&gt;For now, though, let&amp;#8217;s fix our one error in &lt;code&gt;ContactModelTest&lt;/code&gt;. This test is exercising the pseudo model that was created when we generated our contact mailer. The test inside of the test class looks like this:&lt;/p&gt;
&lt;pre&gt;
  def test_contact_form
    @expected.subject = 'ContactMailer#contact_form'
    @expected.body    = read_fixture('contact_form')
    @expected.date    = Time.now

    assert_equal @expected.encoded, ContactMailer.create_contact_form(@expected.date).encoded
  end
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Mailer related unit tests are a little strange. What this code is trying to do is test to see if the mail message that gets generated by the &lt;code&gt;ContactMailer.contact_form&lt;/code&gt; method is identical to a canned email fixture. &lt;code&gt;@expected&lt;/code&gt; is an instance variable created for us by the testing class that embodies a TMailer object and which we&amp;#8217;ll load with data and subsequently ask to generate a properly formed email body and headers (that is what &lt;code&gt;encoded&lt;/code&gt; does).&lt;/p&gt;
&lt;p&gt;Here we see that the other special calling convention on a mailer is being used:   &lt;code&gt;create_contact_form&lt;/code&gt;. Like &lt;code&gt;deliver_contact_form&lt;/code&gt;, this method uses the &lt;code&gt;contact_form&lt;/code&gt; method we wrote, but rather than building a mail message and then actually emailing it out, this form just returns a string that contains the body of the email message.&lt;/p&gt;
&lt;p&gt;To fix this test, we need to make the generated email body equal to the canned version of the email message we prepare in the @expected instance variable. Let&amp;#8217;s do that.&lt;/p&gt;
&lt;h3&gt;a. Call contact_form correctly&lt;/h3&gt;
&lt;p&gt;The error we received in the first place indicated that we are calling the underlying contact_form incorrectly (not enough parameters). We need to supply name, email, subject, message, and a sent_at time into that method:&lt;/p&gt;
&lt;pre&gt;
  assert_equal @expected.encoded, ContactMailer.create_contact_form('bob', 'bob@bob.com', 'test', 'testing', @expected.date).encoded
&lt;/pre&gt;
&lt;p&gt;If we run the test again (&lt;code&gt;rake test:units&lt;/code&gt;), we see the Error is gone, but now we have a Failure: the messages are not equal. Let&amp;#8217;s fix that.&lt;/p&gt;
&lt;h3&gt;b. Set up fixture data&lt;/h3&gt;
&lt;p&gt;First, lets create a fixture data file with the general body of the email message. This needs to match the view we&amp;#8217;ve created to contain the message body (in contact_form.erb from the previous lab):&lt;/p&gt;
&lt;pre&gt;
   Email from your web site

   From: &amp;lt;%= @name %&amp;gt;

   Message: &amp;lt;%= @message %&amp;gt;	
&lt;/pre&gt;
&lt;p&gt;Our fixture (found in the directory test/fixtures/contact_mailer/contact_form, another peculiar convention for mailers) looks like this:&lt;/p&gt;
&lt;pre&gt;
   Email from your web site

   From: bob

   Message: testing
&lt;/pre&gt;
&lt;p&gt;Note that we use hard-coded values in the fixture to make it an exemplar of a known working message body.&lt;/p&gt;
&lt;p&gt;We are closer, but this still fails. We need to set up the rest of the data needed for the encoded message we are using as our &amp;#8220;correct&amp;#8221; email state.&lt;/p&gt;
&lt;h3&gt;c. Fill in rest of email data&lt;/h3&gt;
&lt;p&gt;We encode the rest of the values in &lt;code&gt;test_contact_form's @expected&lt;/code&gt; variable with the sample data we choose to pass in to our own &lt;code&gt;contact_form&lt;/code&gt; call:&lt;/p&gt;
&lt;pre&gt;
   def test_contact_form
      @expected.subject = 'test'
      @expected.body    = read_fixture('contact_form')
      @expected.date    = Time.now
      @expected.to      = CONTACT_RECIPIENT
      @expected.from    = 'bob@bob.com'
    
      assert_equal @expected.encoded, ContactMailer.create_contact_form('bob', 'bob@bob.com', 'test', 'testing', @expected.date).encoded   
   end
&lt;/pre&gt;
&lt;p&gt;We save and run this now, and Success! Our admittedly mostly empty unit tests are all running in the &amp;#8220;green&amp;#8221; state. Let&amp;#8217;s go on to fix up some of our functional tests.&lt;/p&gt;
&lt;h2&gt;4. Fixing the Functional Tests&lt;/h2&gt;
&lt;p&gt;We now know how to read the test results, so switching our attention to the functional test block results, we see we have our work cut out for ourselves:&lt;/p&gt;
&lt;pre&gt;
   FFFFFFFFFFFFFFEFFF.FFFFFFFFFF......FFFFFFF..........F......FEEEE
&lt;/pre&gt;
&lt;p&gt;Which in my first run was equivalent to 64 tests, 76 assertions, 36 failures, 5 errors. Ouch.&lt;/p&gt;
&lt;p&gt;The Rails scaffold generator does a much better job at creating functional tests than it does with unit tests. Functional tests exercise our controllers. Peeking inside most of the test files in the test/functionals directory, you will note that they follow similar patterns.&lt;/p&gt;
&lt;p&gt;So what went wrong? In scanning through the test diagnostics, we see a lot of tests with similar issues. There are many tests with problems with redirects: &amp;#8220;Expected response to be a &lt;:success&gt;, but was &amp;lt;302&amp;gt;&amp;#8221;. There are many tests where things were not created, updated, or destroyed when expected. Thinking back, the common thread here is that all of these things need you to be logged in, yet, when the tests are automatically generated for us, the generator has no idea about this requirement. Let&amp;#8217;s fix that.
	
h3. a. Adding authentication to existing tests&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;restful_authentication&lt;/code&gt; plugin that we used provides a utility module called &lt;code&gt;AuthenticatedTestHelper&lt;/code&gt;. This module contains useful methods that you can mix-in to your test classes to simulate things like logging in, precisely what we need. Since we protected most of our administrative functions with those &lt;code&gt;before_filter :login_required&lt;/code&gt; checks, that will cause any of our tests that try to directly exercise an action to instead get redirected to a login screen.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s pick one of our controller&amp;#8217;s tests to fix up as our example. The others will follow similar patterns and you can look at the final lab code to see the changes (or try to make the changes yourself as an exercise).&lt;/p&gt;
&lt;p&gt;First, add the authentication system&amp;#8217;s helper at the top of the test class. While there, add the user fixtures so we have some test user accounts to log in with:&lt;/p&gt;
&lt;pre&gt;
   class ContentBlocksControllerTest &amp;lt; ActionController::TestCase
      include AuthenticatedTestHelper
      fixtures :users
&lt;/pre&gt;
&lt;p&gt;Now, let&amp;#8217;s use the new method we have to log in as the user quentin:&lt;/p&gt;
&lt;pre&gt;
   def test_should_get_index
      login_as :quentin
      get :index
      assert_response :success
      assert_not_nil assigns(:content_blocks)
   end
&lt;/pre&gt;&lt;p&gt;Run the tests again. The redirect failure on &lt;code&gt;test_should_get_index&lt;/code&gt; should now be gone.&lt;/p&gt;
&lt;p&gt;Go back to each method in this test class and add the &lt;code&gt;login_as :quentin&lt;/code&gt; line at the start of each test method. All of your redirect failures should now be cleaned up.&lt;/p&gt;
&lt;p&gt;But, there are still failures to fix. Look through the other functional tests and make the same changes to all of the others that are protected with the &lt;code&gt;before_filter&lt;/code&gt; (assets_controller_test, categories_controller_test, contacts_controller_test, links_controller_test).&lt;/p&gt;
&lt;p&gt;For the controllers that are selective about the login requirement, be sure to &lt;em&gt;not&lt;/em&gt; login when it isn&amp;#8217;t needed. For instance, contacts_controller_test&amp;#8217;s new and create tests are not protected by login. See the controller to confirm this.&lt;/p&gt;
&lt;p&gt;Run the tests again. We are getting closer. In my run, the results look like this:&lt;/p&gt;
&lt;pre&gt;
   Started
   FE...EE.......E.............F.......................F......FEEEE
   Finished in 6.451664 seconds.
&lt;/pre&gt;
&lt;h3&gt;b. Testing asset uploader&lt;/h3&gt;
&lt;p&gt;Our asset_controller_test is responsible for a number of those errors and failures, so let&amp;#8217;s clean it up next. Recall we are using the attachment_fu plugin to implement our uploader, and as luck would have it, the associated &lt;span class=&quot;caps&quot;&gt;README&lt;/span&gt; has some guidance about implementation, but not about testing. We&amp;#8217;ll have to dig in to the code to figure that out. We&amp;#8217;ve done that for you, so let&amp;#8217;s walk through what we need to do.&lt;/p&gt;
&lt;p&gt;First, make sure you have a test image file as an asset to play with. This takes the role of a fixture, so make a directory in the test/fixtures directory called &amp;#8220;files&amp;#8221; and place a test file in there. We used an image called &amp;#8220;railsquickstart-train.gif&amp;#8221;; you can use your own if you wish.&lt;/p&gt;
&lt;p&gt;Next, we add information we may need later in our regular fixture file, &lt;code&gt;assets.yml&lt;/code&gt;, which spells out some sample content. This will fix any issues with preloaded data for our Asset model, since the &lt;code&gt;validates_as_attachment&lt;/code&gt; call makes sure the minimum needed data for a legal attachment is present:&lt;/p&gt;
&lt;pre&gt;
	one:
	    name: first
	    content_type: 'image/gif'
	    filename: '/files/railsquickstart-train.gif'
	two:
	    name: second
	    content_type: 'image/gif'
	    filename: '/files/railsquickstart-train.gif'
&lt;/pre&gt;
&lt;p&gt;Just a few more things. Recall that asset uploading is done through a multipart form upload. The default RESTful controller tests that are created for us by the generate script do not know about the multipart form technique, so we need to help things along.&lt;/p&gt;
&lt;p&gt;In our &lt;code&gt;test_should_create_asset&lt;/code&gt; method, we need to use some testing helpers to simulate the file upload &lt;em&gt;and&lt;/em&gt; we need to tell the post method that we want to do a multipart form. Cruising the &lt;a href=&quot;/articles/10-file-upload-form-testing-fixtures&quot;&gt;documentation on BuildingWebApps.com&lt;/a&gt;, we find the fixture_file_upload method, and drop that in:&lt;/p&gt;
&lt;pre&gt;
  def test_should_create_asset
    login_as :quentin
    image_file = fixture_file_upload('/files/railsquickstart-train.gif', 'image/gif')
    assert_difference('Asset.count') do
      post :create, :asset =&amp;gt; { :name =&amp;gt; 'railslogo', :uploaded_data =&amp;gt; image_file },
                    :html =&amp;gt; {:multipart =&amp;gt; true }
    end

    assert_redirected_to asset_path(assigns(:asset))
  end	
&lt;/pre&gt;
&lt;p&gt;We also know that our &lt;code&gt;update&lt;/code&gt; method is going to have the same issue, so let&amp;#8217;s fix that at the same time:&lt;/p&gt;
&lt;pre&gt;
  def test_should_update_asset
    login_as :quentin
    image_file = fixture_file_upload('/files/railsquickstart-train.gif', 'image/gif')
    put :update, {:id =&amp;gt; assets(:one).id, :asset =&amp;gt; { :uploaded_data =&amp;gt; image_file }}
    assert_redirected_to asset_path(assigns(:asset))
  end	
&lt;/pre&gt;
&lt;p&gt;Run the tests. Indeed, we&amp;#8217;ve cleaned up the asset_controller tests.&lt;/p&gt;
&lt;h3&gt;c. User controller cleanup&lt;/h3&gt;
&lt;p&gt;We see there are still some issues around the user_controller_test. This was a test file that was generated for us when we used the restful_authentication plugin, so what&amp;#8217;s up?&lt;/p&gt;
&lt;p&gt;We added a before_filter in the users_controller too, to protect the creation of accounts. Let&amp;#8217;s fix that up quickly just like we did earlier. Add the AuthenticatedSystemHelper and use the login_as methods in the tests.&lt;/p&gt;
&lt;h3&gt;d. Email bits and pieces&lt;/h3&gt;
&lt;p&gt;Down to the wire, and it looks like we have just a few tests to fix up.&lt;/p&gt;
&lt;p&gt;The first appears to be an error in test_should_create_contact(ContactsControllerTest). It can&amp;#8217;t seem to locate a constant &lt;code&gt;ContactMailer::CONTACT_RECIPIENT&lt;/code&gt;. This is an error, so that means some kind of programming mistake is the cause. If you search the code with your editor, you&amp;#8217;ll see that CONTACT_RECIPIENT is indeed defined, but it is in config/environments/development.rb.&lt;/p&gt;
&lt;p&gt;Recall that testing runs in its own, completely separate, environment. Here is a case where a constant defined in the environment is just plain missing in the test environment. This is fixed by adding (or copying from development.rb) the CONTACT_RECIPIENT definition to the config/environments/test.rb file at the bottom:&lt;/p&gt;
&lt;pre&gt;
   CONTACT_RECIPIENT = 'tester@buildingwebapps.com'	
&lt;/pre&gt;
&lt;p&gt;Unless you have a working email connection, you&amp;#8217;ll also want to add a line to ignore errors when trying to connect to the mail system (below the CONTACT_RECIPIENT line is ok):&lt;/p&gt;
&lt;pre&gt;
   config.action_mailer.raise_delivery_errors = false
&lt;/pre&gt;
&lt;p&gt;Now, this is somewhat of a hack. We put mail configuration into the initializer file &lt;code&gt;config/initializers/mail.rb&lt;/code&gt;. Initializers run after the environment is loaded, and since we are globally setting up the sendmail environment there, we actually have our delivery_method setting stepped on and changed from :test to :sendmail.&lt;/p&gt;
&lt;p&gt;In retrospect, we probably should not have put the setup code in the mail.rb initializer file, and instead put the mail setup code in the environment-specific files. That way, we can get the test code to act properly without ignoring possible exceptions.&lt;/p&gt;
&lt;h3&gt;e. Customers use validation&lt;/h3&gt;
&lt;p&gt;In this lab, we don&amp;#8217;t have a lot of detailed testing for our merchant-oriented code. Even so, we still have a couple of failures to clean up.&lt;/p&gt;
&lt;p&gt;test_should_create_customer fails with a pattern that will become familiar to you as you test code that uses validations. Here, the code is trying to make a new customer object. The test, however, by default tries to create an empty customer. Looking inside of the Customer model, we see a validates_presence_of on :first_name, :last_name, :address, :city, :state, and :zip. We better fill in some values for those:&lt;/p&gt;
&lt;pre&gt;
  def test_should_create_customer
    assert_difference('Customer.count') do
      post :create, :customer =&amp;gt; { :first_name =&amp;gt; customers(:one).first_name,
                                    :last_name =&amp;gt; customers(:one).last_name,
                                    :address =&amp;gt; customers(:one).address,
                                    :city =&amp;gt; customers(:one).city,
                                    :state =&amp;gt; customers(:one).state,
                                    :zip =&amp;gt; customers(:one).zip }
    end

    assert_redirected_to customer_path(assigns(:customer))
  end
&lt;/pre&gt;
&lt;p&gt;But, what is this &lt;code&gt;customers(:one)&lt;/code&gt; syntax? Those aren&amp;#8217;t straight string constants. No, instead, we are using references to data we are storing in our fixtures file. Here is what is going on. First, we need the test controller class to load the fixture data, so we add the fixtures :customers line right after the class declaration:&lt;/p&gt;
&lt;pre&gt;
   ...
   class CustomersControllerTest &amp;lt; ActionController::TestCase
      fixtures :customers

      def test_should_get_index
      ...
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now, in our customers.yml fixtures file, we make sure there is some data:&lt;/p&gt;
&lt;pre&gt;
one:
  first_name: 'First'
  last_name: 'Last'
  address: 'Street'
  city: 'City'
  state: 'State'
  zip: 11111
  phone: '123-456-5678'
&lt;/pre&gt;
&lt;p&gt;As a reminder, &lt;span class=&quot;caps&quot;&gt;YAML&lt;/span&gt; files are picky about spacing. The label one: starts in the first column and its attributes (e.g. first_name:) are all tab indented.&lt;/p&gt;
&lt;p&gt;When loaded, fixtures can be referenced by name, using the name of the file (&amp;#8220;customers&amp;#8221;), then in parentheses, the name of the record we want as a symbol (e.g., &lt;code&gt;one:&lt;/code&gt; is the name in the fixtures file, but in code, we use the symbol &lt;code&gt;:one&lt;/code&gt;; note the way that colon moved!). We are now referring to a loaded instance of a model object, so any data field defined in that model is available to us, referred to by a period followed by the attribute name (e.g., &lt;code&gt;customers(:one).first_name&lt;/code&gt;). Now our test data and our test code are each in separate files, which makes it easier to maintain them in the future.&lt;/p&gt;
&lt;h3&gt;f. Subscriptions are compound objects&lt;/h3&gt;
&lt;p&gt;Subscriptions have a similar problem but are a bit more complex. The ActiveMerchant code and the logic to stitch together subscriptions, customers, and credit cards involves a dance of three models and some business logic. Our remaining error is in the &lt;code&gt;create&lt;/code&gt; method, which is the most complex in the controller. Let&amp;#8217;s supply all of the data it needs:&lt;/p&gt;
&lt;pre&gt;
	def test_should_create_subscription
    assert_difference('Subscription.count') do
      post :create, :subscription =&amp;gt; { :amount =&amp;gt; subscriptions(:one).amount,
                                        :period =&amp;gt; subscriptions(:one).period },
                    :customer =&amp;gt; { :first_name =&amp;gt; customers(:one).first_name,
                                    :last_name =&amp;gt; customers(:one).last_name,
                                    :address =&amp;gt; customers(:one).address,
                                    :city =&amp;gt; customers(:one).city,
                                    :state =&amp;gt; customers(:one).state,
                                    :zip =&amp;gt; customers(:one).zip }, 
                    :creditcard =&amp;gt; { :number =&amp;gt; '4111111111111111', 
                                    :type =&amp;gt; 'visa', 
                                    :month =&amp;gt; '10', :year =&amp;gt; '2010', 
                                    :verification_value =&amp;gt; '999'}
    end

    assert_redirected_to root_path
  end
&lt;/pre&gt;
&lt;p&gt;Wow, that&amp;#8217;s a lot!. Here is what we are doing (using a few different techniques for illustration). First, we are supplying a subscription object. The Subscription model requires a value &amp;#8220;amount&amp;#8221;, so we pull one from our fixtures (defined at the top of our file as usual with &lt;code&gt;fixtures :customers, :subscriptions&lt;/code&gt; since we are also using the customers data).&lt;/p&gt;
&lt;p&gt;Next, we need to provide a Customer model object, so we reuse the same fixture data we used earlier for the Customer tests.&lt;/p&gt;
&lt;p&gt;Lastly, we need to supply a CreditCard object, as used by ActiveMerchant. We &lt;em&gt;should&lt;/em&gt; put that data into its own fixture, but here we spell it out explicitly to show that you &lt;em&gt;can&lt;/em&gt; do that, if you need or want to.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;create&lt;/code&gt; method is now happy, receiving the three model objects it will need. On success, however, it doesn&amp;#8217;t send the user back to a &lt;code&gt;show&lt;/code&gt; method, like many RESTful controllers, but rather redirects the user to the home page. We adjust our assertion to point to the &lt;code&gt;root_path&lt;/code&gt;, which is defined in our routes.rb file as a named route for &amp;#8220;home page&amp;#8221;.&lt;/p&gt;
&lt;p&gt;Run the tests. Success! No problems.&lt;/p&gt;
&lt;h3&gt;g. Aside: Write a new functional test&lt;/h3&gt;
&lt;p&gt;We lost a valuable piece of information when we fixed all of those authentication tests. We really should also test that the right thing happens when someone is &lt;em&gt;not&lt;/em&gt; logged in. Let&amp;#8217;s write a quick example of a test that succeeds when an unauthenticated person tries to get a protected resource. In &lt;code&gt;ContentBlocksControllerTest&lt;/code&gt;, add this test:&lt;/p&gt;
&lt;pre&gt;
   def test_should_not_get_index_not_logged_in
      get :index
      assert_redirected_to new_session_path
   end
&lt;/pre&gt;
&lt;p&gt;Here, we are not logged in, and we are trying to get the list of content blocks. We should not be able to do this, and we expect to be redirected to the log-in screen. The &lt;code&gt;assert_redirected_to new_session_path&lt;/code&gt; uses the RESTful route to the session controller&amp;#8217;s new action. This is where login is initiated.&lt;/p&gt;
&lt;p&gt;Run the test, and success!&lt;/p&gt;
&lt;h2&gt;5. Writing New Unit Tests&lt;/h2&gt;
&lt;p&gt;Now that we have our existing tests working, and perhaps you&amp;#8217;ve tried your hands at an extra functional test, let&amp;#8217;s take a brief moment to look at the unit tests of our models. We&amp;#8217;ll build a couple of simple tests to get you started.&lt;/p&gt;
&lt;h3&gt;a. Fixtures with Associations&lt;/h3&gt;
&lt;p&gt;First, we&amp;#8217;ve mentioned in our lectures that fixtures have the ability to not only capture simple values representative of working and broken versions of our models, but also can represent associations such as has_many, has_one, belongs_to, and has_and_belongs_to_many (&lt;span class=&quot;caps&quot;&gt;HABTM&lt;/span&gt;). The fixture implementation in Rails 2 allows us to set these relationships up in a natural way, specifying these associations by name.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s look at the fixtures in two of our models, Links and Categories, to see this in action. These two models have &lt;span class=&quot;caps&quot;&gt;HABTM&lt;/span&gt; associations. When you have models instantiated in code, and you have used the association methods to identify the relationships, you gain a number of useful utility methods to address the members of the relation. In much the same way, we can &amp;#8220;name names&amp;#8221; in fixtures.&lt;/p&gt;
&lt;p&gt;Suppose we had this links.yml file:&lt;/p&gt;
&lt;pre&gt;
	apple:
	  name: Apple
	  url: http://www.apple.com/

	google:
	  name: Google
	  url: http://www.google.com/

	learningrails:
	  name: LearningRails PodCast
	  url: http://www.learningrails.com/
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;and this categories.yml file:&lt;/p&gt;
&lt;pre&gt;
	site:
	  name: Site
	blog:
	  name: Blog
	podcast:
		name: Podcast
&lt;/pre&gt;
&lt;p&gt;To indicate which links are currently categorized, we&amp;#8217;ll add an attribute &amp;#8220;categories:&amp;#8221; and literally list out the name of each category fixture member:&lt;/p&gt;
&lt;pre&gt;
	apple:
	  name: Apple
	  url: http://www.apple.com/
	  categories: site

	google:
	  name: Google
	  url: http://www.google.com/
	  categories: site

	learningrails:
	  name: LearningRails PodCast
	  url: http://www.learningrails.com/
	  categories: podcast, site
&lt;/pre&gt;
&lt;p&gt;Note that for &amp;#8220;apple&amp;#8221; and &amp;#8220;google&amp;#8221;, we&amp;#8217;ve only associated one category with each entry, but we&amp;#8217;ve still named the attribute &amp;#8220;categories&amp;#8221;. Rails will be able to tie the &amp;#8220;site&amp;#8221; category fixture entry to the associated &amp;#8220;link&amp;#8221; fixture when it creates the models for you during tests.&lt;/p&gt;
&lt;p&gt;&amp;#8220;learningrails&amp;#8221;, on the other hand, has two categories associated with it, podcast and site, so we list these out as a comma separated list.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s look at the categories.yml file:&lt;/p&gt;
&lt;pre&gt;
	site:
	  name: site
	  links: apple, google, learningrails

	blog:
	  name: blog

	podcast:
	  name: Podcast
	  links: learningrails
&lt;/pre&gt;
&lt;p&gt;Here we do the same thing, and link up (no pun intended) the association from the other side. We have three instances of a &amp;#8220;site&amp;#8221;, so we list them out as &amp;#8220;links&amp;#8221; (apple, google, learningrails). We have one podcast, but we still identify it with the plural form &amp;#8220;links&amp;#8221;. Note also that we don&amp;#8217;t have any &amp;#8220;blog&amp;#8221; items, so that entry in the fixture doesn&amp;#8217;t use the &amp;#8220;links&amp;#8221; identifier.&lt;/p&gt;
&lt;p&gt;Naming conventions are important here, and the capitalization and pluralization we&amp;#8217;ve used is critical.&lt;/p&gt;
&lt;p&gt;Now that we have a data set set up, let&amp;#8217;s test it out.&lt;/p&gt;
&lt;h3&gt;b. Testing Associations in our Model&lt;/h3&gt;
&lt;p&gt;We are going to load our fixtures into the &lt;code&gt;CategoryTest&lt;/code&gt; class, just like we did for functional tests earlier:&lt;/p&gt;
&lt;pre&gt;
   class CategoryTest &amp;lt; ActiveSupport::TestCase
      fixtures :categories, :links
	
      ...
&lt;/pre&gt;
&lt;p&gt;Note that since there are relationships between the models, we need &lt;em&gt;both&lt;/em&gt; fixture data files. If one or the other was missing, this would not work.&lt;/p&gt;
&lt;p&gt;Now, let&amp;#8217;s write a simple test that confirms the test data we just created (and demonstrates that Rails&amp;#8217; use of Test::Unit, the testing framework behind all of this magic, auto loads our data). This test will first check that our &amp;#8220;site&amp;#8221; category has three links using the notation we explained earlier for referring to fixture members:&lt;/p&gt;
&lt;pre&gt;
   def test_category_has_links
      catsite = categories(:site)
      assert_not_nil catsite
      assert_equal 3, catsite.links.length
   end	
&lt;/pre&gt;
&lt;p&gt;Here, we load a local variable &amp;#8220;catsite&amp;#8221; with the object defined with the &amp;#8220;site&amp;#8221; name in the categories.yml file. Recall we set up three links, so after asserting that we actually loaded the object with &lt;code&gt;assert_not_nil&lt;/code&gt;, we refer to the &lt;span class=&quot;caps&quot;&gt;HABTM&lt;/span&gt; derived method &amp;#8220;links&amp;#8221;, and check that three objects are in that array. Success!&lt;/p&gt;
&lt;p&gt;We could further test that the data is also loaded into the test database by using a finder method:&lt;/p&gt;
&lt;pre&gt;
   def test_category_load_site_category
      catsite = Category.find_by_name('site')
      assert_not_nil catsite
      assert_equal 3, catsite.links.length
   end
&lt;/pre&gt;
&lt;p&gt;Again, we should test and have success!&lt;/p&gt;
&lt;h3&gt;c. One more unit test&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s do a slightly more realistic unit test. Let&amp;#8217;s add a validation to one of our models and make sure it is working. In the &lt;code&gt;Category&lt;/code&gt; model, let&amp;#8217;s validate for the presence of the name attribute:&lt;/p&gt;
&lt;pre&gt;
   class Category &amp;lt; ActiveRecord::Base
      has_and_belongs_to_many :links
      validates_presence_of :name
   end	
&lt;/pre&gt;
&lt;p&gt;In our &lt;code&gt;CategoryTest&lt;/code&gt; class, let&amp;#8217;s add two tests that simply try to create an empty model and another that creates a model with a name. The first should fail and the second should succeed:&lt;/p&gt;
&lt;pre&gt;
  def test_should_create_category_with_name
    assert_difference 'Category.count' do
      Category.create(:name =&amp;gt; 'testname')
    end
  end
  def test_should_not_create_category_no_name
    assert_no_difference 'Category.count' do
      Category.create()
    end
  end	
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;assert_difference&lt;/code&gt; and &lt;code&gt;assert_no_difference&lt;/code&gt; methods run the code snippet provided as a parameter before and after their block executes (in this case, the code that tries to create a Category object).  &lt;code&gt;Category.count&lt;/code&gt; queries the database and gets the number of Category objects that currently exist. In the case where a create method works, the count should be different (one greater). In the case where it fails, the count should be identical.&lt;/p&gt;
&lt;p&gt;We run out tests and&amp;#8230; success? Our new tests are working, but one of our old tests is failing!&lt;/p&gt;
&lt;p&gt;When we added the validation in the Category model, the test case  test_should_create_category(CategoriesControllerTest) broke, because it tries to create an empty Category. To fix this, we can simply supply a name parameter:&lt;/p&gt;
&lt;pre&gt;
  def test_should_create_category
    login_as :quentin
    assert_difference('Category.count') do
      post :create, :category =&amp;gt; { :name =&amp;gt; 'testname' }
    end

    assert_redirected_to category_path(assigns(:category))
  end	
&lt;/pre&gt;
&lt;p&gt;Run the tests again, and finally, we have everything working again.&lt;/p&gt;
&lt;h2&gt;6. Further Exercises&lt;/h2&gt;
&lt;ol&gt;
	&lt;li&gt;Install the ZenTest gem and take autotest for a spin (gem install ZenTest). This will continually run your tests as you code and save files. A real time saver.&lt;/li&gt;
	&lt;li&gt;Fix the remaining (if any, purposely) broken tests in the lab6 or later source (release 6 in Unfuddle).&lt;/li&gt;
	&lt;li&gt;Add a few validations to the existing models and create the associated tests to check good and bad data test cases.&lt;/li&gt;
	&lt;li&gt;links_controller_test is missing a couple of tests for two actions we added later in development. What are they and can you write those tests?&lt;/li&gt;
	&lt;li&gt;Fix up the email settings by moving the initializer code out of mail.rb and in to the different environment files.&lt;/li&gt;
	&lt;li&gt;Add the Spider plugin for a simple Integration Test that crawls your site (script/plugin install svn://caboo.se/plugins/court3nay/spider_test).&lt;/li&gt;
&lt;/ol&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:45 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79179-lab-6-testing</guid>
          <link>http://www.buildingwebapps.com/item/79179-lab-6-testing</link>
        </item>
        
        <item>
          <title>Lab 7. Deployment</title>
          <description>&lt;p&gt;In this final lab, we are going to focus on moving your code into a source code control system, preparing a production server for your application, and finally, conducting an actual deployment.&lt;/p&gt;
&lt;p&gt;While this lab will use specific services, tools, and server software, the general concepts we will discuss can be applied to most environments. That said, we will not be going too deeply into system administrative tasks. For instance, we won&amp;#8217;t be discussing how to properly configure Apache or how to monitor logs. Deploying and conducting daily operations activities on a public production server is a deep topic and is completely independent of your use of Ruby on Rails. If you are new to server administration, it is definitely well worth your time to explore other books, web sites, and experts who can help you out. You&amp;#8217;ll find the links that we&amp;#8217;ve collected at &lt;a href=&quot;http://www.buildingwebapps.com/topic/3-servers&quot;&gt;www.buildingwebapps.com/topic/3-servers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note also that this lab uses files that are available in the sample application available to you in the seminar&amp;#8217;s Subversion repository. If you have been following the previous labs and recreating the project from scratch, we recommend that you get a copy of our code from Subversion so you can easily copy files over. In addition, the essential files are attached as resources to this write-up.&lt;/p&gt;
&lt;h2&gt;1. Putting source code into Subversion&lt;/h2&gt;
&lt;p&gt;The Ruby on Rails ecosystem has developed a number of tools to help make deployment and maintenance of your application easier and less time consuming. Automation is the mainstay of deployment practices where you want a reproducible and controlled process for moving software from development into production.&lt;/p&gt;
&lt;p&gt;Ruby on Rails practice, like many other programming environments, fundamentally assumes that you will use a source code control system (&lt;span class=&quot;caps&quot;&gt;SCCS&lt;/span&gt;) such as Subversion as the intermediary between your development computers and your production servers. While other deployment strategies are supported, using a &lt;span class=&quot;caps&quot;&gt;SCCS&lt;/span&gt;, especially Subversion, is by far the most common, and we&amp;#8217;ll focus on that technique here.&lt;/p&gt;
&lt;p&gt;Getting set up with Subversion is a three-step process:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Export the code from our shared repository to a directory on your computer. (You can skip this step if you are using your own code but you&amp;#8217;ll want a few of our files.)&lt;/li&gt;
	&lt;li&gt;Import the code into your personal repository on Unfuddle (uploading from your computer to your repository).&lt;/li&gt;
	&lt;li&gt;Check out a &lt;em&gt;working copy&lt;/em&gt; from your personal repository on Unfuddle to a directory on your computer. This is the copy you will modify as you continue working on the code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This third step is necessary, even though you already have another copy of the code on your computer from step 3, to create a copy that is linked to your personal repository.&lt;/p&gt;
&lt;p&gt;Once you&amp;#8217;ve done this, adding updates to the repository is simple, and you&amp;#8217;ll have a record of every update you&amp;#8217;ve ever done and the ability to go back to any point in time.&lt;/p&gt;
&lt;p&gt;If you&amp;#8217;re using NetBeans, you can do most of these task using its graphical user interface. For the initial process, though, it&amp;#8217;s easiest to use the command line. Then, to commit updates you&amp;#8217;ve made, you can right-click on your project name, and choose Subversion &amp;gt; Commit.&lt;/p&gt;
&lt;h3&gt;a. Grab clean copy of latest project&lt;/h3&gt;
&lt;p&gt;We are using Subversion as our source code control system. The first thing you need to do is get your project into a new Subversion repository that is yours and yours alone. To do this, however, you need a clean copy of the latest code.&lt;/p&gt;
&lt;p&gt;Subversion works by keeping private metadata in the same directories as your source code. This means that you can&amp;#8217;t simply copy a directory using your operating system that is checked in to another Subversion repository. If you try to do so, you also bring the old metadata, and if you try to add the copied directory into a new repository, Subversion complains.&lt;/p&gt;
&lt;p&gt;Note that there are two cases:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;If you &lt;em&gt;have not&lt;/em&gt; been typing the code in from scratch during the other labs, you should export a clean copy of the code from the seminar repository, as we will use the fresh copy as the basis for our first check-in to your own repository.&lt;/li&gt;
	&lt;li&gt;If you &lt;em&gt;have&lt;/em&gt; been typing in the code from scratch, you should still get a clean copy of the code and stash it in a temporary directory so it will be easier to copy a few files over to your project when we call them out later in this write-up.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First, go in to your command line and make a new directory. Consult your operating system documentation if you aren&amp;#8217;t familiar with this process.&lt;/p&gt;
&lt;p&gt;Next, change your current working directory to be &lt;em&gt;in&lt;/em&gt; the new directory. List the directory contents. It should be empty.&lt;/p&gt;
&lt;p&gt;Now, pull down the latest code with the Subversion command:&lt;/p&gt;
&lt;pre&gt;
svn export --username student http://railsquickstart.unfuddle.com/svn/railsquickstart_labs/trunk/labs
&lt;/pre&gt;
&lt;p&gt;If you don&amp;#8217;t remember the password, check the &lt;a href=&quot;http://groups.google.com/group/railsquickstart/files&quot;&gt;Files area of the private Google group&lt;/a&gt; for a document that lists the shared passwords.&lt;/p&gt;
&lt;p&gt;You should see various messages fly by. You will see something like this (most of the middle messages were deleted here for space):&lt;/p&gt;
&lt;pre&gt;
A    labs
A    labs/test
A    labs/test/unit
A    labs/test/unit/user_test.rb
A    labs/test/unit/asset_test.rb
...
A    labs/public/.htaccess
A    labs/public/stylesheets
A    labs/public/stylesheets/quickstart.css
A    labs/public/stylesheets/scaffold.css
A    labs/public/favicon.ico
Exported revision 10.	
&lt;/pre&gt;
&lt;p&gt;Once the code is downloaded, list your directory again. You should see a &lt;em&gt;labs&lt;/em&gt; directory. Change directory into &lt;em&gt;labs&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If you &lt;em&gt;have&lt;/em&gt; been typing in your own copy, change directory into that location instead.&lt;/p&gt;
&lt;h3&gt;Pushing clean copy into your repository&lt;/h3&gt;
&lt;p&gt;You are now ready to import the clean code base into Subversion. In this lab, we are using the &lt;a href=&quot;http://www.unfuddle.com/&quot;&gt;Unfuddle&lt;/a&gt; service. Unfuddle provides Subversion hosting as well as a number of other useful project management tools (like notes, todo lists, issue tracking, etc.). You received an Unfuddle account welcome sheet at the seminar, and you&amp;#8217;ll need to substitute information from that sheet into the following commands.&lt;/p&gt;
&lt;p&gt;Issue the Subversion import command to establish your new repository:&lt;/p&gt;
&lt;pre&gt;
svn import --username rqsXX_deploy http://rqsXX.unfuddle.com/svn/rqsXX_labs/trunk/labs -m &quot;Initial import&quot;
&lt;/pre&gt;
&lt;p&gt;Substitute your Unfuddle account number for all three instances of &lt;strong&gt;XX&lt;/strong&gt; in the above command. On pressing return, you should see a number of messages fly by indicating that your source is being imported. You will be queried for your password. If you need a reminder of the deploy account password, see the document in the Files area of the Google group.&lt;/p&gt;
&lt;p&gt;Here is what this step looks like on my machine (again, cutting out a chunk of &amp;#8220;Adding&amp;#8221; messages in the middle):&lt;/p&gt;
&lt;pre&gt;
Necrons:~/Development/work/labs chaupt$ svn import --username rqsXX_deploy http://rqsXX.unfuddle.com/svn/rqsXX_labs/trunk/labs -m &quot;Initial import&quot;
Authentication realm: &amp;lt;http://rqsXX.unfuddle.com:80&amp;gt; Unfuddle Repository
Password for 'rqs21_deploy': 
Adding         test
Adding         test/unit
Adding         test/unit/user_test.rb
Adding         test/unit/asset_test.rb
Adding         test/unit/customer_test.rb
Adding         test/unit/contact_mailer_test.rb
...
Adding         public/422.html
Adding         public/.htaccess
Adding         public/stylesheets
Adding         public/stylesheets/quickstart.css
Adding         public/stylesheets/scaffold.css
Adding         public/favicon.ico

Committed revision 1.
&lt;/pre&gt;
&lt;p&gt;Once the source is imported into your own repository, you can delete the local version in preparation for checking out a version-controlled copy. If you checked in your own, typed in source, keep the spare copy of the lab source in a temporary directory, you&amp;#8217;ll want to refer to it later.&lt;/p&gt;
&lt;p&gt;Change directory up one level from the &lt;em&gt;labs&lt;/em&gt; (or your own) directory and use the appropriate operating system command to delete the &lt;em&gt;labs&lt;/em&gt; directory. If you are worried about doing this, archive a copy in a safe place.&lt;/p&gt;
&lt;h3&gt;First checkout of version-controlled source&lt;/h3&gt;
&lt;p&gt;Choose where you are going to keep your development directory. Either stay in the current location or change directory to some other place now. Once ready, issue the Subversion checkout command to get a version controlled copy of your source code:&lt;/p&gt;
&lt;pre&gt;
svn co --username rqsXX_deploy http://rqsXX.unfuddle.com/svn/rqsXX_labs/trunk/labs
&lt;/pre&gt;
&lt;p&gt;Again, remember to substitute your Unfuddle account number for &lt;strong&gt;XX&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The results will be a long list of files:&lt;/p&gt;
&lt;pre&gt;
Necrons:~/Development/work chaupt$ svn co --username rqsXX_deploy http://rqsXX.unfuddle.com/svn/rqsXX_labs/trunk/labs
A    labs/test
A    labs/test/unit
A    labs/test/unit/user_test.rb
A    labs/test/unit/asset_test.rb
A    labs/test/unit/customer_test.rb
A    labs/test/unit/contact_mailer_test.rb
...
A    labs/public/.htaccess
A    labs/public/stylesheets
A    labs/public/stylesheets/quickstart.css
A    labs/public/stylesheets/scaffold.css
A    labs/public/favicon.ico
Checked out revision 1.
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You have now placed your code into source code control, and you have a copy with all of Subversion&amp;#8217;s special metadata on your local disk. From here on out, you need to remember that you &lt;strong&gt;must&lt;/strong&gt; use Subversion commands to manipulate files, and &lt;em&gt;not&lt;/em&gt; your operating system commands. The &lt;a href=&quot;http://svnbook.red-bean.com/&quot;&gt;Subversion Book&lt;/a&gt; is a free, complete reference book about using Subversion that you might want to consult.&lt;/p&gt;
&lt;p&gt;Change directory into the newly checked out code. If you&amp;#8217;re using our default name, you would enter the &lt;em&gt;labs&lt;/em&gt; directory.&lt;/p&gt;
&lt;h2&gt;2. Configuring Capistrano&lt;/h2&gt;
&lt;p&gt;Our next step is to prepare your project for using the Capistrano utility. Capistrano is a tool that can run commands and scripts (sometimes called recipes or tasks) on remote computers. This is particularly handy as you will by issuing the same commands over and over again as you develop your application and it is a pain to continually log in to one or more remote computers, type a bunch of commands, then do it again.&lt;/p&gt;
&lt;p&gt;Note that you enter all Capistrano commands in your &lt;em&gt;local&lt;/em&gt; terminal, even though the Capistrano scripts perform actions on your server. Capistrano makes an &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; connection to the server and issues commands there.&lt;/p&gt;
&lt;h3&gt;a. Capify your project&lt;/h3&gt;
&lt;p&gt;Make sure you are in your project&amp;#8217;s directory now. You should be at the top level of the Rails application that you just checked out. Make extra certain you are in that directory or the rest of this lab will not work! If you list the contents of this directory, you should see the folders app, config, etc.&lt;/p&gt;
&lt;p&gt;Issue the capify command to create the initial Capistrano configuration files:&lt;/p&gt;
&lt;pre&gt;
capify .
&lt;/pre&gt;
&lt;p&gt;Note that period at the end. If you are using the seminar provided code, you will see some &amp;#8220;skip&amp;#8221; messages, otherwise you will see a couple of files are created:&lt;/p&gt;
&lt;pre&gt;
Necrons:~/Development/work/test chaupt$ capify .
[add] writing `./Capfile'
[add] writing `./config/deploy.rb'
[done] capified!
&lt;/pre&gt;
&lt;h3&gt;b. Copy in lab provided recipes&lt;/h3&gt;
&lt;p&gt;This step is &lt;strong&gt;only&lt;/strong&gt; for those of you working on your own project or typing in the labs from scratch. If you are using the exported seminar files, you can skip this step.&lt;/p&gt;
&lt;p&gt;To complete this step you &lt;em&gt;will&lt;/em&gt; need access to the lab files (or the attachments to this write-up).&lt;/p&gt;
&lt;p&gt;First, replace the config/deploy.rb file with the one we provide.&lt;/p&gt;
&lt;p&gt;Second, create the file config/mongrel_cluster.yml and put the following contents inside:&lt;/p&gt;
&lt;pre&gt;
port: 8000
pid_file: /tmp/`hostname`-mongrel.pid
servers: 4
environment: production
address: 127.0.0.1
&lt;/pre&gt;
&lt;p&gt;The mongrel_cluster.yml file specifies on which ports your application server will listen, waiting for traffic to be forwarded to it from Apache. In the default Joyent configuration we are using, four instances of the app server are expected. By default, the app servers will listen on ports 8000, 8001, 8002, and 8003. The corresponding Apache configuration file that is generated during setup will use those ports too.&lt;/p&gt;
&lt;p&gt;If you have another application on the same server, you will want to minimally change the port number to be a set of four ports outside of the 8000-8003 range.&lt;/p&gt;
&lt;p&gt;Third, copy the lab&amp;#8217;s config/accelerator directory into your config directory. It should contain three files: accelerator_tasks.rb, apache_vhost.erb, smf_template.erb.&lt;/p&gt;
&lt;p&gt;Lastly, copy the lab&amp;#8217;s config/quickstart directory into your config directory. It should contain one file: quickstart_tasks.rb.&lt;/p&gt;
&lt;p&gt;At the command line at the top level of your project, issue the command &lt;code&gt;cap -T&lt;/code&gt;. You should see something like this:&lt;/p&gt;
&lt;pre&gt;
cap accelerator:create_smf          # Adds a SMF for the application
cap accelerator:create_vhost        # Creates an Apache 2.2 compatible virtua...
cap accelerator:restart_apache      # Restart apache
cap accelerator:setup_smf_and_vhost # After setup, creates Solaris SMF config...
cap accelerator:smf_delete          # Deletes the configuration
cap accelerator:smf_restart         # Restarts the application
cap accelerator:smf_start           # Starts the application
cap accelerator:smf_stop            # Stops the application
cap accelerator:svcs                # Shows all Services
cap deploy                          # Deploys your project.
cap deploy:check                    # Test deployment dependencies.
cap deploy:cleanup                  # Clean up old releases.
cap deploy:cold                     # Deploys and starts a `cold' application.
cap deploy:migrate                  # Run the migrate rake task.
cap deploy:migrations               # Deploy and run pending migrations.
cap deploy:pending                  # Displays the commits since your last de...
cap deploy:pending:diff             # Displays the `diff' since your last dep...
cap deploy:rollback                 # Rolls back to a previous version and re...
cap deploy:rollback_code            # Rolls back to the previously deployed v...
cap deploy:setup                    # Prepares one or more servers for deploy...
cap deploy:symlink                  # Updates the symlink to the most recentl...
cap deploy:update                   # Copies your project and updates the sym...
cap deploy:update_code              # Copies your project to the remote servers.
cap deploy:upload                   # Copy files to the currently deployed ve...
cap deploy:web:disable              # Present a maintenance page to visitors.
cap deploy:web:enable               # Makes the application web-accessible ag...
cap invoke                          # Invoke a single command on the remote s...
cap quickstart:copy_revision_info   # Make REVISION available from site
cap quickstart:get_db_dump          # Retrieve production DB Dump
cap quickstart:tail_server_logs     # Tail production server log files
cap shell                           # Begin an interactive Capistrano session.

Some tasks were not listed, either because they have no description,
or because they are only used internally by other tasks. To see all
tasks, type `cap -Tv'.

Extended help may be available for these tasks.
Type `cap -e taskname' to view it.
&lt;/pre&gt;
&lt;p&gt;Note the accelerator and quickstart lines. If you don&amp;#8217;t see those, you didn&amp;#8217;t copy the lab files correctly. Go back and check that they are in your config directory.&lt;/p&gt;
&lt;h3&gt;c. Update deploy.rb with your info&lt;/h3&gt;
&lt;p&gt;Open up the deploy.rb file with a programmer&amp;#8217;s editor. We are going to change the project metadata so it points to your personal Accelerator and Subversion repository. Each area we need to edit has a &lt;em&gt;&lt;span class=&quot;caps&quot;&gt;TODO&lt;/span&gt;&lt;/em&gt; comment immediately above it.&lt;/p&gt;
&lt;p&gt;First, if you are not using the name &amp;#8220;labs&amp;#8221; for your application, change this line:&lt;/p&gt;
&lt;pre&gt;
#
# TODO: change to your application (labs)
#
set :application, &quot;labs&quot;
&lt;/pre&gt;
&lt;p&gt;The name of your application is typically the name of the directory your code is sitting in. Rails created this directory if you started your project from scratch. If you peek inside of your config/database.yml file, your application name was also used as the root of the database name (e.g. labs_production).&lt;/p&gt;
&lt;p&gt;In this write-up, we assume you are using &amp;#8220;labs&amp;#8221; for the rest of the exercise. We will point out other places you need change but not always provide full commands in those cases.&lt;/p&gt;
&lt;p&gt;Next, change the location of your Subversion repository:&lt;/p&gt;
&lt;pre&gt;
#
# TODO: change to your subversion repository (railsquickstart -&amp;gt; rqsNN) (_quickstart -&amp;gt; _labs)
#
set :repository,  &quot;http://railsquickstart.unfuddle.com/svn/railsquickstart_labs/trunk/#{application}&quot;
&lt;/pre&gt;
&lt;p&gt;Here, you should change the words &amp;#8220;railsquickstart&amp;#8221; (two of them) to rqsXX, where &lt;strong&gt;XX&lt;/strong&gt; is your Unfuddle number:&lt;/p&gt;
&lt;pre&gt;
set :repository,  &quot;http://rqsXX.unfuddle.com/svn/rqsXX_labs/trunk/#{application}&quot;
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Your IP number is on the credentials welcome page you were handed at the seminar. Update the IP number of the Accelerator you were assigned to in the domain setting:&lt;/p&gt;
&lt;pre&gt;
#
# TODO: Put your public IP here
#
set :domain, '8.17.170.221'
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Also on the credentials welcome page, find your &lt;em&gt;zone&lt;/em&gt; name. This is at the top of the paper and is part of From: line of the email. Look for the string that starts with a &amp;#8220;Z&amp;#8221; and then followed by a seven digit number (e.g. &amp;#8220;z1234567&amp;#8221;).&lt;/p&gt;
&lt;p&gt;Update the deploy.rb file&amp;#8217;s server name and alias lines with your zone name:&lt;/p&gt;
&lt;pre&gt;
#
# TODO: Put your domain name here (zNNNNNAA.textdrive.com)
#
set :server_name, &quot;zNNNNNAA.textdrive.com&quot;
set :server_alias, &quot;*.zNNNNNAA.textdrive.com&quot;
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Save the file.&lt;/p&gt;
&lt;h3&gt;d. Checkin your changes&lt;/h3&gt;
&lt;p&gt;You need to save the changes you have made into your Subversion repository. If you are working with your own code, you have a few extra steps to add files into the repository. Start by typing:&lt;/p&gt;
&lt;pre&gt;
svn status
&lt;/pre&gt;
&lt;p&gt;You will see a list of files that have been added (an initial question mark is displayed) or files that have been modified (an initial M is displayed):&lt;/p&gt;
&lt;pre&gt;
?      Capfile
?      config/accelerator
?      config/quickstart
?      config/deploy.rb
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;For all files that are new (need to be added), issue this command (substituting &amp;#8220;filename&amp;#8221; below with the full path/name string shown above):&lt;/p&gt;
&lt;pre&gt;
svn add filename
&lt;/pre&gt;
&lt;p&gt;Once you have added all files, issue the &lt;code&gt;svn status&lt;/code&gt; command again and be certain no question marks show up.&lt;/p&gt;
&lt;p&gt;Now, check-in your files with:&lt;/p&gt;
&lt;pre&gt;
svn ci -m &quot;Deployment files&quot;
&lt;/pre&gt;
&lt;p&gt;and you will see something akin to:&lt;/p&gt;
&lt;pre&gt;
Sending        config/deploy.rb
Transmitting file data ...
Committed revision 2.	
&lt;/pre&gt;
&lt;p&gt;You should now be all set. If you issue another &lt;code&gt;svn status&lt;/code&gt; at this point, no files should be shown.&lt;/p&gt;
&lt;h2&gt;3. Setting up the server&lt;/h2&gt;
&lt;p&gt;Before starting the deployment process, you need to tweak a few things on your production server.&lt;/p&gt;
&lt;h3&gt;a. Logging in to the server shell&lt;/h3&gt;
&lt;p&gt;Its time to switch over to your Accelerator and make sure you can log in. Using your IP address, fire up your secure shell program and log in (substitute your IP number for the &lt;strong&gt;&lt;span class=&quot;caps&quot;&gt;XXX&lt;/span&gt;&lt;/strong&gt;):&lt;/p&gt;
&lt;pre&gt;
ssh deploy@8.17.170.XXX
Password: 
Last login: Wed Mar  5 18:22:49 2008 from 76.14.mmm.nnn
                                __                       __ 
                       __      / /___  __  _____  ____  / /_
                    __/ /___  / / __ \/ / / / _ \/ __ \/ __/
                   /_  __/ /_/ / /_/ / /_/ /  __/ / / / /_  
                    /_/  \____/\____/\__, /\___/_/ /_/\__/  
                                    /____/ Accelerators
[z1234567:~] deploy$
&lt;/pre&gt;
&lt;p&gt;You will need to use the deploy account&amp;#8217;s default password (see the Files are of the Google group if you can&amp;#8217;t remember what it is). You will want to change it once you have set things up. See the follow-up section at the end of this article.&lt;/p&gt;
&lt;h3&gt;b. Database setup&lt;/h3&gt;
&lt;p&gt;If you are not using &lt;em&gt;labs&lt;/em&gt; as your application (and database) name, or are not using the default database credentials (username and password: seminar), you need to configure a production database now. From the server&amp;#8217;s shell do the following:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;mysql -p -u root&lt;/li&gt;
	&lt;li&gt;create database myapp_production;&lt;/li&gt;
	&lt;li&gt;grant all on myapp_production.* to &amp;#8217;username&amp;#8217;@&amp;#8217;localhost&amp;#8217; identified by &amp;#8216;password&amp;#8217;;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When asked for the password, use the password supplied for MySQL root access on your Accelerator welcome paper. Instead of _myapp_production_, replace &lt;em&gt;myapp&lt;/em&gt; with the name of the application you specified in the deploy.rb file and your database.yml file, and replace &amp;#8220;username&amp;#8221; and &amp;#8220;password&amp;#8221; with whatever you want to use. Remember that these need to match the username and password specified in your database.yml file for the production database.&lt;/p&gt;
&lt;p&gt;Here is what my commands looked like:&lt;/p&gt;
&lt;pre&gt;
[z1234567:~] deploy$ mysql -p -u root
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.0.37-log Source distribution

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql&amp;gt; create database myapp_production;
Query OK, 1 row affected (0.00 sec)

mysql&amp;gt; grant all on myapp_production.* to 'seminar'@'localhost' identified by 'seminar';
Query OK, 0 rows affected (0.00 sec)

mysql&amp;gt; exit
Bye
[z1234567:~] deploy$ 
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;c. Update gems&lt;/h3&gt;
&lt;p&gt;The Accelerators are not currently completely up-to-date with the versions of Ruby gems we need for our projects. Let&amp;#8217;s update them now.&lt;/p&gt;
&lt;p&gt;While still logged in to your private Accelerator issue this command:&lt;/p&gt;
&lt;pre&gt;
sudo gem install -f gem_plugin rails RedCloth
&lt;/pre&gt;
&lt;p&gt;After a while, you should see results similar to the this:&lt;/p&gt;
&lt;pre&gt;
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

Password:
Updating metadata for 335 gems from http://gems.rubyforge.org
...............................................................................................................................................................................................................................................................................................................................................
complete
Successfully installed gem_plugin-0.2.3
Successfully installed rake-0.8.1
Successfully installed activesupport-2.0.2
Successfully installed activerecord-2.0.2
Successfully installed actionpack-2.0.2
Successfully installed actionmailer-2.0.2
Successfully installed activeresource-2.0.2
Successfully installed rails-2.0.2
Successfully installed RedCloth-3.0.4
9 gems installed
Installing ri documentation for gem_plugin-0.2.3...
Installing ri documentation for rake-0.8.1...
Installing ri documentation for activesupport-2.0.2...
Installing ri documentation for activerecord-2.0.2...
Installing ri documentation for actionpack-2.0.2...
Installing ri documentation for actionmailer-2.0.2...
Installing ri documentation for activeresource-2.0.2...
Installing RDoc documentation for gem_plugin-0.2.3...
Installing RDoc documentation for rake-0.8.1...
Installing RDoc documentation for activesupport-2.0.2...
Installing RDoc documentation for activerecord-2.0.2...
Installing RDoc documentation for actionpack-2.0.2...
Installing RDoc documentation for actionmailer-2.0.2...
Installing RDoc documentation for activeresource-2.0.2...	
&lt;/pre&gt;
&lt;p&gt;Note your output may be slightly different if you have used sudo already or otherwise touched your gems.&lt;/p&gt;
&lt;h2&gt;4. Deployment setup and sanity check&lt;/h2&gt;
&lt;p&gt;Go ahead and logout out of the secure shell session and make sure you are back in your local development machine&amp;#8217;s code directory. We are going to do a few one-time setup tasks to get your application prepared and get Capistrano primed for use.&lt;/p&gt;
&lt;h3&gt;a. Deploy setup step&lt;/h3&gt;
&lt;p&gt;Capistrano uses a particular remote directory structure for its deployments. Inside of your application area on the remote server (/var/www/apps with our default settings), Capistrano will create a directory for your application, and within that, it will create three additional directories.&lt;/p&gt;
&lt;p&gt;The first will be a &lt;em&gt;releases&lt;/em&gt; area. This is where Capistrano checks out each version of your application. Capistrano is smart about keeping versions separate, as well as keeping old versions of the application around. It does this so if something goes wrong with a deployment, it is possible to revert to a prior version quickly.&lt;/p&gt;
&lt;p&gt;Second, Capistrano creates a soft link (also known as an alias) called &lt;em&gt;current&lt;/em&gt;. Current will always point to the active application directory in the releases area (this will normally be the most recent version). Capistrano manages this link for you.&lt;/p&gt;
&lt;p&gt;Lastly, Capistrano will make a &lt;em&gt;shared&lt;/em&gt; directory. Shared is used for common directories and files that will be used across deployments. Your log files end up in this directory, for instance.&lt;/p&gt;
&lt;p&gt;In addition, for our environment, Capistrano does a couple of other housekeeping tasks for us. For one, it generates an Apache VHost configuration for our application. For another, it links our application with the Solaris System Management Facility (&lt;span class=&quot;caps&quot;&gt;SMF&lt;/span&gt;). &lt;span class=&quot;caps&quot;&gt;SMF&lt;/span&gt; is the tool that starts and stops our application, both at system startup and shutdown as well as whenever we do a deployment.&lt;/p&gt;
&lt;p&gt;To set this all up, issue the setup command:&lt;/p&gt;
&lt;pre&gt;
cap deploy:setup
&lt;/pre&gt;
&lt;p&gt;You only do this one time, and the results should look something like:&lt;/p&gt;
&lt;pre&gt;
  * executing `deploy:setup'
  * executing &quot;umask 02 &amp;amp;&amp;amp; mkdir -p /var/www/apps/labs /var/www/apps/labs/releases /var/www/apps/labs/shared /var/www/apps/labs/shared/system /var/www/apps/labs/shared/log /var/www/apps/labs/shared/pids&quot;
    servers: [&quot;8.17.170.221&quot;]
Password: 
    [8.17.170.221] executing command
    command finished
    triggering after callbacks for `deploy:setup'
  * executing `accelerator:setup_smf_and_vhost'
  * executing `accelerator:create_smf'
set variables
    servers: [&quot;8.17.170.221&quot;]
  * uploading /var/www/apps/labs/shared/labs-smf.xml
 ** uploading data to 8.17.170.221:/var/www/apps/labs/shared/labs-smf.xml
  * done uploading data to 8.17.170.221:/var/www/apps/labs/shared/labs-smf.xml
    upload finished
  * executing &quot;sudo -p 'sudo password: ' svccfg import /var/www/apps/labs/shared/labs-smf.xml&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
*** [err :: 8.17.170.221] 
    command finished
  * executing `accelerator:create_vhost'
  * executing &quot;ifconfig -a | ggrep -A1 e1000g0 | grep inet | awk '{print $2}'&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
    servers: [&quot;8.17.170.221&quot;]
  * uploading /var/www/apps/labs/shared/labs-apache-vhost.conf
 ** uploading data to 8.17.170.221:/var/www/apps/labs/shared/labs-apache-vhost.conf
  * done uploading data to 8.17.170.221:/var/www/apps/labs/shared/labs-apache-vhost.conf
    upload finished
  * executing &quot;sudo -p 'sudo password: ' cp /var/www/apps/labs/shared/labs-apache-vhost.conf /opt/csw/apache2/etc/virtualhosts/labs.conf&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
  * executing `accelerator:restart_apache'
  * executing &quot;sudo -p 'sudo password: ' svcadm refresh svc:/network/http:cswapache2&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished	
&lt;/pre&gt;
&lt;p&gt;For this and future Capistrano commands, you will often need to supply a password. This is your &lt;em&gt;deploy&lt;/em&gt; account&amp;#8217;s password (see the Files area of the Google group if you can&amp;#8217;t remember the default password. If you change it, remember what you changed it to!).&lt;/p&gt;
&lt;p&gt;(Note: There is an error line in the above output. We are tracking that down, but it appears to be benign at the moment.)&lt;/p&gt;
&lt;h3&gt;b. Check setup&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s confirm that the environment is set up as Capistrano expects it should be:&lt;/p&gt;
&lt;pre&gt;
cap deploy:check
&lt;/pre&gt;
&lt;p&gt;A long list of status messages will flow by, but if you have followed all of the steps up to this point, you should get a message &lt;code&gt;You appear to have all necessary dependencies installed&lt;/code&gt;. If so, you are ready for the first deployment. If not, go back and check to see what piece is missing.&lt;/p&gt;
&lt;h2&gt;5. Cold deployment&lt;/h2&gt;
&lt;p&gt;The very first time you deploy an application with Capistrano, you have to boot-strap the environment. For instance, your database tables don&amp;#8217;t exist yet, so you need to be sure migrations have been run. Also, since you haven&amp;#8217;t loaded the app yet, the application servers don&amp;#8217;t need to be shutdown and restarted. This initial deployment is called the &lt;em&gt;cold&lt;/em&gt; deployment. Like all other tasks, Capistrano has a command to help with this case.&lt;/p&gt;
&lt;h3&gt;a. Going from cold to warm&lt;/h3&gt;
&lt;p&gt;Like setup, cold deployment only happens once, the very first time you deploy a specific application. To get started, issue this command:&lt;/p&gt;
&lt;pre&gt;
cap deploy:cold
&lt;/pre&gt;
&lt;p&gt;Capistrano proceeds to check out your code from your Subversion repository, sets up the &lt;em&gt;current&lt;/em&gt; link to the code, migrates the database, and starts the application. Here is a log of my cold deployment; yours should looks something similar to the following if you are using the seminar code:&lt;/p&gt;
&lt;pre&gt;
  * executing `deploy:cold'
  * executing `deploy:update'
 ** transaction: start
  * executing `deploy:update_code'
  * executing &quot;svn checkout -q --username deploy --password SECRET --no-auth-cache  -r1 http://rqs21.unfuddle.com/svn/rqs21_labs/trunk/labs /var/www/apps/labs/releases/20080306031255 &amp;amp;&amp;amp; (echo 1 &amp;gt; /var/www/apps/labs/releases/20080306031255/REVISION)&quot;
    servers: [&quot;8.17.170.221&quot;]
Password: 
    [8.17.170.221] executing command
    command finished
  * executing `deploy:finalize_update'
  * executing &quot;chmod -R g+w /var/www/apps/labs/releases/20080306031255&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
  * executing &quot;rm -rf /var/www/apps/labs/releases/20080306031255/log /var/www/apps/labs/releases/20080306031255/public/system /var/www/apps/labs/releases/20080306031255/tmp/pids &amp;amp;&amp;amp;\n      mkdir -p /var/www/apps/labs/releases/20080306031255/public &amp;amp;&amp;amp;\n      mkdir -p /var/www/apps/labs/releases/20080306031255/tmp &amp;amp;&amp;amp;\n      ln -s /var/www/apps/labs/shared/log /var/www/apps/labs/releases/20080306031255/log &amp;amp;&amp;amp;\n      ln -s /var/www/apps/labs/shared/system /var/www/apps/labs/releases/20080306031255/public/system &amp;amp;&amp;amp;\n      ln -s /var/www/apps/labs/shared/pids /var/www/apps/labs/releases/20080306031255/tmp/pids&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
  * executing &quot;find /var/www/apps/labs/releases/20080306031255/public/images /var/www/apps/labs/releases/20080306031255/public/stylesheets /var/www/apps/labs/releases/20080306031255/public/javascripts -exec touch -t 200803060315.34 {} ';'; true&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
  * executing `deploy:symlink'
  * executing &quot;rm -f /var/www/apps/labs/current &amp;amp;&amp;amp; ln -s /var/www/apps/labs/releases/20080306031255 /var/www/apps/labs/current&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
 ** transaction: commit
  * executing `deploy:migrate'
  * executing &quot;ls -x /var/www/apps/labs/releases&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
  * executing &quot;cd /var/www/apps/labs/releases/20080306031255; rake RAILS_ENV=production  db:migrate&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
 ** [out :: 8.17.170.221] (in /var/www/apps/labs/releases/20080306031255)
 ** [out :: 8.17.170.221] == 1 CreateContentBlocks: migrating ===========================================
 ** [out :: 8.17.170.221] -- create_table(:content_blocks)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0218s
 ** [out :: 8.17.170.221] == 1 CreateContentBlocks: migrated (0.0220s) ==================================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 2 CreateAssets: migrating ==================================================
 ** [out :: 8.17.170.221] -- create_table(:assets)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0180s
 ** [out :: 8.17.170.221] == 2 CreateAssets: migrated (0.0181s) =========================================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 3 CreateUsers: migrating ===================================================
 ** [out :: 8.17.170.221] -- create_table(&quot;users&quot;, {:force=&amp;gt;true})
 ** [out :: 8.17.170.221] -&amp;gt; 0.0031s
 ** [out :: 8.17.170.221] == 3 CreateUsers: migrated (0.0033s) ==========================================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 4 CreateLinks: migrating ===================================================
 ** [out :: 8.17.170.221] -- create_table(:links)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0069s
 ** [out :: 8.17.170.221] == 4 CreateLinks: migrated (0.0070s) ==========================================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 5 CreateCategories: migrating ==============================================
 ** [out :: 8.17.170.221] -- create_table(:categories)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0018s
 ** [out :: 8.17.170.221] == 5 CreateCategories: migrated (0.0020s) =====================================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 6 AddLinkCategoryJoin: migrating ===========================================
 ** [out :: 8.17.170.221] -- create_table(:categories_links, {:id=&amp;gt;false})
 ** [out :: 8.17.170.221] -&amp;gt; 0.0014s
 ** [out :: 8.17.170.221] == 6 AddLinkCategoryJoin: migrated (0.0016s) ==================================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 7 CreateContacts: migrating ================================================
 ** [out :: 8.17.170.221] -- create_table(:contacts)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0133s
 ** [out :: 8.17.170.221] == 7 CreateContacts: migrated (0.0134s) =======================================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 8 AddDefensioColumnsToContacts: migrating ==================================
 ** [out :: 8.17.170.221] -- add_column(:contacts, :spam, :boolean, {:default=&amp;gt;false})
 ** [out :: 8.17.170.221] -&amp;gt; 0.0037s
 ** [out :: 8.17.170.221] -- add_column(:contacts, :spaminess, :float)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0034s
 ** [out :: 8.17.170.221] -- add_column(:contacts, :signature, :string)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0036s
 ** [out :: 8.17.170.221] == 8 AddDefensioColumnsToContacts: migrated (0.0110s) =========================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 9 CreateCustomers: migrating ===============================================
 ** [out :: 8.17.170.221] -- create_table(:customers)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0027s
 ** [out :: 8.17.170.221] == 9 CreateCustomers: migrated (0.0028s) ======================================
 ** [out :: 8.17.170.221] 
 ** [out :: 8.17.170.221] == 10 CreateSubscriptions: migrating ==========================================
 ** [out :: 8.17.170.221] -- create_table(:subscriptions)
 ** [out :: 8.17.170.221] -&amp;gt; 0.0041s
 ** [out :: 8.17.170.221] == 10 CreateSubscriptions: migrated (0.0043s) =================================
 ** [out :: 8.17.170.221] 
    command finished
  * executing `deploy:start'
  * executing `accelerator:smf_start'
  * executing &quot;sudo -p 'sudo password: ' svcadm enable -r /network/mongrel/labs-production&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
*** [err :: 8.17.170.221] 
    command finished
  * executing `accelerator:restart_apache'
  * executing &quot;sudo -p 'sudo password: ' svcadm refresh svc:/network/http:cswapache2&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;We are very close to testing our app out. One more thing step!&lt;/p&gt;
&lt;h3&gt;b. Warm to hot&lt;/h3&gt;
&lt;p&gt;A small quirk of the Accelerator environment and the System Management Facility requires that we restart the mongrel application server to get it going. We can start, stop, and restart our application using Capistrano, of course. For this task, issue:&lt;/p&gt;
&lt;pre&gt;
cap accelerator:smf_restart
&lt;/pre&gt;
&lt;p&gt;Remember to enter this in your local terminal, &lt;em&gt;not&lt;/em&gt; in an &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; session.&lt;/p&gt;
&lt;p&gt;You will see results something like this:&lt;/p&gt;
&lt;pre&gt;
  * executing `accelerator:smf_restart'
  * executing `accelerator:smf_stop'
  * executing &quot;sudo -p 'sudo password: ' svcadm disable /network/mongrel/labs-production&quot;
    servers: [&quot;8.17.170.221&quot;]
Password: 
    [8.17.170.221] executing command
*** [err :: 8.17.170.221] 
    command finished
  * executing `accelerator:smf_start'
  * executing &quot;sudo -p 'sudo password: ' svcadm enable -r /network/mongrel/labs-production&quot;
    servers: [&quot;8.17.170.221&quot;]
    [8.17.170.221] executing command
    command finished
&lt;/pre&gt;
&lt;p&gt;Again, that error in this case is Capistrano and Joyent misbehaving, and is benign at the moment.&lt;/p&gt;
&lt;h3&gt;c. Try it!&lt;/h3&gt;
&lt;p&gt;Fire up your browser and hit the &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; http://8.17.170.&lt;span class=&quot;caps&quot;&gt;XXX&lt;/span&gt; where &lt;strong&gt;&lt;span class=&quot;caps&quot;&gt;XXX&lt;/span&gt;&lt;/strong&gt; is your correct IP number.&lt;/p&gt;
&lt;p&gt;Success!&lt;/p&gt;
&lt;h2&gt;6. Normal deployment&lt;/h2&gt;
&lt;p&gt;After the initial deployment, your work will typically fall into a pattern of incremental improvements to your code, and then deploying to move that (tested!) code into production. There are two common scenarios.&lt;/p&gt;
&lt;h3&gt;a. Deploying code only&lt;/h3&gt;
&lt;p&gt;When you need to get code into production and haven&amp;#8217;t made any changes to your database, you use Capistrano&amp;#8217;s standard deploy command:&lt;/p&gt;
&lt;pre&gt;
cap deploy
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;deploy&lt;/code&gt; command checks out your code into the releases directory, stops the application server, switches the current directory link, and finally restarts the application server.&lt;/p&gt;
&lt;h3&gt;b. Deploying with database changes&lt;/h3&gt;
&lt;p&gt;If you&amp;#8217;ve made changes to your model, and you have migrations that need to be run, Capistrano has a special command for this scenario. Rather than the plain &lt;code&gt;cap deploy&lt;/code&gt; command shown above, use:&lt;/p&gt;
&lt;pre&gt;
cap deploy:migrations
&lt;/pre&gt;
&lt;p&gt;Like the plain deploy command, deploy:migrations checks out your code, stops the application server, switches the current link, and starts the server again. In addition, it inserts a step to run any pending migrations after the new code is checked out and the server is stopped.&lt;/p&gt;
&lt;h3&gt;c. Dealing with slower deployment&lt;/h3&gt;
&lt;p&gt;The deployment process can be pretty quick, but there may be a small window of time in which your users would see a non-responsive application. If you need a little time to get the deployment done, Capistrano has a built in ability to display a &amp;#8220;down for maintenance&amp;#8221; page. As this page is served directly by Apache, it isn&amp;#8217;t affected by the general deployment process.&lt;/p&gt;
&lt;p&gt;To turn on the maintenance page:&lt;/p&gt;
&lt;pre&gt;
cap deploy:web:disable 	
&lt;/pre&gt;
&lt;p&gt;and then reenable the app:&lt;/p&gt;
&lt;pre&gt;
cap deploy:web:enable 	
&lt;/pre&gt;
&lt;h2&gt;7. Common tasks&lt;/h2&gt;
&lt;h3&gt;a. Checking the production logs&lt;/h3&gt;
&lt;p&gt;Logs are located in /var/www/apps/labs/shared/log (substitute your application name for &lt;em&gt;labs&lt;/em&gt; if need be). You can log in to the server and run:&lt;/p&gt;
&lt;pre&gt;
tail -f production.log
&lt;/pre&gt;
&lt;p&gt;and watch your application in real-time. Alternatively, if you don&amp;#8217;t want to log in, we&amp;#8217;ve provided a Capistrano task for this case. You can use this within your application directory on your local development machine:&lt;/p&gt;
&lt;pre&gt;
cap quickstart:tail_server_logs
&lt;/pre&gt;
&lt;h3&gt;b. Check server to see if it is running&lt;/h3&gt;
&lt;p&gt;If you are logged in to the server, you can use the &lt;span class=&quot;caps&quot;&gt;SMF&lt;/span&gt; command &lt;code&gt;svcs&lt;/code&gt; to see a list of server processes that are running. A process in a healthy state will be &lt;em&gt;online&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;
online         20:18:47 svc:/network/http:cswapache2
online         20:24:58 svc:/network/mongrel/labs-production:default
&lt;/pre&gt;
&lt;p&gt;You can get this list on your local development machine with:&lt;/p&gt;
&lt;pre&gt;
cap accelerator:svcs
&lt;/pre&gt;
&lt;p&gt;If you see an application in &lt;em&gt;maintenance&lt;/em&gt;, something is wrong. You can try restarting it with &lt;code&gt;cap accelerator:restart_apache&lt;/code&gt; for Apache or &lt;code&gt;cap accelerator:smf_restart&lt;/code&gt; for mongrel.&lt;/p&gt;
&lt;h3&gt;c. Stopping and starting apps on the server&lt;/h3&gt;
&lt;p&gt;The Capistrano versions of starting or stopping mongrel are &lt;code&gt;cap accelerator:smf_start&lt;/code&gt; and &lt;code&gt;cap accelerator:smf_start&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you need to do start or stop an application while logged in to the server, you need to first locate the &lt;span class=&quot;caps&quot;&gt;SMF&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;. Issue the &lt;code&gt;svcs&lt;/code&gt; command, and look for the third column that starts with &lt;em&gt;svc&lt;/em&gt;. Note the string. To stop an application, use:&lt;/p&gt;
&lt;pre&gt;
sudo svcadm disable svc:/network/http:cswapache2
&lt;/pre&gt;
&lt;p&gt;and to start an application use:&lt;/p&gt;
&lt;pre&gt;
sudo svcadm enable svc:/network/http:cswapache2
&lt;/pre&gt;
&lt;p&gt;Swap the &lt;em&gt;svc&lt;/em&gt; string with the appropriate identifier for the target application.&lt;/p&gt;
&lt;h3&gt;d. Apache configuration&lt;/h3&gt;
&lt;p&gt;Apache is located in /opt/csw/apache2 in case you need to tweak the configurations. Look inside of the /opt/csw/apache2/etc directory.&lt;/p&gt;
&lt;p&gt;If you change the apache config, restart Apache, using Capistrano: &lt;code&gt;cap accelerator:restart_apache&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;f. Download your production database data for use in local development&lt;/h3&gt;
&lt;p&gt;We&amp;#8217;ve written a custom Capistrano task that you can use to grab a copy of your production data from the MySQL database on the Accelerator:&lt;/p&gt;
&lt;pre&gt;
cap quickstart:get_db_dump
&lt;/pre&gt;
&lt;p&gt;Once you&amp;#8217;ve downloaded your data, you can import it into your local MySQL database and use it for local testing using a command such as:&lt;/p&gt;
&lt;pre&gt;
mysql -p -u root labs_development &amp;lt; dumpfilename
&lt;/pre&gt;
&lt;p&gt;substituting the name of your database for &lt;code&gt;labs_development&lt;/code&gt; and the actual name of the dump file for &lt;code&gt;dumpfilename&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Further Exercises&lt;/h2&gt;
&lt;ol&gt;
	&lt;li&gt;Change all of your passwords to make your personal servers secure:
	&lt;ol&gt;
		&lt;li&gt;Change deployer password in shell;&lt;/li&gt;
		&lt;li&gt;Change deployer password in unfuddle;&lt;/li&gt;
		&lt;li&gt;Change mysql password in shell.&lt;/li&gt;
	&lt;/ol&gt;&lt;/li&gt;
	&lt;li&gt;Change the email addresses used by your app and accounts:
	&lt;ol&gt;
		&lt;li&gt;Change email address at unfuddle;&lt;/li&gt;
		&lt;li&gt;Change the addresses used by the sample app (if you are using it).&lt;/li&gt;
	&lt;/ol&gt;&lt;/li&gt;
	&lt;li&gt;Write your own Capistrano tasks. Check out the quickstart_tasks.rb for some examples.&lt;/li&gt;
&lt;/ol&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:45 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79180-lab-7-deployment</guid>
          <link>http://www.buildingwebapps.com/item/79180-lab-7-deployment</link>
        </item>
        
        <item>
          <title>Startup Camp and Foo Camp</title>
          <description>&lt;p&gt;I feel lucky to have been able to attend last week&amp;#8217;s Startup Camp and Foo Camp. These are unusual, invitation-only events, with extraordinary collections of people. Getting an invitation requires some mix of accomplishment, connections, and luck.&lt;/p&gt;
&lt;p&gt;They are exclusive events not for the sake of exclusivity, but because they only work at a limited size. &lt;a href=&quot;http://oreilly.com/about/&quot;&gt;O&amp;#8217;Reilly Media&lt;/a&gt;, which hosts the events and foots the bill (there&amp;#8217;s no registration fee), gets to pick the attendees from its diverse range of colleagues and contacts.&lt;/p&gt;
&lt;h2&gt;Startup Camp&lt;/h2&gt;
&lt;p&gt;This was the first-ever Startup Camp, created by O&amp;#8217;Reilly&amp;#8217;s venture fund, &lt;a href=&quot;http://oatv.com&quot;&gt;O&amp;#8217;Reilly Alpha Tech Ventures&lt;/a&gt;. &lt;span class=&quot;caps&quot;&gt;OATV&lt;/span&gt; set up a two-day program, with a variety of startup veterans to give talks and lead discussions, and invited startups to apply. We were fortunate to be one of the &lt;a href=&quot;http://www.oatv.com/foo&quot;&gt;7 startups accepted&lt;/a&gt; &amp;#8212; doubly so because getting into Startup Camp provided a much-sought-after invitation to Foo Camp as well.&lt;/p&gt;
&lt;p&gt;The companies invited to Startup Camp spanned an incredibly broad range, from  custom jewelry for tweens  (&lt;a href=&quot;http://whirlybelle.com&quot;&gt;WhirlyBelle&lt;/a&gt; from Replicator) to open-source server management software (Puppet from &lt;a href=&quot;http://reductivelabs.com&quot;&gt;Reductive Labs&lt;/a&gt;) and open-source synthetic biology (&lt;a href=&quot;http://ginkgobioworks.com/&quot;&gt;Ginkgo BioWorks&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The presenters included Tim O&amp;#8217;Reilly and Dale Dougherty (O&amp;#8217;Reilly), Bryce Roberts (&lt;span class=&quot;caps&quot;&gt;OATV&lt;/span&gt;), Esther Dyson, Evan Williams (Blogger, Twitter), Marc Hedlund (Wesabe), Michael Arrington (TechCrunch), Mark Fletcher (Bloglines), Dave McClure (500 Hats), Howard Morgan (First Round Capital), and Kathy Sierra (Creating Passionate Users).&lt;/p&gt;
&lt;div style='width:650px; float:left:padding-right:10px'&gt;
&lt;p&gt;&lt;img src=&quot;/foo_camp_08-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Since this was an off-the-record session, you won&amp;#8217;t see much reporting of the content, and I can&amp;#8217;t add much to that either. Keep it in mind next summer if you find yourself leading a new startup and want to apply.&lt;/p&gt;
&lt;p&gt;Dave McClure&amp;#8217;s &lt;a href=&quot;http://www.slideshare.net/dmc500hats/startup-metrics-for-pirates-foo-camp-2008&quot;&gt;Startup Metrics for Pirates&lt;/a&gt; presentation is one talk whose slides have been made public. And the &amp;#8220;Entrepreneurial Proverbs&amp;#8221; session was inspired by older blog posts by the two presenters, &lt;a href=&quot;http://evhead.com/2005/11/ten-rules-for-web-startups.asp&quot;&gt;Evan Williams&lt;/a&gt; and &lt;a href=&quot;http://radar.oreilly.com/2006/03/entrepreneurial-proverbs.html&quot;&gt;Marc Hedlund&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Foo Camp&lt;/h2&gt;
&lt;p&gt;As exciting as Startup Camp was, it was a prelude to the much larger Foo Camp.&lt;/p&gt;
&lt;p&gt;Foo Camp was created by Tim O&amp;#8217;Reilly and his colleague Sara Winge in 2003, and John Battelle wrote one of the &lt;a href=&quot;http://www.cnn.com/2004/TECH/ptech/01/09/bus2.feat.geek.camp/&quot;&gt;first articles&lt;/a&gt; about it. The name nominally stands for Friends Of O&amp;#8217;Reilly, and it is also a play on the use of &amp;#8220;foo&amp;#8221; as a stand-in variable name in programming examples (a practice that, incidentally, dates back to the 1960&amp;#8217;s). Tim wrote about &lt;a href=&quot;http://radar.oreilly.com/2007/06/foo-camp-takeaways.html&quot;&gt;why Foo Camp&lt;/a&gt; last year.&lt;/p&gt;
&lt;p&gt;The business rationale for Foo Camp is that it gives the O&amp;#8217;Reilly team the opportunity to talk with hundreds of leading-edge thinkers, as they look for ideas for books and conferences.&lt;/p&gt;
&lt;div style='width:650px; float:left:padding-right:10px'&gt;
&lt;p&gt;&lt;img src=&quot;/foo_camp_08-5.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;There&amp;#8217;s actually camping at Foo Camp: while many attendees stay at local hotels, a lot of them camp on the back lawn, and inside the office buildings, at O&amp;#8217;Reilly. (For me, it&amp;#8217;s only a five-minute drive, since O&amp;#8217;Reilly is in my home town of Sebastopol, CA.)&lt;/p&gt;
&lt;p&gt;O&amp;#8217;Reilly aims to have about 250 people at Foo Camp, which requires turning away a lot of past attendees so they can invite lots of new people each year.&lt;/p&gt;
&lt;p&gt;Among the well-known entrepreneurs and technologists attending this year were Jimmy Wales, Joshua Schacter, Steven Souders, Adrian Holovaty, Tom Coates, Scott Berkun, Ze Frank, Dries Buytaert, and Caterina Fake. That&amp;#8217;s just a random selection of the better known names. There were dozens of other well-known folks, and dozens more inspired, creative folks who have lower profiles.&lt;/p&gt;
&lt;p&gt;Most of O&amp;#8217;Reilly&amp;#8217;s editors and conference directors were there, as well as many of its authors. &lt;a href=&quot;http://oatventures.com/investments/&quot;&gt;OATV&amp;#8217;s portfolio companies&lt;/a&gt; were also well represented.&lt;/p&gt;
&lt;div style='width:650px; float:left:padding-right:10px'&gt;
&lt;p&gt;&lt;img src=&quot;/foo_camp_08-2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Foo Camp popularized the &amp;#8220;unconference&amp;#8221; format, in which the attendees create the program. The sponsors provide a chart with a grid of rooms and meeting times, and the attendees fill it in as they desire.&lt;/p&gt;
&lt;p&gt;The unconference format is best known from the proliferation of &lt;a href=&quot;http://barcamp.org/&quot;&gt;Bar Camps&lt;/a&gt;, which were inspired by the early Foo Camps. (More geek humor here, as in foobar.)&lt;/p&gt;
&lt;p&gt;When this format was new, people were often hesitant to propose sessions, but no longer. The schedule board was 80% full within minutes. There&amp;#8217;s so many parallel tracks, and so many fascinating people, that you can only see a fraction of what goes on and meet a scattering of people.&lt;/p&gt;
&lt;div style='width:650px; float:left:padding-right:10px'&gt;
&lt;p&gt;&lt;img src=&quot;/foo_camp_08-3.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;One of my favorite sessions was on &lt;strong&gt;The Future of News&lt;/strong&gt;. Both news, as journalism, and newspapers, as businesses, are in a period of dramatic change and stress. Presenters included &lt;a href=&quot;http://blog.seattlepi.nwsource.com/netnative/bio.asp#bio100435&quot;&gt;Monica Guzman&lt;/a&gt; of the Seattle Post-Intelligencer, &lt;a href=&quot;http://topics.nytimes.com/top/reference/timestopics/people/m/john_markoff/index.html&quot;&gt;John Markoff&lt;/a&gt; and
&lt;a href=&quot;http://nickbilton.com/&quot;&gt;Nick Bilton&lt;/a&gt; from the New York Times, &lt;a href=&quot;http://www.stevenlevy.com/&quot;&gt;Steven Levy&lt;/a&gt; from NewsWeek, and
&lt;a href=&quot;http://public.resource.org&quot;&gt;Carl Malamud&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Other interesting sessions I joined covered topics including ubiquitous computing, curation vs. crowd-sourcing, pragmatic thinking and learning (Andy Hunt), personal genomics (Esther Dyson, 23andMe), and aggregation vs. copyright.&lt;/p&gt;
&lt;div style='width:650px; float:left:padding-right:10px'&gt;
&lt;p&gt;&lt;img src=&quot;/foo_camp_08-6.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Tim O&amp;#8217;Reilly debated Michael Arrington on whether it is important for Microsoft to develop its own search technology, or if getting it from Yahoo is a reasonable strategy. Danny Sullivan moderated. And yes, that is an inflatable elephant.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the pair of blog posts that inspired this session:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://radar.oreilly.com/2008/05/why-search-competition-isnt-the-point.html&quot;&gt;Why Search Isn&amp;#8217;t the Point&lt;/a&gt;, by Tim O&amp;#8217;Reilly&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.techcrunch.com/2008/05/25/the-importance-of-a-competitive-search-market/&quot;&gt;The Importance of a Competitive Search Market&lt;/a&gt;, by Michael Arrington&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Retrospection&lt;/h2&gt;
&lt;p&gt;I have no doubt that this event serves O&amp;#8217;Reilly well, but it nevertheless is a great contribution to the community. Rarely is such a diverse collection of extraordinary people brought together under such casual, low-pressure surroundings.&lt;/p&gt;
&lt;p&gt;Looking back on Foo Camp, there are so many people I wish I had been able to spend time with, and sessions I wish I had attended. I&amp;#8217;ll just have to hope for another invitation in years to come.&lt;/p&gt;
&lt;p&gt;As someone who spent a decade running conferences, I at first found the unconference format unsettling. The completely self-organizing nature of the event means that its quality is determined entirely by the attendees. But with a crowd like this one, that&amp;#8217;s a good thing.&lt;/p&gt;
&lt;h2&gt;More Foo Stuff&lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://laughingsquid.com/foo-camp-2008-photos/&quot;&gt;Laughing Squid&amp;#8217;s Pictures&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.flickr.com/photos/joi/sets/72157606108716088/&quot;&gt;Joi Ito&amp;#8217;s Pictures&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.techcrunch.com/2008/07/14/foo-camp-2008-shangri-la-for-geeks/&quot;&gt;Michael Arrington&amp;#8217;s article&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://perspectives.mvdirona.com/2008/07/14/FooCamp2008.aspx&quot;&gt;James Hamilton&amp;#8217;s article&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.scottberkun.com/blog/2008/what-i-learned-at-foo-camp-08/&quot;&gt;Scott Berkun&amp;#8217;s article&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:49 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79209-startup-camp-and-foo-camp</guid>
          <link>http://www.buildingwebapps.com/item/79209-startup-camp-and-foo-camp</link>
        </item>
        
        <item>
          <title>Setting up Rails on Tiger (Mac OS X 10.4)</title>
          <description>&lt;p&gt;This guide walks you through setup instructions for preparing a Mac OS X 10.4 (aka Tiger) development machine to be used for general Ruby on Rails coding. This baseline setup is what we use for our &lt;a href=&quot;/course&quot;&gt;LearningRails online course&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You will end up with a development machine with the following baseline components:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Ruby and all basic Ruby utilities&lt;/li&gt;
    &lt;li&gt;Ruby Gems package manager&lt;/li&gt;
    &lt;li&gt;Subversion client&lt;/li&gt;
    &lt;li&gt;Git client&lt;/li&gt;
    &lt;li&gt;Native development tools (Xcode, C compiler)&lt;/li&gt;
    &lt;li&gt;MacPorts native code package manager&lt;/li&gt;
    &lt;li&gt;MySQL database client utilities and server&lt;/li&gt;
    &lt;li&gt;Gems for Ruby on Rails, Capistrano, Mongrel, Mongrel Cluster, and MySQL&lt;/li&gt;
    &lt;li&gt;Programmer&amp;rsquo;s editor or &lt;span class=&quot;caps&quot;&gt;IDE&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note: In the command sequences we illustrate here, command line prompts are shown as a dollar sign (&lt;code&gt;$&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;This guide assumes you have a Macintosh computer running the current Tiger operating system with up-to-date System Update patches applied. It also assumes you have not set up alternate Ruby on Rails tools prior to running through this guide. If you have, then small adjustments may be required as you walk through the following instructions.&lt;/p&gt;
&lt;p&gt;You will need to have access to an Internet connection to complete various download steps.&lt;/p&gt;
&lt;p&gt;You will need to have administrator access to your computer to complete this guide. We will be using the &lt;code&gt;sudo&lt;/code&gt; command to run various command line programs and some of the Mac OS X native installers will also ask you for your password.&lt;/p&gt;
&lt;p&gt;It is helpful if you have access to your operating system installation discs.&lt;/p&gt;
&lt;h2&gt;The Recipe&lt;/h2&gt;
&lt;p&gt;Follow this recipe in sequence. If you have previously installed a particular component, you can usually skip the associated step.&lt;/p&gt;
&lt;p&gt;If you are planning on upgrading to Mac OS X 10.5 (Leopard), you should do so now and save some time setting up the Ruby environment. &lt;a href=&quot;/articles/79197-setting-up-rails-on-leopard-mac&quot;&gt;Leopard comes with a current version of Ruby and it makes setting things up much easier&lt;/a&gt;. If you follow this guide and then upgrade to Leopard later, it is pretty simple to deactivate (or remove) the redundant components that are no longer needed.&lt;/p&gt;
&lt;h3&gt;Native Development Tools (X11 and Xcode 2.5)&lt;/h3&gt;
&lt;p&gt;MacPorts will use native development tools when installing many of the utilities listed in this guide. You will also need a native compiler to build many of the gems&amp;rsquo; native libraries you are going to use. Apple provides a free native compiler tool set called Xcode. If you have your Tiger installation &lt;span class=&quot;caps&quot;&gt;DVD&lt;/span&gt;, load it now. If you don&amp;rsquo;t have an installation &lt;span class=&quot;caps&quot;&gt;DVD&lt;/span&gt;, you can download the &lt;a href=&quot;http://developer.apple.com/tools/download/&quot;&gt;Xcode 2.5 tools at Apple&amp;rsquo;s Developer Web Site&lt;/a&gt;. (Note: Apple developer accounts are free.)&lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;Xcode Tools&lt;/code&gt; folder (or &lt;span class=&quot;caps&quot;&gt;DMG&lt;/span&gt; file if you downloaded the package). Double click on the &lt;code&gt;XcodeTools.mpkg&lt;/code&gt; installer and select a standard install. This will take a few minutes to run:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;/installmac104_xcode.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As an optional step, some tools require that the X11 window manager application be installed. Under Tiger, this is an optional install. You can find X11 on your Tiger installation &lt;span class=&quot;caps&quot;&gt;DVD&lt;/span&gt; or &lt;a href=&quot;http://www.apple.com/downloads/macosx/apple/macosx_updates/x11update2006113.html&quot;&gt;download it from Apple&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;MacPorts&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://www.macports.org/&quot;&gt;MacPorts&lt;/a&gt; is a native code package manager for Macintosh software. This guide uses MacPorts to setup the Ruby tools, MySQL, and the Git distributed version control system. There are many other tools available in the MacPorts library, so it is well worth checking out.&lt;/p&gt;
&lt;p&gt;Download the &lt;a href=&quot;http://svn.macports.org/repository/macports/downloads/MacPorts-1.6.0/MacPorts-1.6.0-10.4-Tiger.dmg&quot;&gt;Tiger Universal version&lt;/a&gt; (1.6.0 at the time of this writing) and double click the MacPorts &lt;span class=&quot;caps&quot;&gt;DMG&lt;/span&gt; file to open it up. Double click on &amp;ldquo;MacPorts-1.6.0.pkg&amp;rdquo; to start the installer and select the default options.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;/installmac104_ports.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After MacPorts completes installation, you need to adjust your command line &lt;code&gt;PATH&lt;/code&gt; environment variable so you can run the &lt;code&gt;port&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Fire up the Terminal program and enter the command:&lt;/p&gt;
&lt;pre&gt;
  $ open .bash_profile
&lt;/pre&gt;
&lt;p&gt;Note that there is a period in front of &amp;ldquo;bash_profile&amp;rdquo;. The bash shell configuration file should open in the TextEdit program.  If you don&amp;rsquo;t have a .bash_profile, you can create a new one in your text editor and save it in your home directory.&lt;/p&gt;
&lt;p&gt;(By the way, we recommend &lt;a href=&quot;http://iterm.sourceforge.net/&quot;&gt;iTerm&lt;/a&gt; as a nice open source replacement to the Apple Terminal program.)&lt;/p&gt;
&lt;p&gt;Inside of .bash_profile, find the line that starts with &lt;code&gt;export PATH=&lt;/code&gt;, if present. You are going to insert the new directories used by MacPorts into your path:&lt;/p&gt;
&lt;pre&gt;
  export PATH=&amp;quot;/opt/local/bin:/opt/local/sbin:$PATH&amp;quot;
&lt;/pre&gt;
&lt;p&gt;If you don&amp;rsquo;t have a line that exports your &lt;span class=&quot;caps&quot;&gt;PATH&lt;/span&gt;, use the text exactly as above. If you do already have such a line, add the &lt;code&gt;/opt/local/bin:/opt/local/sbin:&lt;/code&gt; (note colons) after the first quote, but before any other paths. Here is an example:&lt;/p&gt;
&lt;pre&gt;
  export PATH=&amp;quot;/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/local/sbin:$PATH&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Save the file and close TextEdit. Open a new terminal window and have MacPorts update itself with the command:&lt;/p&gt;
&lt;pre&gt;
  $ sudo port selfupdate
&lt;/pre&gt;
&lt;h3&gt;Ruby and Ruby Utilities (irb, ri, rdoc)&lt;/h3&gt;
&lt;p&gt;Tiger comes pre-installed with a version of Ruby that is out of date for day to day use. You will use MacPorts to upgrade to the latest production version of the Ruby toolset. In a terminal window type:&lt;/p&gt;
&lt;pre&gt;
  $ sudo port install ruby
  Password:
  ---&amp;gt;  Fetching ncursesw
  ---&amp;gt;  Attempting to fetch ncurses-5.6.tar.gz from http://ftp.gnu.org/gnu/ncurses
  ---&amp;gt;  Verifying checksum(s) for ncursesw
  ---&amp;gt;  Extracting ncursesw
  ---&amp;gt;  Applying patches to ncursesw
  ---&amp;gt;  Configuring ncursesw
  ---&amp;gt;  Building ncursesw with target all
  ---&amp;gt;  Staging ncursesw into destroot
...
  ---&amp;gt;  Installing ncurses 5.6_0+darwin_8
  ---&amp;gt;  Activating ncurses 5.6_0+darwin_8
  ---&amp;gt;  Cleaning ncurses
  ---&amp;gt;  Fetching openssl
  ---&amp;gt;  Attempting to fetch openssl-0.9.8e.tar.gz from http://www.openssl.org/source/
...
  ---&amp;gt;  Fetching ruby
  ---&amp;gt;  Attempting to fetch ruby-1.8.6.tar.gz from http://www.ibiblio.org/pub/languages/ruby/1.8
  ---&amp;gt;  Attempting to fetch ruby-1.8.6.tar.gz from http://mirrors.sunsite.dk/ruby/1.8
  ---&amp;gt;  Verifying checksum(s) for ruby
  ---&amp;gt;  Extracting ruby
  ---&amp;gt;  Applying patches to ruby
  ---&amp;gt;  Configuring ruby
  ---&amp;gt;  Building ruby with target all
  ---&amp;gt;  Staging ruby into destroot
  ---&amp;gt;  Packaging tgz archive for ruby 1.8.6_0+thread_hooks
  ---&amp;gt;  Installing ruby 1.8.6_0+thread_hooks
  ---&amp;gt;  Activating ruby 1.8.6_0+thread_hooks
  ---&amp;gt;  Cleaning ruby
&lt;/pre&gt;
&lt;p&gt;Your output may be slightly different depending on the versions of software at the time you run these commands. The listing here was edited for brevity.&lt;/p&gt;
&lt;p&gt;You can check that Ruby is installed with:&lt;/p&gt;
&lt;pre&gt;
  $ which ruby
  /opt/local/bin/ruby
  $ ruby -v
  ruby 1.8.6 (2007-03-13 patchlevel 0) [powerpc-darwin8.11]
&lt;/pre&gt;
&lt;p&gt;You should seem similar responses. The important one is the first one which reports back the path of the program. If you are using the MacPorts Ruby, it will be located in &lt;code&gt;/opt/local/bin/&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Ruby Gems Package Manager&lt;/h3&gt;
&lt;p&gt;You next need to install the latest version of &lt;code&gt;gem&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;
  $ sudo port install rb-rubygems rb-termios
&lt;/pre&gt;
&lt;p&gt;Check the version that gets installed:&lt;/p&gt;
&lt;pre&gt;
  $ gem -v
  0.9.4
&lt;/pre&gt;
&lt;p&gt;You need 1.0.1 or newer. If you have an older version, you can update with the command line: &lt;code&gt;sudo gem update --system&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Installing a Subversion client&lt;/h3&gt;
&lt;p&gt;Subversion will be used to access your source code repository during development and deployments. You will need a current version of the Subversion client and you can use MacPorts to install it:&lt;/p&gt;
&lt;pre&gt;
  $ sudo port install subversion +tools
&lt;/pre&gt;
&lt;h3&gt;Installing Git via MacPorts&lt;/h3&gt;
&lt;p&gt;Git is all the rage in the Rails world now and has pretty much replaced Subversion as the version control system of choice. That said, both are in common use. Currently Leopard doesn&amp;rsquo;t install git by default, so we&amp;rsquo;ll use MacPorts to quickly install git:&lt;/p&gt;
&lt;pre&gt;
   $ sudo port install git-core +doc +svn
&lt;/pre&gt;
&lt;p&gt;This command installs the core git tools, the man page documentation, and integration with Subversion. This last is useful if you plan on migrating from or need to work with a legacy Subversion repository. The installation will take a while; it has a lot to load.&lt;/p&gt;
&lt;p&gt;Besides your own project version control, you&amp;rsquo;ll typically use git when loading Rails 2.1 and various 3rd party plugins.&lt;/p&gt;
&lt;h3&gt;Installing MySQL via MacPorts&lt;/h3&gt;
&lt;p&gt;By leveraging MacPorts to install MySQL, maintenance of the software is slightly easier, especially when you want to upgrade over time. We are using MySQL on our development machine as we prefer to have identical software across our environments. Rails 2.0.2, 2.1, and newer uses SQLite by default, which is fine for development and experimentation, but not appropriate for production code.&lt;/p&gt;
&lt;p&gt;To get started, type into your terminal window:&lt;/p&gt;
&lt;pre&gt;
  $ sudo port install mysql5 +server
&lt;/pre&gt;
&lt;p&gt;This command downloads and installs the baseline MySQL client programs and server software. Next, you want to configure MySQL&amp;rsquo;s server so it launches when your computer boots up:&lt;/p&gt;
&lt;pre&gt;
  $ sudo launchctl load -w /Library/LaunchDaemons/org.macports.mysql5.plist
&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;launchctl&lt;/code&gt; is an Apple tool that administers the system daemon that controls the boot process and background programs. Here you are loading the instructions for how to manage MySQL.&lt;/p&gt;
&lt;p&gt;A fresh MySQL installation requires its database storage area to be initialized, so you do that next:&lt;/p&gt;
&lt;pre&gt;
  $ sudo mysql_install_db5 --user=mysql
&lt;/pre&gt;
&lt;p&gt;When you configure the storage area with this command, you&amp;rsquo;re making sure it is owned by the user &amp;ldquo;mysql&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;MySQL creates a special system file used for program to program communication, called the &amp;ldquo;socket&amp;rdquo; file. By default, the MacPorts installation of MySQL server creates this in the directory &amp;ldquo;/opt/local/var/run/mysql5/mysqld.sock&amp;rdquo;. Ruby on Rails applications can deal with this just fine if you change the settings in your database.yml file to include a &lt;code&gt;socket&lt;/code&gt; entry that points to the correct place. However, we are going to tweak things so all applications can find the file in a fairly standard place: &lt;strong&gt;/tmp/mysql.sock&lt;/strong&gt; with little or no modification.&lt;/p&gt;
&lt;p&gt;First, you need to move the default configuration file, &lt;code&gt;my.cnf&lt;/code&gt;, to the correct place:&lt;/p&gt;
&lt;pre&gt;
  $ sudo mv /opt/local/etc/my.cnf /opt/local/etc/mysql5/my.cnf
&lt;/pre&gt;
&lt;p&gt;If the installation program didn&amp;rsquo;t put a file into /opt/local/etc/, try this instead:&lt;/p&gt;
&lt;pre&gt;
  $ sudo mv /opt/local/share/mysql5/mysql/my-medium.cnf /opt/local/etc/mysql5/my.cnf
&lt;/pre&gt;
&lt;p&gt;Now, you need to edit the configuration file to change where the socket file is stored:&lt;/p&gt;
&lt;pre&gt;
  $ sudo pico /opt/local/etc/mysql5/my.cnf
&lt;/pre&gt;
&lt;p&gt;If you aren&amp;rsquo;t familiar with the &lt;code&gt;pico&lt;/code&gt; command line editor, we explain the few commands you will need here.&lt;/p&gt;
&lt;p&gt;Inside of &lt;code&gt;pico&lt;/code&gt;, use your arrow keys to move down to the line where you first see &amp;ldquo;[client]&amp;rdquo; and make the following changes (this is just a small part of the whole file):&lt;/p&gt;
&lt;pre&gt;
  ...
  # In this file, you can use all long options that a program supports.
  # If you want to know which options a program supports, run the program
  # with the &amp;quot;--help&amp;quot; option.
  
  [mysqld_safe]
  socket          = /tmp/mysql.sock

  # The following options will be passed to all MySQL clients
  [client]
  #password       = your_password
  port            = 3306
  socket          = /tmp/mysql.sock

  # Here follows entries for some specific programs

  # The MySQL server
  [mysqld]
  port            = 3306
  socket          = /tmp/mysql.sock
  ...
&lt;/pre&gt;
&lt;p&gt;You are adding the &amp;ldquo;[mysqld_safe]&amp;rdquo; section (2 lines) just above &amp;ldquo;[client]&amp;rdquo; and then changing the two instances of the &amp;quot;socket = &amp;quot; lines in the &amp;ldquo;[client]&amp;rdquo; and &amp;ldquo;[mysqld]&amp;rdquo; sections to be &lt;code&gt;/tmp/mysql.sock&lt;/code&gt;. Once done, press &lt;code&gt;Control-X&lt;/code&gt;, answer &lt;code&gt;Y&lt;/code&gt; when asked to save, and press return to accept the default file name (&amp;ldquo;my.cnf&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;Now you start the server up manually:&lt;/p&gt;
&lt;pre&gt;
  $ cd /opt/local ; sudo /opt/local/lib/mysql5/bin/mysqld_safe &amp;amp;   
&lt;/pre&gt;
&lt;p&gt;You can confirm that MySQL is running by trying to fire it up:&lt;/p&gt;
&lt;pre&gt;
  $ mysql5 -p -u root
  Welcome to the MySQL monitor.  Commands end with ; or \g.
  Your MySQL connection id is 1
  Server version: 5.0.45 Source distribution

  Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

  mysql&amp;gt; exit
&lt;/pre&gt;
&lt;p&gt;When you reboot your computer, MySQL should start automatically in the future.&lt;/p&gt;
&lt;h3&gt;Gems&lt;/h3&gt;
&lt;p&gt;Tiger requires that you install the base collection of gems you will be using:&lt;/p&gt;
&lt;pre&gt;
  $ sudo gem install rake rails capistrano mongrel mongrel_cluster
&lt;/pre&gt;
&lt;p&gt;All installed gems (including Ruby on Rails and its dependencies, Rake, Capistrano, Mongrel, and Mongrel_Cluster) will get installed at their latest versions.&lt;/p&gt;
&lt;p&gt;The MySQL adapter gem needs to be installed, and it is a little finicky due to our use of MacPorts.&lt;/p&gt;
&lt;p&gt;If you are running Tiger on an Intel processor, use this command (on one line):&lt;/p&gt;
&lt;pre&gt;
  $ ARCHFLAGS=&amp;quot;-arch i386&amp;quot; sudo gem install mysql -- --with-mysql-config=/opt/local/bin/mysql_config5
&lt;/pre&gt;
&lt;p&gt;and if you are on a PowerPC processor, use this command:&lt;/p&gt;
&lt;pre&gt;
  ARCHFLAGS=&amp;quot;-arch ppc&amp;quot; sudo gem install mysql -- --with-mysql-config=/opt/local/bin/mysql_config5
&lt;/pre&gt;
&lt;p&gt;Once the command completes, you should be all set with the baseline gems you will need for the LearningRails courses.&lt;/p&gt;
&lt;h3&gt;Code Editing Tools&lt;/h3&gt;
&lt;p&gt;While you can get by with using a plain text editor like TextEdit, or even Apples Xcode &lt;span class=&quot;caps&quot;&gt;IDE&lt;/span&gt;, you will have much high productivity if you use a programming editor that is highly tuned to Ruby on Rails development.&lt;/p&gt;
&lt;p&gt;We use the commercial &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; programmer&amp;rsquo;s editor for much of our day to day work. TextMate is highly extensible through a collection of community supplied &amp;ldquo;bundles&amp;rdquo;. Many add-ons accelerate development by enhancing the editor (for example, adding language specific short cuts to reduce your typing) or by tying in to other utilities, such as Subversion or Rake, to allow you to quickly get tasks done without leaving the editor&amp;rsquo;s environment.&lt;/p&gt;
&lt;p&gt;There are a variety of good open source or free programmer editors available too. On the open source side, Leopard comes pre-installed with both vim and emacs. &lt;a href=&quot;http://www.jedit.org/&quot;&gt;jEdit&lt;/a&gt; is a very extensible open source editor written in Java. &lt;a href=&quot;http://www.barebones.com/products/textwrangler/&quot;&gt;TextWrangler&lt;/a&gt; is a free programmer&amp;rsquo;s editor from BareBones.&lt;/p&gt;
&lt;p&gt;Whatever editor you choose, be certain that it provides easy navigation among a large number of open files. Working with Rails applications generally involves dealing with a lot of small files, and that process needs to be efficient.&lt;/p&gt;
&lt;p&gt;If you prefer an all-in-one tool, you should look at one of several integrated development environments that exist for Ruby. We use &lt;a href=&quot;http://www.netbeans.org/&quot;&gt;Netbeans&lt;/a&gt; in our LearningRails courses, but you should check out the numerous &lt;a href=&quot;/topic/24404-ides-integrated-development-environments-for-web&quot;&gt;other options listed at BuildingWebApps.com&lt;/a&gt;.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:48 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79198-setting-up-rails-on-tiger-mac</guid>
          <link>http://www.buildingwebapps.com/item/79198-setting-up-rails-on-tiger-mac</link>
        </item>
        
        <item>
          <title>Setting up Rails on Leopard (Mac OS X 10.5)</title>
          <description>&lt;p&gt;This guide walks you through setup instructions for preparing a Mac OS X 10.5 (aka Leopard) development machine to be used for general Ruby on Rails coding. This baseline setup is what we use for our &lt;a href=&quot;/course&quot;&gt;LearningRails online course&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You will end up with a development machine with the following baseline components:&lt;/p&gt;
&lt;ol&gt;
    &lt;li&gt;Ruby and all basic Ruby utilities&lt;/li&gt;
    &lt;li&gt;Ruby Gems package manager&lt;/li&gt;
    &lt;li&gt;Subversion client&lt;/li&gt;
    &lt;li&gt;Git client&lt;/li&gt;
    &lt;li&gt;Native development tools (Xcode, C compiler)&lt;/li&gt;
    &lt;li&gt;MacPorts native code package manager&lt;/li&gt;
    &lt;li&gt;MySQL database client utilities and server&lt;/li&gt;
    &lt;li&gt;Gems for Ruby on Rails, Capistrano, Mongrel, Mongrel Cluster, and MySQL&lt;/li&gt;
    &lt;li&gt;Programmer&amp;rsquo;s editor or &lt;span class=&quot;caps&quot;&gt;IDE&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note: In the command sequences we illustrate here, command line prompts are shown as a dollar sign (&lt;code&gt;$&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;This guide assumes you have a Macintosh computer running the current Leopard operating system with up-to-date System Update patches applied. It also assumes you have not set up alternate Ruby on Rails tools prior to running through this guide. If you have, then small adjustments may be required as you walk through the following instructions.&lt;/p&gt;
&lt;p&gt;You will need to have access to an Internet connection to complete various download steps.&lt;/p&gt;
&lt;p&gt;You will need to have administrator access to your computer to complete this guide. We will be using the &lt;code&gt;sudo&lt;/code&gt; command to run various command line programs and some of the Mac OS X native installers will also ask you for your password.&lt;/p&gt;
&lt;p&gt;It is helpful if you have access to your operating system installation discs.&lt;/p&gt;
&lt;h2&gt;The Recipe&lt;/h2&gt;
&lt;p&gt;Follow this recipe in sequence. If you have previously installed a particular component, you can usually skip the associated step.&lt;/p&gt;
&lt;h3&gt;Ruby and Ruby Utilities (irb, ri, rdoc)&lt;/h3&gt;
&lt;p&gt;Leopard comes pre-installed with Ruby 1.8.6 and its associated utilities. You can use these programs as is. To check them out, open a Terminal window and type:&lt;/p&gt;
&lt;pre&gt;
  $ which ruby
  /usr/bin/ruby
  $ ruby -v
  ruby 1.8.6 (2007-09-24 patchlevel 111) [universal-darwin9.0]
&lt;/pre&gt;
&lt;p&gt;You should seem similar responses. The important one is the first one which reports back the path of the Ruby interpreter program. If you are using the built-in Ruby, it will be located in &lt;code&gt;/usr/bin/&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Ruby Gems Package Manager&lt;/h3&gt;
&lt;p&gt;Leopard comes with a pre-installed version of &lt;code&gt;gem&lt;/code&gt;. Be sure you have the latest version:&lt;/p&gt;
&lt;pre&gt;
  $ gem -v
  1.0.1
&lt;/pre&gt;
&lt;p&gt;You need 1.0.1 or newer. If you have an older version, you can update with the command line: &lt;code&gt;sudo gem update --system&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Native Development Tools (Xcode 3.0)&lt;/h3&gt;
&lt;p&gt;You will need a native compiler to build many of the gems&amp;rsquo; native libraries you are going to use. You will also occasionally build other native tools via the MacPorts tool described below. Apple provides a free native compiler tool set called Xcode. If you have your Leopard installation &lt;span class=&quot;caps&quot;&gt;DVD&lt;/span&gt;, load it now. If you don&amp;rsquo;t have an installation &lt;span class=&quot;caps&quot;&gt;DVD&lt;/span&gt;, you can download the &lt;a href=&quot;http://developer.apple.com/tools/download/&quot;&gt;Xcode 3.0 tools at Apple&amp;rsquo;s Developer Web Site&lt;/a&gt;. (Note: Apple developer accounts are free.)&lt;/p&gt;
&lt;p&gt;Open the &lt;code&gt;Optional Installs&lt;/code&gt; folder, and then the &lt;code&gt;Xcode Tools&lt;/code&gt; folder. Double click on the &lt;code&gt;XcodeTools.mpkg&lt;/code&gt; installer and select a standard install. This will take a few minutes to run:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;/installmac105_xcode.jpg&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;MacPorts&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://www.macports.org/&quot;&gt;MacPorts&lt;/a&gt; is a native code package manager for Macintosh software. This guide uses MacPorts to setup an installation of MySQL and the Git distributed version control system. There are many other tools available in the MacPorts library, so it is well worth checking out.&lt;/p&gt;
&lt;p&gt;Download the &lt;a href=&quot;http://svn.macports.org/repository/macports/downloads/MacPorts-1.6.0/MacPorts-1.6.0-10.5-Leopard.dmg&quot;&gt;Leopard Universal version&lt;/a&gt; (1.6.0 at the time of this writing) and double click the MacPorts &lt;span class=&quot;caps&quot;&gt;DMG&lt;/span&gt; file to open it up. Double click on &amp;ldquo;MacPorts-1.6.0.pkg&amp;rdquo; to start the installer and select the default options.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;/installmac105_ports.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After MacPorts completes installation, you need to adjust your command line &lt;code&gt;PATH&lt;/code&gt; environment variable so you can run the &lt;code&gt;port&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Fire up the Terminal program and enter the command:&lt;/p&gt;
&lt;pre&gt;
  $ open .bash_profile
&lt;/pre&gt;
&lt;p&gt;Note that there is a period in front of &amp;ldquo;bash_profile&amp;rdquo;. The bash shell configuration file should open in the TextEdit program. If you don&amp;rsquo;t have a .bash_profile, you can create a new one in your text editor and save it in your home directory.&lt;/p&gt;
&lt;p&gt;(By the way, we recommend &lt;a href=&quot;http://iterm.sourceforge.net/&quot;&gt;iTerm&lt;/a&gt; as a nice open source replacement for the Apple Terminal program.)&lt;/p&gt;
&lt;p&gt;Inside of .bash_profile, find the line that starts with &lt;code&gt;export PATH=&lt;/code&gt;, if present. You are going to insert the new directories used by MacPorts into your path:&lt;/p&gt;
&lt;pre&gt;
  export PATH=&amp;quot;/opt/local/bin:/opt/local/sbin:$PATH&amp;quot;
&lt;/pre&gt;
&lt;p&gt;If you don&amp;rsquo;t have a line that exports your &lt;span class=&quot;caps&quot;&gt;PATH&lt;/span&gt;, use the text exactly as above. If you do already have such a line, add the &lt;code&gt;/opt/local/bin:/opt/local/sbin:&lt;/code&gt; (note colons) after the first quote, but before any other paths. Here is an example:&lt;/p&gt;
&lt;pre&gt;
  export PATH=&amp;quot;/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/local/sbin:$PATH&amp;quot;
&lt;/pre&gt;
&lt;p&gt;Save the file and close TextEdit. Open a new terminal window and have MacPorts update itself with the command:&lt;/p&gt;
&lt;pre&gt;
  $ sudo port selfupdate
&lt;/pre&gt;
&lt;h3&gt;Installing Git via MacPorts&lt;/h3&gt;
&lt;p&gt;Git is all the rage in the Rails world now and has pretty much replaced Subversion as the version control system of choice. That said, both are in common use. Currently Leopard doesn&amp;rsquo;t install git by default, so we&amp;rsquo;ll use MacPorts to quickly install git:&lt;/p&gt;
&lt;pre&gt;
   $ sudo port install git-core +doc +svn
&lt;/pre&gt;
&lt;p&gt;This command installs the core git tools, the man page documentation, and integration with Subversion. This last is useful if you plan on migrating from or need to work with a legacy Subversion repository. The installation will take a while; it has a lot to load.&lt;/p&gt;
&lt;p&gt;Besides your own project version control, you&amp;rsquo;ll typically use git when loading Rails 2.1 and various 3rd party plugins.&lt;/p&gt;
&lt;h3&gt;Installing MySQL via MacPorts&lt;/h3&gt;
&lt;p&gt;By leveraging MacPorts to install MySQL, maintenance of the software is slightly easier, especially when you want to upgrade over time. We are using MySQL on our development machine as we prefer to have identical software across our environments. Rails 2.0.2, 2.1 and newer uses SQLite by default, which is fine for development and experimentation, but not appropriate for production code.&lt;/p&gt;
&lt;p&gt;To get started, type into your terminal window:&lt;/p&gt;
&lt;pre&gt;
  $ sudo port install mysql5 +server
&lt;/pre&gt;
&lt;p&gt;This command downloads and installs the baseline MySQL client programs and server software. Next, you want to configure MySQL&amp;rsquo;s server so it launches when your computer boots up:&lt;/p&gt;
&lt;pre&gt;
  $ sudo launchctl load -w /Library/LaunchDaemons/org.macports.mysql5.plist
&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;launchctl&lt;/code&gt; is an Apple tool that administers the system daemon that controls the boot process and background programs. Here you are loading the instructions for how to manage MySQL.&lt;/p&gt;
&lt;p&gt;A fresh MySQL installation requires its database storage area to be initialized, so you do that next:&lt;/p&gt;
&lt;pre&gt;
  $ sudo mysql_install_db5 --user=mysql
&lt;/pre&gt;
&lt;p&gt;When you configure the storage area with this command, you&amp;rsquo;re making sure it is owned by the user &amp;ldquo;mysql&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;MySQL creates a special system file used for program to program communication, called the &amp;ldquo;socket&amp;rdquo; file. By default, the MacPorts installation of MySQL server creates this in the directory &amp;ldquo;/opt/local/var/run/mysql5/mysqld.sock&amp;rdquo;. Ruby on Rails applications can deal with this just fine if you change the settings in your database.yml file to include a &lt;code&gt;socket&lt;/code&gt; entry that points to the correct place. However, we are going to tweak things so all applications can find the file in a fairly standard place: &lt;strong&gt;/tmp/mysql.sock&lt;/strong&gt; with little or no modification.&lt;/p&gt;
&lt;p&gt;First, you need to move the default configuration file, &lt;code&gt;my.cnf&lt;/code&gt;, to the correct place:&lt;/p&gt;
&lt;pre&gt;
  $ sudo mv /opt/local/etc/my.cnf /opt/local/etc/mysql5/my.cnf
&lt;/pre&gt;
&lt;p&gt;If the installation program didn&amp;rsquo;t put a file into /opt/local/etc/, try this instead:&lt;/p&gt;
&lt;pre&gt;
  $ sudo mv /opt/local/share/mysql5/mysql/my-medium.cnf /opt/local/etc/mysql5/my.cnf
&lt;/pre&gt;
&lt;p&gt;Now, you need to edit the configuration file to change where the socket file is stored:&lt;/p&gt;
&lt;pre&gt;
  $ sudo pico /opt/local/etc/mysql5/my.cnf
&lt;/pre&gt;
&lt;p&gt;If you aren&amp;rsquo;t familiar with the &lt;code&gt;pico&lt;/code&gt; command line editor, we explain the few commands you will need here.&lt;/p&gt;
&lt;p&gt;Inside of &lt;code&gt;pico&lt;/code&gt;, use your arrow keys to move down to the line where you first see &amp;ldquo;[client]&amp;rdquo; and make the following changes (this is just a small part of the whole file):&lt;/p&gt;
&lt;pre&gt;
  ...
  # In this file, you can use all long options that a program supports.
  # If you want to know which options a program supports, run the program
  # with the &amp;quot;--help&amp;quot; option.
  
  [mysqld_safe]
  socket          = /tmp/mysql.sock

  # The following options will be passed to all MySQL clients
  [client]
  #password       = your_password
  port            = 3306
  socket          = /tmp/mysql.sock

  # Here follows entries for some specific programs

  # The MySQL server
  [mysqld]
  port            = 3306
  socket          = /tmp/mysql.sock
  ...
&lt;/pre&gt;
&lt;p&gt;You are adding the &amp;ldquo;[mysqld_safe]&amp;rdquo; section (2 lines) just above &amp;ldquo;[client]&amp;rdquo; and then changing the two instances of the &amp;quot;socket = &amp;quot; lines in the &amp;ldquo;[client]&amp;rdquo; and &amp;ldquo;[mysqld]&amp;rdquo; sections to be &lt;code&gt;/tmp/mysql.sock&lt;/code&gt;. Once done, press &lt;code&gt;Control-X&lt;/code&gt;, answer &lt;code&gt;Y&lt;/code&gt; when asked to save, and press return to accept the default file name (&amp;ldquo;my.cnf&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;Now you start the server up manually:&lt;/p&gt;
&lt;pre&gt;
  $ cd /opt/local ; sudo /opt/local/lib/mysql5/bin/mysqld_safe &amp;amp;   
&lt;/pre&gt;
&lt;p&gt;You can confirm that MySQL is running by trying to fire it up:&lt;/p&gt;
&lt;pre&gt;
  $ mysql5 -p -u root
  Welcome to the MySQL monitor.  Commands end with ; or \g.
  Your MySQL connection id is 1
  Server version: 5.0.45 Source distribution

  Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

  mysql&amp;gt; exit
&lt;/pre&gt;
&lt;p&gt;When you reboot your computer, MySQL should start automatically in the future.&lt;/p&gt;
&lt;h3&gt;Gems&lt;/h3&gt;
&lt;p&gt;Leopard conveniently pre-installs the base collection of gems you will be using, but you need to make sure they are up to date:&lt;/p&gt;
&lt;pre&gt;
  $ sudo gem update
&lt;/pre&gt;
&lt;p&gt;All installed gems (including Ruby on Rails and its dependencies, Rake, Capistrano, Mongrel, and Mongrel_Cluster) will get updated.&lt;/p&gt;
&lt;p&gt;The MySQL adapter gem needs to be installed, and it is a little finicky due to our use of MacPorts.&lt;/p&gt;
&lt;p&gt;If you are running Leopard on an Intel processor, use this command (on one line):&lt;/p&gt;
&lt;pre&gt;
  $ ARCHFLAGS=&amp;quot;-arch i386&amp;quot; sudo gem install mysql -- --with-mysql-config=/opt/local/bin/mysql_config5
&lt;/pre&gt;
&lt;p&gt;and if you are on a PowerPC processor, use this command:&lt;/p&gt;
&lt;pre&gt;
  ARCHFLAGS=&amp;quot;-arch ppc&amp;quot; sudo gem install mysql -- --with-mysql-config=/opt/local/bin/mysql_config5
&lt;/pre&gt;
&lt;p&gt;Once the command completes, you should be all set with the baseline gems you will need for the LearningRails courses.&lt;/p&gt;
&lt;h3&gt;Code Editing Tools&lt;/h3&gt;
&lt;p&gt;While you can get by with using a plain text editor like TextEdit, or even Apple&amp;rsquo;s Xcode &lt;span class=&quot;caps&quot;&gt;IDE&lt;/span&gt;, you will be more productive if you use a programming editor that is highly tuned to Ruby on Rails development.&lt;/p&gt;
&lt;p&gt;We use the commercial &lt;a href=&quot;http://macromates.com/&quot;&gt;TextMate&lt;/a&gt; programmer&amp;rsquo;s editor for much of our day-to-day work. TextMate is highly extensible through a collection of community supplied &amp;ldquo;bundles&amp;rdquo;. Many add-ons accelerate development by enhancing the editor (for example, adding language specific short cuts to reduce your typing) or by tying in to other utilities, such as Subversion or Rake, to allow you to quickly get tasks done without leaving the editor&amp;rsquo;s environment.&lt;/p&gt;
&lt;p&gt;There are a variety of good open source or free programmer editors available too. On the open source side, Leopard comes pre-installed with both vim and emacs. &lt;a href=&quot;http://www.jedit.org/&quot;&gt;jEdit&lt;/a&gt; is a very extensible open source editor written in Java. &lt;a href=&quot;http://www.barebones.com/products/textwrangler/&quot;&gt;TextWrangler&lt;/a&gt; is a free programmer&amp;rsquo;s editor from BareBones.&lt;/p&gt;
&lt;p&gt;Whatever editor you choose, be certain that it provides easy navigation among a large number of open files. Working with Rails applications generally involves dealing with a lot of small files, and that process needs to be efficient.&lt;/p&gt;
&lt;p&gt;If you prefer an all-in-one tool, ook at one of the integrated development environments for Ruby. We use &lt;a href=&quot;http://www.netbeans.org/&quot;&gt;Netbeans&lt;/a&gt; when we aren&amp;rsquo;t using TextMate, but the numerous &lt;a href=&quot;/topic/24404-ides-integrated-development-environments-for-webs&quot;&gt;other options listed at BuildingWebApps.com&lt;/a&gt; are worth a look.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:47 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79197-setting-up-rails-on-leopard-mac</guid>
          <link>http://www.buildingwebapps.com/item/79197-setting-up-rails-on-leopard-mac</link>
        </item>
        
        <item>
          <title>Podcasts and Screencasts on Ruby on Rails</title>
          <description>&lt;p&gt;There&amp;#8217;s a variety of screencasts and podcasts available for Ruby on Rails developers. Here&amp;#8217;s a quick rundown of the major ones, and how they differ.&lt;/p&gt;
&lt;h2&gt;Podcasts&lt;/h2&gt;
&lt;p&gt;Audio podcasts are great for listening on the go, and for things such as news and interviews.&lt;/p&gt;
&lt;h3&gt;Tutorial: Learning Rails&lt;/h3&gt;
&lt;p&gt;Our own &lt;a href=&quot;/course&quot;&gt;Learning Rails&lt;/a&gt; series consists of 8 podcasts that cover the fundamental concepts behind Ruby on Rails.&lt;/p&gt;
&lt;h3&gt;People: Ruby on Rails Podcast&lt;/h3&gt;
&lt;p&gt;The longest-running podcast in the Rails world is the &amp;#8220;official&amp;#8221; &lt;a href=&quot;http://podcast.rubyonrails.org/&quot;&gt;Ruby on Rails podcast&lt;/a&gt;, first created by Scott Barron but produced for most of its life by Geoffrey Grosenbach. This is a great series of interviews with many of the leading developers in the Ruby and Rails world.&lt;/p&gt;
&lt;h3&gt;News: Rails Envy&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;http://railsenvy.com/podcast&quot;&gt;Rails Envy podcast&lt;/a&gt; comes out every week and provides a fantastic summary of the latest developments in the Rails world. It&amp;#8217;s put together by Gregg Pollack, Jason Seifer, and Steven Bristol, who diligently track a wide variety of Rails blogs to provide you with a distilled and entertaining weekly update.&lt;/p&gt;
&lt;h2&gt;Screencasts&lt;/h2&gt;
&lt;p&gt;Screencasts let you watch what&amp;#8217;s going on on the screen while the presenter explains what they&amp;#8217;re doing. It&amp;#8217;s like looking over the shoulder of a developer while they work. Screencasts are a great teaching tool.&lt;/p&gt;
&lt;h3&gt;Learning Rails&lt;/h3&gt;
&lt;p&gt;Our &lt;a href=&quot;/screencasts&quot;&gt;Learning Rails&lt;/a&gt; screencast series shows how to build a simple web site using Ruby on Rails, step-by-step. Designed for people who are new to Ruby on Rails.&lt;/p&gt;
&lt;h3&gt;Envycasts&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://envycasts.com&quot;&gt;Envycasts&lt;/a&gt; is the latest entrant into the Rails screencasts business. They&amp;#8217;re created by the Rails Envy folks, Gregg Pollack and Jason Seifer, who bring their trademark wit and creative production to advanced technical screencasts. There&amp;#8217;s nothing else like them. Priced at $9 each.&lt;/p&gt;
&lt;h3&gt;Railscasts&lt;/h3&gt;
&lt;p&gt;Ryan Bates has producing more than 100 free &lt;a href=&quot;http://railscasts.com&quot;&gt;Railscasts&lt;/a&gt; screencasts. They&amp;#8217;re short episodes focused on a single feature of Rails, and are a great way to learn how to do specific things with Rails.&lt;/p&gt;
&lt;h3&gt;Peepcode&lt;/h3&gt;
&lt;p&gt;Geoffrey Grosenbach&amp;#8217;s &lt;a href=&quot;http://peepcode.com&quot;&gt;Peepcode&lt;/a&gt; are the gold standard for Ruby on Rails screencasts. At $9 each, they aren&amp;#8217;t free, but they&amp;#8217;re still a great value. They&amp;#8217;re typically about an hour long and deeply explore specific topics, from Git to rSpec.&lt;/p&gt;
&lt;h3&gt;Pragmatic Screencasts&lt;/h3&gt;
&lt;p&gt;The Pragmatic Programmer, well known for their Ruby and Rails books, has teamed with Mike Clark of Pragmatic Studio, a leading provider of Rails training, to produce the &lt;a href=&quot;http://pragmatic.tv&quot;&gt;Pragmatic Screencasts&lt;/a&gt;. The 20 to 30 minutes screencasts cost $5 each. So far, most of them are not about Ruby or Rails, but there is a series by Ryan Bates on &amp;#8220;Everyday Active Record.&amp;#8221;&lt;/p&gt;
&lt;h3&gt;RubyPlus&lt;/h3&gt;
&lt;p&gt;Bala Paranj has produce more than 70 free screencasts on various Ruby and Ruby on Rails techniques at &lt;a href=&quot;http://rubyplus.org&quot;&gt;RubyPlus.org&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Coderpath&lt;/h3&gt;
&lt;p&gt;Miles K. Forrest recorded a few early Rails screencasts at &lt;a href=&quot;http://coderpath.com&quot;&gt;coderpath&lt;/a&gt; a couple years ago. He&amp;#8217;s been quiet since, but he&amp;#8217;s talking about restarting the series.&lt;/p&gt;
&lt;h3&gt;Zenunit Sensei&lt;/h3&gt;
&lt;p&gt;A short series of Rails screencasts, free for now with some talk of paid screencasts in the future, at &lt;a href=&quot;http://sensei.zenunit.com/&quot;&gt;Zenunit Sensei&lt;/a&gt;. From Australia, apparently by Julian Leviston.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:49 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79207-podcasts-and-screencasts-on-ruby-on</guid>
          <link>http://www.buildingwebapps.com/item/79207-podcasts-and-screencasts-on-ruby-on</link>
        </item>
        
        <item>
          <title>The Rebuilding and Scaling of YellowPages.com</title>
          <description>&lt;p&gt;&lt;div style='float:left; width: 210px'&gt;
&lt;p&gt;&lt;img src=&quot;/johnstraw.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;John Straw, chief software architect at &lt;a href=&quot;http://www.yellowpages.com&quot;&gt;YellowPages.com&lt;/a&gt;, gave an excellent talk at RailsConf about YellowPages&amp;#8217; conversion to Rails. We&amp;#8217;ve pointed to YellowPages in the past as being one of the highest-traffic Rails sites, proving that &lt;a href=&quot;/articles/13-can-rails-scale-absolutely&quot;&gt;Rails can scale&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;John&amp;#8217;s talk covered the scaling issues, but the talk was just as much about the process of successfully doing a big rewrite of a critical application at a large company. (YellowPages.com is part of AT&amp;amp;T.)&lt;/p&gt;
&lt;p&gt;You can &lt;a href=&quot;http://en.oreilly.com/rails2008/public/asset/attachment/2765&quot;&gt;download John&amp;#8217;s presentation&lt;/a&gt; from the RailsConf site.&lt;/p&gt;
&lt;p&gt;With 2.5M searches per day and more than 170 million page views per month, YellowPages.com is now the biggest site AT&amp;amp;T runs, generating more traffic than att.com.&lt;/p&gt;
&lt;h2&gt;Why the rewrite?&lt;/h2&gt;
&lt;p&gt;The previous version was written in Java in 2004 and 2005 by consultants, and had fundamental design problems. This version had 125K lines of code and no automated tests, making new features hard to implement. The Rails version ended up with fewer than 20K lines of code, including tests.&lt;/p&gt;
&lt;p&gt;The Java site also had lots of usability issues and &lt;span class=&quot;caps&quot;&gt;SEO&lt;/span&gt; problems. As an example of the difficulty of adding features, John noted that it took three months to add a rating feature.&lt;/p&gt;
&lt;p&gt;The requirements for the new version included:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Absolute control of URLs&lt;/li&gt;
	&lt;li&gt;No sessions&lt;/li&gt;
	&lt;li&gt;Be agile&lt;/li&gt;
	&lt;li&gt;Develop easy-to-leverage core business services&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new product was built by a cross-functional team of 20 people, including project managers, user experience experts, advertising people, search experts, and content and community managers. There were never more than 5 developers. The entire team sat together for the duration of the project.&lt;/p&gt;
&lt;p&gt;The architecture they designed has three tiers: a web tier that delivers the front-end web experience; a services tier that responds to requests from the web tier; and a search cluster that performs the actual searches.&lt;/p&gt;
&lt;p&gt;The team built an initial Rails prototype in early 2007 that looked like the existing site, just to get some experience with Rails. (Only one of the team members had any prior Rails experience.) They also built prototype search code in Python. They then started a new Rails prototype with designs from user experience team. They also built a Django prototype to explore that option, and evaluated EJB3/JBoss as a service tier platform.&lt;/p&gt;
&lt;h2&gt;Why Rails?&lt;/h2&gt;
&lt;p&gt;The team rejected Java for the front end in part because of its inadequate &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; control. In terms of Rails vs. Django, John called the decision a near toss-up. Platform maturity was an important attribute that led them to choose Rails. They also felt that it had better automated testing integration and a clearer path to moving parts of it to C if necessary for performance. Ultimately, the development team simply felt more comfortable with it.&lt;/p&gt;
&lt;p&gt;John had initially expected they would use Java for the service tier, but after an evaluation of EJB3 the team felt there were no real advantages over Ruby or Python for their application. All the reasons for choosing Rails for the web tier applied equally to service tier, and having a single software environment has obvious advantages.&lt;/p&gt;
&lt;h2&gt;Keeping the project moving&lt;/h2&gt;
&lt;p&gt;The success of this rewrite had as much to do with adeptly managing the process as with technology. One key to keeping it moving was giving one person decision-making authority. Another important factor was freezing development on the existing site, so they wouldn&amp;#8217;t have a moving target. An ad-hoc rule they came up with was that if you can&amp;#8217;t figure out quickly how you&amp;#8217;d like to change something about the existing site, then don&amp;#8217;t change it.&lt;/p&gt;
&lt;p&gt;Another important process was early and frequent communication with the sales team. A previous site redesign had nearly failed because of lack of support from sales. The project lead, who came from the user experience team, met with 20 to 40 salespeople a week to review the proposed changes. This gave them the buy-in they needed for the site to be successful from a business perspective.&lt;/p&gt;
&lt;p&gt;The site was made available to friends and family as a closed beta on 4/26/07, an open beta on 5/17/07, and then went live at the end of June, less than six months after beginning work on their first Rails prototype. This was the first time AT&amp;amp;T had released a beta of any kind.&lt;/p&gt;
&lt;p&gt;A few things were farmed out to other teams, including &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;/&lt;span class=&quot;caps&quot;&gt;CSS&lt;/span&gt; coding, rewrite rules for legacy &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; translation, and performance evaluation for the production deployment configuration.&lt;/p&gt;
&lt;h2&gt;System architecture&lt;/h2&gt;
&lt;p&gt;The web tier, service tier, and search cluster are each fronted by an &lt;a href=&quot;http://www.f5.com/products/big-ip/&quot;&gt;F5 load balancer&lt;/a&gt;. The web tier initially used Apache with 16 Mongrels on each server, while the service tier uses Apache with 30 Mongrels and memcached on each server.&lt;/p&gt;
&lt;p&gt;The communication between tiers uses stateless &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; transactions. The service tier provides a set of RESTful services that return &lt;span class=&quot;caps&quot;&gt;JSON&lt;/span&gt; to the web tier. The search cluster uses the &lt;a href=&quot;http://www.fastsearch.com/l3a.aspx?m=1031&quot;&gt;&lt;span class=&quot;caps&quot;&gt;FAST&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;ESP&lt;/span&gt;&lt;/a&gt; search engine.&lt;/p&gt;
&lt;p&gt;The entire system consists of 25 machines, each with two dual-core, 64-bit processors. Two of the 25 are used for the database. This compares to 21 servers for the previous Java-based solution. There are two data centers with identical setups; each is designed to handle the entire load, so they provide geographically distributed redundancy.&lt;/p&gt;
&lt;p&gt;They run Solaris on the database machines and CentOS 5 on all the others. John called Solaris &amp;#8220;a mistake they wouldn&amp;#8217;t repeat,&amp;#8221; not because of any particular problems with it but because of a lack of system administrators in their organization who had experience with it.&lt;/p&gt;
&lt;p&gt;They use Oracle for the database engine, which had issues with the large numbers of connections created by the large number of Mongrels. As a result, memory usage on the database servers was high, and these machines were upgraded to 12G of &lt;span class=&quot;caps&quot;&gt;RAM&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;Performance optimization&lt;/h2&gt;
&lt;p&gt;The performance goals, which were achieved, were:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Sub-second home page load time&lt;/li&gt;
	&lt;li&gt;4-second average search time&lt;/li&gt;
	&lt;li&gt;Never dies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the course of performance optimization, they considered &lt;a href=&quot;http://haproxy.1wt.eu/&quot;&gt;HAProxy&lt;/a&gt; and &lt;a href=&quot;http://swiftiply.swiftcore.org/&quot;&gt;Swiftiply&lt;/a&gt; but chose to go with the F5 because they had the hardware and understood how to use it.&lt;/p&gt;
&lt;p&gt;They wrote Mongrel handlers to pick requests off from the web tier and send them to the service tier without involving Rails. They also wrote a C library for parsing search cluster responses and turning them into hashes.&lt;/p&gt;
&lt;p&gt;In the web tier, they switched to &lt;a href=&quot;http://www.kuwata-lab.com/erubis/&quot;&gt;Erubis&lt;/a&gt; for rendering views. They found asset download times to be a bigger issue than the speed of the framework. Following the Yahoo performance guidelines, they minified the JavaScript. They also switched from Prototype to jQuery.&lt;/p&gt;
&lt;p&gt;They used the asset_packager plug-in (now made obsolete by Rails 2) and moved image serving to an &lt;a href=&quot;http://www.akamai.com/html/solutions/media_delivery.html&quot;&gt;Akamai&lt;/a&gt; edge cache.&lt;/p&gt;
&lt;p&gt;They found that Apache was slow serving the 42-byte single-pixel GIFs that they use as analytics tags, which drove a shift to &lt;a href=&quot;/topic/156-nginx&quot;&gt;Nginx&lt;/a&gt; for the web tier.&lt;/p&gt;
&lt;p&gt;After this tuning, performance was better than the Java site. In terms of availability, all site availability issues in first six months were due to database problems.&lt;/p&gt;
&lt;p&gt;All things considered, it was a very successful rewrite. While the problems with the prior Java site cannot all be blamed on the technology, it is clear that Rails gave the team significant advantages with no loss of performance.&lt;/p&gt;&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:49 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79208-the-rebuilding-and-scaling-of-yellowpages-com</guid>
          <link>http://www.buildingwebapps.com/item/79208-the-rebuilding-and-scaling-of-yellowpages-com</link>
        </item>
        
        <item>
          <title>New Crop of Rails 2.0 Books</title>
          <description>&lt;p&gt;In 2007, more than a dozen Ruby on Rails books debuted &amp;#8212; only to be almost instantly made out-of-date by the release of Rails 2 toward the end of the year. In many respects, books written for Rails 1.2 are usable with Rails 2.0, but they can be problematic. If they use the scaffold generator in their examples, or dynamic scaffolding, the examples simply won&amp;#8217;t work as written. And there are many small ways, such as the enhanced syntax for migrations, in which a Rails 1.2 book will teach a way that works, but is no longer optimal.&lt;/p&gt;
&lt;p&gt;There are now some great Rails 2.0 books out, making life a lot easier for new and experienced Ruby on Rails programmers. Here&amp;#8217;s a quick rundown of the recent books.&lt;/p&gt;
&lt;h3&gt;For Beginners&lt;/h3&gt;
&lt;p&gt;If you&amp;#8217;re new to Rails and don&amp;#8217;t have a lot of programming experience, Patrick Lenz&amp;#8217;s &lt;a href=&quot;http://www.amazon.com/exec/obidos/ASIN/0980455200/buildicom-20&quot;&gt;Simply Rails 2&lt;/a&gt; provides a great start. It explains all the concepts clearly and doesn&amp;#8217;t assume much software development background. The example site built throughout the book is a Digg clone, which serves well to illustrate all the key topics.&lt;/p&gt;
&lt;h3&gt;Getting Started with Rails, for Experienced Developers&lt;/h3&gt;
&lt;p&gt;If you have some web application and object-oriented programming experience, then you may want a book that goes deeper and explains things more fully, but doesn&amp;#8217;t take time to explain programming basics. Agile Web Development with Rails, which was for some time the &lt;em&gt;only&lt;/em&gt; Rails book, is a great book for developers with some experience.&lt;/p&gt;
&lt;p&gt;The second edition of this book, which is the newest available in print, is based on Rails 1.2, and the nicely detailed &amp;#8220;Depot&amp;#8221; shopping-cart example fails under Rails 2.0. The depth of this book also creates many places where the differences between the versions is significant.&lt;/p&gt;
&lt;p&gt;Fortunately, the third edition is now available as a &lt;a href=&quot;http://pragprog.com/titles/rails3/agile-web-development-with-rails-third-edition&quot;&gt;beta book&lt;/a&gt;. You can download today&amp;#8217;s version as a &lt;span class=&quot;caps&quot;&gt;PDF&lt;/span&gt; file, in which most of the critical updates have already been made, which comes with frequent updates, and also pre-order a printed copy so you&amp;#8217;ll have one as soon it comes out the fall.&lt;/p&gt;
&lt;h3&gt;Going Deeper&lt;/h3&gt;
&lt;p&gt;After a slew of tutorial books in 2007, most of which are written for Rails 1.2, there&amp;#8217;s been a recent surge in deeper, more advanced books that add a tremendous richness to the available Rails books.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/exec/obidos/ASIN/0321445619/buildicom-20&quot;&gt;The Rails Way&lt;/a&gt; is great reference work for Rails developers. It is not a book to start with, but it&amp;#8217;s one I&amp;#8217;d want to have by my side for reference once I had my bearings.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/exec/obidos/ASIN/0978739221/buildicom-20&quot;&gt;Advanced Rails Recipes&lt;/a&gt; includes dozens of short, focused articles explaining how to accomplish a particular Rails task. There&amp;#8217;s a tremendous amount of accumulated community knowledge in this book, which was written by Mike Clark with contributions from many people in the community.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/exec/obidos/ASIN/1590599942/buildicom-20&quot;&gt;Practical &lt;span class=&quot;caps&quot;&gt;REST&lt;/span&gt; on Rails 2 Projects&lt;/a&gt;, by Ben Scofield, is the first book to deeply explore the use of RESTful web services as implemented in Rails 2. It even includes an example of a Facebook application.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/exec/obidos/ASIN/0978739205/buildicom-20&quot;&gt;Deploying Rails Applications&lt;/a&gt; is the first book to really tackle Rails deployment in all of its complexities. With a cast of authors that includes Ezra Zygmuntowicz, Bruce Tate, Clinton Begin, Geoffrey Grosenbach, and Brian Hogan, this book encapsulates a lot of hard-earned knowledge and will save a lot of people a lot of pain.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:49 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79206-new-crop-of-rails-2-0-books</guid>
          <link>http://www.buildingwebapps.com/item/79206-new-crop-of-rails-2-0-books</link>
        </item>
        
        <item>
          <title>Easy Text Formatting with Textile</title>
          <description>&lt;p&gt;When I&amp;#8217;m writing content for the web, I hate dealing with &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; coding. &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; is rather verbose as a markup language, and having to include closing tags is messy and error-prone.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;re all pretty much stuck with delivering &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; code from our web sites, but that doesn&amp;#8217;t mean we have to write in it. There are various markup approaches that are superior to &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; when it comes to content creation, and which can be used as source code from which to create &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;. Two that are widely used are Textile and Markdown.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://textism.com/tools/textile/&quot;&gt;Textile&lt;/a&gt; was originally developed by Dean Allen for the TextPattern content management system and is now widely used in wikis and blogs. &lt;a href=&quot;http://daringfireball.net/projects/markdown/basics&quot;&gt;Markdown&lt;/a&gt;, created by John Gruber and Aaron Swartz, is an alternative that is conceptually similar but with a different syntax. Many of the popular blog engines provide a choice of Textile or Markdown (or &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;) for posts and comments (this site offers Textile).&lt;/p&gt;
&lt;p&gt;In this article, I&amp;#8217;ll show the basics of Textile markup and how easy it is to use it in Rails applications.&lt;/p&gt;
&lt;h1&gt;What is Textile?&lt;/h1&gt;
&lt;p&gt;Textile strives to make text markup as clean and simple as possible. You don&amp;#8217;t have to put &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; in front of each paragraph; bare text is automatically surrounded by &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;/p&amp;gt;&lt;/code&gt; tags. To make a first-level heading, just start the line with @h1. @. For example:&lt;/p&gt;
&lt;pre&gt;
h1. This is a heading and will be wrapped in &amp;lt;h1&amp;gt; and &amp;lt;/h1&amp;gt; tags

This is a text paragraph and will be wrapped in &amp;lt;p&amp;gt; and &amp;lt;/p&amp;gt; tags.

This is another paragraph.
&lt;/pre&gt;
&lt;p&gt;And I bet you can guess how to make a second-level heading, or a third-level heading.&lt;/p&gt;
&lt;p&gt;You never need to use close tags; a pair of carriage returns automatically triggers the appropriate end tag. Quotes are automatically converted to open and close quotes; hyphens are converted to n-dashes (if there is a space on either side); and double hyphens are converted to m-dashes.&lt;/p&gt;
&lt;p&gt;To make text bold, surround it with asterisks; to make it italic, with underscores:&lt;/p&gt;
&lt;pre&gt;
*This text will render in bold*
_This text will render in italic*
_*This text will render in bold italic*_
&lt;/pre&gt;
&lt;p&gt;To add a link to a piece of text, surround the text in quotes, and follow it by a colon and the &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt;:&lt;/p&gt;
&lt;pre&gt;
&quot;text to be linked&quot;:http://thisistheurl.com
&lt;/pre&gt;
&lt;p&gt;To include an image, type its &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; surrounded by exclamation points:&lt;/p&gt;
&lt;pre&gt;
!http://domain.com/path/to/image.jpg!
&lt;/pre&gt;
&lt;p&gt;To create a bullet list, start each line with an asterisk and a space, like this:&lt;/p&gt;
&lt;pre&gt;
* First item
* Second item
* Third item
&lt;/pre&gt;
&lt;p&gt;Want a nested bullet list? Just indent the asterisk by three spaces.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s more, but this should give you a feel for it. You can also use any &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; you want for things that Textile doesn&amp;#8217;t cover &amp;#8212; &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; code is simply passed through. For more details, see the &lt;a href=&quot;http://hobix.com/textile/&quot;&gt;Textile Reference&lt;/a&gt;. You can try out Textile online at the &lt;a href=&quot;http://www.textism.com/tools/textile/&quot;&gt;Textile home page&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Implementing Textile&lt;/h1&gt;
&lt;p&gt;There are Textile implementations for various environments. In my case, using Ruby on Rails, Textile is implemented by &lt;a href=&quot;http://whytheluckystiff.net&quot;&gt;whytheluckystiff&lt;/a&gt; &amp;#8217;s &lt;a href=&quot;http://whytheluckystiff.net/ruby/redcloth/&quot;&gt;RedCloth&lt;/a&gt; gem. (The current implementation does have some flaws, and a much-enhanced version called &lt;a href=&quot;http://code.whytheluckystiff.net/redcloth/wiki/SuperRedCloth&quot;&gt;Super RedCloth&lt;/a&gt; is in development.) To install, just enter the following in the console:&lt;/p&gt;
&lt;pre&gt;
gem install RedCloth
&lt;/pre&gt;
&lt;p&gt;With RedCloth installed, you create a new RedCloth object and pass some Textile-marked-up text to it:&lt;/p&gt;
&lt;pre&gt;
textile_styled_text =&quot;h1. This is an example of a heading in Textile&quot;
textile_object = RedCloth.new(textile_styled_text)
&lt;/pre&gt;
&lt;p&gt;Then, when you want to render this as &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt;, you simply use:&lt;/p&gt;
&lt;pre&gt;
html = textile_object.to_html
&lt;/pre&gt;
&lt;h1&gt;Making Textile Even Easier&lt;/h1&gt;
&lt;p&gt;That&amp;#8217;s pretty easy, but you can make it even easier. There&amp;#8217;s a Rails helper &amp;#8220;textilize,&amp;#8221; but thanks to  the &lt;a href=&quot;http://errtheblog.com/post/14&quot;&gt;acts_as_textiled&lt;/a&gt; plug from Chris Wanstrath, it&amp;#8217;s even easier than that. Install this plugin:&lt;/p&gt;
&lt;pre&gt;
ruby script/plugin install
 svn://errtheblog.com/svn/plugins/acts_as_textiled
&lt;/pre&gt;
&lt;p&gt;And then all you have to do is declare a model attribute as Textile-formatted in your model class:&lt;/p&gt;
&lt;pre&gt;
acts_as_textiled  :field_name
&lt;/pre&gt;
&lt;p&gt;That&amp;#8217;s it &amp;#8212; everything just works! (Remember, though, that you need to restart the server for the model change to take effect.) Textile marked up text is stored in the database. Form fields automatically show unrendered textile. But any other use of the model attribute automatically delivers the rendered &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; instead of the Textile source.&lt;/p&gt;
&lt;h1&gt;Markup without Coding&lt;/h1&gt;
&lt;p&gt;You can also simplify the entry of Textile markup by using the &lt;a href=&quot;http://slateinfo.blogs.wvu.edu/plugins/textile_editor_helper&quot;&gt;textile_editor_helper&lt;/a&gt; from Dave Olsen and Chris Scharf. This plug-in renders a set of icons for the common markup tasks in your form view, just above the text entry area:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/textile_helper.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When you click an icon, JavaScript code inserts the appropriate markup into your text.&lt;/p&gt;
&lt;p&gt;To use this, first install the plugin:&lt;/p&gt;
&lt;pre&gt;
ruby script/plugin install
 http://svn.webtest.wvu.edu/repos/rails/plugins/textile_editor_helper

rake textile_editor_helper:install
&lt;/pre&gt;
&lt;p&gt;And then replace the text_area tag in the form in which the user enters the Textile-formatted text with textile_editor:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;%= textile_editor 'object', 'field' -%&amp;gt;
&lt;/pre&gt;
&lt;p&gt;textile_editor takes all the same options as text_area.&lt;/p&gt;
&lt;p&gt;and at the end of the form add:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;%= textile_editor_initialize -%&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Finally, if you don&amp;#8217;t already include prototype.js, you&amp;#8217;ll need to add that to your layout:&lt;/p&gt;
&lt;pre&gt;
&amp;lt;%= javascript_include_tag 'prototype.js' %&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Presto! Now you have a row of icons above the text area so your users don&amp;#8217;t have to remember any markup at all. They&amp;#8217;ll still see the markup in the text area (this is not a wysiwyg editor), which will help them learn the markup code.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:46 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79187-easy-text-formatting-with-textile</guid>
          <link>http://www.buildingwebapps.com/item/79187-easy-text-formatting-with-textile</link>
        </item>
        
        <item>
          <title>Web 2.0 Expo Highlights and Factoids</title>
          <description>&lt;p&gt;Last week&amp;#8217;s Web 2.0 Expo had an overwhelming amount of content. Like any large conference, the quality was uneven, but there were some great talks and a lot of good ones. Among the main-session talks, &lt;a href=&quot;http://www.shirky.com/&quot;&gt;Clay Shirky&lt;/a&gt; gave a wonderful talk on the implications of the web. I especially liked his view on the &amp;#8220;cognitive surplus&amp;#8221; created by the shift from rural to urban lifestyles, and how this has historically been consumed by alcohol, and then by television. Now this cognitive surplus fuels wikipedia, and all the other forms of user participation on the web. Where does the time come from? Just a fraction of the time spent watching television.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://blip.tv/file/855937&quot;&gt;Video of Clay&amp;#8217;s talk&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.shirky.com/herecomeseverybody/2008/04/looking-for-the-mouse.html&quot;&gt;Transcript of Clay&amp;#8217;s talk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;#8217;m halfway through his new book, &lt;a href=&quot;http://www.amazon.com/exec/obidos/ASIN/1594201536/buildicom-20&quot;&gt;Here Comes Everybody: The Power of Organizing without Organizations&lt;/a&gt;, and it promises to be a worthwhile read.&lt;/p&gt;
&lt;p&gt;The most entertaining talk of the conference, by a long shot, was from &lt;a href=&quot;http://fakesteve.blogspot.com/&quot;&gt;Fake Steve Jobs&lt;/a&gt;, also known as Dan Lyons in his day job at Forbes. The &lt;a href=&quot;http://fakesteve.blogspot.com/&quot;&gt;Secret Diary of Steve Jobs&lt;/a&gt; is one of the best pieces of technology satire around.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://blip.tv/file/858285&quot;&gt;Video of Dan&amp;#8217;s talk at Web 2.0&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;http://web2expo.blip.tv/#864781&quot;&gt;Videos of all the other keynote presentations&lt;/a&gt; are also available.&lt;/p&gt;
&lt;p&gt;Among the most technical talks, and the only one with any real Ruby on Rails content, was Gregg Pollack&amp;#8217;s excellent talk on &lt;a href=&quot;http://www.railsenvy.com/ArtOfTestingWebApplications.pdf&quot;&gt;The Art of Testing Web Applications&lt;/a&gt;. (We proposed several Rails-related talks, but they didn&amp;#8217;t make the cut; apparently the organizers chose to avoid most technology-specific talks this year.)&lt;/p&gt;
&lt;p&gt;Many of the &lt;a href=&quot;http://en.oreilly.com/webexsf2008/public/schedule/proceedings&quot;&gt;presentation files&lt;/a&gt; have been posted, and more are likely to be added soon.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s an assortment of random factoids from my notes:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;70% of &lt;a href=&quot;http://current.com&quot;&gt;current.com&lt;/a&gt; users are on their computer at the same time they&amp;#8217;re watching TV&lt;/li&gt;
	&lt;li&gt;There&amp;#8217;s 2.43 billion photos on &lt;a href=&quot;http://flickr.com&quot;&gt;flickr&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;28 million people a month share a video on &lt;a href=&quot;http://youtube.com&quot;&gt;YouTube&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;MySQL is downloaded 70,000 times a day&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://aim.com&quot;&gt;&lt;span class=&quot;caps&quot;&gt;AIM&lt;/span&gt;&lt;/a&gt; stats:
	&lt;ul&gt;
		&lt;li&gt;Delivers 2 billion instant messages every day&lt;/li&gt;
		&lt;li&gt;Has had as many as 14 million simultaneous users&lt;/li&gt;
		&lt;li&gt;&lt;span class=&quot;caps&quot;&gt;AIM&lt;/span&gt; sees a usage drop when there&amp;#8217;s a popular show on TV, and then a peak when it ends&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://ning.com&quot;&gt;Ning&lt;/a&gt; stats:
	&lt;ul&gt;
		&lt;li&gt;Ning has about 250,000 individual social networks&lt;/li&gt;
		&lt;li&gt;70% of the networks are active (used in last 30 days)&lt;/li&gt;
		&lt;li&gt;Adding 1 million registered users per month&lt;/li&gt;
		&lt;li&gt;Adding 1,500 new networks a day&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://myspace.com&quot;&gt;MySpace&lt;/a&gt; stats:
	&lt;ul&gt;
		&lt;li&gt;117M unique users last month&lt;/li&gt;
		&lt;li&gt;100 billion rows of data in the database&lt;/li&gt;
		&lt;li&gt;85 gigs of bandwidth&lt;/li&gt;
		&lt;li&gt;5 million concurrent users&lt;/li&gt;
		&lt;li&gt;200K to 400K new users per day&lt;/li&gt;
		&lt;li&gt;11% of all on-line minutes are spent on myspace&lt;/li&gt;
		&lt;li&gt;50 million messages sent per day&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;http://wordpress.com&quot;&gt;WordPress&lt;/a&gt; stats:
	&lt;ul&gt;
		&lt;li&gt;54 million unique visitors in the U.S. each month &amp;#8212; one in four people&lt;/li&gt;
		&lt;li&gt;99.999% of WordPress blogs receive under 10,000 pageviews a day&lt;/li&gt;
		&lt;li&gt;In aggregate, there are 10+ million pageviews a day to permalink pages (45% of traffic)&lt;/li&gt;
		&lt;li&gt;An amount of content equivalent to 1.5 wikipedias is created on WordPress each month&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:49 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79205-web-2-0-expo-highlights-and-factoids</guid>
          <link>http://www.buildingwebapps.com/item/79205-web-2-0-expo-highlights-and-factoids</link>
        </item>
        
        <item>
          <title>Tim Bray: Ruby is the Leading Language for Web Applications</title>
          <description>&lt;p&gt;&lt;div style='float:left; width:210px'&gt;
&lt;p&gt;&lt;img src=&quot;/tim_bray.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;In his keynote talk at the recent Silicon Valley Ruby Conference, Sun&amp;#8217;s &lt;a href=&quot;http://www.tbray.org/ongoing/misc/Tim&quot;&gt;Tim Bray&lt;/a&gt; asserted that &lt;strong&gt;&amp;#8220;&lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; and Ruby are the languages of choice for new web apps.&amp;#8221;&lt;/strong&gt; Coming from most people, this wouldn&amp;#8217;t be a remarkable comment, but given that Tim is Sun&amp;#8217;s director of web technologies, it is worthy of note. Java wasn&amp;#8217;t even on Tim&amp;#8217;s list, much less his recommended solution.&lt;/p&gt;
&lt;p&gt;Tim raised two major concerns about &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt;: security and maintainability. He also pointed out that the object-oriented features &amp;#8220;seem bolted on,&amp;#8221; and don&amp;#8217;t feel right as compared to Ruby. He did note that there are some great &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; applications, such as &lt;a href=&quot;http://www.mediawiki.org&quot;&gt;MediaWiki&lt;/a&gt;, &lt;a href=&quot;http://wordpress.com/&quot;&gt;WordPress&lt;/a&gt;, and &lt;a href=&quot;http://drupal.org&quot;&gt;Drupal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Tim asserted that Rails is ahead of Java in terms of maintainability because you have so much less code. And, of course, Rails is best for time to market, which can be all-important to the success of a project.&lt;/p&gt;
&lt;p&gt;As for when to avoid Rails, Tim cited these situations:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;If the IT group will only deploy Java or .&lt;span class=&quot;caps&quot;&gt;NET&lt;/span&gt;&lt;/li&gt;
	&lt;li&gt;When you need a lot of experienced developers quickly&lt;/li&gt;
	&lt;li&gt;If you have a complex database schema you don&amp;#8217;t control&lt;/li&gt;
	&lt;li&gt;For an application that does a lot of computation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tim asserted that &amp;#8220;waterfall development just doesn&amp;#8217;t work any more.&amp;#8221; Some principles for success he cited:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Be human&lt;/li&gt;
	&lt;li&gt;Update often&lt;/li&gt;
	&lt;li&gt;Link&lt;/li&gt;
	&lt;li&gt;Don&amp;#8217;t try to be sticky&lt;/li&gt;
	&lt;li&gt;Look good&lt;/li&gt;
	&lt;li&gt;Balance hubris and humility&lt;/li&gt;
	&lt;li&gt;Free the conversation&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:49 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79203-tim-bray-ruby-is-the-leading</guid>
          <link>http://www.buildingwebapps.com/item/79203-tim-bray-ruby-is-the-leading</link>
        </item>
        
        <item>
          <title>Validating Email Addresses with Ruby</title>
          <description>&lt;p&gt;In any application in which a user enters an email address, there is the very real possibility that the user will make a typo and your application will end up with an invalid address. You can have them enter it twice, but this seems clunky. And you can, of course, send an email with an activation link, which provides the only true validation, but there&amp;#8217;s no need to bother sending the email if you know the address is no good. Furthermore, once you&amp;#8217;re past the page where the user enters their email address, you&amp;#8217;ve missed your chance to tell them there&amp;#8217;s something wrong and they should correct it.&lt;/p&gt;
&lt;p&gt;So you really should do what you can to validate the address when the user enters it. I recently made a simple addition to my applications that helps a lot: verify that the domain name is valid.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s surprisingly easy to do &amp;#8212; especially with a little help from Peter Cooper&amp;#8217;s excellent &lt;a href=&quot;http://www.mslater.com/2007/5/26/great-new-ruby-book&quot;&gt;Beginning Ruby&lt;/a&gt;, which has a very useful chapter on network programming. The following code is adapted from his examples:&lt;/p&gt;
&lt;pre&gt;
require 'resolv'
def validate_email_domain(email)
      domain = email.match(/\@(.+)/)[1]
      Resolv::DNS.open do |dns|
          @mx = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
      end
      @mx.size &amp;gt; 0 ? true : false
end
&lt;/pre&gt;
&lt;p&gt;This example makes use of the Ruby standard library &amp;#8220;resolv&amp;#8221;, so you need to require it first.&lt;/p&gt;
&lt;p&gt;The first step is to separate the domain name from the rest of the email address. The regular expression captures the part of the string that follows the @ symbol.&lt;/p&gt;
&lt;p&gt;Then the code creates a new &lt;span class=&quot;caps&quot;&gt;DNS&lt;/span&gt; resolver object and queries the resolver for an MX (mail exchanger) resource at the specified domain. This returns an array, which will be empty if there is no MX record for the domain.&lt;/p&gt;
&lt;p&gt;(Note: In a previous version of this article, I used Resolv.getaddress to see if there is a &lt;span class=&quot;caps&quot;&gt;DNS&lt;/span&gt; entry for the domain, instead of checking for an MX record. This approach works most of the time, but it rejects any domain for which there is no A record. If a domain is used only for email and not for a web server, there might not be an A record. Also, some domains have an A record only for www.domain.com, which will also fail the simple getaddress test.)&lt;/p&gt;
&lt;p&gt;You can use something like the following in the validate method within the appropriate model:&lt;/p&gt;
&lt;pre&gt;
unless email.blank?
    unless email =~ /^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/
        errors.add(:email, &quot;Your email address does not appear to be valid&quot;)
    else
        errors.add(:email, &quot;Your email domain name appears to be incorrect&quot;) unless validate_email_domain(email)
    end
end
&lt;/pre&gt;
&lt;p&gt;I first check to make sure the email address is not blank, because that&amp;#8217;s detected by a simple validates_presence_of :email statement that produces a different error message.&lt;/p&gt;
&lt;p&gt;Then I make sure that the email address is at least syntactically reasonable, with a rather ugly regular expression, before bothering to check the &lt;span class=&quot;caps&quot;&gt;DNS&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;It should be noted that the regex I use here isn&amp;#8217;t designed to cover all of the &lt;a href=&quot;http://rfc.net/rfc2822.html&quot;&gt;RFC2822&lt;/a&gt; cases, nor with other &lt;span class=&quot;caps&quot;&gt;RFC&lt;/span&gt; drafts dealing with non-&lt;span class=&quot;caps&quot;&gt;ASCII&lt;/span&gt; addressing.&lt;/p&gt;
&lt;p&gt;An even better approach would be to use an observer to validate the address with an Ajax call before the user submits the form.&lt;/p&gt;
&lt;p&gt;It is possible to take this a step further by sending the &lt;span class=&quot;caps&quot;&gt;SMTP&lt;/span&gt; server referenced in the MX record a &amp;#8220;&lt;span class=&quot;caps&quot;&gt;RCPT&lt;/span&gt; TO:&amp;#8221; command. In theory, this would check that the user name is valid as well as the domain name. This takes additional time, however, and I&amp;#8217;ve read that the response from mail servers is often not reliable. If anyone has tried this, I&amp;#8217;d appreciate any feedback on how well it worked.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:46 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79182-validating-email-addresses-with-ruby</guid>
          <link>http://www.buildingwebapps.com/item/79182-validating-email-addresses-with-ruby</link>
        </item>
        
        <item>
          <title>Exception Handling and Old URLs</title>
          <description>&lt;p&gt;Whenever I deploy a Rails site, I install the exception notification plugin so I get an email if a user provokes a bug I hadn&amp;#8217;t found. It&amp;#8217;s a piece of cake to install:&lt;/p&gt;
&lt;p&gt;1. Install the plugin&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ruby script/plugin install exception_notification&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2. Include the plugin in your ApplicationController (in the application.rb file)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;include ExceptionNotifiable&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3. Add one line to your environment.rb file to specify where to send the email&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ExceptionNotifier.exception_recipients = %w(person1@domain.com person2@domain.com)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4. Make sure you have ActionMailer configured (to use either sendmail or &lt;span class=&quot;caps&quot;&gt;STMP&lt;/span&gt;), which you&amp;#8217;ll already have done if your app sends email for any purpose.&lt;/p&gt;
&lt;h2&gt;What&amp;#8217;s this? Errors already?&lt;/h2&gt;
&lt;p&gt;A few minutes after I first installed the Exception Notification plugin, I checked my inbox and found a dozen error emails from the site! After a brief moment of panic, I realized that all were coming from search engine spiders, and that the URLs were all invalid. I had replaced an old, crufty, static &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; site, and the spiders were rechecking pages they had indexed in the past.&lt;/p&gt;
&lt;p&gt;So the next question was what to do with the old URLs. I could remap each &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; to the most appropriate page on the new site, but the old site got little traffic and there wasn&amp;#8217;t a clear mapping between the two sets of pages, so it hardly seemed worth it. I decided to map index.html to the new home page, since many people might have bookmarked that page, and it was clear what it should map to. As for everything else, I wanted a way to tell the spiders to stop trying to index them, and to tell anyone who accessed them that this was no longer a valid &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; and they should explore the new site.&lt;/p&gt;
&lt;p&gt;The heart of the fix is to add a few lines to routes.rb. Fortunately, the old site design had put all the &lt;span class=&quot;caps&quot;&gt;HTML&lt;/span&gt; pages except for the index page into a directory called html. There were also some &lt;span class=&quot;caps&quot;&gt;PDF&lt;/span&gt; files, conveniently in a directory named web_pdfs.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the routes, which I added at the end just before the default route:&lt;/p&gt;
&lt;pre&gt;
&lt;code&gt;
map.connect '/index.html', :controller =&amp;gt; 'page', :action =&amp;gt; 'home'
map.connect '/html/*any', :controller =&amp;gt; 'page', :action =&amp;gt; 'oldpage'
map.connect '/web_pdfs/*any', :controller =&amp;gt; 'page', :action =&amp;gt; 'oldpage'
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;I have a controller called page_controller that manages the public parts of the site. The first route simply maps index.html to the home page action.&lt;/p&gt;
&lt;p&gt;The second route maps any &lt;span class=&quot;caps&quot;&gt;URL&lt;/span&gt; in the html directory to a new page, which I called simply &amp;#8220;oldpage&amp;#8221;. The oldpage.rhtml file simply has some text telling the visitor that this page no longer exists, and gives them some suggestions for exploring the new site.&lt;/p&gt;
&lt;p&gt;The &amp;#8220;*any&amp;#8221; in the route definition absorbs whatever comes after &amp;#8220;/html/&amp;#8221;. With this technique, I don&amp;#8217;t have to worry about whether there might be subdirectories within html, or what the file name extensions might be.&lt;/p&gt;
&lt;p&gt;The final piece was to tell the spiders not to bother with these pages any more. I didn&amp;#8217;t want to use a redirect, because the page to which I wanted to redirect the user wasn&amp;#8217;t the new permanent page, but rather one that explained that the site had been changed. I decided to set the &lt;span class=&quot;caps&quot;&gt;HTTP&lt;/span&gt; header to the &amp;#8220;gone&amp;#8221; status code (410). This just takes one line in the page controller oldpage action:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
def oldpage
  render :template =&amp;gt; 'page/oldpage', :status =&amp;gt; :gone
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eventually the spiders will stop spidering these old pages, but since index.html will still lead to the new home page without any error code, they will find all the new pages from there.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:46 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79184-exception-handling-and-old-urls</guid>
          <link>http://www.buildingwebapps.com/item/79184-exception-handling-and-old-urls</link>
        </item>
        
        <item>
          <title>Using SSH Keys to Speed Login</title>
          <description>&lt;p&gt;With a basic &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; setup, you have to enter your password every time you log in to the server, which is not unreasonable from a security perspective. But if you want to automate tasks and use deployment tools such as &lt;a href=&quot;http://manuals.rubyonrails.com/read/book/17&quot;&gt;Capistrano&lt;/a&gt;, you&amp;#8217;ll end up typing that password over and over again, even for a single deployment process. Fortunately, there is a mechanism to avoid this while still preserving good security. But, as with most such things in Windows, it takes a little effort to set it up. (Things are a little simple on Macs, which we&amp;#8217;ll cover in a future article.)&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; authentication uses &lt;a href=&quot;http://en.wikipedia.org/wiki/Public-key_cryptography&quot;&gt;public key cryptography&lt;/a&gt;, in which you have a private key available only to you on your local system, and a matching public key that can be published on your server. Authentication software can confirm that the public and private keys match, but hackers cannot derive your private key from your public key. Once you set up a public-private key pair, these keys can be used to authenticate your &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; sessions, and you won&amp;#8217;t ever have to type your password again.&lt;/p&gt;
&lt;p&gt;There&amp;#8217;s a couple different programs you can use to accomplish this; I&amp;#8217;m going to explain how to do it with PuTTY and its associated programs, PuTTYgen and Pageant. If you installed the full PuTTY package, you&amp;#8217;ll have all three programs already installed. If not, &lt;a href=&quot;http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html&quot;&gt;download the installer&lt;/a&gt; and run it now. (Be sure to get the full package, under the heading &amp;#8220;A Windows installer for everything except PuTTYtel,&amp;#8221; and not just putty.exe.)&lt;/p&gt;
&lt;h1&gt;Creating Your Keys with PuTTYgen&lt;/h1&gt;
&lt;p&gt;To create your public-private key pair, run PuTTYgen. There&amp;#8217;s several types of keys, but &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt;-2 &lt;span class=&quot;caps&quot;&gt;RSA&lt;/span&gt; is the most common and is selected by default. (If this doesn&amp;#8217;t work, you&amp;#8217;ll need to check with your host to see what type of key their &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; server is expecting.) The number of bits defaults to 1024, which is fine. So all you have to do in the PuTTYgen window is click the Generate button, and then wiggle the mouse around a bit. The mouse movements generate random data that ensures that your key is unique.&lt;/p&gt;
&lt;p&gt;When PuTTYgen is done creating the key, it will show a long string of characters that make up the public key. Select this text and paste it into a file, named something like id.pub (using notepad or any simple text editor). I made a folder at the root level of my C drive called &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; to store these keys and other related info, but you can put it anywhere you can find it later. (Note: you can also click the Save Public Key button and enter a file name, but &lt;strong&gt;this file won&amp;#8217;t work&lt;/strong&gt; as an alternative to the id.pub file we generated with cut-and-paste. It includes line break characters that confuse the server-side &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; code.)&lt;/p&gt;
&lt;p&gt;Now you need to save your private key. If you just click the Save Private Key button, PuTTYgen will ask if you really want to save it without a passphrase, because we didn&amp;#8217;t enter one. Here you have a choice to make between convenience and security.&lt;/p&gt;
&lt;p&gt;The passphrase is essentially a password for accessing the key. Once you have your public key uploaded to your server (which we&amp;#8217;ll do shortly), anyone who has access to your private key will have access to your server. If you use password protection on your PC, and you&amp;#8217;re the only one with access to it, you might be comfortable going without a passphrase. But it is safest to use a passphrase, and we&amp;#8217;ll soon see how you can make it so you only need to enter it once each time you boot your system. So to set a passphrase and save the private key:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Enter it twice, once in the Key Passphrase field and once in the Confirm Passphrase field. Keep in mind that this passphrase is essentially the key to accessing your server, so make it a robust password.&lt;/li&gt;
	&lt;li&gt;Click the Save Private Key button, and enter a file name (no extension) for your private key. The .ppk extension is automatically appended.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You now have your key pair and are done with PuTTYgen. Next you need to upload your public key to your server and set up your PC to access your private key.&lt;/p&gt;
&lt;h1&gt;Uploading Your Public Key&lt;/h1&gt;
&lt;p&gt;The details of uploading your public key may vary depending on the server configuration. The instructions below are for &lt;a href=&quot;http://www.railsmachine.com&quot;&gt;Rails Machine&lt;/a&gt; and are derived from the Mac and Linux oriented instructions they provide.&lt;/p&gt;
&lt;p&gt;Open an &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; session to your server (using PuTTy, or another client if you prefer, as described in my &lt;a href=&quot;http://www.mslater.com/2006/12/8/remote-linux-admin-for-windows-users&quot;&gt;previous post&lt;/a&gt;.) You probably have more than one user account; in my case, following the recommended practices from the Rails Machine folks, I have a root account that I never log into directly, and regular user accounts of Michael and Deploy. The Deploy account is the one I use for almost all communication with the server. So log into that account, or its equivalent for your setup. You&amp;#8217;ll have to manually enter the password one last time.&lt;/p&gt;
&lt;p&gt;Now, in the shell window that is connected to your server, create a directory for the private key file:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;mkdir ~/.ssh&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This creates a directory named .ssh within your home directory, which is where the &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; server will look for the public key.&lt;/p&gt;
&lt;p&gt;Now set the permissions for this directory so you, but only you, have all privileges:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;chmod 700 ~/.ssh&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now you have a directory on your server to hold your public key, and you need to move the key up there. There&amp;#8217;s various tools you can use to do this. One tool you should become comfortable with is scp, or secure copy. It is not built in to Windows, but there is a version of it that comes with PuTTY, called pscp. If you add the path to the PuTTY program directory to your system path, you&amp;#8217;ll be able to use pscp in any command window. (You may also want to install a set of Unix-style utilities; you can install the entire &lt;a href=&quot;http://www.cygwin.com&quot;&gt;Cygwin&lt;/a&gt; environment, or if you want something lighter weight just for &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt;-related tasks, get just the &lt;a href=&quot;http://sshwindows.sourceforge.net/&quot;&gt;OpenSSH&lt;/a&gt; utilities. In either case, make sure to add to your Windows system path the folder in which these programs are stored, so you can use them from any command window without having to type their full path.)&lt;/p&gt;
&lt;p&gt;To copy the public key, follow these steps:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Open a Windows shell in the folder in which you&amp;#8217;ve stored your public key. (If you installed the &lt;a href=&quot;http://download.microsoft.com/download/whistler/Install/2/WXP/EN-US/CmdHerePowertoySetup.exe&quot;&gt;Command Here&lt;/a&gt; utility as I recommended in the previous article, you can just right-click the folder and choose Open Command Window Here.)&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;In the command window, type&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;pscp id.pub username@hostname.com:~/.ssh/authorized_keys&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(Of course, you&amp;#8217;ll need to replace &amp;#8220;username&amp;#8221; with your actual user name, and &amp;#8220;hostname.com&amp;#8221; with the name of your server. If you&amp;#8217;ve named your public key something other than id.pub, replace that name as well. Finally, if you&amp;#8217;re using scp from OpenSSH instead of PuTTY&amp;#8217;s pscp, drop the p in the command name.) This will copy your public key to a file called authorized_keys in the .ssh directory in your home directory.&lt;/p&gt;
&lt;p&gt;Finally, to make the key file a little more secure, go back to your &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; window (remember, we started there but then switched to the Windows console), and type:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;chmod 600 ~/.ssh/authorized_keys&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This ensures that only the owner of this file (that&amp;#8217;s the user name you began your &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; session with) can read or write it.&lt;/p&gt;
&lt;h1&gt;Making Your Private Key Available in Windows&lt;/h1&gt;
&lt;p&gt;OK, we&amp;#8217;re almost there. Now we need to enable Windows programs making &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; connections to access your private key file. You could set PuTTY to use the key file, but that doesn&amp;#8217;t buy you much, since it will ask for the passphrase every time you open a connection, and it won&amp;#8217;t be available to other programs (such as Capistrano). So, you need to use another program called Pageant, which is installed along with PuTTY, to load the key into memory and make it available to other programs.&lt;/p&gt;
&lt;p&gt;You can run Pageant directly via Start &amp;gt; All Programs &amp;gt; PuTTY &amp;gt; Pageant, and then you can tell Pageant to load your private key. But assuming you want the private key to always be available, you want it to load automatically upon startup. To do so, create a text file called load_private_key.bat (or whatever), with the following contents:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;start &amp;#8220;Pageant&amp;#8221; &amp;#8220;c:/Program Files/PuTTY/Pageant.exe&amp;#8221; c:/ssh/id.ppk&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that you&amp;#8217;ll need to change the path to Pageant.exe if you didn&amp;#8217;t install PuTTY in its default location. The id.ppk file is the private key file that you generated from PuTTYgen. (Using the &amp;#8220;start&amp;#8221; command, rather than simply providing the path to Pageant directly, prevents a &lt;span class=&quot;caps&quot;&gt;DOS&lt;/span&gt; window from being left on the screen. Thanks to Tim Jervis for this tip.)&lt;/p&gt;
&lt;p&gt;Finally, add this batch file to your startup tasks (Click Startup &amp;gt; All Programs &amp;gt; right click on Startup and choose Open, then right-click the load_private_key.bat file, drag it into the startup folder, and choose Create Shortcut from the menu that appears when you release the mouse).&lt;/p&gt;
&lt;p&gt;Now, when you reboot your system, the batch file will run, Pageant will load your private key, and you&amp;#8217;ll be prompted for the passphrase that you specified when you created the key. Enter this passphrase just this once, and your private key is now available to all &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; functions. When you shut your computer down, everything is secure again.&lt;/p&gt;
&lt;h1&gt;Setting up Subversion&lt;/h1&gt;
&lt;p&gt;If you&amp;#8217;re using Subversion, you need to take one more step to enable it to use the private key generated by PuTTYgen: adding a line to Subversion&amp;#8217;s configuration file.&lt;/p&gt;
&lt;p&gt;Subversion&amp;#8217;s configuration file is located in the Application Data directory under your user account. The full path is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C:\Documents and Settings\{your windows user name}\Application Data\Subversion\config&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Note that Application Data is a hidden folder, so to locate this file you must have Windows set to show hidden files and folders.&lt;/p&gt;
&lt;p&gt;Open the config file in any plain text editor (such as Notepad) and add the following line:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ssh = $SVN_SSH plink.exe&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;plink.exe is the command-line link setup program that is included with PuTTY.&lt;/p&gt;
&lt;p&gt;You&amp;#8217;ll also need to make sure that the PuTTY directory is listed in your system&amp;#8217;s Path.&lt;/p&gt;
&lt;p&gt;Unfortunately, plink insists on popping up a &lt;span class=&quot;caps&quot;&gt;DOS&lt;/span&gt; window, which is annoying. If anyone knows how to stop it from doing this, please let me know!&lt;/p&gt;
&lt;h1&gt;You&amp;#8217;re Done!&lt;/h1&gt;
&lt;p&gt;That was simple, wasn&amp;#8217;t it? :-)  This may seem like a lot of trouble to go to just to avoid having to type your password, but once you&amp;#8217;ve set this up once, you&amp;#8217;re done. And if you&amp;#8217;re using an automated deployment tool such as Capistrano, you&amp;#8217;d have to type your password multiple times for a single deployment (since one deployment involved multiple &lt;span class=&quot;caps&quot;&gt;SSH&lt;/span&gt; commands and other actions); with this setup, it can be fully automated.&lt;/p&gt;</description>
          <pubDate>Thu, 24 Jul 2008 08:00:46 GMT</pubDate>
          <guid>http://www.buildingwebapps.com/item/79185-using-ssh-keys-to-speed-login</guid>
          <link>http://www.buildingwebapps.com/item/79185-using-ssh-keys-to-speed-login</link>
        </item>
    
  </channel>
</rss>