Setting up Ubuntu for Ruby on Rails development

NOTE: This is a work-in-progress. I’m publishing it early so that Sam can use it. If it doesn’t work for you exactly as expected, well, that’s to be expected.

Every time I setup a new Ubuntu machine, I have to search around to come up with a procedure for getting everything I need to develop Ruby on Rails apps. Here it is pre-packaged.

(The configuration below is based on my current preferences: Ubuntu 9.04, ruby 1.8, rails 2.3.2, mysql 5, subversion plugin for Eclipse (subclipse), Eclipse with Aptana and RadRails. Ruby 1.9 is starting to gain momentum, but I haven’t used it yet. Oh, and most of the instructions below are for command line users. Try it. You’ll like it.)

After installing Ubuntu 9.04 (which is not covered here), install Eclipse, subversion, ruby and mysql like so:

sudo apt-get install eclipse ruby libmysql-ruby mysql-client mysql-server irb
mongrel rdoc ri ruby-dev rubygems subversion

The first thing to note is that while Eclipse works with the out-of-the box java installed with Ubuntu 9.04, Aptana does not. So install the Sun Java JRE like so:

sudo apt-get install sun-java6-jre

Then, configure the system to use the new java by default:

sudo update-alternatives --config java

With Eclipse, you can’t rely on the command above to make it use the Sun Java. So, edit the Eclipse configuration file:

sudo nano /etc/eclipse/java_home

Comment out the java-gcj line and add a line for sun just after it:

#/usr/lib/jvm/java-gcj
/usr/lib/jvm/java-6-sun

Fire up Eclipse from the Applications menu. (Applications -> Programming -> Eclipse). Then, install the Aptana plugin. Within Eclipse, click on Help -> Software Updates -> Find and Install -> Search for New Features to Install. Add a new Remote Site for updates with the following URL:

http://update.aptana.com/update/studio/3.2/

Make sure it is checked, then click Finish. Eclipse should then walk through the Aptana installation. After restarting, you’ll see the Aptana welcome screen. Look for the link which will install RadRails. Once you’ve done that, it is time to install subclipse–the subversion plugin for Eclipse. Go to Help -> Software Updates -> Find and Install -> Search for New Features and create a new remote site with the following URL:

http://subclipse.tigris.org/update

Go through the same steps from the previous step to add the SVN client.

That’s it for the installation. You’ll probably want to customize some settings in Eclipse. Make sure the settings for the ruby interpreter, rake, mongrel, etc.  are accurate. I like to turn line numbers on in the Text Editor preferences.

Your project may require one or more “gems” for ruby. Most can be installed with a command like this:

sudo gem install [gem-name]

Of course, you haven’t even installed rails yet. So do that:

sudo gem install rails

One gem you’ll need is rake. The rake package from the Ubuntu repository is old. Get the new one and then symlink it to make it more accessible:

sudo gem install rake
sudo ln -s /var/lib/gems/1.8/gems/rake-0.8.4/bin/rake /usr/bin/rake

And, it isn’t a bad idea to put the gems’ bin folder in the system path so things like autospec work correctly. To do so, create this file:

sudo nano /etc/profile.d/gems.sh
# Here's the one-line for the file:
export PATH=$PATH:/var/lib/gems/1.8/bin

Reboot or just logout to trigger it. This wordpress editor is super annoying, so I’m done now.

Rain8net Ruby Library v1.0.0

I previously wrote about a product called Rain8net. I’m happy to announce the initial release of my Rain8net ruby library. The library is now available from rubyforge.org.

Or, just install it like this:

gem install rain8net

Now you won’t have to worry about sending the specific codes I detailed in my last post. Controlling your sprinklers is as easy as:

r8 = Rain8net.new
r8.turn_on_zone(1)
r8.turn_off_zone(1)

See the online documentation at the project homepage for more information.

Build a Development Infrastructure…or buy a Mac

Disclaimer: Let me start out by saying I am not anti-Mac. That being said, I am very pro-Linux. I am not a Dell lover either. I just picked them for comparison.

