<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>Jeff Dean - Ruby on Rails Programmer</title>
  <id>tag:zilkey.com,2011</id>
  <generator uri="http://zilkey.com" version="0.0.1">Jeff Dean</generator>
  <link href="http://zilkey.com/feed/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://zilkey.com/" rel="alternate" type="text/html"/>
  <updated>2012-03-17T06:47:22+00:00</updated>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2012-03-17:20120317064722</id>
      <published>2012-03-17T06:47:22+00:00</published>
      <updated>2012-03-17T06:47:22+00:00</updated>
        <category term="node"/>
      <link href="http://zilkey.com/2012/3/17/serving-a-nodejs-server-with-apache-and-forever" rel="alternate" type="text/html"/>
      <title>Serving a NodeJS coffee-script app with apache and forever</title>
      <content type="html">
        
&lt;p&gt;NodeJS seems to be all the rage these days so I thought I'd try to test-drive an app, then deploy it on my CentOS server.&lt;/p&gt;

&lt;p&gt;In this post I'll describe how I set the server up for a very simplistic deployment with Apache and Forever.&lt;/p&gt;
&lt;p&gt;I should start by saying that I took all of this from these blog posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://catchvar.com/nodejs-server-and-web-sockets-on-amazon-ec2-w&quot;&gt;http://catchvar.com/nodejs-server-and-web-sockets-on-amazon-ec2-w&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://davybrion.com/blog/2012/01/hosting-a-node-js-site-through-apache/&quot;&gt;http://davybrion.com/blog/2012/01/hosting-a-node-js-site-through-apache/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.nodejitsu.com/keep-a-nodejs-server-up-with-forever&quot;&gt;http://blog.nodejitsu.com/keep-a-nodejs-server-up-with-forever&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;To start with, I installed node:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd /tmp&amp;#x000A;GIT_SSL_NO_VERIFY=true sudo git clone --depth 1 https://github.com/joyent/node.git&amp;#x000A;cd node&amp;#x000A;git checkout v0.6.12&amp;#x000A;./configure --prefix=/usr/local&amp;#x000A;make&amp;#x000A;make install&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then I installed NPM, the node package manager:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd /tmp&amp;#x000A;git clone git://github.com/isaacs/npm.git&amp;#x000A;cd npm&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I wrote the app in CoffeeScript, so I decided to install that globally:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd apps/myapp/current&amp;#x000A;npm install -d&amp;#x000A;sudo npm install -g coffee-script&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I setup a VirtualHost to proxy the non-socket requests:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;LoadModule proxy_module modules/mod_proxy.so&amp;#x000A;LoadModule proxy_ftp_module modules/mod_proxy_ftp.so&amp;#x000A;LoadModule proxy_http_module modules/mod_proxy_http.so&amp;#x000A;LoadModule proxy_connect_module modules/mod_proxy_connect.so&amp;#x000A;&amp;#x000A;&amp;lt;VirtualHost *:80&amp;gt;&amp;#x000A;  ServerName myapp.com&amp;#x000A;  ServerAlias www.myapp.com&amp;#x000A;&amp;#x000A;  ProxyRequests off&amp;#x000A;&amp;#x000A;  &amp;lt;Proxy *&amp;gt;&amp;#x000A;    Order deny,allow&amp;#x000A;    Allow from all&amp;#x000A;  &amp;lt;/Proxy&amp;gt;&amp;#x000A;&amp;#x000A;  &amp;lt;Location /&amp;gt;&amp;#x000A;    ProxyPass http://localhost:8080/&amp;#x000A;    ProxyPassReverse http://localhost:8080/&amp;#x000A;  &amp;lt;/Location&amp;gt;&amp;#x000A;&amp;lt;/VirtualHost&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To get the node app started, and keep it running, I used &lt;a href=&quot;https://github.com/nodejitsu/forever&quot;&gt;Forever&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo npm install -g forever&amp;#x000A;NODE_ENV=production forever start -c coffee server.coffee&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once I restarted apache, voila!  The app worked!  However, since sockets wouldn't work,
I had to configure the app to make the browser's sockets point directly to the port that node runs on.
To do so, here's what my server.coffee looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;express = require 'express'&amp;#x000A;exports.app = app = express.createServer()&amp;#x000A;io = require('socket.io').listen(app)&amp;#x000A;&amp;#x000A;host = null&amp;#x000A;port = null&amp;#x000A;&amp;#x000A;app.configure -&amp;gt;&amp;#x000A;  app.set('views', __dirname + '/app/views')&amp;#x000A;  app.set('view engine', 'jade')&amp;#x000A;  app.use(express.bodyParser())&amp;#x000A;  app.use(express.methodOverride())&amp;#x000A;  app.use(express.static(__dirname + '/public'))&amp;#x000A;&amp;#x000A;app.configure 'development', -&amp;gt;&amp;#x000A;  host = 'localhost'&amp;#x000A;  port = 3000&amp;#x000A;  # ...&amp;#x000A;&amp;#x000A;app.configure 'test', -&amp;gt;&amp;#x000A;  host = 'localhost'&amp;#x000A;  port = 3000&amp;#x000A;  # ...&amp;#x000A;&amp;#x000A;app.configure 'production', -&amp;gt;&amp;#x000A;  host = 'myapp.com'&amp;#x000A;  port = 8080&amp;#x000A;  # ...&amp;#x000A;&amp;#x000A;app.get '/javascripts/dynamic/javascript-host.js', (req, res) -&amp;gt;&amp;#x000A;  res.header('Content-Type', 'text/javascript')&amp;#x000A;  res.send(&quot;window.SocketConfig = {host: '#{host}', port: #{port} };&quot;)&amp;#x000A;&amp;#x000A;io.sockets.on 'connection', (socket) -&amp;gt;&amp;#x000A;  socket.on 'publish', (data) -&amp;gt;&amp;#x000A;    socket.broadcast.emit &quot;someMessage&quot;, data&amp;#x000A;&amp;#x000A;app.listen port&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that I'm serving up a dynamic javascript file at  &lt;code&gt;/javascripts/dynamic/javascript-host.js&lt;/code&gt;
which will supply the client with the correct host and port to listen on.&lt;/p&gt;