As a web developer, I have found it extremely beneficial to have access to my own servers for development purposes. I have my own infrastructure for version control. I maintain my own DNS servers and can quickly provide access to a temporary site for clients.  While developing TheBigFork, I developed several long-running data cleanup scripts. These would not have been possible without an always-on server to do the work.

Great. So, what does that have to do with buying a mac? Nothing, really. I recently have become aware that more and more web developers (especially Ruby developers) are developing on a Mac. Frankly, I don’t get it. I’ve tried it, and it doesn’t come close to developing on Linux. But, that’s really a discussion for another day. This article is about price.

I compared the price of a MacBook Pro 17″ 2.5 gHz notebook with a collection of Dell machines. The MacBook is listed at $2799. For that much money, you could buy:

Seven Dell Vostro 200 desktops with a 20″ widescreen display and a 250 gig hard drive. These come with Windows XP, but you can easily reformat it and install Ubuntu or Fedora. Doing so opens up a whole world of freely available open-source software. (And, don’t argue that Linux isn’t ready for the desktop. If you can switch from Windows to Mac, you can easily make the switch to Linux.)

With 7 computers, you can hire a receptionist, bookkeeper and project manager and still have a few extra machines for some junior developers. Granted, these aren’t the most powerful machines out there and they probably aren’t worthy of acting as a server–unless you’re desperate.

Here’s another scenario. Buy a Dell PowerEdge 840 server plus a Dell Precision T4300 workstation (which alone rivals the macbook’s specs). The only problem is, you’ll have almost $1400 extra. I guess you’ll have to buy a Dell Latitude D830 laptop for working on-the-go, and a second 21″ monitor for your workstation. Wait–you still have extra money. So, grab a Vostro 200 from the previous scenario. Now you have four machines: a server, a high-powered 64-bit workstation, a nice notebook and an extra desktop for your assistant.

If you really need a laptop with more horsepower than the MacBook, just get the Precision M6300 mobile workstation instead of the workstation and notebook above. You’ll still have enough for a server and the spare desktop.

Finally, if computers aren’t your thing, put a down-payment on a nice new car!

Bottom line: Macs are very nice. To me, they are simply not worth the extra cash–especially when you have work to get done.

In case you’re wondering, the best Ruby IDE that I’ve found is Aptana Radrails which runs poorly on Mac and excellent on Linux. Textmate is the Ruby IDE of choice on Mac. I found it lacking in features and difficult to use.

Subversion with Active Directory Authentication via Apache

Laura–skip this one.

I recently started using Subversion at work. It has been popular enough, that several other employees have found a need for it. So, I decided I’d better figure out Active Directory authentication so I don’t end up maintaining a separate set of passwords for everyone.  It took quite a bit of trial-and-error. Here’s my Apache config for the subversion site:

<VirtualHost *:80>
ServerName svn.domain.com
DocumentRoot /var/svn/www
<Location /repos/>
DAV svn
SVNParentPath /var/svn/repos
SVNListParentPath on
AuthzSVNAccessFile /var/svn/svnaccess
AuthType Basic
AuthName "SVN Server"
AuthBasicProvider ldap
AuthzLDAPAuthoritative Off
AuthLDAPBindDN "DOMAIN\administrator"
AuthLDAPBindPassword password_for_administrator
AuthLDAPURL ldap://domain_controller:389/dc=ad,dc=domain,dc=com?sAMAccountName?sub?(objectClass=*)
Require valid-user
</Location>
</VirtualHost>

No, it is not a good idea to use your domain administrator in the config above. Do it for testing. Then, replace it with an account with read-only access in your domain.

Many of the examples on the web were geared towards non-Active Directory implementations. Those that were specific for AD still didn’t work until I removed the “cn=Users” from the first part of the ldapurl. Our users are not all part of the “Users” group. Removing this from the string means that all AD accounts can login. So, then I turned to the AuthzSVNAccessFile to fine-tune the access to the various repositories. Here is an example of that file:

[groups]
it=username1,username2
engineers=username3,username4

[:/]
*=r

[/]
*=r

[intranet:/]
@it = rw

[helpdesk:/]
@it = rw

[product_development:/]
@it = r
@engineers=rw

Have fun.

Things I commonly do when starting a Rails project

Here’s another post from me for me. When starting a new Ruby on Rails project, I tend to do several things the same each time.

1. Setup SVN repository. Most of my projects are completely unrelated, so I usually have to setup a separate repository. The best walkthrough I’ve found is here.

2. Create development/test databases in mysql:

create database project_development;
create database project_test;

3. Setup ActiveRecord sessions:

rake db:sessions:create
rake db:migrate

(uncomment line in config/environment.rb)

config.action_controller.session_store = :active_record_store

4. Setup initial controller…something like:

script/generate controller Main index

And make it the default route (edit the file config/routes.rb)

# Install the default route as the lowest priority.
map.connect ':controller/:action/:id', :controller => 'main'

(Don’t forget to delete or rename public/index.html)

5. I almost always need user accounts. The ActsAsAuthenticated plugin works well:

script/plugin install http://svn.techno-weenie.net/projects/plugins/acts_as_authenticated
script/generate authenticated user account
rake db:migrate

Look in the newly created app/controller/account_controller.rb for a few things to move to app/controller/application.rb

6. While editing app/controller/application.rb, here are a bunch of date/time formatting shortcuts I use a lot:
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(
:date_time12 => "%m/%d/%Y %I:%M%p",
:regular => "%b %d %I:%M %p",
:date_time24 => "%m/%d/%Y %H:%M",
:pretty_date => "%B %d, %Y",
:month_year => "%B %Y",
:short_month_year => "%b %Y",
:month_day => "%b %d",
:pretty_date_time => "%B %d, %Y %I:%M %p",
:short_date => "%b %d, %Y",
:short_12 => "%b %d %I:%M %p",
:standard => "%m/%d/%Y"
)
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
:pretty_date => "%B %d, %Y",
:month_year => "%B %Y",
:short_month_year => "%b %Y",
:year_only => "%Y",
:month_day => "%b %d",
:short_date => "%b %d, %Y",
:standard => "%m/%d/%Y"
)

Just add all of that to the bottom of application.rb, then use like this: object.created_at.to_s(:standard)

7. Other essential plugins include:

CalendarDateSelect

script/plugin install http://calendardateselect.googlecode.com/svn/tags/calendar_date_select

FileColumn

script/plugin install http://opensvn.csie.org/rails_file_column/plugins/file_column/trunk
mv vendor/plugins/trunk vendor/plugins/filecolumn

ValidatesEmailVeracityOf

script/plugin install http://svn.savvica.com/public/plugins/validates_email_veracity_of

DynamicSessionExp

script/plugin install http://svn.codahale.com/dynamic_session_exp/trunk
mv vendor/plugins/trunk vendor/plugins/dynamic_session_exp

8. Create a default layout for all controllers: app/views/layouts/application.rhtml (If you have a controller needing its own layout, just create one called, “controller.rhtml”) Here is a basic application.rhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><%= @page_title || 'Site Name' %></title>
<%= stylesheet_link_tag 'all', :media=>'all' %>
<%= stylesheet_link_tag 'print', :media=>'print' %>
<%= javascript_include_tag :defaults %>
<%= calendar_date_select_includes %>
<!--[if lt IE 7]>
<script defer src="/javascripts/pngfix.js" type="text/javascript"></script>
<![endif]-->
</head>
<body>
<% if flash[:notice] -%>
<div class="flash_notice" id="Flash"><%= flash[:notice] %></div>
<% end -%>
<% if flash[:alert] -%>
<div class="flash_alert" id="Flash"><%= flash[:alert] %></div>
<% end -%>
<%= yield %>
</body>
</html>

9. Default public stuff (pngfix.js, latest scriptaculous, stylesheets, images, etc.)

Hostmonster on Rails

A client of mine recently moved their web hosting to Hostmonster, and I was tasked with migrating a Ruby on Rails application to the new server. I ran into a few snags.

First of all, I followed their tutorial.