&lt;p&gt;Then I included that js file like I would any other js file in jade:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script(type='text/javascript', src='/javascripts/dynamic/javascript-host.js')&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The browser-side socket code looks like this (in CoffeeScript):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;socket = io.connect(&quot;http://#{SocketConfig.host}:#{SocketConfig.port}&quot;)&amp;#x000A;&amp;#x000A;socket.on &quot;connect&quot;, -&amp;gt;&amp;#x000A;  #  do something&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This configuration is nowhere near production ready.  The biggest problem I see is that there is a
single node process, so I'll have to add more, then figure out how to load balance on that port with
something that understands sockets, like haproxy and also make sure that apache still proxies the other
requests appropriately.&lt;/p&gt;

&lt;p&gt;My overall first impression of deploying with node is that it's in it's infancy since so many people
on the blogosphere recommend running node apps with screen or nohup.  But setting up node with forever
and apache was simple and fast for this toy app, and I was pleasantly surprised at how pain-free it was.&lt;/p&gt;


      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2012-03-13:20120313035903</id>
      <published>2012-03-13T03:59:03+00:00</published>
      <updated>2012-03-13T03:59:03+00:00</updated>
        <category term="gems"/>
      <link href="http://zilkey.com/2012/3/13/setting-up-a-simple-private-gem-server" rel="alternate" type="text/html"/>
      <title>Setting up a simple, private gem server</title>
      <content type="html">
        
&lt;p&gt;At &lt;a href=&quot;http://www.broadstripes.com/&quot;&gt;Broadstripes&lt;/a&gt; we have a handful of gems that we use internally, but we don't want to publish them on a public site
like &lt;a href=&quot;http://rubygems.org&quot;&gt;RubyGems.org&lt;/a&gt;.  In this post, I'll describe how I setup a simple, static gem server
with basic auth and a simple &lt;a href=&quot;https://github.com/capistrano/capistrano/wiki/&quot;&gt;capistrano&lt;/a&gt; recipe I added to our gems in order to publish them.&lt;/p&gt;
&lt;h2&gt;The basics of gem sources&lt;/h2&gt;

&lt;p&gt;When you install gems, it looks through all of your various gem sources.  You can see the sources like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gem sources&amp;#x000A;&amp;#x000A;*** CURRENT SOURCES ***&amp;#x000A;&amp;#x000A;http://rubygems.org/&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;At it's simplest, a gem server is simply a directory with some static files that looks something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;.&amp;#x000A;|-- Marshal.4.8&amp;#x000A;|-- Marshal.4.8.Z&amp;#x000A;|-- gems&amp;#x000A;|   |-- my-custom-gem-0.5.1.gem&amp;#x000A;|-- latest_specs.4.8&amp;#x000A;|-- latest_specs.4.8.gz&amp;#x000A;|-- prerelease_specs.4.8&amp;#x000A;|-- prerelease_specs.4.8.gz&amp;#x000A;|-- quick&amp;#x000A;|   `-- Marshal.4.8&amp;#x000A;|       |-- my-custom-gem-0.5.1.gemspec.rz&amp;#x000A;|-- specs.4.8&amp;#x000A;`-- specs.4.8.gz&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As long as that root directory is located at a URL accessible to a machine with rubygems installed, it can be used as a valid gem source.&lt;/p&gt;

&lt;h2&gt;Generating the index&lt;/h2&gt;

&lt;p&gt;Generating the files necessary for the gem server is very simple.  Just put create a gems directory, add your gem, and call &lt;code&gt;gem generate_index&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;mkdir -p /path/to/virtual/host/root&amp;#x000A;cd !$&amp;#x000A;mkdir gems&amp;#x000A;cp my-custom-gem-0.5.1.gem gems&amp;#x000A;gem generate_index&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that's all there is to creating the necessary files for the gem server.  You can read more &lt;a href=&quot;http://guides.rubygems.org/command-reference/#gem_generate_index&quot;&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Making it private&lt;/h2&gt;

&lt;p&gt;I chose to use basic auth over HTTPS to protect our gems, since we needed to access them from a variety of servers that were not connected via VPN.&lt;/p&gt;

&lt;p&gt;Here's what our nginx config looks like for the gem server:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;server {&amp;#x000A;  listen [::]:443;&amp;#x000A;  ssl on;&amp;#x000A;  # ssl config etc....&amp;#x000A;&amp;#x000A;  location / {&amp;#x000A;    auth_basic              &quot;Gems Web Access&quot;;&amp;#x000A;    auth_basic_user_file    /opt/nginx/conf/gems.htpasswd;&amp;#x000A;  }&amp;#x000A;}&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All pretty standard stuff - since basic auth uses clear-text crednetials, we use ssl to encrypt the transmission.&lt;/p&gt;

&lt;h2&gt;Referencing the private gem server&lt;/h2&gt;

&lt;p&gt;Basic auth allow you to add the credentials in the url itself, like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://username:password@gems.mydomain.com&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The downside to this is that the credentials have to be stored in clear text on the machine that has rubygems installed, but for us that wasn't an issue.&lt;/p&gt;

&lt;p&gt;So to add that as a source, just run:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gem source https://username:password@gems.mydomain.com&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Pushing gems&lt;/h2&gt;

&lt;p&gt;Since the gems are on a private server, commands like &lt;code&gt;gem push&lt;/code&gt; will not work.  So I created a quick capistrano task
to push the gem up, and rebuild the index.  It looks something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;set :user, &quot;deploy&quot;&amp;#x000A;set :deploy_to, &quot;/home/deploy/rails_apps/gems/&quot;&amp;#x000A;role :gem_server, &quot;gems.myserver.com&quot;&amp;#x000A;&amp;#x000A;task :publish, :roles =&amp;gt; :gem_server do&amp;#x000A;  latest_gem_path = Dir.glob(&quot;*.gem&quot;).sort.last&amp;#x000A;  gem_name = File.basename(latest_gem_path)&amp;#x000A;  upload(&quot;#{latest_gem_path}&quot;, &quot;#{current_path}/public/gems/#{gem_name}&quot;)&amp;#x000A;  run &quot;cd #{current_path}/public &amp;amp;&amp;amp; gem generate_index&quot;&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This simple cap recipe assumes that you have run &lt;code&gt;gem build mygem.gemspec&lt;/code&gt;, and it grabs the latest version
of the gem, pushes it to the remote server and rebuilds the index.&lt;/p&gt;

&lt;p&gt;While this is not appropriate if your gem server has highly sensitive gems, it's a fast and simple solution
for situations where you'd like to host your own private gems.&lt;/p&gt;


      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2012-03-13:20120313020953</id>
      <published>2012-03-13T02:09:53+00:00</published>
      <updated>2012-03-13T02:09:53+00:00</updated>
        <category term="oracle"/>
      <link href="http://zilkey.com/2012/3/13/developing-locally-with-oracle-and-ruby-on-a-mac" rel="alternate" type="text/html"/>
      <title>Developing locally with Oracle and Ruby on a Mac</title>
      <content type="html">
        
&lt;p&gt;In this post I'll describe how you can get setup to develop an app backed by an Oracle database using Ruby on a Mac.
Specifically I'll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to install and configure a pre-built VM with Oracle running&lt;/li&gt;
&lt;li&gt;How to install the Oracle instant client on OSX Lion&lt;/li&gt;
&lt;li&gt;How to compile a 32-bit version of Ruby 1.9.2 w/ RVM&lt;/li&gt;
&lt;li&gt;How to connect your Ruby app to the Oracle database using ruby-oci8&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At a high level, you need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install VirtualBox&lt;/li&gt;
&lt;li&gt;Install an Oracle VM, configure it correctly and add a user&lt;/li&gt;
&lt;li&gt;Install Oracle's instant client on your mac&lt;/li&gt;
&lt;li&gt;Get a 32-bit version of ruby running on your mac&lt;/li&gt;
&lt;li&gt;Setup the correct connection info&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Install VirtualBox&lt;/h3&gt;
&lt;p&gt;
Go to
&lt;a href='https://www.virtualbox.org/wiki/Downloads'&gt;https://www.virtualbox.org/wiki/Downloads&lt;/a&gt;
and download the _VirtualBox 4.1.8 for OS X hosts_ file
&lt;/p&gt;
&lt;h3&gt;Install the Oracle Virtual Machine&lt;/h3&gt;
&lt;p&gt;
Go to
&lt;a href='http://www.oracle.com/technetwork/database/enterprise-edition/databaseappdev-vm-161299.html'&gt;http://www.oracle.com/technetwork/database/enterprise-edition/databaseappdev-vm-161299.html&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Download the _Oracle Developer Day.ova_ file and follow the instructions for importing it.&lt;/p&gt;
&lt;h3&gt;Configuring the Virtual Machine&lt;/h3&gt;
&lt;p&gt;Before starting, do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Increase the video memory so you can get better resolution: Setting &gt; Display &gt; Video Memory&lt;/li&gt;
&lt;li&gt;Add a CD Drive under Settings &gt; Storage &gt; (green plus sign) and set it to &quot;Empty&quot;&lt;/li&gt;
&lt;li&gt;Enable network connectivity by going to Settings &gt; Network &gt; Adapter 1 and make sure you have the correct settings (see below)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The correct network settings will vary based on your configuration, but start out with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Attached to: Bridged Adapter&lt;/li&gt;
&lt;li&gt;Name: en0 Ethernet&lt;/li&gt;
&lt;li&gt;Advanced: Cable Connected (checked)&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Oracle-vm-network-settings&quot; src=&quot;http://www.jeffmdean.com/assets/oracle-vm-network-settings-de033e8ae4497a05afa7c3c7a2ff4fca.png&quot; /&gt;
&lt;p&gt;Start the Virtual Machine, and login with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;username: oracle&lt;/li&gt;
&lt;li&gt;password: oracle&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Oracle-vm-username&quot; src=&quot;http://www.jeffmdean.com/assets/oracle-vm-username-26712a9f83bcb93ea2dc77d8d9f934cb.png&quot; width=&quot;700px&quot; /&gt;
&lt;img alt=&quot;Oracle-vm-password&quot; src=&quot;http://www.jeffmdean.com/assets/oracle-vm-password-093e5f060a9ccfd683a5def3809eac8e.png&quot; width=&quot;700px&quot; /&gt;
&lt;p&gt;Once logged in, you should have an IP address which will appear when the terminal opesn.  If that doesn't work, try executing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/sbin/ifconfig&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look for the en0 section.  You should be able to ssh in to that address, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh oracle@10.1.10.30&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
If you are working on a wireless connection, you will have to configure it slightly differently (using en1: airport) which you can read about
&lt;a href='http://www.virtualbox.org/manual/ch06.html'&gt;here&lt;/a&gt;
.
&lt;/p&gt;
&lt;p&gt;You may want to install Guest Additions from _Devices &gt; Install Guest Additions_ (from the running virtual machine's menu) - that's why you needed to install the CD drive.&lt;/p&gt;
&lt;h3&gt;Setting up the Oracle Database&lt;/h3&gt;
&lt;p&gt;Start the program called &quot;SQL Developer 3.0&quot; and login with the following credentials:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;username: sys&lt;/li&gt;
&lt;li&gt;password: oracle&lt;/li&gt;
&lt;li&gt;connection type: basic&lt;/li&gt;
&lt;li&gt;role: sysdba&lt;/li&gt;
&lt;li&gt;hostname: localhost.localdomain&lt;/li&gt;
&lt;li&gt;port: 1521&lt;/li&gt;
&lt;li&gt;sid: orcl&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Oracle-sql-developer-connection-info&quot; src=&quot;http://www.jeffmdean.com/assets/oracle-sql-developer-connection-info-6d06f434e8ed471721a49bdd032ab6c0.png&quot; width=&quot;700px&quot; /&gt;
&lt;p&gt;Once you've entered, create a new user, with the following attributes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;username: someuser&lt;/li&gt;
&lt;li&gt;password: somepassword&lt;/li&gt;
&lt;li&gt;roles: Grant All, Admin All, Default All&lt;/li&gt;
&lt;li&gt;System Privileges: Grant All, Admin All&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the user you'll use to connect from the Mac.&lt;/p&gt;
&lt;h3&gt;Using the Oracle Web Console&lt;/h3&gt;
&lt;p&gt;From the virtual machine, you can enter the virtual web console by issuing this command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;emctl start dbconsole&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It takes an embarassingly long time to load, but once it does, you can open https://localhost.localdomain:1158/em&lt;/p&gt;
&lt;p&gt;To login here, use these settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;username: sys&lt;/li&gt;
&lt;li&gt;password: oracle&lt;/li&gt;
&lt;li&gt;connect as: sysdba&lt;/li&gt;
&lt;/ul&gt;
&lt;img alt=&quot;Oracle-web-console&quot; src=&quot;http://www.jeffmdean.com/assets/oracle-web-console-8d33245ac1fec0cf973cead312a25089.png&quot; width=&quot;700px&quot; /&gt;
&lt;h3&gt;Installing Instant Client on the Mac&lt;/h3&gt;
&lt;p&gt;
Go to
&lt;a href='http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html'&gt;http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Download and unzip the following files (NOTE: you want the 32-bit version - even if you have a 64-bit operating system):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;instantclient-basiclite-10.2.0.4.0-macosx-x86.zip&lt;/li&gt;
&lt;li&gt;instantclient-sqlplus-10.2.0.4.0-macosx-x86.zip&lt;/li&gt;
&lt;li&gt;instantclient-sdk-10.2.0.4.0-macosx-x86.zip&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
On your mac, download the following libraries, then unzip them, then copy the contents of those directories into a single directory.  Then move that directory to /usr/local.
The resulting directory tree should look like this:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/usr/local/instantclient-10.2.0.4.0/
├── BASIC_README
├── SQLPLUS_README
├── classes12.jar
├── genezi
├── glogin.sql
├── libclntsh.dylib -&gt; libclntsh.dylib.10.1
├── libclntsh.dylib.10.1
├── libnnz10.dylib
├── libocci.dylib.10.1
├── libociei.dylib
├── libocijdbc10.dylib
├── libocijdbc10.jnilib
├── libsqlplus.dylib
├── libsqlplusic.dylib
├── ojdbc14.jar
├── sdk
│   ├── SDK_README
│   ├── demo
│   │   ├── cdemo81.c
│   │   ├── demo.mk
│   │   ├── occidemo.sql
│   │   ├── occidemod.sql
│   │   ├── occidml.cpp
│   │   ├── occiobj.cpp
│   │   └── occiobj.typ
│   ├── include
│   │   ├── nzerror.h
│   │   ├── nzt.h
│   │   ├── occi.h
│   │   ├── occiAQ.h
│   │   ├── occiCommon.h
│   │   ├── occiControl.h
│   │   ├── occiData.h
│   │   ├── occiObjects.h
│   │   ├── oci.h
│   │   ├── oci1.h
│   │   ├── oci8dp.h
│   │   ├── ociap.h
│   │   ├── ociapr.h
│   │   ├── ocidef.h
│   │   ├── ocidem.h
│   │   ├── ocidfn.h
│   │   ├── ociextp.h
│   │   ├── ocikpr.h
│   │   ├── ocixmldb.h
│   │   ├── odci.h
│   │   ├── oratypes.h
│   │   ├── ori.h
│   │   ├── orid.h
│   │   ├── orl.h
│   │   ├── oro.h
│   │   ├── ort.h
│   │   └── xa.h
│   ├── ott
│   └── ottclasses.zip
└── sqlplus&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once that's in place, you have to run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /usr/local/instantclient-10.2.0.4.0/
ln -s libclntsh.dylib.10.1 libclntsh.dylib&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have all the oracle-related things installed, and you have to get your Ruby setup correct.  In order for ruby to see these files, you'll have to add them to your bash profile:&lt;/p&gt;
&lt;p&gt;In ~/.bash_profile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export DYLD_LIBRARY_PATH=/usr/local/instantclient-10.2.0.4.0
export ORACLE_HOME=/usr/local/instantclient-10.2.0.4.0
export SQLPATH=$ORACLE_HOME
export NLS_LANG=&quot;AMERICAN_AMERICA.UTF8&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Install a 32-bit version of Ruby 1.9.2 on your machine&lt;/h3&gt;
&lt;p&gt;
On command line, run the following (this assumes you have RVM - if not,
&lt;a href='https://rvm.beginrescueend.com/'&gt;go get it&lt;/a&gt;
):
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rvm get stable
# close your shell and reopen - to be sure you are on the latest RVM
rvm cleanup all
rvm_archflags=&quot;-arch i386&quot; CFLAGS=&quot;-arch i386&quot; LDFLAGS=&quot;-arch i386&quot; rvm install 1.9.2 --patch osx-arch-fix -n i386
rvm alias create i386 ruby-1.9.2-p290-i386&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install a 32-bit version of ruby, aliased to i386, and your other versions of 1.9.2 will not been affected.&lt;/p&gt;
&lt;p&gt;Modify the .rvmrc file in your project directory to look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rvm i386@my-project-name --create&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
You can read more about this craziness
&lt;a href='https://forums.oracle.com/forums/thread.jspa?threadID=2187558&amp;amp;start=45&amp;amp;tstart=0'&gt;here&lt;/a&gt;
.
&lt;/p&gt;
&lt;p&gt;At this point it's probably worth closing your terminal windows and re-opening to make sure you have a clean RVM setup, then cd to your project directory.&lt;/p&gt;
&lt;p&gt;You should now be able to install the ruby-oci8 gem and test it out:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem install ruby-oci8
irb
&gt; require 'rubygems'
&gt; require 'oci8'
&gt; connection = OCI8.new(&quot;someuser&quot;, &quot;somepassword&quot;, &quot;//10.1.10.30:1521/orcl&quot;)&lt;/code&gt;
When connecting to the Oracle database, you use the IP address (or hostname), port and instance name for the &quot;database name&quot;.&lt;/pre&gt;
&lt;h3&gt;Running commands with ruby-oci8&lt;/h3&gt;
&lt;p&gt;If you are using Rails, you can set your database connection to use a similar configuration to the one above.&lt;/p&gt;
&lt;p&gt;If you are using plain-old-ruby, you can execute Oracle commands directly, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require 'rubygems'
require 'oci8'
connection = OCI8.new(&quot;someuser&quot;, &quot;somepassword&quot;, &quot;//10.1.10.30:1521/orcl&quot;)
connection.exec(&quot;create table foo (bar varchar(20) not null)&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One gotcha I noticed was that sometimes OCI complains when you execute a statement that has a semi-colon at the end, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;connection.exec(&quot;create table foo (bar varchar(20) not null);&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
And that's it!  If you've gotten this far, you deserve some sort of mood-altering drug.  Probably some sort of stimulant, since you are probably as depressed as I was having to go
through that ridiculous setup.
&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2011-11-29:20111129064625</id>
      <published>2011-11-29T06:46:25+00:00</published>
      <updated>2011-11-29T06:46:25+00:00</updated>
      <link href="http://zilkey.com/2011/11/29/rolling-your-own-akismet-client" rel="alternate" type="text/html"/>
      <title>Rolling your own Akismet client in Ruby</title>
      <content type="html">
        
&lt;p&gt;I recently wanted to some blog comments through &lt;a href=&quot;http://akismet.com/&quot;&gt;Akismet's&lt;/a&gt; spam filter.  I noticed that there were a number of
&lt;a href=&quot;https://github.com/search?langOverride=&amp;amp;language=Ruby&amp;amp;q=akismet&amp;amp;repo=&amp;amp;start_value=1&amp;amp;type=Repositories&amp;amp;x=20&amp;amp;y=17&quot;&gt;plugins and gems on Github&lt;/a&gt;,
but it seemed to me that it was overkill to add a gem dependency to my app just to make a simple http call.  In this post I'll
describe how to interact with the Akismet api with a simple, home-rolled Ruby client.&lt;/p&gt;
&lt;p&gt;The basic idea is to take every comment that a user submits, send it to Akismet to check for spam, and mark the comment appropriately.
I chose to put this directly in a controller, but it could just as easily be added to a background job.  Here's what the controller looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;  class CommentsController &amp;lt; ApplicationController&amp;#x000A;    def create&amp;#x000A;      @post = Post.find(params[:post_id])&amp;#x000A;      @comment = @post.comments.build(params[:comment])&amp;#x000A;      @comment.spammy = SpamValidator.spammy?(@comment, params[:permalink], request)&amp;#x000A;      @comment.save&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that the &lt;code&gt;SpamValidator&lt;/code&gt; takes a permalink param, which is supposed to be the full url to the
blog post where the comment originated.  Since HTTP_REFERER's are sometimes
inaccurate or missing, I decided to pass that directly from comment form, like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;= form_for [@post, @comment] do |f|&amp;#x000A;  = hidden_field_tag :permalink, request.protocol + request.host_with_port + request.fullpath&amp;#x000A;  = f.label :name&amp;#x000A;  = f.text_field :name, class: input_span&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;SpamValidator&lt;/code&gt; is where the heavy-lifting happens.  While Ruby comes with a built-in class for making HTTP requests (Net::HTTP),
I find it a bit kludgy to use.  Instead, I prefer to use &lt;a href=&quot;https://github.com/dbalatero/typhoeus&quot;&gt;Typhoeus&lt;/a&gt;.  The Akismet api
requires some post parameters and a User-Agent header, and returns a simple true or false.  Here's a simplified Akismet client class:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class SpamValidator&amp;#x000A;  BASE_URL = &quot;http://yourapikey.rest.akismet.com&quot;&amp;#x000A;  USER_AGENT = &quot;Your Blog/1.0 | Custom/1.0&quot;&amp;#x000A;  COMMENT_CHECK_PATH = &quot;/1.1/comment-check&quot;&amp;#x000A;&amp;#x000A;  def self.spammy?(comment, permalink, request)&amp;#x000A;    params = {&amp;#x000A;      blog: &quot;http://www.yourblog.com/&quot;,&amp;#x000A;      user_ip: request.remote_ip,&amp;#x000A;      user_agent: request.env[&quot;HTTP_USER_AGENT&quot;],&amp;#x000A;      referrer: request.env[&quot;HTTP_REFERER&quot;],&amp;#x000A;      permalink: permalink,&amp;#x000A;      comment_type: &quot;comment&quot;,&amp;#x000A;      comment_author: comment.name,&amp;#x000A;      comment_author_email: comment.email,&amp;#x000A;      comment_author_url: comment.url,&amp;#x000A;      comment_content: comment.body&amp;#x000A;    }&amp;#x000A;&amp;#x000A;    response = Typhoeus::Request.post(&amp;#x000A;      &quot;#{BASE_URL}#{COMMENT_CHECK_PATH}&quot;,&amp;#x000A;      :params =&amp;gt; params,&amp;#x000A;      :headers =&amp;gt; {&quot;User-Agent&quot; =&amp;gt; USER_AGENT}&amp;#x000A;    )&amp;#x000A;&amp;#x000A;    response.body == &quot;true&quot;&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;Typhoeus::Request&lt;/code&gt; takes a path, url params and headers and takes care of the rest.&lt;/p&gt;

&lt;p&gt;Akismet recommends that you also implement methods to identify missed spam and false positives, so in a real-world application you would likely
expand the &lt;code&gt;SpamValidator&lt;/code&gt; class to include those other two API calls, but I'll leave that as an exercise for the reader.&lt;/p&gt;


      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2011-11-28:20111128060636</id>
      <published>2011-11-28T06:06:36+00:00</published>
      <updated>2011-11-28T06:06:36+00:00</updated>
      <link href="http://zilkey.com/2011/11/28/speed-up-your-ruby-loops-with-hashes" rel="alternate" type="text/html"/>
      <title>Speed up your Ruby loops with hashes</title>
      <content type="html">
        
&lt;p&gt;I recently wrote a background job that looped over thousands of ActiveRecord objects, and it took about 45 minutes to run.
In this post I'll describe how I cut that down to 1 minute by using hash lookups.&lt;/p&gt;
&lt;p&gt;The particular piece of code that was taking so long was trying to find possible duplicates by iterating over all
people in the database, and comparing them to all other people in the database.  The code looked something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;people = Person.all(:include =&amp;gt; :phone_numbers)&amp;#x000A;&amp;#x000A;people.each do |person1|&amp;#x000A;  people.each do |person2|&amp;#x000A;    next if person1 == person2&amp;#x000A;    ( person1.phone_numbers.map(&amp;amp;:number) &amp;amp; person2.phone_numbers.map(&amp;amp;:number) ).present?&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let's say that there are 1,000 people, and each person has 3 phone numbers.  That inner loop:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;calls &lt;code&gt;phone_numbers&lt;/code&gt; roughly 2 million times&lt;/li&gt;
&lt;li&gt;calls &lt;code&gt;map&lt;/code&gt; roughly 2 million times&lt;/li&gt;
&lt;li&gt;calls &lt;code&gt;number&lt;/code&gt; roughly 6 million times&lt;/li&gt;
&lt;li&gt;calls &lt;code&gt;present?&lt;/code&gt; roughly 1 million times&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;With 11 million method calls, it's understandable that this snippet of code would take a a long time to run.  One solution to making
the code more efficient is to reduce the number of method calls in the inside loop.  This is easy with hashes:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;people = Person.all(:include =&amp;gt; :phone_numbers)&amp;#x000A;&amp;#x000A;phone_numbers = people.inject({}) do |hash, person|&amp;#x000A;  hash[person.id] = person.phone_numbers.map(&amp;amp;:number)&amp;#x000A;  hash&amp;#x000A;end&amp;#x000A;&amp;#x000A;people.each do |person1|&amp;#x000A;  people.each do |person2|&amp;#x000A;    next if person1 == person2&amp;#x000A;    ( phone_numbers[person1.id] &amp;amp; phone_numbers[person2.id] ).present?&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the example above, the &lt;code&gt;phone_numbers&lt;/code&gt; method is called 1,000 times, and the &lt;code&gt;number&lt;/code&gt; method is called 3,000.  Inside the loop,
there are still roughly 1 million hash lookups, but in my case hash lookups ended up being much faster than method calls.&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2011-11-26:66</id>
      <published>2011-11-26T11:02:00+00:00</published>
      <updated>2011-11-26T11:02:00+00:00</updated>
        <category term="haml"/>
      <link href="http://zilkey.com/2011/11/26/haml-filters" rel="alternate" type="text/html"/>
      <title>Using Haml filters to embed data in Haml templates</title>
      <content type="html">
        
&lt;p&gt;
  I recently decided to revive this blog, but I didn't want to continue using Mephisto for my blogging platform.
  I wanted to be able to write my blog posts in markdown or Haml, and store all meta-data about the posts in the Haml template.
  In addition I wanted to have an ActiveRecord-esque interface for blog post meta-data.  In this post, I'll describe
  how I used Haml filters and ActiveHash to create this blogging platform.
&lt;/p&gt;
&lt;p&gt;My requirements for this blog are simple - I'd like to be able to keep track of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the title&lt;/li&gt;
&lt;li&gt;the published date&lt;/li&gt;
&lt;li&gt;tags&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Here's an example of what the final Haml template looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:yaml&amp;#x000A;  id: 24&amp;#x000A;  permalink: /2011/11/26/some-permalink&amp;#x000A;  title: &quot;Here is some title&quot;&amp;#x000A;  published_at: 2011-11-26T11:02:00+00:00&amp;#x000A;  tags: haml&amp;#x000A;&amp;#x000A;:summary&amp;#x000A;  %p  Here is some summary text&amp;#x000A;&amp;#x000A;%p My requirements for this blog are simple - I'd like to be able to keep track of:&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first step was to make sure that the :yaml fitler never shows it's content.
This is pretty easy with a custom Haml filter that overrides the #render method&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module Haml::Filters::Yaml&amp;#x000A;  include Haml::Filters::Base&amp;#x000A;&amp;#x000A;  def render(text)&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The second step was to capture text in the :yaml section in order to be able to convert it to YAML later on.
Haml has a largely undocumented #compile method that provides the perfect hook for grabbing the text provided
in a filter.  Here's the complete custom filter:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module Haml::Filters::Yaml&amp;#x000A; include Haml::Filters::Base&amp;#x000A;&amp;#x000A; def compile(compiler, text)&amp;#x000A;   compiler.file_data = text if compiler.respond_to?(:file_data)&amp;#x000A;   super&amp;#x000A; end&amp;#x000A;&amp;#x000A; def render(text)&amp;#x000A; end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The filter above checks to see if the rendering engine (the &quot;compiler&quot;) responds to a method called &lt;code&gt;file_data&lt;/code&gt; and if so it
appends the raw text from the template into the compiler's file_data accessor.&lt;/p&gt;

&lt;p&gt;The default Haml engine doesn't have a &lt;code&gt;file_data&lt;/code&gt; method, so that code will be ignored when the view is rendered.  However,
it's simple to create a new Haml renderer that does have a &lt;code&gt;file_data&lt;/code&gt; method, like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class EngineWithData &amp;lt; Haml::Engine&amp;#x000A;  attr_accessor :file_data&amp;#x000A;end&amp;#x000A;&amp;#x000A;haml_engine = EngineWithData.new(File.read(Rails.root.join(&quot;app&quot;, &quot;views&quot;, &quot;posts&quot;, &quot;content&quot;, &quot;_somepost.rb&quot;)))&amp;#x000A;haml_engine.render&amp;#x000A;YAML.load(haml_engine.file_data) # =&amp;gt; this will contain a hash like {&quot;title&quot; =&amp;gt; &quot;Here is some title&quot;....}&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I'm accustomed to working with ActiveRecord objects in my views and controllers, so the next step for me was to go through all
of the files in my blog posts directory, extract the meta-data and add it to an ActiveRecord-esque object.  I chose to go with
ActiveHash, since it was built exactly for that purpose.  Here's what my Post class ended up looking like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Post &amp;lt; ActiveHash::Base&amp;#x000A;  fields :title, :published_at, :tags, :permalink, :filename, :partial_name&amp;#x000A;&amp;#x000A;  class EngineWithData &amp;lt; Haml::Engine&amp;#x000A;    attr_accessor :file_data&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  Dir.glob(Rails.root.join(&quot;app&quot;, &quot;views&quot;, &quot;posts&quot;, &quot;content&quot;, &quot;*&quot;)).each do |path|&amp;#x000A;    haml_engine = EngineWithData.new(File.read(path))&amp;#x000A;    haml_engine.render&amp;#x000A;&amp;#x000A;    data = YAML.load(haml_engine.file_data)&amp;#x000A;&amp;#x000A;    create(&amp;#x000A;      id: data[&quot;id&quot;],&amp;#x000A;      partial_name: File.basename(path).sub(/^_/, &quot;&quot;),&amp;#x000A;      title: data['title'],&amp;#x000A;      published_at: data['published_at'].to_time,&amp;#x000A;      tags: (data['tags'] || '').split(&quot;,&quot;).map(&amp;amp;:strip),&amp;#x000A;      permalink: data['permalink']&amp;#x000A;    )&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;First, I declared the fields that the ActiveHash object would respond to (&lt;code&gt;title&lt;/code&gt;, &lt;code&gt;tags&lt;/code&gt; etc...).  Next, I declared the
new Haml renderer that has the &lt;code&gt;file_data&lt;/code&gt; accessor.  Finally, I looped through every file in my blog posts directory,
rendered the contents, pulled the meta data out and created a Post with the given data.&lt;/p&gt;

&lt;p&gt;Now, whenever the Rails app starts, it parses every file, creates a set of in-memory rows with post data that I can access in a way
similar to ActiveRecord objects.  For example, if I wanted to get posts with a given tag, I could do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Post.all.select{|post| post.tags.include?(tag)}&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Rendering these posts is straightforward.  My PostsController looks like any other normal Rails controller:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class PostsController &amp;lt; ApplicationController&amp;#x000A;  def show&amp;#x000A;    @post = Post.find_by_permalink(params[:id])&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The view is very similar as well.  One difference is that instead of pulling the blog post's content from the database,
I just render that Haml template as a partial.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- content_for :title, @post.title&amp;#x000A;&amp;#x000A;%article&amp;#x000A;  %h1= @post.title&amp;#x000A;  %p= @post.published_at.to_date.to_s(:long)&amp;#x000A;&amp;#x000A;  .content&amp;#x000A;    = render &quot;/posts/content/#{@post.partial_name}&quot;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With those simple steps, I can now write my blog posts in Haml (or markdown, or erb or any other filter that Haml understands),
version my posts in Git and I'm no longer dependent on 3rd-party blogging software.&lt;/p&gt;

&lt;p&gt;In a follow-up post, I'll describe how to use a similar technique to create summary text for blog posts.&lt;/p&gt;


      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-07-05:65</id>
      <published>2008-07-05T02:11:00+00:00</published>
      <updated>2008-07-05T02:11:00+00:00</updated>
        <category term="cache"/>
        <category term="rails"/>
      <link href="http://zilkey.com/2008/7/5/rails-cache-memcached-development-mode-and-offline-cache-invalidation" rel="alternate" type="text/html"/>
      <title>Rails.cache: Memcached, development mode and offline cache invalidation</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;Rails.cache rocks, but it can be tricky to set it up for development mode.  For my purposes I need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep config.cache_classes to false so that I don't have to restart my server while I develop&lt;/li&gt;
&lt;li&gt;Cache all kinds of objects, not just strings&lt;/li&gt;
&lt;li&gt;Be able to invalidate the cache easily from cron scripts or other offline processes&lt;/li&gt;
&lt;li&gt;Test caching locally before deploying&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first thing I did was check out the excellent &lt;a href=&quot;http://railscasts.com/episodes/115&quot;&gt;railscast&lt;/a&gt; and I read through the blog posts mentioned there.  However, I couldn't quite figure out how to get things to work - I kept getting strange errors where all of the methods were being stripped from my classes, rails was complaining that my classes didn't exist or I was getting dreadful &quot;singleton can't be dumped&quot; errors.  After a lot of googling and experimentation, here is what finally worked for me:&lt;/p&gt;

&lt;h2&gt;Environment files&lt;/h2&gt;

&lt;p&gt;I like to develop quickly, test caching on my local and then deploy.  To accomplish this I have 3 environments, setup like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# config/environments/development.rb&amp;#x000A;config.action_controller.perform_caching = false&amp;#x000A;config.cache_classes = false&amp;#x000A;config.cache_store = :mem_cache_store, '127.0.0.1:11211', {:namespace =&amp;gt; &quot;dev&quot;}&amp;#x000A;&amp;#x000A;# config/environments/dev_with_caching.rb&amp;#x000A;config.action_controller.perform_caching  = true&amp;#x000A;config.cache_classes = true&amp;#x000A;config.cache_store = :mem_cache_store, '127.0.0.1:11211', {:namespace =&amp;gt; &quot;dev_with_caching&quot;}&amp;#x000A;&amp;#x000A;# config/environments/production.rb&amp;#x000A;config.action_controller.perform_caching  = true&amp;#x000A;config.cache_classes = true&amp;#x000A;config.cache_store = :mem_cache_store, '127.0.0.1:11211', {:namespace =&amp;gt; &quot;production&quot;}&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here are a few interesting points:&lt;/p&gt;

&lt;h3&gt;You don't need to have memcached installed to develop locally&lt;/h3&gt;

&lt;p&gt;If  you run your app locally without memcached installed, or without memcached running, you will see entries like this in your log&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;MemCacheError (No connection to server): No connection to server&amp;#x000A;Cache miss: Post.all ({:force=&amp;gt;false})&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However, your app will work just fine.  Rails will always execute the contents of the fetch blocks, and will return nil for any reads.&lt;/p&gt;

&lt;h3&gt;If memcached is running, you need to set cache_classes to true&lt;/h3&gt;

&lt;p&gt;To run memcached locally, you need to install memcached.  I develop on a mac and manage packages with macports, so for me it was as easy as:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo port install memcached&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once memcached is installed, you can start it with&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;memcached -m 500 -l 127.0.0.1 -p 11211 -vv&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;which will print verbose logging to STDERR, or you can start it as a daemon like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;memcached -m 500 -l 127.0.0.1 -p 11211 -d&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Either of these will start a memcached process running on port 11211, and it will allocate 500MB RAM (most apps can get by with 128MB, or so I've heard).&lt;/p&gt;

&lt;p&gt;Once this is running, though, you need to set config.cache_classes to true - otherwise you're app will blow up.&lt;/p&gt;

&lt;h3&gt;Marshal.dump is finicky&lt;/h3&gt;

&lt;p&gt;Rails.cache calls Marshal.dump on any object you try to put in the cache.  Marshal won't work on everything though - and you may need to write your own serialization script.  I've had problems with classes that have lots of module_eval statements that create methods dynamically and similar meta-programming techniques.  If you start getting errors like &quot;singleton can't be dumped&quot;, check to see if you have any meta-programming going on.  I've also had issues with REXML objects.&lt;/p&gt;

&lt;p&gt;If you do have an issue with a class that Rails won't cache, you can easily bypass the built-in serialization by writing your own &lt;em&gt;dump and &lt;/em&gt;load methods.  See the &lt;a href=&quot;http://ruby-doc.org/core/classes/Marshal.html&quot;&gt;ruby docs&lt;/a&gt; for more info.&lt;/p&gt;

&lt;h3&gt;Use a separate environment to test locally&lt;/h3&gt;

&lt;p&gt;I have a new environment named dev_with_caching that I use to test caching locally.  I set up my database.yml file so that it points to the development database, but performs caching and in all other respects mirrors the production environment.  To test locally with that environment, I use:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/server -e dev_with_caching -p 3001&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Clearing the cache&lt;/h2&gt;

&lt;p&gt;I mostly use Rails.cache to cache data - and mostly for arrays of objects - like Category.all.  As such, it's to keep all of this in the model, but cache invalidation can be trickly to manage.  Here's a pattern I've started to use a lot:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Category &amp;lt; ActiveRecord::Base&amp;#x000A;&amp;#x000A;  after_save      :reset_cache&amp;#x000A;  after_destroy :reset_cache&amp;#x000A;&amp;#x000A;  def reset_cache&amp;#x000A;    self.class.reset_cache&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  class &amp;lt;&amp;lt; self&amp;#x000A;&amp;#x000A;    def reset_cache&amp;#x000A;      cached_all(true)&amp;#x000A;    end&amp;#x000A;&amp;#x000A;    def cached_all(force = false)&amp;#x000A;      Rails.cache.fetch(&quot;Category.all&quot;, :force =&amp;gt; force) do&amp;#x000A;        Category.find(:all, :conditions =&amp;gt; {:active=&amp;gt;true}, :order=&amp;gt;'position')&amp;#x000A;      end&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here's what's happening:&lt;/p&gt;

&lt;p&gt;The first time you call Category.cached_all it looks for the &quot;Category.all&quot; item in the cache.  If it's not there, it executes the contents of the block, and adds it to the cache.  When you save or destroy a record the cache is invalidated.&lt;/p&gt;

&lt;p&gt;If you want to force a refresh of the cache, just specify Category.cached_all(true) and it will be reloaded from the database.  Once this is in place, it's easy to write cache invalidation scripts that both clear the cache &lt;em&gt;and&lt;/em&gt; reload it at the same time.&lt;/p&gt;

&lt;p&gt;I've done this by adding a class method that reloads the data, which is triggered by after_save and after_destroy callbacks.  I'm sure there are a number of plugins that will do all that and more, but for my purposes this simple pattern works for me most of the time.&lt;/p&gt;

&lt;h2&gt;Clearing the cache with cron&lt;/h2&gt;

&lt;p&gt;Finally, if you want to clear the cache at specified intervals you can do so easily with rake and cron.  First, create a rake task that calls the model's reset_cache method - since I normally have several classes with caching behavior I normally create a loop like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;namespace :cache do&amp;#x000A;  namespace :reset do&amp;#x000A;    %w{Category Forum Post}.each do |klass|&amp;#x000A;      desc &quot;Clear the #{klass} cache&quot;&amp;#x000A;      task klass.underscore.gsub(&quot;/&quot;,&quot;_&quot;).pluralize =&amp;gt; :environment do&amp;#x000A;        klass.constantize.reset_cache&amp;#x000A;      end&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you can run &lt;code&gt;rake cache:reset:categories&lt;/code&gt; and your &lt;code&gt;Category.reset_cache&lt;/code&gt; method will be called.   To make this work with cron, you'll need a slightly different syntax.  The following command is suitable to execute from a cron script, or manually from the command line:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;RAILS_ENV=production rake -f /var/www/apps/yourapp/current/Rakefile cache:reset:categories&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It might take a little while to grok Rails.cache - but once you do your apps will be faster and you'll quickly become a wild caching fiend!&lt;/p&gt;


      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-07-05:64</id>
      <published>2008-07-05T01:51:00+00:00</published>
      <updated>2008-07-05T01:51:00+00:00</updated>
      <link href="http://zilkey.com/2008/7/5/networking-mac-and-windows-with-vmware" rel="alternate" type="text/html"/>
      <title>Networking Mac and Windows with VMWare</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;I recently discovered how easy it is to view my local development websites on multiple OS's using VMWare.  I use this primarily to see how awful my apps look in IE.  Here's how you can do it too:&lt;/p&gt;
&lt;h2&gt;Step1: Get Setup (the expensive part)&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Buy a &lt;a href=&quot;http://www.apple.com/mac&quot;&gt;mac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Buy &lt;a href=&quot;http://www.vmware.com/products/fusion/&quot;&gt;VMWare Fusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Buy Windows.  Yes - if you want to run IE6 and IE7 you'll have to buy &lt;em&gt;two&lt;/em&gt; licenses.  Yes, it will take you several hours of frustration and several hours on the phone with MicroSoft to get your licenses installed with VMWare.&lt;/li&gt;
&lt;li&gt;Download a few real OS's and add them as virtual machines&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Step 2: Find your network address&lt;/h2&gt;

&lt;p&gt;When you installed VMWare, it configured all of the necessary IP addresses for you.  To find out what they are, open Terminal and type:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ifconfig&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You'll see something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;lo0: flags=8049&amp;lt;UP,LOOPBACK,RUNNING,MULTICAST&amp;gt; mtu 16384&amp;#x000A;  inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1&amp;#x000A;  inet 127.0.0.1 netmask 0xff000000&amp;#x000A;  inet6 ::1 prefixlen 128&amp;#x000A;  inet6 fdd3:5091:e6df:4c3d:21b:63ff:feab:d72e prefixlen 128&amp;#x000A;gif0: flags=8010&amp;lt;POINTOPOINT,MULTICAST&amp;gt; mtu 1280&amp;#x000A;stf0: flags=0&amp;lt;&amp;gt; mtu 1280&amp;#x000A;en0: flags=8863&amp;lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&amp;gt; mtu 1500&amp;#x000A;  ether 00:1b:63:ab:d7:2e&amp;#x000A;  media: autoselect status: inactive&amp;#x000A;  supported media: autoselect 10baseT/UTP &amp;lt;half-duplex&amp;gt; 10baseT/UTP &amp;lt;full-duplex&amp;gt; 10baseT/UTP &amp;lt;full-duplex,hw-loopback&amp;gt; 10baseT/UTP &amp;lt;full-duplex,flow-control&amp;gt; 100baseTX &amp;lt;half-duplex&amp;gt; 100baseTX &amp;lt;full-duplex&amp;gt; 100baseTX &amp;lt;full-duplex,hw-loopback&amp;gt; 100baseTX &amp;lt;full-duplex,flow-control&amp;gt; 1000baseT &amp;lt;full-duplex&amp;gt; 1000baseT &amp;lt;full-duplex,hw-loopback&amp;gt; 1000baseT &amp;lt;full-duplex,flow-control&amp;gt; none&amp;#x000A;en1: flags=8863&amp;lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&amp;gt; mtu 1500&amp;#x000A;  inet6 fe80::21c:b3ff:fe7c:916e%en1 prefixlen 64 scopeid 0x5&amp;#x000A;  inet6 2002:4452:63ee::21c:b3ff:fe7c:916e prefixlen 64 autoconf&amp;#x000A;  inet 10.0.1.199 netmask 0xffffff00 broadcast 10.0.1.255&amp;#x000A;  ether 00:1c:b3:7c:91:6e&amp;#x000A;  media: autoselect status: active&amp;#x000A;  supported media: autoselect&amp;#x000A;fw0: flags=8863&amp;lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&amp;gt; mtu 4078&amp;#x000A;  lladdr 00:1d:4f:ff:fe:73:a1:ba&amp;#x000A;  media: autoselect &amp;lt;full-duplex&amp;gt; status: inactive&amp;#x000A;  supported media: autoselect &amp;lt;full-duplex&amp;gt;&amp;#x000A;vmnet8: flags=8863&amp;lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&amp;gt; mtu 1500&amp;#x000A;  inet 172.16.192.1 netmask 0xffffff00 broadcast 172.16.192.255&amp;#x000A;  ether 00:50:56:c0:00:08&amp;#x000A;vmnet1: flags=8863&amp;lt;UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST&amp;gt; mtu 1500&amp;#x000A;  inet 172.16.43.1 netmask 0xffffff00 broadcast 172.16.43.255&amp;#x000A;  ether 00:50:56:c0:00:01&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the last entry, vmnet1 - the inet address listed there is the address that all of your virtual machines can use to access your localhost.  In my case, this is 172.16.43.1&lt;/p&gt;

&lt;p&gt;Let's say you have a local rails app running on http://localhost:3000/ - to access that app from anywhere (your mac or any or your virtual machines) just type http://172.16.43.1:3000/ in your browser.&lt;/p&gt;

&lt;h3&gt;References&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.lunenburg.org/wade/articles/2007/12/11/theres-no-place-like-lo0/&quot;&gt;http://www.lunenburg.org/wade/articles/2007/12/11/theres-no-place-like-lo0/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-05-29:38</id>
      <published>2008-05-29T21:38:00+00:00</published>
      <updated>2008-05-29T21:38:00+00:00</updated>
        <category term="rails"/>
        <category term="tutorials"/>
      <link href="http://zilkey.com/2008/5/30/tutorial-creating-plugins-in-rails" rel="alternate" type="text/html"/>
      <title>Tutorial: Creating plugins in Rails</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;div class='alert-message block-message'&gt;
You can see the most recent version of this post at
&lt;a href='http://guides.rubyonrails.org/plugins.html'&gt;http://guides.rubyonrails.org/plugins.html&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;There's a new effort underway to add tutorials to the Rails API documentation.  Here's the first draft of a tutorial I just wrote describing how to create plugins.&lt;/p&gt;
&lt;h1&gt;Creating Plugin Basics&lt;/h1&gt;

&lt;p&gt;Pretend for a moment that you are an avid bird watcher.  Your favorite bird is the Yaffle, and you want to create a plugin that allows other developers to share in the Yaffle goodness.&lt;/p&gt;

&lt;p&gt;In this tutorial you will learn how to create a plugin that includes:&lt;/p&gt;

&lt;p&gt;Core Extensions - extending String:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Anywhere&amp;#x000A;&quot;hello&quot;.squawk # =&amp;gt; &quot;squawk! hello! squawk!&quot;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An acts_as_yaffle method for Active Record models that adds a &quot;squawk&quot; method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Hickwall &amp;lt; ActiveRecord::Base&amp;#x000A;  acts_as_yaffle :yaffle_text_field =&amp;gt; :last_sang_at&amp;#x000A;end&amp;#x000A;&amp;#x000A;Hickwall.new.squawk(&quot;Hello World&quot;)&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A view helper that will print out squawking info:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;squawk_info_for(@hickwall)&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A generator that creates a migration to add squawk columns to a model:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/generate yaffle hickwall&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A custom generator command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class YaffleGenerator &amp;lt; Rails::Generator::NamedBase&amp;#x000A;  def manifest&amp;#x000A;      m.yaffle_definition&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A custom route method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ActionController::Routing::Routes.draw do |map|&amp;#x000A;  map.yaffles&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In addition you'll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test your plugins&lt;/li&gt;
&lt;li&gt;work with init.rb, how to store model, views, controllers, helpers and even other plugins in your plugins&lt;/li&gt;
&lt;li&gt;create documentation for your plugin.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Create the basic app&lt;/h2&gt;

&lt;p&gt;In this tutorial we will create a basic rails application with 1 resource: bird.  Start out by building the basic rails app:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;The following instructions will work for sqlite3.  For more detailed instructions on how to create a rails app for other databases see the API docs.&lt;/p&gt;&lt;/blockquote&gt;

&lt;pre&gt;&lt;code&gt;rails plugin_demo&amp;#x000A;cd plugin_demo&amp;#x000A;script/generate scaffold bird name:string&amp;#x000A;rake db:migrate&amp;#x000A;script/server&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then navigate to &lt;a href=&quot;http://localhost:3000/birds&quot;&gt;http://localhost:3000/birds&lt;/a&gt;.  Make sure you have a functioning rails app before continuing.&lt;/p&gt;

&lt;h2&gt;Create the plugin&lt;/h2&gt;

&lt;p&gt;The built-in Rails plugin generator stubs out a new plugin. Pass the plugin name, either CamelCased or under_scored, as an argument. Pass --with-generator to add an example generator also.&lt;/p&gt;

&lt;p&gt;This creates a plugin in vendor/plugins including an init.rb and README as well as standard lib, task, and test directories.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;./script/generate plugin BrowserFilters&amp;#x000A;./script/generate plugin BrowserFilters --with-generator&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Later in the plugin we will create a generator, so go ahead and add the --with-generator option now:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/generate plugin yaffle --with-generator&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should see the following output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;create  vendor/plugins/yaffle/lib&amp;#x000A;create  vendor/plugins/yaffle/tasks&amp;#x000A;create  vendor/plugins/yaffle/test&amp;#x000A;create  vendor/plugins/yaffle/README&amp;#x000A;create  vendor/plugins/yaffle/MIT-LICENSE&amp;#x000A;create  vendor/plugins/yaffle/Rakefile&amp;#x000A;create  vendor/plugins/yaffle/init.rb&amp;#x000A;create  vendor/plugins/yaffle/install.rb&amp;#x000A;create  vendor/plugins/yaffle/uninstall.rb&amp;#x000A;create  vendor/plugins/yaffle/lib/yaffle.rb&amp;#x000A;create  vendor/plugins/yaffle/tasks/yaffle_tasks.rake&amp;#x000A;create  vendor/plugins/yaffle/test/core_ext_test.rb&amp;#x000A;create  vendor/plugins/yaffle/generators&amp;#x000A;create  vendor/plugins/yaffle/generators/yaffle&amp;#x000A;create  vendor/plugins/yaffle/generators/yaffle/templates&amp;#x000A;create  vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb&amp;#x000A;create  vendor/plugins/yaffle/generators/yaffle/USAGE&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For this plugin you won't need the file vendor/plugins/yaffle/lib/yaffle.rb so you can delete that.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rm vendor/plugins/yaffle/lib/yaffle.rb&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;&lt;p&gt;Editor's note:  many plugin authors prefer to keep this file, and add all of the require statements in it.  That way, they only line in init.rb would be &lt;code&gt;require &quot;yaffle&quot;&lt;/code&gt;
If you are developing a plugin that has a lot of files in the lib directory, you may want to create a subdirectory like lib/yaffle and store your files in there.  That way your init.rb file stays clean&lt;/p&gt;&lt;/blockquote&gt;

&lt;h2&gt;Testing Setup&lt;/h2&gt;

&lt;p&gt;Testing plugins that use the entire Rails stack can be complex, and the generator doesn't offer any help.  In this tutorial you will learn how to test your plugin against multiple different adapters using ActiveRecord.  This tutorial will not cover how to use fixtures in plugin tests.&lt;/p&gt;

&lt;p&gt;To setup your plugin to allow for easy testing you'll need to add 3 files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A database.yml file with all of your connection strings&lt;/li&gt;
&lt;li&gt;A schema.rb file with your table definitions&lt;/li&gt;
&lt;li&gt;A test helper that sets up the database before your tests&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;For this plugin you'll need 2 tables/models, Hickwalls and Wickwalls, so add the following files:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/test/database.yml&amp;#x000A;&amp;#x000A;sqlite:&amp;#x000A;  :adapter: sqlite&amp;#x000A;  :dbfile: yaffle_plugin.sqlite.db&amp;#x000A;sqlite3:&amp;#x000A;  :adapter: sqlite3&amp;#x000A;  :dbfile: yaffle_plugin.sqlite3.db&amp;#x000A;postgresql:&amp;#x000A;  :adapter: postgresql&amp;#x000A;  :username: postgres&amp;#x000A;  :password: postgres&amp;#x000A;  :database: yaffle_plugin_test&amp;#x000A;  :min_messages: ERROR&amp;#x000A;mysql:&amp;#x000A;  :adapter: mysql&amp;#x000A;  :host: localhost&amp;#x000A;  :username: rails&amp;#x000A;  :password:&amp;#x000A;  :database: yaffle_plugin_test&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/test/test_helper.rb&amp;#x000A;&amp;#x000A;ActiveRecord::Schema.define(:version =&amp;gt; 0) do&amp;#x000A;  create_table :hickwalls, :force =&amp;gt; true do |t|&amp;#x000A;    t.string :name&amp;#x000A;    t.string :last_squawk&amp;#x000A;    t.datetime :last_squawked_at&amp;#x000A;  end&amp;#x000A;  create_table :wickwalls, :force =&amp;gt; true do |t|&amp;#x000A;    t.string :name&amp;#x000A;    t.string :last_tweet&amp;#x000A;    t.datetime :last_tweeted_at&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/test/test_helper.rb&amp;#x000A;&amp;#x000A;ENV['RAILS_ENV'] = 'test'&amp;#x000A;ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'&amp;#x000A;&amp;#x000A;require 'test/unit'&amp;#x000A;require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))&amp;#x000A;&amp;#x000A;config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))&amp;#x000A;ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + &quot;/debug.log&quot;)&amp;#x000A;&amp;#x000A;db_adapter = ENV['DB']&amp;#x000A;&amp;#x000A;# no db passed, try one of these fine config-free DBs before bombing.&amp;#x000A;db_adapter ||=&amp;#x000A;  begin&amp;#x000A;    require 'rubygems'&amp;#x000A;    require 'sqlite'&amp;#x000A;    'sqlite'&amp;#x000A;  rescue MissingSourceFile&amp;#x000A;    begin&amp;#x000A;      require 'sqlite3'&amp;#x000A;      'sqlite3'&amp;#x000A;    rescue MissingSourceFile&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;&amp;#x000A;if db_adapter.nil?&amp;#x000A;  raise &quot;No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3.&quot;&amp;#x000A;end&amp;#x000A;&amp;#x000A;ActiveRecord::Base.establish_connection(config[db_adapter])&amp;#x000A;&amp;#x000A;load(File.dirname(__FILE__) + &quot;/schema.rb&quot;)&amp;#x000A;&amp;#x000A;require File.dirname(__FILE__) + '/../init.rb'&amp;#x000A;&amp;#x000A;class Hickwall &amp;lt; ActiveRecord::Base&amp;#x000A;  acts_as_yaffle&amp;#x000A;end&amp;#x000A;&amp;#x000A;class Wickwall &amp;lt; ActiveRecord::Base&amp;#x000A;  acts_as_yaffle :yaffle_text_field =&amp;gt; :last_tweet, :yaffle_date_field =&amp;gt; :last_tweeted_at&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Update a core class: Adding &quot;to_squawk&quot; to String&lt;/h2&gt;

&lt;p&gt;To update a core class you will have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write tests for the desired functionality&lt;/li&gt;
&lt;li&gt;Create a file for the code you wish to use&lt;/li&gt;
&lt;li&gt;Require that file from your init.rb&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Most plugins store their code classes in the plugin's lib directory.  When you add a file to the lib directory, you must also require that file from init.rb.  The file you are going to add for this tutorial is &lt;code&gt;lib/core_ext.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First, you need to write the tests.  Testing plugins is very similar to testing rails apps.  The generated test file should look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/test/core_ext_test.rb&amp;#x000A;&amp;#x000A;require 'test/unit'&amp;#x000A;&amp;#x000A;class CoreExtTest &amp;lt; Test::Unit::TestCase&amp;#x000A;  # Replace this with your real tests.&amp;#x000A;  def test_this_plugin&amp;#x000A;    flunk&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Start off by removing the default test, and adding a require statement for your test helper.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/test/core_ext_test.rb&amp;#x000A;&amp;#x000A;require 'test/unit'&amp;#x000A;require File.dirname(__FILE__) + '/test_helper.rb'&amp;#x000A;&amp;#x000A;class CoreExtTest &amp;lt; Test::Unit::TestCase&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Navigate to your plugin directory and run &lt;code&gt;rake test&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd vendor/plugins/yaffle&amp;#x000A;rake test&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Your test should fail with &lt;code&gt;no such file to load -- ./test/../lib/core_ext.rb (LoadError)&lt;/code&gt; because we haven't created any file yet.  Create the file &lt;code&gt;lib/core_ext.rb&lt;/code&gt; and re-run the tests.  You should see a different error message:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.) Failure ...&amp;#x000A;No tests were specified&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Great - now you are ready to start development.  The first thing we'll do is to add a method to String called &lt;code&gt;to_squawk&lt;/code&gt; which will prefix the string with the word &quot;squawk! &quot;.  The test will look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/init.rb&amp;#x000A;&amp;#x000A;class CoreExtTest &amp;lt; Test::Unit::TestCase&amp;#x000A;  def test_string_should_respond_to_squawk&amp;#x000A;    assert_equal true, &quot;&quot;.respond_to?(:to_squawk)&amp;#x000A;  end&amp;#x000A;  def test_string_prepend_empty_strings_with_the_word_squawk&amp;#x000A;    assert_equal &quot;squawk!&quot;, &quot;&quot;.to_squawk&amp;#x000A;  end&amp;#x000A;  def test_string_prepend_non_empty_strings_with_the_word_squawk&amp;#x000A;    assert_equal &quot;squawk! Hello World&quot;, &quot;Hello World&quot;.to_squawk&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/init.rb&amp;#x000A;&amp;#x000A;require &quot;core_ext&quot;&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/lib/core_ext.rb&amp;#x000A;&amp;#x000A;class String&amp;#x000A;  # returns the current string, prefixed by &quot;squawk!&quot;&amp;#x000A;  def to_squawk&amp;#x000A;    &quot;squawk! #{self}&quot;.strip&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To test that your method does what it says it does, run the unit tests.  To make sure your code is picked up by&lt;/p&gt;

&lt;p&gt;To test this, fire up a console and start squawking:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/console&amp;#x000A;&amp;gt;&amp;gt; &quot;Hello World&quot;.to_squawk&amp;#x000A;=&amp;gt; &quot;squawk! Hello World&quot;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If that worked, congratulations!  You just created your first test-driven plugin that extends a core ruby class.&lt;/p&gt;

&lt;h2&gt;Adding an &lt;code&gt;acts_as_yaffle&lt;/code&gt; method to ActiveRecord models&lt;/h2&gt;

&lt;p&gt;A common pattern in plugins is to add a method called &lt;code&gt;acts_as_something&lt;/code&gt; to models.  In this case, you want to write a method called &lt;code&gt;acts_as_yaffle&lt;/code&gt; that adds a squawk method to your models.&lt;/p&gt;

&lt;p&gt;To keep things clean, create a new test file called &lt;code&gt;acts_as_yaffle_test.rb&lt;/code&gt; in your plugin's test directory and require your test helper.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb&amp;#x000A;&amp;#x000A;require File.dirname(__FILE__) + '/test_helper.rb'&amp;#x000A;&amp;#x000A;class Hickwall &amp;lt; ActiveRecord::Base&amp;#x000A;  acts_as_yaffle&amp;#x000A;end&amp;#x000A;&amp;#x000A;class ActsAsYaffleTest &amp;lt; Test::Unit::TestCase&amp;#x000A;end&amp;#x000A;&amp;#x000A;# File: vendor/plugins/lib/acts_as_yaffle.rb&amp;#x000A;&amp;#x000A;module Yaffle&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;One of the most common plugin patterns for &lt;code&gt;acts_as_yaffle&lt;/code&gt; plugins is to structure your file like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module Yaffle&amp;#x000A;  def self.included(base)&amp;#x000A;    base.send :extend, ClassMethods&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  module ClassMethods&amp;#x000A;    # any method placed here will apply to classes, like Hickwall&amp;#x000A;    def acts_as_something&amp;#x000A;      send :include, InstanceMethods&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  module InstanceMethods&amp;#x000A;    # any method placed here will apply to instaces, like @hickwall&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With structure you can easily separate the methods that will be used for the class (like &lt;code&gt;Hickwall.some_method&lt;/code&gt;) and the instance (like &lt;code&gt;@hickwell.some_method&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Let's add class method named &lt;code&gt;acts_as_yaffle&lt;/code&gt; - testing it out first.  You already defined the ActiveRecord models in your test helper, so if you run tests now they will fail.&lt;/p&gt;

&lt;p&gt;Back in your &lt;code&gt;acts\_as\_yaffle&lt;/code&gt; file, update ClassMethods like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;module ClassMethods&amp;#x000A;  def acts_as_yaffle(options = {})&amp;#x000A;    send :include, InstanceMethods&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that test should pass.  Since your plugin is going to work with field names, you need to allow people to define the field names, in case there is a naming conflict.  You can write a few simple tests for this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb&amp;#x000A;&amp;#x000A;require File.dirname(__FILE__) + '/test_helper.rb'&amp;#x000A;&amp;#x000A;class ActsAsYaffleTest &amp;lt; Test::Unit::TestCase&amp;#x000A;  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk&amp;#x000A;    assert_equal &quot;last_squawk&quot;, Hickwall.yaffle_text_field&amp;#x000A;  end&amp;#x000A;  def test_a_hickwalls_yaffle_date_field_should_be_last_squawked_at&amp;#x000A;    assert_equal &quot;last_squawked_at&quot;, Hickwall.yaffle_date_field&amp;#x000A;  end&amp;#x000A;  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet&amp;#x000A;    assert_equal &quot;last_tweet&quot;, Wickwall.yaffle_text_field&amp;#x000A;  end&amp;#x000A;  def test_a_wickwalls_yaffle_date_field_should_be_last_tweeted_at&amp;#x000A;    assert_equal &quot;last_tweeted_at&quot;, Wickwall.yaffle_date_field&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To make these tests pass, you could modify your &lt;code&gt;acts_as_yaffle&lt;/code&gt; file like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/lib/acts_as_yaffle.rb&amp;#x000A;&amp;#x000A;module Yaffle&amp;#x000A;  def self.included(base)&amp;#x000A;    base.send :extend, ClassMethods&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  module ClassMethods&amp;#x000A;    def acts_as_yaffle(options = {})&amp;#x000A;      cattr_accessor :yaffle_text_field, :yaffle_date_field&amp;#x000A;      self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s&amp;#x000A;      self.yaffle_date_field = (options[:yaffle_date_field] || :last_squawked_at).to_s&amp;#x000A;      send :include, InstanceMethods&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  module InstanceMethods&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you can add tests for the instance methods, and the instance method itself:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/test/acts_as_yaffle_test.rb&amp;#x000A;&amp;#x000A;require File.dirname(__FILE__) + '/test_helper.rb'&amp;#x000A;&amp;#x000A;class ActsAsYaffleTest &amp;lt; Test::Unit::TestCase&amp;#x000A;&amp;#x000A;  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk&amp;#x000A;    assert_equal &quot;last_squawk&quot;, Hickwall.yaffle_text_field&amp;#x000A;  end&amp;#x000A;  def test_a_hickwalls_yaffle_date_field_should_be_last_squawked_at&amp;#x000A;    assert_equal &quot;last_squawked_at&quot;, Hickwall.yaffle_date_field&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  def test_a_wickwalls_yaffle_text_field_should_be_last_squawk&amp;#x000A;    assert_equal &quot;last_tweet&quot;, Wickwall.yaffle_text_field&amp;#x000A;  end&amp;#x000A;  def test_a_wickwalls_yaffle_date_field_should_be_last_squawked_at&amp;#x000A;    assert_equal &quot;last_tweeted_at&quot;, Wickwall.yaffle_date_field&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  def test_hickwalls_squawk_should_populate_last_squawk&amp;#x000A;    hickwall = Hickwall.new&amp;#x000A;    hickwall.squawk(&quot;Hello World&quot;)&amp;#x000A;    assert_equal &quot;squawk! Hello World&quot;, hickwall.last_squawk&amp;#x000A;  end&amp;#x000A;  def test_hickwalls_squawk_should_populate_last_squawked_at&amp;#x000A;    hickwall = Hickwall.new&amp;#x000A;    hickwall.squawk(&quot;Hello World&quot;)&amp;#x000A;    assert_equal Date.today, hickwall.last_squawked_at&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  def test_wickwalls_squawk_should_populate_last_tweet&amp;#x000A;    wickwall = Wickwall.new&amp;#x000A;    wickwall.squawk(&quot;Hello World&quot;)&amp;#x000A;    assert_equal &quot;squawk! Hello World&quot;, wickwall.last_tweet&amp;#x000A;  end&amp;#x000A;  def test_wickwalls_squawk_should_populate_last_tweeted_at&amp;#x000A;    wickwall = Wickwall.new&amp;#x000A;    wickwall.squawk(&quot;Hello World&quot;)&amp;#x000A;    assert_equal Date.today, wickwall.last_tweeted_at&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/lib/acts_as_yaffle.rb&amp;#x000A;&amp;#x000A;module Yaffle&amp;#x000A;  def self.included(base)&amp;#x000A;    base.send :extend, ClassMethods&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  module ClassMethods&amp;#x000A;    def acts_as_yaffle(options = {})&amp;#x000A;      cattr_accessor :yaffle_text_field, :yaffle_date_field&amp;#x000A;      self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s&amp;#x000A;      self.yaffle_date_field = (options[:yaffle_date_field] || :last_squawked_at).to_s&amp;#x000A;      send :include, InstanceMethods&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  module InstanceMethods&amp;#x000A;    def squawk(string)&amp;#x000A;      write_attribute(self.class.yaffle_text_field, string.to_squawk)&amp;#x000A;      write_attribute(self.class.yaffle_date_field, Date.today)&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note the use of write_attribute to write to the field in model.&lt;/p&gt;

&lt;h2&gt;Create a view helper&lt;/h2&gt;

&lt;p&gt;Creating a view helper is a 3-step process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add an appropriately named file to the lib directory&lt;/li&gt;
&lt;li&gt;Require the file and hooks in init.rb&lt;/li&gt;
&lt;li&gt;Write the tests&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;First, create the test to define the functionality you want:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/test/view_helpers_test.rb&amp;#x000A;&amp;#x000A;require File.dirname(__FILE__) + '/test_helper.rb'&amp;#x000A;include YaffleViewHelper&amp;#x000A;&amp;#x000A;class ViewHelpersTest &amp;lt; Test::Unit::TestCase&amp;#x000A;  def test_squawk_info_for_should_return_the_text_and_date&amp;#x000A;    time = Time.now&amp;#x000A;    hickwall = Hickwall.new&amp;#x000A;    hickwall.last_squawk = &quot;Hello World&quot;&amp;#x000A;    hickwall.last_squawked_at = time&amp;#x000A;    assert_equal &quot;Hello World, #{time.to_s}&quot;, squawk_info_for(hickwall)&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then add the following statements to init.rb:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/init.rb&amp;#x000A;&amp;#x000A;require &quot;view_helpers&quot;&amp;#x000A;ActionView::Base.send :include, YaffleViewHelper&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then add the view helpers file and&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/lib/view_helpers.rb&amp;#x000A;&amp;#x000A;module YaffleViewHelper&amp;#x000A;  def squawk_info_for(yaffle)&amp;#x000A;    returning &quot;&quot; do |result|&amp;#x000A;      result &amp;lt;&amp;lt; yaffle.read_attribute(yaffle.class.yaffle_text_field)&amp;#x000A;      result &amp;lt;&amp;lt; &quot;, &quot;&amp;#x000A;      result &amp;lt;&amp;lt; yaffle.read_attribute(yaffle.class.yaffle_date_field).to_s&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can also test this in script/console by using the &quot;helper&quot; method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/console&amp;#x000A;&amp;gt;&amp;gt; helper.squawk_info_for(@some_yaffle_instance)&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Create a migration generator&lt;/h2&gt;

&lt;p&gt;When you created the plugin above, you specified the --with-generator option, so you already have the generator stubs in your plugin.&lt;/p&gt;

&lt;p&gt;We'll be relying on the built-in rails generate template for this tutorial.  Going into the details of generators is beyond the scope of this tutorial.&lt;/p&gt;

&lt;p&gt;Type:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/generate&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should see the line:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Plugins (vendor/plugins): yaffle&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When you run &lt;code&gt;script/generate yaffle&lt;/code&gt; you should see the contents of your USAGE file.  For this plugin, the USAGE file looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Description:&amp;#x000A;    Creates a migration that adds yaffle squawk fields to the given model&amp;#x000A;&amp;#x000A;Example:&amp;#x000A;    ./script/generate yaffle hickwall&amp;#x000A;&amp;#x000A;    This will create:&amp;#x000A;        db/migrate/TIMESTAMP_add_yaffle_fields_to_hickwall&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you can add code to your generator:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb&amp;#x000A;&amp;#x000A;class YaffleGenerator &amp;lt; Rails::Generator::NamedBase&amp;#x000A;  def manifest&amp;#x000A;    record do |m|&amp;#x000A;      m.migration_template 'migration:migration.rb', &quot;db/migrate&quot;, {:assigns =&amp;gt; yaffle_local_assigns,&amp;#x000A;        :migration_file_name =&amp;gt; &quot;add_yaffle_fields_to_#{custom_file_name}&quot;&amp;#x000A;      }&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  private&amp;#x000A;    def custom_file_name&amp;#x000A;      custom_name = class_name.underscore.downcase&amp;#x000A;      custom_name = custom_name.pluralize if ActiveRecord::Base.pluralize_table_names&amp;#x000A;    end&amp;#x000A;&amp;#x000A;    def yaffle_local_assigns&amp;#x000A;      returning(assigns = {}) do&amp;#x000A;        assigns[:migration_action] = &quot;add&quot;&amp;#x000A;        assigns[:class_name] = &quot;add_yaffle_fields_to_#{custom_file_name}&quot;&amp;#x000A;        assigns[:table_name] = custom_file_name&amp;#x000A;        assigns[:attributes] = [Rails::Generator::GeneratedAttribute.new(&quot;last_squawk&quot;, &quot;string&quot;)]&amp;#x000A;        assigns[:attributes] &amp;lt;&amp;lt; Rails::Generator::GeneratedAttribute.new(&quot;last_squawked_at&quot;, &quot;datetime&quot;)&amp;#x000A;      end&amp;#x000A;    end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that you need to be aware of whether or not table names are pluralized.&lt;/p&gt;

&lt;p&gt;This does a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reuses the built in rails migration_template method&lt;/li&gt;
&lt;li&gt;Reuses the built-in rails migration template&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;When you run the generator like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;script/generate yaffle bird&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will see a new file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: db/migrate/20080529225649_add_yaffle_fields_to_birds.rb&amp;#x000A;&amp;#x000A;class AddYaffleFieldsToBirds &amp;lt; ActiveRecord::Migration&amp;#x000A;  def self.up&amp;#x000A;    add_column :birds, :last_squawk, :string&amp;#x000A;    add_column :birds, :last_squawked_at, :datetime&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  def self.down&amp;#x000A;    remove_column :birds, :last_squawked_at&amp;#x000A;    remove_column :birds, :last_squawk&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Adding custom generator commands&lt;/h2&gt;

&lt;p&gt;You may have noticed above that you can used one of the built-in rails migration commands &lt;code&gt;m.migration_template&lt;/code&gt;.  You can create your own commands for these, using the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the require and hook statements to init.rb&lt;/li&gt;
&lt;li&gt;Create the commands - creating 3 sets, Create, Destroy, List&lt;/li&gt;
&lt;li&gt;Add the method to your generator&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Working with the internals of generators is beyond the scope of this tutorial, but here is a basic example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/init.rb&amp;#x000A;&amp;#x000A;require &quot;commands&quot;&amp;#x000A;Rails::Generator::Commands::Create.send   :include,  Yaffle::Generator::Commands::Create&amp;#x000A;Rails::Generator::Commands::Destroy.send  :include,  Yaffle::Generator::Commands::Destroy&amp;#x000A;Rails::Generator::Commands::List.send     :include,  Yaffle::Generator::Commands::List&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/lib/commands.rb&amp;#x000A;&amp;#x000A;require 'rails_generator'&amp;#x000A;require 'rails_generator/commands'&amp;#x000A;&amp;#x000A;module Yaffle #:nodoc:&amp;#x000A;  module Generator #:nodoc:&amp;#x000A;    module Commands #:nodoc:&amp;#x000A;      module Create&amp;#x000A;        def yaffle_definition&amp;#x000A;          file(&quot;definition.txt&quot;, &quot;definition.txt&quot;)&amp;#x000A;        end&amp;#x000A;      end&amp;#x000A;&amp;#x000A;      module Destroy&amp;#x000A;        def yaffle_definition&amp;#x000A;          file(&quot;definition.txt&quot;, &quot;definition.txt&quot;)&amp;#x000A;        end&amp;#x000A;      end&amp;#x000A;&amp;#x000A;      module List&amp;#x000A;        def yaffle_definition&amp;#x000A;          file(&quot;definition.txt&quot;, &quot;definition.txt&quot;)&amp;#x000A;        end&amp;#x000A;      end&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/generators/yaffle/templates/definition.txt&amp;#x000A;&amp;#x000A;Yaffle is a bird&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/generators/yaffle/yaffle_generator.rb&amp;#x000A;&amp;#x000A;class YaffleGenerator &amp;lt; Rails::Generator::NamedBase&amp;#x000A;  def manifest&amp;#x000A;      m.yaffle_definition&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This example just uses the built-in &quot;file&quot; method, but you could do anything that ruby allows.&lt;/p&gt;

&lt;h2&gt;Adding Routes&lt;/h2&gt;

&lt;p&gt;Testing routes in plugins can be complex, especially if the controllers are also in the plugin itself.  Jamis Buck showed a great example of this in &lt;a href=&quot;http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2&quot;&gt;http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/test/routing_test.rb&amp;#x000A;&amp;#x000A;require &quot;#{File.dirname(__FILE__)}/test_helper&quot;&amp;#x000A;&amp;#x000A;class RoutingTest &amp;lt; Test::Unit::TestCase&amp;#x000A;&amp;#x000A;  def setup&amp;#x000A;    ActionController::Routing::Routes.draw do |map|&amp;#x000A;      map.yaffles&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  def test_yaffles_route&amp;#x000A;    assert_recognition :get, &quot;/yaffles&quot;, :controller =&amp;gt; &quot;yaffles_controller&quot;, :action =&amp;gt; &quot;index&quot;&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  private&amp;#x000A;&amp;#x000A;    # yes, I know about assert_recognizes, but it has proven problematic to&amp;#x000A;    # use in these tests, since it uses RouteSet#recognize (which actually&amp;#x000A;    # tries to instantiate the controller) and because it uses an awkward&amp;#x000A;    # parameter order.&amp;#x000A;    def assert_recognition(method, path, options)&amp;#x000A;      result = ActionController::Routing::Routes.recognize_path(path, :method =&amp;gt; method)&amp;#x000A;      assert_equal options, result&amp;#x000A;    end&amp;#x000A;end&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/init.rb&amp;#x000A;&amp;#x000A;require &quot;routing&quot;&amp;#x000A;ActionController::Routing::RouteSet::Mapper.send :include, Yaffle::Routing::MapperExtensions&amp;#x000A;&amp;#x000A;# File: vendor/plugins/yaffle/lib/routing.rb&amp;#x000A;&amp;#x000A;module Yaffle #:nodoc:&amp;#x000A;  module Routing #:nodoc:&amp;#x000A;    module MapperExtensions&amp;#x000A;      def yaffles&amp;#x000A;        @set.add_route(&quot;/yaffles&quot;, {:controller =&amp;gt; &quot;yaffles_controller&quot;, :action =&amp;gt; &quot;index&quot;})&amp;#x000A;      end&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&amp;#x000A;# File: config/routes.rb&amp;#x000A;&amp;#x000A;ActionController::Routing::Routes.draw do |map|&amp;#x000A;  ...&amp;#x000A;  map.yaffles&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can also see if your routes work by running &lt;code&gt;rake routes&lt;/code&gt; from your app directory.&lt;/p&gt;

&lt;h2&gt;Generate RDoc Documentation&lt;/h2&gt;

&lt;p&gt;Once your plugin is stable, the tests pass on all database and you are ready to deploy do everyone else a favor and document it!  Luckily, writing documentation for your plugin is easy.&lt;/p&gt;

&lt;p&gt;The first step is to update the README file with detailed information about how to use your plugin.  A few key things to include are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your name&lt;/li&gt;
&lt;li&gt;How to install&lt;/li&gt;
&lt;li&gt;How to add the functionality to the app (several examples of common use cases)&lt;/li&gt;
&lt;li&gt;Warning, gotchas or tips that might help save users time&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Once your README is solid, go through and add rdoc comments to all of the methods that developers will use.&lt;/p&gt;

&lt;p&gt;Before you generate your documentation, be sure to go through and add nodoc comments to those modules and methods that are not important to your users.&lt;/p&gt;

&lt;p&gt;Once your comments are good to go, navigate to your plugin directory and run&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rake rdoc&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Working with init.rb&lt;/h2&gt;

&lt;p&gt;The plugin initializer script init.rb is invoked via &lt;code&gt;eval&lt;/code&gt; (not require) so it has slightly different behavior.&lt;/p&gt;

&lt;p&gt;If you reopen any classes in init.rb itself your changes will potentially be made to the wrong module.  There are 2 ways around this:&lt;/p&gt;

&lt;p&gt;The first way is to explicitly define the top-level module space for all modules and classes, like ::Hash&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/init.rb&amp;#x000A;&amp;#x000A;class ::Hash&amp;#x000A;  def is_a_special_hash?&amp;#x000A;    true&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;OR you can use module_eval or class_eval&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/init.rb&amp;#x000A;&amp;#x000A;Hash.class_eval do&amp;#x000A;  def is_a_special_hash?&amp;#x000A;    true&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Storing models, views, helpers, and controllers in your plugins&lt;/h2&gt;

&lt;p&gt;You can easily store models, views, helpers and controllers in plugins.  Just create a folder for each in the lib folder, add them to the load path and remove them from the load once path:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: vendor/plugins/yaffle/init.rb&amp;#x000A;&amp;#x000A;%w{ models controllers helpers }.each do |dir|&amp;#x000A;  path = File.join(directory, 'lib', dir)&amp;#x000A;  $LOAD_PATH &amp;lt;&amp;lt; path&amp;#x000A;  Dependencies.load_paths &amp;lt;&amp;lt; path&amp;#x000A;  Dependencies.load_once_paths.delete(path)&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Adding directories to the load path makes them appear just like files in the the main app directory - except that they are only loaded once, so you have to restart the web server to see the changes in the browser.&lt;/p&gt;

&lt;p&gt;Adding directories to the load once paths allow those changes to picked up as soon as you save the file - without having to restart the web server.&lt;/p&gt;

&lt;h2&gt;Storing plugins in alternate locations&lt;/h2&gt;

&lt;p&gt;You can store plugins wherever you want - you just have to add those plugins to the plugins path in environment.rb&lt;/p&gt;

&lt;p&gt;Since the plugin is only loaded after the plugin paths are defined, you can't redefine this in your plugins - but it may be good to now.&lt;/p&gt;

&lt;p&gt;You can even store plugins inside of other plugins for complete plugin madness!&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;config.plugin_paths &amp;lt;&amp;lt; File.join(RAILS_ROOT,&quot;vendor&quot;,&quot;plugins&quot;,&quot;yaffle&quot;,&quot;lib&quot;,&quot;plugins&quot;)&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Plugin Loaders and Plugin Locators&lt;/h2&gt;

&lt;p&gt;If the built-in plugin behavior is inadequate, you can change almost every aspect of the location and loading process.  You can write your own plugin locators and plugin loaders, but that's beyond the scope of this tutorial.&lt;/p&gt;

&lt;h2&gt;Custom Plugin Generators&lt;/h2&gt;

&lt;p&gt;If you are an RSpec fan, you can install the rspec_plugin_generator, which will generate the spec folder and database for you.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/pat-maddox/rspec-plugin-generator/tree/master&quot;&gt;http://github.com/pat-maddox/rspec-plugin-generator/tree/master&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i&quot;&gt;http://nubyonrails.com/articles/the-complete-guide-to-rails-plugins-part-i&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii&quot;&gt;http://nubyonrails.com/articles/2006/05/09/the-complete-guide-to-rails-plugins-part-ii&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/technoweenie/attachment_fu/tree/master&quot;&gt;http://github.com/technoweenie/attachment_fu/tree/master&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html&quot;&gt;http://daddy.platte.name/2007/05/rails-plugins-keep-initrb-thin.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-05-20:34</id>
      <published>2008-05-20T21:02:00+00:00</published>
      <updated>2008-05-20T21:02:00+00:00</updated>
        <category term="demo"/>
      <link href="http://zilkey.com/2008/5/21/demo-apps" rel="alternate" type="text/html"/>
      <title>Demo apps</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;I love demo apps.  When coutenay came out with his &lt;a href=&quot;http://caboose.org/articles/2007/4/11/sample-rails-application&quot;&gt;rails demo app&lt;/a&gt; I learned a lot.  I've since created a few feature-specific demo apps and I hope that others can learn something from them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/zilkey/will_paginate_demo_app/tree&quot;&gt;will_paginate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/zilkey/complex_forms_demo_app/tree&quot;&gt;complex_forms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://github.com/zilkey/nested_set_demo/tree&quot;&gt;nested_set&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;As I've built these, I've realized that it would be helpful if all of these demo apps had a similar structure, so I'm going to propose one here.  If you have any thoughts, either fork one of those apps at the top and add them to your fork, or add comments here below.  Here's my first round of thoughts:&lt;/p&gt;

&lt;h3&gt;There should be a demo app start kit&lt;/h3&gt;

&lt;p&gt;This starter kit would have a readme with a README.rdoc page with a few sections outlined already, like &quot;Installation&quot;, &quot;What this demonstrates&quot; etc...&lt;/p&gt;

&lt;p&gt;Another great feature for a starter kit would be a helper that reads source files and displays them, syntax highlighted, to the users so that while you are looking at a page in the app and you say &quot;what does the controller look like for this page&quot; you could just click &quot;view the controller code&quot; and have it appear.&lt;/p&gt;

&lt;p&gt;Yet another feature that would be slick in a demo app would be a page that detects whether or not the database is installed, and if not, provides you with a simple database configuration form that allows you to specify the provider, username/password and optionally the timeout or the socket.&lt;/p&gt;

&lt;p&gt;The idea for the starter kit would be to make it easy for developers to create a starter kit that &quot;just works&quot; by installing the app and running script/server.&lt;/p&gt;

&lt;h3&gt;Vendor everything&lt;/h3&gt;

&lt;p&gt;Rails makes this easy with the new gem dependencies, but I think all rails apps should have vendor/rails and all of the related gems vendored.  I haven't been consistent about this with my demo apps, but I realize how this would be important.&lt;/p&gt;

&lt;h3&gt;Pre-populate the database&lt;/h3&gt;

&lt;p&gt;Whether you use the dataset plugin, custom rake tasks or sqlite3 databases prepopulated, every demo app should have an initial dataset so people can test the full range of the app without having to first add data - especially important for things like the complex forms demo, where the app shows how multiple models relate to each other.&lt;/p&gt;

&lt;h3&gt;Demos should ideally have specs&lt;/h3&gt;

&lt;p&gt;I haven't put meaninful specs into any of my demos, but as people download them and point out bugs I realize how helpful they would have been.  I hope to add specs to these apps as time goes on and fix whatever bugs I find.  If you do this, be sure to vendor your rspec/rspec-rails along with your vendor/rails.&lt;/p&gt;

&lt;h3&gt;Documentation should be redundant&lt;/h3&gt;

&lt;p&gt;Things that are in the README should also appear on the actual rendered pages, as well as in the source code.  When developers first load up the app it should be obvious what's happening.&lt;/p&gt;

&lt;p&gt;Again - I haven't done most of these in my demo apps, but I realize how important they would be, especially if more than 1 or 2 people are downloading the app.  If someone is checking out your demo app, it probably means that don't know how to do whatever you are demonstrating, and a long or frustrating demo (or a buggy one) can be worse than none at all.&lt;/p&gt;

&lt;p&gt;Have you built a demo app?  Any other suggestions or anything I've missed?&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-05-05:21</id>
      <published>2008-05-05T00:04:00+00:00</published>
      <updated>2008-05-05T00:04:00+00:00</updated>
        <category term="tips"/>
      <link href="http://zilkey.com/2008/5/5/rails-view-tips" rel="alternate" type="text/html"/>
      <title>Rails View Tips</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;Here is a list of quick tips that I find helpful using Ruby and Rails, inspired by the &lt;a href=&quot;http://railscasts.com/contest&quot;&gt;Railscasts Contest&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;1. Access view helpers from the console&lt;/h2&gt;

&lt;p&gt;Sometimes I need a quick reminder of the syntax of view helpers - like is it truncate(length, text) or truncate(text, length)?  When this happens I just fire up a script/console and use the &quot;helper&quot; method.  For example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;helper.truncate &quot;Big sentence&quot;, 5&amp;#x000A;=&amp;gt; &quot;Bi...&quot;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;2. Word Wrap&lt;/h2&gt;

&lt;p&gt;Speaking of view helpers, you probably know about pluralize, truncate and simple_format, but did you know that rails can word wrap for you?  Frequently I find that I have a column of text that just doesn't look good when some lines are a lot longer than others, and I need to have more fine-grained control over the output.  Word wrap comes to the rescue.  You can test this in script/console by doing the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;text = &quot;This is some very very long text that needs to be word wrapped&quot;&amp;#x000A;helper.word_wrap text, 50&amp;#x000A;=&amp;gt; &quot;This is some very very long text that needs to be\nword wrapped&quot;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice that it inserts a newline, not a br element.  If you want to convert that newline to an html br element, you can always use simple_format.  What I particularly like about word_wrap is that it only breaks on whitespace characters, so you'll never end up with a half-word.&lt;/p&gt;

&lt;h2&gt;3. Partial Counter&lt;/h2&gt;

&lt;p&gt;Let's say you need to render a partial for a collection, and that you need to generate a sequential number that can't be inferred from the database, like &quot;Post #0, Post #1, Post #2&quot; etc. where the numbers will always start with 0 and always be sequential.  To start you might try something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@posts.each_with_index do |post, index|&amp;#x000A;  render :partial =&amp;gt; &quot;post&quot;, :object =&amp;gt; post, :locals =&amp;gt; {:index =&amp;gt; index}&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then from your partial, you would reference it like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Post #&amp;lt;%= index %&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However, if you render a partial from a collection, Rails keeps track of the index for you auto-magically, in a local variable named partial_counter (in this case it would be post_counter).  Refactored, it would look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;render :partial =&amp;gt; @posts&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And from the partial itself:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Post #&amp;lt;%= post_counter %&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Remember that partial_counter is zero-based.  For regular partials, called with just an object, it will return 0.&lt;/p&gt;

&lt;h2&gt;4. Keep selects and model validations in sync with constants&lt;/h2&gt;

&lt;p&gt;The problem: you have a field in your model that validates_inclusion_of some array of values.  You want to provide a dropdown list in your UI that matches these values, but provides a human readable description beyond what &quot;humanize&quot; can provide.&lt;/p&gt;

&lt;p&gt;The solution: Constants.  Create a constant in your model that has a hash with the names and the descriptions like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Page &amp;lt; ActiveRecord::Base&amp;#x000A;  VISIBILITIES = {&quot;Anyone can see it&quot;=&amp;gt;&quot;public&quot;, &quot;Nobody can see it&quot;=&amp;gt;&quot;private&quot;}&amp;#x000A;  validates_inclusion_of :visibility, :in =&amp;gt; VISIBILITIES.values&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice how you can validate the inclusion of the field just in the values.  Then in the view just add a select field helper and pass it the constant:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;select :page, :visibility, Page::VISIBILITIES&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This keeps the code simple, readable and DRY.&lt;/p&gt;

&lt;h2&gt;5. Safely render environment-specific content&lt;/h2&gt;

&lt;p&gt;I like to use google analytics to keep track of my client's site visitors.  However, I don't want to track my development machine, or the staging version of the site - just the production site.  Rails makes this easy by making the RAILS_ENV environmental variable available.  So anywhere in your rails app you can execute code conditionally - like this snippet from a view:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%- if &quot;production&quot; == RAILS_ENV -%&amp;gt;&amp;#x000A;some google analytics javascript...&amp;#x000A;&amp;lt;%- end -%&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you're not careful, however, you can actually change the rails environment!  Take this snippet of code, for example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%- if RAILS_ENV = &quot;development&quot; -%&amp;gt;&amp;#x000A;output some super-secret code trace...&amp;#x000A;&amp;lt;%- end -%&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Can you spot what's wrong?  Notice the &quot;=&quot; instead of the &quot;==&quot; - when the view execute this code it will actually change the rails_env to development, potentially wreaking all kinds of havoc on your app.  In script/console this outputs a warning, and I haven't tried it on production mode, but just to be safe always put the &quot;production&quot; part before the RAILS_ENV part - even if you forget an &quot;=&quot; it will just throw an error.&lt;/p&gt;

&lt;p&gt;Another great use of this is creating a banner to alert users to what environment they are looking at.  Since staging sites often look exactly like production sites, I like to output a banner at the top of my page to say &quot;hey - you're on the staging site&quot;.  In my application.html.erb file I often have the following snippet:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%- unless 'production' == RAILS_ENV -%&amp;gt;&amp;#x000A;  &amp;lt;div style=&quot;padding:.5em;font-weight:bold;text-align:center;background:orange;&quot;&amp;gt;&amp;#x000A;    YOU ARE CURRENTLY ON THE &amp;lt;%= RAILS_ENV.upcase %&amp;gt; SITE&amp;#x000A;  &amp;lt;/div&amp;gt;&amp;#x000A;&amp;lt;%- end -%&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In edge rails this is a little easier with the Rails object - stay tuned for more on that later.&lt;/p&gt;

&lt;h2&gt;6. Locals with default values in partials&lt;/h2&gt;

&lt;p&gt;You can pass locals to partials, making partials very flexible.  But sometimes you want to be able to optionally pass a local variable to a partial.  You can accomplish this a variety of ways, but my favorite is just to use a simple ||= block at the top.  For example if you have an optional &quot;author&quot; local you want to pass in, you can just write:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%- author ||= nil -%&amp;gt;&amp;#x000A;Written by &amp;lt;%= author %&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The added benefit of this method is that the partial is self documenting, since you see all of the optional locals you can pass into it right at the top.  You can optionally add comments for the required locals as well - depending on how busy you like your partials to be.&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-05-04:29</id>
      <published>2008-05-04T14:37:00+00:00</published>
      <updated>2008-05-04T14:37:00+00:00</updated>
        <category term="capistrano"/>
        <category term="ferret"/>
        <category term="railsmachine"/>
      <link href="http://zilkey.com/2008/5/4/starting-ferret-at-reboot-using-capistrano" rel="alternate" type="text/html"/>
      <title>Starting ferret at reboot using Capistrano</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;div class='alert-message block-message'&gt;Update: 11/26/2011 - Since writing this post I've completely discontinued the use of ferret in favor of Postgres's built-in full-text search&lt;/div&gt;
&lt;p&gt;I like using &lt;a href=&quot;http://ferret.davebalmain.com/trac/&quot;&gt;Ferret&lt;/a&gt; and &lt;a href=&quot;http://projects.jkraemer.net/acts_as_ferret/&quot;&gt;acts_as_ferret&lt;/a&gt; to add full-text search to my models in Rails in a database-independent way.  I've had a very difficult time getting this to work smoothly with my deployment process because the ferret server needs to be run in a separate process.&lt;/p&gt;
&lt;p&gt;Every time the server reboots, you need to start the ferret server, and everytime you deploy your app you need to restart the ferret server.  You can go to the acts_as_ferret site to get more information about deployment strategies, but here's what's been working for me for a while now.&lt;/p&gt;

&lt;p&gt;First, you need to create a startup script that can be run on boot.  I've taken mine from the &lt;a href=&quot;https://support.railsmachine.com/index.php?pg=kb.page&amp;amp;id=147&quot;&gt;railsmachine&lt;/a&gt; tutorial, but you can also use the one from the &lt;a href=&quot;http://projects.jkraemer.net/acts_as_ferret/wiki/DrbServer&quot;&gt;acts_as_ferret&lt;/a&gt; tutorial.  Mine looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# located in config/templates/ferret_ctl.erb&amp;#x000A;#!/bin/bash&amp;#x000A;#&amp;#x000A;# This script starts and stops the ferret DRb server&amp;#x000A;# chkconfig: 2345 89 36&amp;#x000A;# description: Ferret search engine for ruby apps.&amp;#x000A;#&amp;#x000A;# save the current directory&amp;#x000A;CURDIR=`pwd`&amp;#x000A;PATH=/usr/local/bin:$PATH&amp;#x000A;&amp;#x000A;RORPATH=&quot;&amp;lt;%= current_path %&amp;gt;&quot;&amp;#x000A;&amp;#x000A;case &quot;$1&quot; in&amp;#x000A;  start)&amp;#x000A;     cd $RORPATH&amp;#x000A;     echo &quot;Starting ferret DRb server.&quot;&amp;#x000A;     RAILS_ENV=&amp;lt;%= rails_env %&amp;gt; script/ferret_start&amp;#x000A;     ;;&amp;#x000A;  stop)&amp;#x000A;     cd $RORPATH&amp;#x000A;     echo &quot;Stopping ferret DRb server.&quot;&amp;#x000A;     RAILS_ENV=&amp;lt;%= rails_env %&amp;gt; script/ferret_stop&amp;#x000A;    ;;&amp;#x000A;  *)&amp;#x000A;     echo $&quot;Usage: $0 {start, stop}&quot;&amp;#x000A;     exit 1&amp;#x000A;     ;;&amp;#x000A;esac&amp;#x000A;&amp;#x000A;cd $CURDIR&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, add the following capistrano tasks to your deploy.rb file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# =============================================================================&amp;#x000A;# FERRET&amp;#x000A;# =============================================================================&amp;#x000A;set :ferret_script_name, &quot;ferret_#{application}_ctl&quot;&amp;#x000A;set :ferret_ctl, &quot;/etc/init.d/#{ferret_script_name}&quot;&amp;#x000A;&amp;#x000A;namespace :ferret do&amp;#x000A;  desc &quot;Uploads the ferret startup script&quot;&amp;#x000A;  task :install, :roles =&amp;gt; :app, :only =&amp;gt; {:primary =&amp;gt; true} do&amp;#x000A;    require 'erb'&amp;#x000A;    upload_path = &quot;#{shared_path}/ferret&quot;&amp;#x000A;    template = File.read(&quot;config/templates/ferret_ctl.erb&quot;)&amp;#x000A;    file = ERB.new(template).result(binding)&amp;#x000A;    put file, upload_path, :mode =&amp;gt; 0755&amp;#x000A;    sudo &quot;cp #{upload_path} #{ferret_ctl}&quot;&amp;#x000A;    sudo &quot;chmod +x #{ferret_ctl}&quot;&amp;#x000A;    sudo &quot;/sbin/chkconfig #{ferret_script_name} on&quot;&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  desc &quot;Starts the ferret server&quot;&amp;#x000A;  task :start, :roles =&amp;gt; :app, :only =&amp;gt; {:primary =&amp;gt; true} do&amp;#x000A;    sudo &quot;#{ferret_ctl} start&quot;&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  desc &quot;Stops the ferret server&quot;&amp;#x000A;  task :stop, :roles =&amp;gt; :app, :only =&amp;gt; {:primary =&amp;gt; true} do&amp;#x000A;    sudo &quot;#{ferret_ctl} stop&quot;&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  desc &quot;Restarts the ferret server&quot;&amp;#x000A;  task :restart, :roles =&amp;gt; :app, :only =&amp;gt; {:primary =&amp;gt; true} do&amp;#x000A;    ferret.stop&amp;#x000A;    ferret.start&amp;#x000A;  end&amp;#x000A;&amp;#x000A;  desc &quot;Deletes the ferret startup script&quot;&amp;#x000A;  task :uninstall, :roles =&amp;gt; :app, :only =&amp;gt; {:primary =&amp;gt; true} do&amp;#x000A;    sudo &quot;/sbin/chkconfig #{ferret_script_name} off&quot;&amp;#x000A;    sudo &quot;rm -rf #{ferret_ctl}&quot;&amp;#x000A;  end&amp;#x000A;&amp;#x000A;end&amp;#x000A;after &quot;deploy:symlink&quot;, &quot;ferret:restart&quot;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you are ready to deploy - just execute the following commands to get set up with your script:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cap ferret:install&amp;#x000A;cap deploy&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The installation will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload your startup script to /etc/init.d and set the correct permissions&lt;/li&gt;
&lt;li&gt;When you deploy it will restart the drb servers before restarting mongrel&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Thanks to the folks at Railsmachine for providing detailed instructions about chkconfig, and the acts_as_ferret folks for providing the drb server.&lt;/p&gt;


      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-05-01:28</id>
      <published>2008-05-01T05:15:00+00:00</published>
      <updated>2008-05-01T05:15:00+00:00</updated>
        <category term="core"/>
        <category term="rails"/>
      <link href="http://zilkey.com/2008/5/1/adding-patches-to-rails-now-that-it-s-on-git" rel="alternate" type="text/html"/>
      <title>Adding patches to Rails now that it&#x27;s on git</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;It took me a while to figure out how to try to contribute to Rails now that it's on Lighthouse / Github.  Here's what I do now, and it seems to work:&lt;/p&gt;
&lt;p&gt;First, create a clone of the main git repository (not your fork of it - there's really no reason to fork unless you want others to pull your changes before core accepts them):&lt;/p&gt;

&lt;h2&gt;Setup your development directories&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;mkdir rails&amp;#x000A;cd rails&amp;#x000A;mkdir patches&amp;#x000A;git clone git://github.com/rails/rails.git source&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you have:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;rails&amp;#x000A;|--patches&amp;#x000A;`--source&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Set up your dev branch&lt;/h2&gt;

&lt;p&gt;Then create a new branch where you'll store just the changes you make for &lt;em&gt;this patch&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git checkout -b your_patch_name master&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Create the patch&lt;/h2&gt;

&lt;p&gt;Make your test-driven changes and when you are ready to create the patch run:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git diff -p master &amp;gt; ../patches/your_patch_name.patch&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Setup a throwaway test branch&lt;/h2&gt;

&lt;p&gt;If you want to test your change&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a new branch and apply the patch (this is useful if other commits have happened since you first created your patch)&lt;/li&gt;
&lt;li&gt;run the tests and make sure that everything passes&lt;/li&gt;
&lt;li&gt;&lt;p&gt;clear your changes and delete the test branch&lt;/p&gt;

&lt;p&gt;  git checkout master
  git pull
  git checkout -b your_patch_name_test master
  git apply ../patches/your_patch_name.patch
  git stash ...
  git checkout master
  git stash clear
  git branch -D your_patch_name&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Using git stash allows you to move back to the master branch without taking your changes with you - leaving you with a clean master branch.  To learn more about git stash syntax, see &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-stash.html&quot;&gt;the git documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;Continue development&lt;/h2&gt;

&lt;p&gt;When it's time to update the code you put in the patch, you can just rebase from the master branch:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git co your_patch_name&amp;#x000A;git rebase master&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then you can fix whatever changes you need to fix and recreate your patch (or create a new one if your old changes were accepted).&lt;/p&gt;

&lt;h2&gt;Share you patch&lt;/h2&gt;

&lt;p&gt;The next step is to go to &lt;a href=&quot;http://rails.lighthouseapp.com/&quot;&gt;http://rails.lighthouseapp.com/&lt;/a&gt; and create a ticket.  Make sure that you&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tag it with &quot;patch&quot; as well as whatever else it applies to&lt;/li&gt;
&lt;li&gt;don't forget to upload the patch itself.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;When you upload, it appear in the middle of the right-hand column as a blue link - it's hard to find, so look carefully.  Then get as many people as possible to grab your changes and test them and add +1's where necessary.&lt;/p&gt;

&lt;h2&gt;Summary&lt;/h2&gt;

&lt;p&gt;When all is said and done you are left with a directory full of patches you can apply, a clean master working copy and individual branches for all of your patches that you can maintain over time.  While this was possible with subversion, it's &lt;em&gt;way&lt;/em&gt; cleaner with git.  Contributing to rails is easier than ever!&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.culann.com/2008/01/contributing-to-rails-with-git&quot;&gt;http://www.culann.com/2008/01/contributing-to-rails-with-git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-stash.html&quot;&gt;http://www.kernel.org/pub/software/scm/git/docs/git-stash.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-04-29:26</id>
      <published>2008-04-29T07:42:00+00:00</published>
      <updated>2008-04-29T07:42:00+00:00</updated>
        <category term="migrations"/>
        <category term="rails"/>
      <link href="http://zilkey.com/2008/4/29/new-rails-core-feature-proposal-super-sexy-migrations" rel="alternate" type="text/html"/>
      <title>New Rails Core Feature Proposal: Super Sexy Migrations</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;If you are running Rails Edge from github, you can now get Super Sexy Migrations, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;change_table :videos do |t|&amp;#x000A;  t.timestamps&amp;#x000A;  t.belongs_to :goat&amp;#x000A;  t.string :name, :email, :limit =&amp;gt; 20&amp;#x000A;  t.remove :name, :email # =&amp;gt; that's right - remove finally takes an array!&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Check out the &lt;a href=&quot;http://railscasts.com/episodes/107&quot;&gt;railscast video&lt;/a&gt; for more details.&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-04-29:25</id>
      <published>2008-04-29T01:29:00+00:00</published>
      <updated>2008-04-29T01:29:00+00:00</updated>
        <category term="acts_as_state_machine"/>
        <category term="gems"/>
        <category term="plugins"/>
      <link href="http://zilkey.com/2008/4/29/actsasstatemachine-gets-hippified" rel="alternate" type="text/html"/>
      <title>ActsAsStateMachine gets hippified</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;I was very pleased to learn earlier tonight that acts_as_state_machine has become infinitely hipper.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It's on github, making patches oh so easy to push&lt;/li&gt;
&lt;li&gt;It works with any class, not just active record&lt;/li&gt;
&lt;li&gt;The &quot;state&quot; column is renamed to aasm_state so that you can have a city, state and zip in your active record model (man that was a pain before)&lt;/li&gt;
&lt;li&gt;It's a gem (in both senses of the word)&lt;/li&gt;
&lt;li&gt;It's tested with specs, and there's decent coverage&lt;/li&gt;
&lt;li&gt;The api is basically the same, making it easy to upgrade&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;It still has the same issues that the old subversion plugin had, so I incorporated my acts_as_state_machine_hacks plugin into my own fork of aasm on github, which you can check out at &lt;a href=&quot;http://github.com/zilkey/aasm/wikis&quot;&gt;http://github.com/zilkey/aasm/wikis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also published_at some rdocs for my fork at &lt;a href=&quot;http://aasm.zilkey.com/&quot;&gt;http://aasm.zilkey.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to Scott Barron for updating me on the latest changes, and for a killer gem.&lt;/p&gt;

&lt;p&gt;To install my fork of the app:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/zilkey/aasm.git aasm&amp;#x000A;cd aasm&amp;#x000A;rake gem&amp;#x000A;sudo uninstall aasm # =&amp;gt; if you already have a version installed&amp;#x000A;sudo gem install pkg/aasm-3.0.0.gem&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-04-05:18</id>
      <published>2008-04-05T17:05:00+00:00</published>
      <updated>2008-04-05T17:05:00+00:00</updated>
      <link href="http://zilkey.com/2008/4/5/complex-forms-with-correct-ids" rel="alternate" type="text/html"/>
      <title>Extending Complex Forms: generate valid dom ids</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;A few weeks ago I was hired to build a simple event registration form.  The requirements were that users needed to be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register multiple attendees for the same organization in one step&lt;/li&gt;
&lt;li&gt;Specify a meal preference for each attendee, using radio buttons&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since I had recently bought &lt;a href=&quot;http://www.pragprog.com/titles/fr_arr&quot;&gt;Advanced Rails Recipes&lt;/a&gt; and watched the &lt;a href=&quot;http://railscasts.com/episodes/75&quot;&gt;Ryan Bates' excellent Railscast on complex forms&lt;/a&gt; I thought I was all set to go.&lt;/p&gt;

&lt;p&gt;I followed Ryan's instructions exactly and within a few minutes I was up and running with the javascript additions, skinny controllers and fat models and life was good.    I fired up my browser, added a few attendee subforms and went to set their meal preference with the radio button when, to my dismay, clicking on any attendee's meal preference set the meal preference for the first!  Not good.&lt;/p&gt;

&lt;p&gt;Whenever these things happen I run the page through the &lt;a href=&quot;http://validator.w3.org/&quot;&gt;w3c html validator&lt;/a&gt; to make sure that I've got valid HTML.  When I ran it through, it gave me several errors - I had lots of repeated ids.  Why?  If you follow the ARR/railscast example, all of the new subforms will have the same id.  In reality, there are 2 things that need to happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when the page is loaded Rails needs to set an :index on all of the fields&lt;/li&gt;
&lt;li&gt;when the form is added dynamically via javascript, the javascript needs to insert the correct id&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Do your homework&lt;/h2&gt;

&lt;p&gt;ARR prohibits people from reproducing their code from their tutorials without permission (which I didn't obtain).  So this blog post won't make much sense unless you read &lt;a href=&quot;http://www.pragprog.com/titles/fr_arr&quot;&gt;Advanced Rails Recipes&lt;/a&gt; and/or watch &lt;a href=&quot;http://railscasts.com/episodes/75&quot;&gt;Railscast on complex forms&lt;/a&gt; - both of which I highly recommend.  Once you have a working example of that, the rest of this post will make sense.&lt;/p&gt;

&lt;h2&gt;The setup&lt;/h2&gt;

&lt;p&gt;For this example, I'll use the following classes:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Registration &amp;lt; ActiveRecord::Base&amp;#x000A;  has_many :attendees&amp;#x000A;end&amp;#x000A;&amp;#x000A;class Attendee &amp;lt; ActiveRecord::Base&amp;#x000A;  belongs_to :registration&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The javascript&lt;/h2&gt;

&lt;p&gt;So let's get started.  First, we need to create a javascript method that will enable us to increment the index value every time - I decided to go with a generic one so that I could reuse it across my app, and have mutliple subfrms of different types on the same page but still be dry:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var Subform = Class.create({&amp;#x000A;  lineIndex: 1,&amp;#x000A;  parentElement: &quot;&quot;,&amp;#x000A;  initialize: function(rawHTML, lineIndex, parentElement) {&amp;#x000A;    this.rawHTML        = rawHTML;&amp;#x000A;    this.lineIndex      = lineIndex;&amp;#x000A;    this.parentElement  = parentElement;&amp;#x000A;  },&amp;#x000A;  parsedHTML: function() {&amp;#x000A;    return this.rawHTML.replace(/INDEX/g, this.lineIndex++);&amp;#x000A;  },&amp;#x000A;  add: function() {&amp;#x000A;    new Insertion.Bottom($(this.parentElement), this.parsedHTML());&amp;#x000A;  }&amp;#x000A;});&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next, we need to set the index when the page loads.  I accomplished that like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# /app/views/layouts/application.html.erb&amp;#x000A;&amp;#x000A;&amp;lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&amp;gt;&amp;#x000A;  &amp;lt;head&amp;gt;&amp;#x000A;    &amp;lt;title&amp;gt;&amp;lt;%= yield :title %&amp;gt;&amp;lt;/title&amp;gt;&amp;#x000A;    &amp;lt;%= javascript_include_tag :defaults, :cache =&amp;gt; 'defaults' %&amp;gt;&amp;#x000A;    &amp;lt;%= yield :head %&amp;gt;&amp;#x000A;  &amp;lt;/head&amp;gt;&amp;#x000A;  &amp;lt;body&amp;gt;&amp;#x000A;    &amp;lt;%= yield %&amp;gt;&amp;#x000A;  &amp;lt;/body&amp;gt;&amp;#x000A;&amp;lt;/html&amp;gt;&amp;#x000A;&amp;#x000A;# /app/views/registrations/_form.html.erb&amp;#x000A;&amp;#x000A;&amp;lt;%- content_for :head do -%&amp;gt;&amp;#x000A;  &amp;lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&amp;gt;&amp;#x000A;    //&amp;lt;![CDATA[&amp;#x000A;    attendeeForm = new Subform('&amp;lt;%= escape_javascript(render(:partial =&amp;gt; &quot;attendee&quot;, :object =&amp;gt; Attendee.new)) %&amp;gt;',&amp;lt;%= @registration.attendees.length %&amp;gt;,'attendees');&amp;#x000A;    //]]&amp;gt;&amp;#x000A;  &amp;lt;/script&amp;gt;&amp;#x000A;&amp;lt;%- end -%&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In Ryan's original recipe he creates a rails helper to create the add link.  Now that we've written the javscript ourselves we no longer need the helper - our &quot;add&quot; link now look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= link_to_function 'Add Attendee', &quot;attendeeForm.add()&quot; %&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The partial&lt;/h2&gt;

&lt;p&gt;We need to add the index to all of the form helpers, which will require some work.  In addition, we need to make sure that all of the radio buttons have unique ids and that all of the labels have ids that match up with the radio buttons.  So here's what my partial looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;attendee&quot;&amp;gt;&amp;lt;%-&amp;#x000A;  index ||= &quot;INDEX&quot;&amp;#x000A;  new_or_existing = attendee.new_record? ? 'new' : 'existing'&amp;#x000A;  id_or_index = attendee.new_record? ? index : attendee.id&amp;#x000A;  prefix = &quot;registration[#{new_or_existing}_attendee_attributes][]&quot;&amp;#x000A;-%&amp;gt;&amp;#x000A;&amp;#x000A;  &amp;lt;% fields_for prefix, attendee do |attendee_form| -%&amp;gt;&amp;#x000A;  &amp;lt;p&amp;gt;&amp;lt;%= attendee_form.label :email, nil, :index =&amp;gt; id_or_index %&amp;gt; &amp;lt;%= attendee_form.text_field :email, :index =&amp;gt; id_or_index %&amp;gt; &amp;lt;/p&amp;gt;&amp;#x000A;&amp;#x000A;  &amp;lt;%- MealPreference.find(:all).each do |preference| -%&amp;gt;&amp;#x000A;    &amp;lt;p&amp;gt;&amp;#x000A;       &amp;lt;%- radio_id = &quot;registration_#{new_or_existing}_attendee_attributes_#{id_or_index}_meal_preference_#{preference.name}&quot; -%&amp;gt;&amp;#x000A;       &amp;lt;%= attendee_form.radio_button :meal_preference, preference.name, :id =&amp;gt; radio_id, :index =&amp;gt; id_or_index %&amp;gt;&amp;#x000A;       &amp;lt;%= content_tag :label, preference.name, :class =&amp;gt; &quot;radio&quot;, :for =&amp;gt; radio_id %&amp;gt;&amp;#x000A;     &amp;lt;/p&amp;gt;&amp;#x000A;  &amp;lt;%- end -%&amp;gt;&amp;#x000A;  &amp;lt;%= content_tag :p, link_to_function(&quot;Remove this attendee&quot;, &quot;if(confirm('Are you sure?')){$(this).up('.attendee').remove()}&quot;) %&amp;gt;&amp;#x000A;  &amp;lt;% end -%&amp;gt;&amp;#x000A;&amp;lt;/div&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What just happened?  I added an index to every field.  For existing records, this index will correspond to the attendee.id.  For new records, it will be the string &quot;INDEX&quot;.  If you recall from the javascript above the parsedHTML function replaced the word INDEX with the correct numeric index.&lt;/p&gt;

&lt;p&gt;NOTE:  getting correct radio_button ids requires &lt;a href=&quot;http://zilkey.com/2008/4/5/introducing-label_with_index-a-simple-rails-plugin&quot;&gt;my label_with_index plugin&lt;/a&gt; if you are running gem rails, but it looks like radio buttons create correct ids in edge.&lt;/p&gt;

&lt;h2&gt;The form&lt;/h2&gt;

&lt;p&gt;This means, however, that you need to pass an index into the partial for existing records.  In my form, I've got the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;h3&amp;gt;Attendees&amp;lt;/h3&amp;gt;&amp;#x000A;&amp;lt;div id=&quot;attendees&quot;&amp;gt;&amp;#x000A;  &amp;lt;%- @registration.attendees.each_with_index do |attendee, index| -%&amp;gt;&amp;#x000A;    &amp;lt;%= render :partial =&amp;gt; &quot;attendee&quot;, :object =&amp;gt; attendee, :locals =&amp;gt; {:index =&amp;gt; index} %&amp;gt;&amp;#x000A;  &amp;lt;%- end -%&amp;gt;&amp;#x000A;&amp;lt;/div&amp;gt;&amp;#x000A;&amp;lt;p id=&quot;add-attendee&quot;&amp;gt;&amp;#x000A;&amp;lt;%= link_to_function 'Add Attendee', &quot;attendeeForm.add()&quot; %&amp;gt;&amp;#x000A;&amp;lt;/p&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note the use of each_with_index so that we can pass the correct index in.  &quot;But wait!&quot; you say, &quot;What if the attendee is an existing record?  Won't that mess this up?&quot;  Fear not - in the partial we first check whether it's a new record, and only use the index if it's a new record.&lt;/p&gt;

&lt;h2&gt;The model&lt;/h2&gt;

&lt;p&gt;Making the changes in the model is trivial.  The existing_attributes stay exactly the same - but we have to make one small change to the new attributes:&lt;/p&gt;

&lt;p&gt;Old:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# app/models/registration.rb&amp;#x000A;&amp;#x000A;# add all new attendees&amp;#x000A;def new_attendee_attributes=(attendee_attributes)&amp;#x000A;  attendee_attributes.each do |attributes|&amp;#x000A;    attendees.build(attributes)&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Changes to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# app/models/registration.rb&amp;#x000A;&amp;#x000A;# add all new attendees&amp;#x000A;def new_attendee_attributes=(attendee_attributes)&amp;#x000A;  attendee_attributes.each do |index, attributes|&amp;#x000A;    attendees.build(attributes)&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And voila!  Correct dom ids and reusable javascript.&lt;/p&gt;

&lt;h2&gt;Credits&lt;/h2&gt;

&lt;p&gt;While I wrote the javascript in that example I reconstructed it from another javascript snippet I had seen (maybe on a railscast, or in the source code for some other rails app like Basecamp or Blinksale).  If that looks like your javascript, please contact me at jeff at zilkey . com - I can't find the original source right now.  Other credits include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://railscasts.com/episodes/75&quot;&gt;http://railscasts.com/episodes/75&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.pragprog.com/titles/fr_arr&quot;&gt;http://www.pragprog.com/titles/fr_arr/errata&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;References&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.prototypejs.org/api/class/create&quot;&gt;http://www.prototypejs.org/api/class/create&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://zilkey.com/2008/4/5/introducing-label_with_index-a-simple-rails-plugin&quot;&gt;http://zilkey.com/2008/4/5/introducing-label_with_index-a-simple-rails-plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-04-05:19</id>
      <published>2008-04-05T16:08:00+00:00</published>
      <updated>2008-04-05T16:08:00+00:00</updated>
        <category term="plugins"/>
      <link href="http://zilkey.com/2008/4/5/introducing-label_with_index-a-simple-rails-plugin" rel="alternate" type="text/html"/>
      <title>Introducing label_with_index: a simple rails plugin</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;In this post I will describe a plugin that extends the functionality of Rails labels.&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;

&lt;p&gt;A while back Rails introduced the label helper, so you can can easily add labels to forms like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= label :user, :name %&amp;gt;&amp;#x000A;# =&amp;gt; &amp;lt;label for=&quot;user_2_name&quot;&amp;gt;Name&amp;lt;/label&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Unfortunately the person who committed that change neglected to add support for the auto index and :index options.  For example, take the following code snippet:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= text_field :user, :name, :index =&amp;gt; 1%&amp;gt;&amp;#x000A;# =&amp;gt; &amp;lt;input id=&quot;user_1_name&quot; name=&quot;user[1][name]&quot; size=&quot;30&quot; type=&quot;text&quot; /&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Following the principle of least surprise, you might assume that labels would work the same way, but they don't:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= label :user, :name, nil, :index =&amp;gt; 1%&amp;gt;&amp;#x000A;# =&amp;gt; &amp;lt;label for=&quot;user_name&quot; index=&quot;1&quot;&amp;gt;Name&amp;lt;/label&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The fix&lt;/h2&gt;

&lt;p&gt;I've just released a plugin called label_with_index that fixes this.  Now label helpers take advantage of the autoindex feature and :index keys as you would expect:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= label :user, :name, nil, :index =&amp;gt; 1%&amp;gt;&amp;#x000A;# =&amp;gt; &amp;lt;label for=&quot;user_1_name&quot;&amp;gt;Name&amp;lt;/label&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Fixing radio button ids&lt;/h2&gt;

&lt;p&gt;Rails has also had a bug for quite some time where it will by default emit radio button ids that will fail XHTML validation. To boot, they even advertise this in the docs. Here's what's written on &lt;a href=&quot;http://api.rubyonrails.com/classes/ActionView/Helpers/FormHelper.html&quot;&gt;http://api.rubyonrails.com/classes/ActionView/Helpers/FormHelper.html&lt;/a&gt; :&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Let's say that @post.category returns &quot;rails&quot;:&amp;#x000A;radio_button(&quot;post&quot;, &quot;category&quot;, &quot;rails&quot;)&amp;#x000A;radio_button(&quot;post&quot;, &quot;category&quot;, &quot;java&quot;)&amp;#x000A;# =&amp;gt; &amp;lt;input type=&quot;radio&quot; id=&quot;post_category&quot; name=&quot;post[category]&quot; value=&quot;rails&quot; checked=&quot;checked&quot; /&amp;gt;&amp;#x000A;#    &amp;lt;input type=&quot;radio&quot; id=&quot;post_category&quot; name=&quot;post[category]&quot; value=&quot;java&quot; /&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;See how those two radio buttons have the same id?  Not only will that fail validation, but it can cause unexpected javascript behavior and will cause very odd label-clicking results (clicking the label will most likely highlight the first radio button, regardless of which label is clicked).&lt;/p&gt;

&lt;p&gt;label_with_index also fixes this in two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes the id unique (or at least, as unique as the option is)&lt;/li&gt;
&lt;li&gt;Adds a :value option to the label helper that can match the radio button's value&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Before:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;radio_button(&quot;post&quot;, &quot;category&quot;, &quot;java&quot;)&amp;#x000A;label(&quot;post&quot;, &quot;category&quot;)&amp;#x000A; # =&amp;gt; &amp;lt;input type=&quot;radio&quot; id=&quot;post_category&quot; name=&quot;post[category]&quot; value=&quot;java&quot; /&amp;gt;&amp;#x000A; # =&amp;gt; &amp;lt;label for=&quot;post_category&quot;&amp;gt;Category&amp;lt;/label&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;radio_button(&quot;post&quot;, &quot;category&quot;, &quot;java&quot;)&amp;#x000A;label(&quot;post&quot;, &quot;category&quot;, nil, :value =&amp;gt; &quot;java&quot;)&amp;#x000A; # =&amp;gt; &amp;lt;input type=&quot;radio&quot; id=&quot;post_category_java&quot; name=&quot;post[category]&quot; value=&quot;java&quot; /&amp;gt;&amp;#x000A; # =&amp;gt; &amp;lt;label for=&quot;post_category_java&quot;&amp;gt;Category&amp;lt;/label&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So with this plugin the rails label helper, and the radio_button helper have support for creating valid ids out of the box.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/zilkey/label_with_index.git vendor/plugins/label_with_index&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;See the rdoc at &lt;a href=&quot;http://label_with_index.zilkey.com/&quot;&gt;http://label_with_index.zilkey.com/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;To Do&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a fork of rails on github and add this to core with tests&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;References&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://dev.rubyonrails.org/changeset/7541&quot;&gt;http://dev.rubyonrails.org/changeset/7541&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://dev.rubyonrails.org/ticket/9915&quot;&gt;http://dev.rubyonrails.org/ticket/9915&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Notes:&lt;/h2&gt;

&lt;p&gt;It appears that the radio button id may have been fixed on edge rails - I haven't tested it out fully and I'm not sure if all of the svn patches are synced up on github, so I'll wait until things settle a bit and make changes as necessary.&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-03-25:12</id>
      <published>2008-03-25T01:45:00+00:00</published>
      <updated>2008-03-25T01:45:00+00:00</updated>
        <category term="cycle-of-doom"/>
      <link href="http://zilkey.com/2008/3/25/breaking-the-cycle-of-doom-part-1-the-build-or-buy-fallacy" rel="alternate" type="text/html"/>
      <title>Breaking the cycle of doom, Part 1: Understanding the cycle</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;I've seen it time and time again - it all starts with a 70-page RFP from a non-profit that wants donor-relation management, content management, events management, complete real-time integration with their legacy systems, web services integration with their partners, mass-email capabilities, online donations and an online store, real-time tracking of the work they are doing in the field updated from satellites.  They want a quote, and they want it now.  The cheaper the better.&lt;/p&gt;
&lt;p&gt;Then the 60-page responses come flooding in as well - there's the Indian firm that promises all of that, plus several weeks of training for $15K - delivered in 6 weeks or less.  There's the massive vendor that's throws in $125K as a starting point, vague about the timing and the custom programming costs but more than willing to put you in contact with one of their 50 sales representatives.  There's the small firm that recommends using a few existing tools, using a few pre-built tools and offers an expensive but competitive bid.&lt;/p&gt;

&lt;p&gt;Depending on what the non-profit chooses, one of a few things happen.  The Indian firm ends up charging 3-4 times what they originally planned since nobody at the non-profit realized that for $15K the requirements gathering phase amounts to little more than reading the RFP again, so every piece of code that was written had to be re-written multiple times before it was adequate.  That big vendor?  Well, they forgot to mention the $1 per email fee, the $500/hour training fee, the $4,500 AS-400 integration fee and the fact that nobody actually knows how to download that data from the field to a GPS, so that feature was scrapped.  Oh, did I mention the $2K per month hosting fee?  Over at the smaller firm they are really making an effort - but staffing is an issue and their contractors are spread thinly across multiple projects and progress is very slow.  As the requirements change, the small firm adapts quickly, but it means that lots of the core functionality keeps getting pushed off.&lt;/p&gt;

&lt;p&gt;As bleak as those scenarios may seem, more often than not what I've observed in real life is that many non-profits experience most if not all of these in an almost cyclical way.  Once burned by big vendors, they turn the projects in-house, but then have staffing problems and outsource again, each time around paying again and again for the same 90% of the functionality they already had and never really getting that last 10% that actually makes people's lives better.  This is the cycle of doom.&lt;/p&gt;

&lt;p&gt;I don't propose to have a solution to the cycle of doom but I am convinced that using Ruby on Rails and agile methodologies can be a big part of the solution.  Over the next few weeks I'll be posting several non-technical posts for those in non-profits who are trying to figure out what they need as well as technical posts for developers who are trying to use Ruby on Rails to build their personal product-lines for non-profits.&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-03-23:11</id>
      <published>2008-03-23T21:57:00+00:00</published>
      <updated>2008-03-23T21:57:00+00:00</updated>
      <link href="http://zilkey.com/2008/3/24/advanced-acts_as_list-scope-with-multiple-columns" rel="alternate" type="text/html"/>
      <title>Advanced acts_as_list scope with multiple columns</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;I just ran across a problem that I'm sure very few other people will encounter, but if I can help just one googler....&lt;/p&gt;
&lt;h2&gt;Scenario&lt;/h2&gt;

&lt;p&gt;You are using acts_as_list, and you want to scope your list by more than one column &lt;em&gt;and&lt;/em&gt; one of the columns is a reserved word.  You went to the &lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveRecord/Acts/List/ClassMethods.html%20and%20you%20added%20something%20like%20this%20to%20your%20model:&quot;&gt;rails documentation page for acts_as_list&lt;/a&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Element &amp;lt; ActiveRecord::Base&amp;#x000A;   belongs_to :page&amp;#x000A;   acts_as_list :scope =&amp;gt; 'page_id = #{page_id} and `group` = \'#{group}\' '&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Whoa - that's some ugly code.  Let's see what's happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The string you pass in to :scope is used to dynamically create a method called &quot;scope_condition&quot; with the contents of the string&lt;/li&gt;
&lt;li&gt;The :scope has to be in single quotes so that the contents of the #{} blocks don't get evaluated&lt;/li&gt;
&lt;li&gt;The word &quot;group&quot; has to be in special quotes, otherwise it will throw a MYSQL error&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;There are a number of problems with the code above - namely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The combination of single, double and angled quotes makes it very difficult to read&lt;/li&gt;
&lt;li&gt;The angled-quotes will not work for every database adapter&lt;/li&gt;
&lt;li&gt;The string contains #{} blocks, but they are not evaluated - instead they are evaluated later&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;The fix&lt;/h2&gt;

&lt;p&gt;Making the code database-independent is easy.  Rails provides a method on every connection called &quot;quote_column_name&quot; which adds the correct quotes for whichever database adapter you are using.  The connection object is a part of every Active Record instance, so the following line does the trick:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Element &amp;lt; ActiveRecord::Base&amp;#x000A;  belongs_to :page&amp;#x000A;  acts_as_list :scope =&amp;gt; 'page_id = #{page_id} AND #{connection.quote_column_name(&quot;group&quot;)} = \'#{group}\' '&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Making the code easier to read is almost as simple - just define your own &quot;scope_condition&quot; method.  The final code will look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Element &amp;lt; ActiveRecord::Base&amp;#x000A;  belongs_to :page&amp;#x000A;  acts_as_list&amp;#x000A;&amp;#x000A;  # scope_condition for acts_as_list&amp;#x000A;  def scope_condition&amp;#x000A;    &quot;page_id = #{page_id} AND #{connection.quote_column_name(&quot;group&quot;)} = #{quote_value(group)}&quot;&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we're talkin'.  The scope_condition is now database-independent, the quotes make sense and programs like TextMate will highlight the syntax appropriately.&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://zigotos.ca/2007/10/25/multiple-scopes-values-for-acts_as_list/&quot;&gt;http://zigotos.ca/2007/10/25/multiple-scopes-values-for-acts_as_list/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://api.rubyonrails.org/classes/ActiveRecord/Acts/List/ClassMethods.html&quot;&gt;http://api.rubyonrails.org/classes/ActiveRecord/Acts/List/ClassMethods.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-03-16:7</id>
      <published>2008-03-16T15:59:00+00:00</published>
      <updated>2008-03-16T15:59:00+00:00</updated>
      <link href="http://zilkey.com/2008/3/16/rendering-will_paginate-links-without-previous-and-next-buttons" rel="alternate" type="text/html"/>
      <title>Rendering will_paginate links without previous and next buttons</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;I recently needed to build a page that had a paginated list of items.  I immediately did what I always do for pagination and grabbed the &lt;a href=&quot;http://errtheblog.com/posts/56-im-paginating-again&quot;&gt;will_paginate&lt;/a&gt; plugin (also &lt;a href=&quot;http://groups.google.com/group/will_paginate/browse_thread/thread/4cacdeab9aa7d83a&quot;&gt;released as a gem&lt;/a&gt;).  2 lines of code later and pagination was working.&lt;/p&gt;
&lt;p&gt;Then I noticed that the design mockup I had been given did not include room for previous or next buttons, despite the fact that &lt;a href=&quot;http://kurafire.net/log/archive/2007/06/22/pagination-101&quot;&gt;most&lt;/a&gt; &lt;a href=&quot;http://www.smashingmagazine.com/2007/11/16/pagination-gallery-examples-and-good-practices/&quot;&gt;pagination&lt;/a&gt; &lt;a href=&quot;http://developer.yahoo.com/ypatterns/pattern.php?pattern=searchpagination&quot;&gt;guides&lt;/a&gt; recommend using them.  My first thought was to see if setting the :prev_label and :next_label to nil would work, but it still output the html tags and also output the current page number twice - not quite what I wanted.&lt;/p&gt;

&lt;p&gt;My next thought was that I could patch the LinkRenderer - and then I realized that the folks over at err.the_blog already prepared for exactly this scenario by allowing others to write custom link renderers.  Here's how it works:&lt;/p&gt;

&lt;p&gt;In your view, add the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;%= will_paginate @items, :renderer =&amp;gt; 'CustomPaginationRenderer' %&amp;gt;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, drop a file anywhere in your load path (config/initializers, lib directory, a plugin etc...) with the following code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class CustomPaginationRenderer &amp;lt; WillPaginate::LinkRenderer&amp;#x000A;  def to_html&amp;#x000A;    links = @options[:page_links] ? windowed_links : []&amp;#x000A;    html = links.join(@options[:separator])&amp;#x000A;    @options[:container] ? @template.content_tag(:div, html, html_attributes) : html&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This creates a new link renderer that inherits from the default WillPaginate::LinkRenderer and overrides just one method: to_html.  I just copied the to_html from the original renderer and removed the two lines that added the previous and next links.&lt;/p&gt;

&lt;p&gt;Given the complexity of the logic behind rendering links and all of the subtle differences in the way links can be displayed, will_paginate could have been a much more complex plugin.  Instead, it is simple and extremely easy to understand and extend.  I highly recommend that plugin authors and paginators alike check it out.&lt;/p&gt;

      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-03-10:5</id>
      <published>2008-03-10T19:34:00+00:00</published>
      <updated>2008-03-10T19:34:00+00:00</updated>
        <category term="capistrano"/>
        <category term="git"/>
        <category term="railsmachine"/>
      <link href="http://zilkey.com/2008/3/10/setting-up-and-deploying-a-rails-app-to-a-remote-git-server" rel="alternate" type="text/html"/>
      <title>Setting up and deploying a rails app to a remote git server</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;(Updated on 3/31/08 with new &quot;edge&quot; branch checkout)&lt;/p&gt;
&lt;p&gt;In a &lt;a href=&quot;http://zilkey.com/tags/git&quot;&gt;previous post&lt;/a&gt; I wrote about how to get git installed on CentOS.  Now that git is installed it's time to actually set up a remote repository with git.  In this tutorial I'll explain how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a git user on the remote server and configure the new git user correctly&lt;/li&gt;
&lt;li&gt;configure the deploy user correctly&lt;/li&gt;
&lt;li&gt;create a rails app locally and push it to the remote git server&lt;/li&gt;
&lt;li&gt;set up capistrano for deploying the app using git&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Assumptions&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;you deploy under the user &quot;deploy&quot;&lt;/li&gt;
&lt;li&gt;your domain name is example.com&lt;/li&gt;
&lt;li&gt;you have installed git at /usr/local/bin/git&lt;/li&gt;
&lt;li&gt;you have root access (otherwise it might have to skip the part about setting up the git user's shell)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Prepare the remote server&lt;/h2&gt;

&lt;p&gt;First, log into your server as deploy, create the git user and setup your ssh keys.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo adduser git&amp;#x000A;sudo passwd git&amp;#x000A;ssh-keygen -t rsa -f ~/.ssh/id_rsa -C &quot;deploy@example.com&quot;&amp;#x000A;ssh git@example.com 'mkdir ~/.ssh;chmod 700 ~/.ssh'&amp;#x000A;scp ~/.ssh/id_rsa.pub git@example.com:~/.ssh/authorized_keys&amp;#x000A;ssh git@example.com 'chmod 600 ~/.ssh/authorized_keys'&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As a convenience, you may want to add your ssh key to the git user's authorized_keys.  To do so, execute the following on your &lt;em&gt;local&lt;/em&gt; machine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cat ~/.ssh/id_rsa.pub | ssh git@example.com 'sh -c &quot;cat - &amp;gt;&amp;gt;~/.ssh/authorized_keys&quot;'&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(you may have to change this to id_dsa.pub - or whatever your public key name is).&lt;/p&gt;

&lt;p&gt;Back on the server, if you haven't already, you'll also want to set up your git config files for the deploy user:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git config --global user.name &quot;Deploy&quot;&amp;#x000A;git config --global user.email &quot;deploy@example.com&quot;&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That name and username can be anything you want it to be.&lt;/p&gt;

&lt;p&gt;Next, you'll have to add a shell to /etc/shells.  To do so you'll need to be root:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;su -&amp;#x000A;echo '/usr/local/bin/git-shell'&amp;gt;&amp;gt; /etc/shells&amp;#x000A;exit&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To make sure this worked, you can type&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cat /etc/shells&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now log in as the git user and set a few things up:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;su - git&amp;#x000A;git config --global user.name &quot;Git&quot;&amp;#x000A;git config --global user.email &quot;git@example.com&quot;&amp;#x000A;chsh&amp;#x000A;[when prompted - enter /usr/local/bin/git-shell]&amp;#x000A;exit&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Create the app&lt;/h2&gt;

&lt;p&gt;So now you have a git user with a slightly more secure git shell and your ssh keys are set up from both your local machine and your deploy user's account.  It's time to create the rails app on your local machine.  For this to work, you'll need to make sure that you have the capistrano and railsmachine gems installed.  Next create a rails app, capify and railsmachinify it and type a few git commands.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo gem install capistrano railsmachine --no-ri --no-rdoc&amp;#x000A;rails git_import_test&amp;#x000A;cd git_import_test&amp;#x000A;capify .&amp;#x000A;railsmachine --apply-to . --name git_import_test --domain example.com&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now go into config/deploy.rb and add the following task to the bottom of the file (you can also add this to your ~/.caprc file to use it across all of your rails apps):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'fileutils'&amp;#x000A;Capistrano::Configuration.instance(:must_exist).load do&amp;#x000A;&amp;#x000A;  namespace :git do&amp;#x000A;    desc &quot;Setup directory structure and initialize git repository on remote server&quot;&amp;#x000A;    task :setup, :roles =&amp;gt; :scm do&amp;#x000A;      dir =  &quot;#{deploy_to}/git&quot;&amp;#x000A;      run    &quot;mkdir -p #{dir}&quot;&amp;#x000A;      sudo   &quot;chown -R deploy:deploy #{dir}&quot;&amp;#x000A;      run   &quot;cd #{dir} &amp;amp;&amp;amp; git --bare init&quot;&amp;#x000A;      run   &quot;chmod 770 #{dir}&quot;&amp;#x000A;      sudo   &quot;chown -R git:git #{dir}&quot;&amp;#x000A;    end&amp;#x000A;&amp;#x000A;    desc &quot;Import code into remote git repository.&quot;&amp;#x000A;    task :import  do&amp;#x000A;      puts &quot;Initializing local git repository&quot;&amp;#x000A;      system  &quot;git init&quot;&amp;#x000A;&amp;#x000A;      puts &quot;Adding remote server pointing to #{repository}&quot;&amp;#x000A;      system  &quot;git remote add origin #{repository}&quot;&amp;#x000A;&amp;#x000A;      puts &quot;Adding .gitignore file&quot;&amp;#x000A;      system &quot;echo 'log/*'&amp;gt;&amp;gt; .gitignore&quot;&amp;#x000A;      system &quot;echo 'tmp/*'&amp;gt;&amp;gt; .gitignore&quot;&amp;#x000A;      system &quot;echo '.DS_Store'&amp;gt;&amp;gt; .gitignore&quot;&amp;#x000A;      system &quot;echo 'public/cache/**/*'&amp;gt;&amp;gt; .gitignore&quot;&amp;#x000A;      system &quot;git add .gitignore&quot;&amp;#x000A;&amp;#x000A;      puts &quot;Committing application locally&quot;&amp;#x000A;      system &quot;git add *&quot;&amp;#x000A;      system 'git commit -a -v -m &quot;initial import of site&quot;'&amp;#x000A;&amp;#x000A;      puts &quot;Pushing application to the remote server.  The name of the branch is:&quot;&amp;#x000A;      system  &quot;git remote&quot;&amp;#x000A;      system  &quot;git push origin master&quot;&amp;#x000A;      puts &quot;Creating edge branch on remote&quot;&amp;#x000A;      system &quot;git push origin master:refs/heads/edge&quot;&amp;#x000A;      puts &quot;create a local tracking edge branch&quot;&amp;#x000A;      system &quot;git branch --track edge origin/edge&quot;&amp;#x000A;      puts &quot;checking out edge repository&quot;&amp;#x000A;      system &quot;git checkout edge&quot;&amp;#x000A;      puts &quot;git setup complete&quot;&amp;#x000A;      puts &quot;You can clone this repository with git clone #{repository}&quot;&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the local repository&lt;/li&gt;
&lt;li&gt;Add an ignore file&lt;/li&gt;
&lt;li&gt;Add all files&lt;/li&gt;
&lt;li&gt;Push to the master&lt;/li&gt;
&lt;li&gt;Create a new edge branch on the local and remote&lt;/li&gt;
&lt;li&gt;Switch the local branch to edge&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Next, update your deploy.rb file to point to your new git repository and you'll be ready to deploy.  Make sure your deploy.rb file has entries that look like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;set :repository,    &quot;ssh://git@example.com#{deploy_to}/git&quot;&amp;#x000A;set :scm,           :git&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Deploy&lt;/h2&gt;

&lt;p&gt;To deploy you can use the tasks that come with the railsmachine gem, along with your new git capistrano task:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cap deploy:setup&amp;#x000A;cap git:setup&amp;#x000A;cap apache:configure&amp;#x000A;cap mongrel:cluster:configure&amp;#x000A;cap git:import&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you are using mysql, you will have to also run&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cap mysql:setup&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you can deploy, run migrations, start your mongrels and restart your web server with&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cap deploy:cold&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;References:&lt;/h2&gt;

&lt;p&gt;This post heavily references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://support.railsmachine.com/index.php?pg=kb.page&amp;amp;id=12&quot;&gt;https://support.railsmachine.com/index.php?pg=kb.page&amp;amp;id=12&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://toolmantim.com/article/2007/12/5/setting_up_a_new_remote_git_repository&quot;&gt;http://toolmantim.com/article/2007/12/5/setting_up_a_new_remote_git_repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.nanorails.com/git-rails&quot;&gt;http://blog.nanorails.com/git-rails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Related posts:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://jointheconversation.org/railsgit&quot;&gt;http://jointheconversation.org/railsgit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-03-08:3</id>
      <published>2008-03-08T03:40:00+00:00</published>
      <updated>2008-03-08T03:40:00+00:00</updated>
        <category term="bootstrap_your_app"/>
      <link href="http://zilkey.com/2008/3/8/bootstrap-your-app-plug-in-with-sake" rel="alternate" type="text/html"/>
      <title>Bootstrap your app: plug-in with sake</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;This is the first in a series called &quot;Bootstrap Your App&quot; where I'll detail how I've set up my local development environment to get new rails sites up and running quickly using &quot;sake&quot;:http://errtheblog.com/posts/60-sake-bomb and generators.&lt;/p&gt;
&lt;p&gt;I'm a ruby on rails developer who focuses primarily on websites for small-businesses and non-profits, so I build lots of small rails apps from the ground up.  Every time I start a site, I go through a few common tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install all of my favorite plugins&lt;/li&gt;
&lt;li&gt;Run the generators from those plugins (like &lt;a href=&quot;http://rspec.info/&quot;&gt;rspec&lt;/a&gt; and &lt;a href=&quot;http://technoweenie.stikipad.com/plugins/show/Acts+as+Authenticated&quot;&gt;acts_as_authenticated&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Install the hacks, patches and recipes I've gathered along the way, but never bothered to put into plugins&lt;/li&gt;
&lt;li&gt;Set the app's name as the prefix for the &lt;a href=&quot;http://svn.rubyonrails.org/rails/plugins/exception_notification/&quot;&gt;exception notifier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;For some apps, I'll install and configure &lt;a href=&quot;http://svn.rubyonrails.org/rails/plugins/ssl_requirement/&quot;&gt;ssl&lt;/a&gt;, for others I might install and configure &lt;a href=&quot;http://svn.rubyonrails.org/rails/plugins/account_location/&quot;&gt;account_location&lt;/a&gt;.  Some of my clients deploy to &lt;a href=&quot;http://www.railsmachine.com&quot;&gt;RailsMachine&lt;/a&gt;, and some to &lt;a href=&quot;http://www.slicehost.com/&quot;&gt;Slicehost&lt;/a&gt;.  Some require email notification for user registrations, others don't.  Some use SVN, others git.&lt;/p&gt;

&lt;p&gt;When I started to think about all of the different configurations that a client might need, I quickly realized that approaches like Courtenay's &lt;a href=&quot;http://blog.caboo.se/articles/2007/4/21/sample-rails-app-branch-with-ssl&quot;&gt;sample rails apps&lt;/a&gt; didn't quite fit for me.  I needed a way to quickly add features to a site, and have complete customization.&lt;/p&gt;

&lt;h2&gt;Grok Sake&lt;/h2&gt;

&lt;p&gt;After going through several different setups, I've settled into using rake tasks (through sake) and building generators.  Sake and generators are both simple and portable and when used correctly can give you massive productivity gains.&lt;/p&gt;

&lt;p&gt;If you haven't checked out &lt;a href=&quot;http://errtheblog.com/posts/60-sake-bomb&quot;&gt;sake&lt;/a&gt; do so immediately.  Do not pass go.  Do not collect $200.  Sake allows you share rake tasks across different sites (and even different machines).  Sake makes bootstrapping your apps very easy.&lt;/p&gt;

&lt;h2&gt;Plug-In&lt;/h2&gt;

&lt;p&gt;So the first task on my list above was to install common plugins.  I went through my apps and identified the ones I used most commonly, and split them up into roughly four categories: common, speccing, ecommerce and cms (content management system) - your mileage will vary.  Then I wrote a quick rake task that would install the plugins using the standard rails script/plugin install, or using piston import.  The rake file looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;namespace :zilkey do&amp;#x000A;  # A set of tasks that add plugins to the app either using script/plugin or piston&amp;#x000A;  namespace :plugins do&amp;#x000A;    desc &quot;installs common plugins via piston or rails.  Options:\n  * set=[common|ecommerce|speccing|cms] (required)\n  * method=[piston|rails]\n  * pretend=[true|false]&quot;&amp;#x000A;    task :install do&amp;#x000A;      sets = {&amp;#x000A;        :speccing =&amp;gt; {&amp;#x000A;          :rspec =&amp;gt; &quot;svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec&quot;,&amp;#x000A;          :rspec_on_rails =&amp;gt; &quot;svn://rubyforge.org/var/svn/rspec/tags/CURRENT/rspec_on_rails&quot;,&amp;#x000A;          :spider_test =&amp;gt; &quot;svn://caboo.se/plugins/court3nay/spider_test&quot;,&amp;#x000A;        },&amp;#x000A;        :common =&amp;gt; {&amp;#x000A;          :attachment_fu =&amp;gt; &quot;http://svn.techno-weenie.net/projects/plugins/attachment_fu/&quot;,&amp;#x000A;          :will_paginate =&amp;gt; &quot;svn://errtheblog.com/svn/plugins/will_paginate&quot;,&amp;#x000A;          :geokit =&amp;gt;&quot;svn://rubyforge.org/var/svn/geokit/trunk&quot;,&amp;#x000A;          :annotate_models =&amp;gt; &quot;http://repo.pragprog.com/svn/Public/plugins/annotate_models/&quot;,&amp;#x000A;          :exception_notification =&amp;gt; &quot;http://dev.rubyonrails.org/svn/rails/plugins/exception_notification/&quot;,&amp;#x000A;          :tztime =&amp;gt; &quot;http://dev.rubyonrails.com/svn/rails/plugins/tztime/&quot;,&amp;#x000A;          :tzinfo_timezone =&amp;gt; &quot;http://dev.rubyonrails.org/svn/rails/plugins/tzinfo_timezone/&quot;,&amp;#x000A;          :atom_feed_helper =&amp;gt; &quot;http://svn.rubyonrails.org/rails/plugins/atom_feed_helper/&quot;,&amp;#x000A;          :action_mailer_tls =&amp;gt; &quot;http://svn.nanorails.com/plugins/action_mailer_tls/&quot;,&amp;#x000A;          :acts_as_authenticated =&amp;gt; &quot;http://svn.techno-weenie.net/projects/plugins/acts_as_authenticated/&quot;,&amp;#x000A;        },&amp;#x000A;        :ecommerce =&amp;gt; {&amp;#x000A;          :active_merchant =&amp;gt; &quot;http://activemerchant.googlecode.com/svn/trunk/active_merchant&quot;,&amp;#x000A;          :ssl_requirement =&amp;gt; &quot;http://svn.rubyonrails.org/rails/plugins/ssl_requirement/&quot;,&amp;#x000A;        },&amp;#x000A;        :cms =&amp;gt; {&amp;#x000A;          :fckeditor =&amp;gt; &quot;svn://rubyforge.org/var/svn/fckeditorp/trunk/fckeditor&quot;,&amp;#x000A;          :better_nested_set =&amp;gt;&quot;svn://rubyforge.org/var/svn/betternestedset/trunk&quot;,&amp;#x000A;          :acts_as_ferret =&amp;gt;&quot;svn://projects.jkraemer.net/acts_as_ferret/tags/stable/acts_as_ferret&quot;,&amp;#x000A;          :us_states =&amp;gt;&quot;http://svn.techno-weenie.net/projects/plugins/us_states/&quot;,&amp;#x000A;        },&amp;#x000A;      }&amp;#x000A;&amp;#x000A;      raise &quot;You must specify set=[#{sets.keys.join(&quot;,&quot;)}]&quot; unless sets.keys.map{|k|k.to_s}.include?(ENV['set'])&amp;#x000A;&amp;#x000A;      set = sets[ENV['set'].to_sym]&amp;#x000A;      plugins = set.to_a.map&amp;#x000A;      text = set.to_a.map{|a|a.first}.join(&quot;\n  * &quot;)&amp;#x000A;      run_method = ENV['pretend'] == &quot;true&quot; ? :p : :system&amp;#x000A;      install_method = (ENV['method'] || &quot;rails&quot;).to_sym&amp;#x000A;&amp;#x000A;      set.each do |name,url|&amp;#x000A;        if install_method == :piston&amp;#x000A;          send run_method, &quot;svn up&quot;&amp;#x000A;          send run_method, &quot;piston import #{url} vendor/plugins/#{name}&quot;&amp;#x000A;        elsif install_method == :external&amp;#x000A;          send run_method, &quot;script/plugin install #{url} -x&quot;&amp;#x000A;        else&amp;#x000A;          send run_method, &quot;script/plugin install #{url} --force&quot;&amp;#x000A;        end&amp;#x000A;      end&amp;#x000A;      p &quot;don't forget to run svn commit -m \&quot;Added #{text} plugins to vendor/plugins via piston\&quot;&quot; if run_method == :piston&amp;#x000A;    end&amp;#x000A;  end&amp;#x000A;&amp;#x000A;end&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's not the most beautiful code I've ever written, but it works.  The bulk of the task is just the hash that stores all of the names and urls of the plugins, grouped into the categories that I find most useful right now.  To run it, I use commands like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$rake zilkey:plugins:install set=common #=&amp;gt; installs using script/plugin install&amp;#x000A;$rake zilkey:plugins:install set=common method=external # =&amp;gt; installas using script/plugin install -x&amp;#x000A;$rake zilkey:plugins:install set=common method=piston # =&amp;gt; installs via piston import&amp;#x000A;$rake zilkey:plugins:install set=common pretend=true # =&amp;gt; echos the commands to the screen, but doesn't execute them&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Great - so the task is written and working.  To get it up and running with sake I just typed:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$sake -i lib/tasks/zilkey.rake zilkey:plugins:install&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now I can install these plugins to any app on my machine (or any other machine, for that matter: Google sake for more info on how to share tasks).  Let's try it out:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$rails newsite &amp;amp;&amp;amp; cd newsite&amp;#x000A;$sake zilkey:plugins:install set=speccing&amp;#x000A;$ls vendor/plugins&amp;#x000A;rspec rspec_on_rails spider_test&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There you have it - one common task down.  In the next article I'll discuss how to take advantage of custom generators to productize your apps.&lt;/p&gt;


      </content>
    </entry>
    <entry xml:base="http://zilkey.com/">
      <author>
        <name>Jeff Dean</name>
      </author>
      <id>tag:zilkey.com,2008-03-05:1</id>
      <published>2008-03-05T00:05:00+00:00</published>
      <updated>2008-03-05T00:05:00+00:00</updated>
        <category term="git"/>
        <category term="railsmachine"/>
        <category term="sysadmin"/>
      <link href="http://zilkey.com/2008/3/5/installing-git-on-railsmachine-centos-4-4" rel="alternate" type="text/html"/>
      <title>Installing Git on Railsmachine (CentOS 4.4)</title>
      <content type="html">
        
&lt;div class='alert-message block-message error'&gt;This blog post was written in 2008.  Information and links in this post may be outdated.&lt;/div&gt;
&lt;p&gt;[updated 03/09/2008]&lt;/p&gt;

&lt;p&gt;[updated 03/21/2008 for Git 1.5.4.4]&lt;/p&gt;

&lt;p&gt;[you can also &lt;a href=&quot;http://wiki.railsmachine.com/wiki/show/InstallingGit&quot;&gt;follow the RailsMachine wiki instructions&lt;/a&gt; ]&lt;/p&gt;

&lt;h3&gt;Keeping up with the Joneses&lt;/h3&gt;
&lt;p&gt;A few days ago I read that &lt;a href=&quot;http://www.caboo.se/articles/2008/2/3/mephisto-0-8&quot;&gt;mephisto 0.8 was released&lt;/a&gt; and that all of the cool kids were grabbing it from the &lt;a href=&quot;http://git.or.cz/&quot;&gt;git&lt;/a&gt; repository.    I promptly got hip to git from John Nunemaker's post on &lt;a href=&quot;http://railstips.org/2008/2/16/git-and-github/&quot;&gt;git and github&lt;/a&gt; and grabbed the &lt;a href=&quot;http://peepcode.com/products/git&quot;&gt;peepcode screencast&lt;/a&gt; to get started.&lt;/p&gt;
&lt;p&gt;A few minutes later I was ready to deploy to my &lt;a href=&quot;http://railsmachine.com/&quot;&gt;Rails Machine&lt;/a&gt; slice running CentOS 4.4.&lt;/p&gt;

&lt;h3&gt;Not so yummy&lt;/h3&gt;

&lt;p&gt;I dig package managers, and since git is really a collection of a few dozen executables it's the perfect candidate.  So I naively typed&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;yum install git&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;thinking that it might &quot;just work&quot;, but instead it completely blew up on me with errors such as:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Error: Missing Dependency: libaprutil-0.so.0 is needed by package subversion&amp;#x000A;Error: Missing Dependency: libapr-0.so.0 is needed by package subversion&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After a few support emails with Rails Machine, it seemed like there might be some discrepancies with the rpms from the RailsMachine repo and the rpmforge repo.  Not being familiar with yum, I had no idea what to do (and still don't), so I abandoned yum altogether.&lt;/p&gt;

&lt;h3&gt;Consider the source&lt;/h3&gt;

&lt;p&gt;Thanks to Rob over at Rails Machine and cactus over at &lt;a href=&quot;http://forum.slicehost.com/comments.php?DiscussionID=1379/&quot;&gt;the slicehost forum&lt;/a&gt; I got git installed from source on the slice.  Here's what worked for me:&lt;/p&gt;

&lt;p&gt;Follow the &lt;a href=&quot;http://danielinsley.com/2008/2/21/installing-git-on-centos-4-6&quot;&gt;instructions from Dan Insley&lt;/a&gt; and setup the following repositories:&lt;/p&gt;

&lt;p&gt;to follow the steps below and git will be installed in a snap:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sudo touch /etc/yum.repos.d/atrpms.repo /etc/yum.repos.d/dag.repo&amp;#x000A;&amp;#x000A;[atrpms]&amp;#x000A;name=ATrpms for Enterprise Linux $releasever - $basearch&amp;#x000A;baseurl=http://dl.atrpms.net/el$releasever-$basearch/atrpms/stable&amp;#x000A;enabled=0&amp;#x000A;gpgcheck=1&amp;#x000A;gpgkey=http://ATrpms.net/RPM-GPG-KEY.atrpms&amp;#x000A;&amp;#x000A;[dag]&amp;#x000A;name=Dag&amp;#x000A;enabled=0&amp;#x000A;baseurl=http://dag.freshrpms.net/redhat/el4/en/$basearch/dag&amp;#x000A;http://ftp.heanet.ie/pub/freshrpms/pub/dag/redhat/el4/en/i386/dag/&amp;#x000A;gpgcheck=1&amp;#x000A;gpgkey=http://dag.wieers.com/packages/RPM-GPG-KEY.dag.txt&amp;#x000A;&amp;#x000A;sudo yum --enablerepo=dag --enablerepo=atrpms install asciidoc xmlto curl curl-devel&amp;#x000A;mkdir -p ~/sources&amp;#x000A;cd ~/sources&amp;#x000A;wget http://kernel.org/pub/software/scm/git/git-1.5.4.4.tar.gz&amp;#x000A;tar xvzf git-1.5.4.4.tar.gz&amp;#x000A;cd git-1.5.4.4&amp;#x000A;make configure&amp;#x000A;./configure --prefix=/usr/local&amp;#x000A;NO_TCLTK=yes make all doc&amp;#x000A;NO_TCLTK=yes sudo make install install-doc&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can also save yourself some time if you don't need the docs by changing those last 2 lines to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;NO_TCLTK=yes make all&amp;#x000A;NO_TCLTK=yes sudo make install&amp;#x000A;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Depending on your setup, the &quot;NO_TCLTK=yes&quot; flag might not be necessary.  You can always try it without first if you are unsure, but I've needed it on all of my RailsMachine slices.&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://git.or.cz/&quot;&gt;http://git.or.cz/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://peepcode.com/products/git&quot;&gt;http://peepcode.com/products/git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://railstips.org/2008/2/16/git-and-github/&quot;&gt;http://railstips.org/2008/2/16/git-and-github/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://danielinsley.com/2008/2/21/installing-git-on-centos-4-6&quot;&gt;http://danielinsley.com/2008/2/21/installing-git-on-centos-4-6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://forum.slicehost.com/comments.php?DiscussionID=1379&quot;&gt;http://forum.slicehost.com/comments.php?DiscussionID=1379&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
</feed>