The default rails welcome page worked fine, but I kept getting “Application Error” on everything else. The logs showed nothing. I found several message board posts pointing to the permissions on the “public” folder.  Dr. Chuck had the best article on the subject.

Dr. Chuck’s instructions fixed the problem for a month or two.  Then it stopped working again. I figured some server-side process had changed the permissions on my files. So, I set it up again. It still didn’t work.

I actually had to contact tech support. A couple of days later they responded with the solution (thankfully, this isn’t a mission-critical app). Hostmonster had recently updated to a newer version of Rails. So, I just commented out the version in the rails environment file:

#config/environment.rb
#...
#RAILS_GEM_VERSION = '1.2.5' unless defined? RAILS_GEM_VERSION

I believe that tells the application to use whatever version of rails it can find (the most recent version on the server). Since version 1.2.5 had apparently been replaced with 1.2.6, removing the declaration for 1.2.5 fixed the problem.

The part that still confuses me is, I had tried generating the application from scratch while logged into the Hostmonster server (via ssh). Doing so still generated an environment.rb file for version 1.2.5.

Of course, the other part that confuses me is why did 1.2.5 stop working?  When I update my own servers with ‘gem update’, I keep old versions available for backwards compatibility of existing rails apps. As far as I know, there is no problem doing it this way. Am I wrong?

Converting DivX files to mpegs for an NTSC DVD

Problem:

You have a bunch of DivX avi files and want to put them on a DVD for a friend. The friend wants to play the final product on a DVD player (not on a PC).

Solution:

Believe it or not, my first inclination was to fire up my old Windows XP computer because it has all of the Sony programs on it–including DVD Architect. To make a long story short, Windows proved itself once again as an inferior system. The DivX files have to be converted to mpeg. I tried several converters which ran for hours at a time. I finally had 3 converted files ready to burn. I thought everything worked fine until I played the DVD on a machine. It looked horrible and skipped a lot…unacceptable.

Fortunately I have Linux which helped produce a much better final product. My originals were all 16:9 widescreen, but I wanted to make them work on a 4:3 system. After installing ffmpeg (on Fedora, do this: sudo yum install ffmpeg -y), here is the command I used to do the conversion:

ffmpeg -i originalfile.avi -target ntsc-dvd -aspect 4:3 -s 720x400 -padtop 40 -padbottom 40 outputfile.mpg

Looks hairy, but it works well. I discovered the actual size of my original files are 720×400 by looking at the output of ffmpeg. So I kept this size and padded 40 to the top and bottom to create the letterbox. For reference, here some sample output from ffmpeg:


Seems stream 0 codec frame rate differs from container frame rate: 30000.00 (30000/1) -> 23.98 (24000/1001)
Input #0, avi, from 'filename.avi':
Duration: 00:44:12.1, start: 0.000000, bitrate: 1239 kb/s
Stream #0.0: Video: mpeg4, yuv420p, 720x400, 23.98 fps(r)
Stream #0.1: Audio: mp3, 44100 Hz, stereo, 112 kb/s
Output #0, dvd, to 'filename.mpg':
Stream #0.0: Video: mpeg2video, yuv420p, 720x480, q=2-31, 6000 kb/s, 29.97 fps(c)
Stream #0.1: Audio: ac3, 48000 Hz, stereo, 448 kb/s
Stream mapping:
Stream #0.0 -> #0.0
Stream #0.1 -> #0.1
Press [q] to stop encoding

Then I decided to wrap it in a little perl script so I could do all 24 of the files without sitting around watching the progress:


$dir = '/home/adam/';
chdir($dir);
opendir(DIR, $dir);
@files = readdir(DIR);
closedir DIR;


foreach (@files) {
next unless $_ =~ m/^([\w\.]*)\.avi$/i;
# This should all be on one line...
print `ffmpeg -i $_ -target ntsc-dvd -aspect 4:3 -s 720x400 -padtop 40 -padbottom 40 -y $1.mpg`;
}

As usual, this is mostly for my reference, but maybe somebody else will also find it useful.