Sunday, August 15, 2010

Helper Testing in Rails 3

This post shows how to test both simple and complicated testers in Rails 3. The method outlined here should work in Rails 2.3 as well, but it might not be the best method.

Summary
The skeleton helper testcase class generated by Rails 3 inherits from ActionView::TestCase. Simple helpers can be tested by comparing their return value with a golden string. For more complex helpers, use render :text to fill the view state with the helper return value, then use assert_select. assert_tag doesn't work.

Simple Helpers
Helpers that produce a single tag, or a string, are probably best tested simply by comparing their output with an expected value. Helpers can be called directly from the test class, as inheriting from ActionView::TestCase will set up an appropriate test environment, which includes a mock controller if necessary.

The code listing below demonstrates this.


Complex Helpers
To me, canonical example of a complex helper is one that includes a call to form_tag or form_for. These methods produce a div inside the form tag, and the contents of the div has been very brittle during Rails 3 development, mostly due to the snowman debate. A test shouldn't depend on the div contents, if all that matters is that there's a form in the helper's output.

This post suggests that at some point, Rails had an assert_tag_in method, which works similarly to assert_select, but takes a string input instead of testing against the current view output. Unfortunately, the method isn't available in Rails 3.

However, ActionView::TestCase provides a render method, which can be used to create fake view output. assert_select checks for the fake view output, but assert_tag doesn't, so your tests will have to stick to the former method.

The code listing below demonstrates this approach.


Motivation
I wrote this post because I wanted to properly test a helper with a form tag. I spent a few hours sifting through blog posts, RDocs, LightHouse tickets and GitHub commits. I hope this post helps other developers avoid duplicating my effort.

Conclusion
Rails 3 makes testing helpers easy, as long as you know what methods to call. I hope you found my post useful, and I'm looking forward to your comments and suggestions!

Monday, August 9, 2010

Replicating GitHub's Repository Serving Setup

This post describes my approach to replicating GitHub's awesome setup for hosting SSH repositories. GitHub's engineers already outlined the main ideas in their blog, so this post fills in the details by going over all the little roadblocks that I've stumbled on, and explaining the solutions I adopted for each of them.

Assumptions
GitHub allows you to create a git repository via a Web interface, then interact with the repository using a few protocols. I focused on git+ssh, because it's the only protocol that allows pushing to repositories.

I assume that the Git server is a Linux box, and I tested my work on Ubuntu. The setup will most likely require changes for MacOS. I use the already-available infrastructure, such as git and ssh. On Debian, you should have the git-core and openssh-server packages installed.

I assume you're not willing to change the OpenSSH server configuration, as you want to stick to the secure defaults on your production infrastructure.

My Web application is written in Ruby on Rails, and it uses the grit gem, also written by the GitHub engineers. While the code is specific to this technology, most of the article is relevant outside the Rails world.

To avoid edge cases in putting together shell commands, I assume repositories and Web users have very sane names (only letters, digits, underscores).

The code for this article is available here (GitHub). All the code links in this article reference a particular tree, so that the code would match the writing. Future revisions are available in the same repository, but they may be more optimized and harder to follow.

Big Picture
A GitHub blog post describes their SSH serving architecture (section Tracing a SSH Request). Scaling aside, these are the main components:
  • the server has a git user; git pushes and pulls get processed under that user account
  • authorized_keys (the sshd configuration) for the git user contains all the public SSH keys in the Web application (GitHub modified openssh to query a database, since their authorized_keys would have been huge)
  • authorized_keys also sets up sshd to run a restricted shell instead of the user's normal login shell (git provides git-shell for this purpose), so users can't use the git account to get shell access on the server
  • sshd is not pointed to git-shell directly; instead, a proprietary wrapper checks that the SSH key's owner is allowed to access the repository and, if the operation succeeds, flushes or updates any Web application caches associated with the modified repository
The Git User
I use this script to set up the git user. The script accepts any user name, but to keep it simple, I'll use git in this article. The script's effects are undone by this script, which I won't cover here.

I don't expect that the Web application will run under the git account, so instead I set the git user's primary group to be the same as a group on the Web application's account. I assume that the Web application will run under its own user, and nothing else will use that user and group. It's also possible to create a git group, and add the Web application to it. What matters is that the Web application can write to the git user's home directory.

The git repositories are stored in the repos directory, under git's homedir. The repos directory must be writable by the Web application, so its permission bits are 770 (rwxrwx---).

Recent versions of sshd are very strict with the permissions on authorized_keys. I appease them by setting the git home directory bits to 750 (rwxr-x---), setting .ssh's bits to 700 (rwx------), and setting .ssh/authorized_keys bits to 600 (rw-------). So the Web application will not be able to change authorized_keys directly, which is a problem, since I'd like to manage authorized SSH keys in the Web application.

To compensate for the issue above, I create an install_keys setuid executable in git's homedir that overwrites authorized_keys with the contents of repos/.ssh_keys. Due to the setup above, only the Web application should be able to write to this file. Furthermore, install_keys's bits are 750 (rwxr-x---), so it can only be run by the Web application.

I encountered two more minor but annoying issues. install_keys cannot be a script, because the setuid flag doesn't work for scripts with shebang lines. My setup script writes out a C program and runs gcc to obtain a binary. The setuid bit is lost when a file is chowned, so chmod must be called after chown.

SSH Configuration and Integration
authorized_key has one line for each key. The code that generates the lines is here. I used all the options I could find to lock down git's shell. The command line points to a shell script in my Rails application, and passes the key's ID as an argument, so I can identify the Web application user.

My keys are standard Rails models, and I use the after_save and after_destroy ActiveRecord callbacks to keep authorized_keys in sync with the database. More specifically, for each key addition or modification, I re-generate the contents of authorized_keys, write it to /home/git/repos/.ssh_keys and then run install_keys to get the actual file modified. If response time becomes an issue, I can move this process to a background work queue.

git's restricted shell script (stub here, real code here, test here) is stored in my Rails application, for my development convenience. Once the implementation is crystallized, it could be moved in git's home directory, so the git user doesn't need read access to the Web application's directory. The script performs the following steps:
  • parses the command line to extract the git command to be executed, the repository path, and the key ID
  • checks that the git command line matches the list of allowed commands, and determines whether the command does a pull or a push
  • issues a HTTP request against the Web application to check whether the SSH key's owner has permission to pull or push to the repository
  • runs the git command, prefixing the repository path with repos/ 
  • if the command succeeds, pings the Web application so it can update its caches
  • relays the git command's exit code

Web Application and Testing Considerations
An application user is modeled by a Profile, and a git repo is represented by a Repository. Both models use ActiveRecord callbacks to synchronize on-disk state: profiles correspond to directories under repos/ (sync code here) and repositories correspond to bare Git repositories created by grit (sync code here). The callbacks are slightly more complicated than for SSH keys, because I need the old name when a profile or repository's name changes, in order to rename the corresponding directory.

Unit tests stub out the methods that compute the path to repos/ (code here and here), so they can run without a full setup. This is desirable so I can get CI running later. I also have a giant integration test (code) that runs all the code described in this article. Since it creates a new user, it requires sudo access, which makes it unlikely that I'll ever be able to set up CI for it.

A particularly challenging for the integration test was pointing git to a SSH key to be used when pushing and pulling. Git doesn't take a command-line argument, and the easiest solution I found was to override the ssh command used by git with a custom wrapper (wrapper-generating code) that contains the options I need to point to a custom key. Overriding is achieved by setting the GIT_SSH environment variable (code). Also, the permission bits on the key file must be set to 600 (rw-------), otherwise ssh will ignore the key.

Motivation
I find GitHub's setup to be awesome, and I always wanted to have my own server implementation to play with. Since I read the team's blog post on how to do it, I wanted to give it a try. When I finally found the time, my experience was rewarding, but also frustrating. POSIX permission bits are limiting, and working around them is non-trivial, at least for me.

Conclusion
I hope you found this post useful, or at least interesting. I look forward to your comments and suggestions for simplifying the setup, or improving its security!

Thursday, May 6, 2010

URLs for Kindle Software Updates

This post contains links to the files needed for manually updating the firmware on Amazon Kindles.
Motivation
Amazon replaced the previously useful Kindle Software Update page with an announcement for the 2.5 firmware which will be released in the future. In the process, they removed the download links for manual firmware upgrades.

I'm mostly putting up these links for my own reference, but maybe they will help others as well.

Thursday, April 29, 2010

Ubuntu 10.04 on Mac Mini

This post describes a short procedure for installing Ubuntu 10.04 on a Mac mini. The instructions were initially written (and should still work) for older Ubuntu versions, down to 8.10. However, I haven't tested the old versions for a long time.

The process is roughly the same as my previous instructions for Ubuntu 8.04, but the write-up is more detailed to address the questions I have received last time. So don't the post length scare you!

Outline
The article starts with a warning describing the shortcomings of installing Ubuntu on the newest Mac Mini model. That is followed by a step-by-step guide for installing Ubuntu which should work on any Mac (tested only on Minis though). The post ends with instructions on how to restore your dual-boot solution if Mac updates break it, and a list of (community-contributed) tricks for getting some hardware to work on the latest Mini.

Warning (skip if you're sure you want Ubuntu)
Ubuntu will not work seamlessly on the new Mac mini (model MacMini3,1 with 5 USB ports). This is based on the release version of Ubuntu 10.04.

Wireless does not work right after installation, so Ethernet is needed, at least for bootstrapping. Once the system gains Internet access, it offers to install a Broadcom STA wireless driver which offers good performance. Grub 2 will get stuck and not show the boot menu about one times in five. This is uncomfortable if you're planning to use your Mini as a server that sits somewhere far away. For desktop usage, you'll probably want to install the nVidia proprietary drivers. On the open-source nouveau drivers, my screen doesn't get recognized when using mini-Display Port, but it works reasonably well when connected via the mini-DVI port. Sound doesn't work out of the box, and you'll need to hack your configuration files to get it to work (see below).

Method
  1. Use a Leopard or Snow Leopard (Desktop or Server) install disc to get your Mac in pristine form. This step is intended to undo anything that might have gone wrong in your previous attempts. You can skip it if you have a fresh install.
  2. Use Software Update to ensure you have all your updates installed. Update and reboot until there are no updates left.
  3. Start up Boot Camp Assistant (press Apple+Space to open Spotlight, then start typing the name until the application appears) and create a "Windows" partition.
  4. Do not let Boot Camp Assistant reboot your computer. Use Apple+Q to quit once it's done partitioning.
  5. Download and install the latest version of rEFIt (0.14 at the time of this writing) from http://refit.sourceforge.net/
  6. Open up Terminal (use Spotlight if you don't have it on your dock) and type the following commands:
    cd /efi/refit
    ./enable-always.sh
  7. Insert your Ubuntu CD, and shut down the computer, then power it back up.
  8. You should see the rEFIt boot screen.
  9. Select the Ubuntu CD (it should have a penguin on it) and go through the normal installation process. If rEFIt doesn't recognize the Ubuntu CD, power-cycle your Mac, and use Bootcamp to boot the Ubuntu CD - press and hold the Alt key as soon as the computer starts up, until the BootCamp screen shows up; select the CD image.
  10. When you have to do partitioning, choose Manual. Remove the Windows partition (the big FAT32 partition at the end). Create an ext4 partition (be sure to to allow for swap space) and set it to mount to /, then create a swap partition. If you're unfamiliar with partitioning a Linux system, read the recommendations below.
    • Click on the FAT32 partition, then click the Delete Partition button.
    • Click on the free space entry at the bottom, then click the New partition button. Select Ext4 journaling file system under Use as:, check the Format the partition: box and select / as the Mount point:. Now subtract twice your RAM size from the partition size. For example, if your partition size is 53575 Mb and you have 1Gb of RAM, you would write in 51527, which is 53575 - 2*1024. Press OK when you're done.
    • Click on the free space entry at the bottom, then click the New partition button. Select swap area under Use as: then press OK.
  11. Unless you know what you're doing, do not change the Advanced settings on the last installation screen. Specifically, don't change the default Grub installation location (hd0).
  12. When the installation is done, the Mac will reboot (if you're lucky) or beep multiple times. If it beeps, turn it off (press the power button for 4 seconds) then turn it back on.
  13. When you get the rEFIt boot screen, go to Partitioning Tool (bottom row, second icon from the left). It will prompt you if you want to update the MBR to reflect the GPT. Press Y, and watch the system reboot.
  14. Power down the system by pulling the power cord. Then power up again.
  15. Select Macintosh HD, and make sure you can boot into OSX. If it doesn't boot after 2 minutes, power cycle (see previous step) again.
  16. Optionally, switch the boot default to Linux. Open up /efi/refit/refit.conf in TextEdit, and uncomment the line saying #legacyfirst (at the bottom of the file).
  17. Reboot your Mac mini, and enjoy choice!
OSX software updates might remove your ability to dual-boot. The colorful boot menu will disappear, and your computer will boot into Mac OSX. If that happens, you can restore dual-booting with the following commands (step 6 from the guide above):
  • Open up Terminal (use Spotlight if you don't have it on your dock) and type the following commands:
    cd /efi/refit

    ./enable-always.sh
Hint: you can bookmark this post to have the solution handy. I won't mind :)

Getting Hardware to Work (community-contributed)
The tips here should help if you want to go beyond Ubuntu's out-of-the-box hardware support. These were contributed by others, and I just put them together on one page.
Sound (by nonspeaking) - not needed after Ubuntu 9.10 Beta
To get the sound working, add the following line to /etc/modprobe.d/alsa-base.conf
options snd-hda-intel model=imac24

Motivation
If you're curious, the following reasons determined me to write this post
  • My readers commented on my installation method for Ubuntu 8.04, and said it doesn't work for 8.10. Takeaway: please do comment! I listen :)
  • I got a new Mac Mini (MB464LL/A, 5 USB ports) to replace the one that was stolen from me.
Enjoy Ubuntu on your Mac!
Please leave a comment if you find a shorter way, or if something is not working for you.

Wednesday, April 21, 2010

Rails 3 Development Setup for Ubuntu

This post describes the steps needed to set up a fresh Ubuntu installation so you can start playing with Rails 3 and contribute to it

Method
This method isn't the most conventional way of developing on Ubuntu (I do replace the Ubuntu-supplied RubyGems binaries, so that I can get the latest version, and get gem binaries on my path). It also is not the most secure method (you will create install a ton of packages and create some random users on your database servers). It's quite fast and effective for development, though.

I have tested the steps below on Ubuntu 10.04 (Lucid Lynx), and I have good reasons to believe that they will work on Ubuntu 9.04 (Karmic Koala) as well.

Start off by installing some Ubuntu packages:
sudo apt-get update; sudo apt-get dist-upgrade
sudo apt-get install ruby rubygems ri ruby-dev libopenssl-ruby build-essential mysql-client mysql-server libmysqlclient-dev postgresql postgresql-client libpq-dev sqlite3 libsqlite3-dev curl git-core git-completion git-doc git-gui

Override Ubuntu's crippled RubyGems with the proper version:
sudo gem install rubygems-update
gem env | grep 'EXECUTABLE DIRECTORY'
sudo `gem env | grep 'EXECUTABLE DIRECTORY' | ruby -e "puts gets.split(': ', 2).last"`/update_rubygems

Create the rails user in MySQL:
echo "GRANT ALL ON *.* TO 'rails'@'localhost';" | mysql -uroot

Create a superuser in PostgreSQL:
sudo -u postgres createuser --superuser $USER
echo "\\password $USER" | sudo -u postgres psql

Install rvm:
sudo gem install rvm
rvm-install
echo "if [[ -s /home/$USER/.rvm/scripts/rvm ]] ; then source /home/$USER/.rvm/scripts/rvm ; fi" | cat - /home/$USER/.bashrc > /home/$USER/.bashrc2
rm /home/$USER/.bashrc; mv /home/$USER/.bashrc2 /home/$USER/bashrc


Install the Rails-preferred VM and create a clean gem set:
rvm install 1.8.7
rvm use 1.8.7
rvm gemset create rails3
rvm gemset use rails3
gem install bundler mocha

Prepare to work on Rails (do this every time you start a shell):
rvm use 1.8.7
rvm gemset use rails3

Check out Rails and install its dependencies:
git clone git://github.com/rails/rails.git
cd rails
bundle install
rake mysql:build_databases
rake postgresql:build_databases

Run the Rails test suite. If it passes, your environment is all set up. 
rake  # This should be run in the rails directory.

If you want to create a test application, then you should install the gems from the edge Rails:
rake install  # This should be run in the rails directory.

Happy hacking!

Motivation
At one of Boston.rb's hackfests, Dan Pickett held a workshop on contributing to Rails 3. I think I was the only one in the room not using OS X, and I had to follow a slightly different sequence of steps to get my setup working. I figured it's nice to summarize all the steps for future reference.

Conclusion
Thank you for reading! I hope the post above has been helpful, and hope you'll have fun playing with Rails 3. I look forward to your comments and suggestions for improving the method outlined here.

Friday, March 12, 2010

Quick Way Out of Ubuntu's Rubygems Jail

This post offers a quick method for overwriting the system-supplied RubyGems binary on Ubuntu (and any other Debian derivative) with a pristine version from the RubyGems authors.

Method
The following commands will do the trick.
sudo apt-get install rubygems-update
sudo /var/lib/gems/1.8/bin/update_rubygems

After installing the pristine version of RubyGems, you will have to re-install all your gems. This is because Ubuntu/Debian also modifies the path where RubyGems stores its gem data.

Motivation
Debian cripples RubyGems in various ways, to make it fit in with the overall Debian philosophy.

The most unfortunate change is that RubyGems will not update itself automatically, and we have to wait for Debian maintainers to package the updates. Another annoying change is that Debian's RubyGems will not install launcher scripts in /usr/bin, so we cannot use rails or rake by straight-forward gem installation.

While the path problem is easy to fix (sudo echo "PATH=/var/lib/gems/1.8/bin:$PATH" > /etc/profile.d/rubygems1.8.sh), there's no straight-forward solution for getting updated RubyGems. At the time of this writing, bundler (needed by the Rails 3 beta) requires RubyGems 1.3.6, and the version in Debian is 1.3.5.

I hope you have found this post to be useful, and I'm looking forward to your comments and suggestions.

Thursday, January 14, 2010

Hosting Ruby on Rails on scripts.mit.edu

This post describes a method for developing a Rails application and deploying it on scripts.mit.edu, a free hosting service for MIT students and staff. The information here is primarily intended for students in MIT's Web Programming Competition (6.470), but might be useful for other MIT students and staff, as well as for Rails developers deploying in constrained environments.

Summary
The first part of my post shows how to create a Rails application on scripts.mit.edu. By default, the application will use the Rails version that is installed on scripts, which will usually be behind the edge version. The second part of the post copies the application to the developer's computer, and modifies it to use the latest version of Rails. The third part of the post describes the steps needed to deploy a new version of the application back on the scripts.mit.edu servers.

Creating the Application
In the steps below, replace costan with your Athena username, and substitute sixprep with the name of your application.
  1. SSH into Athena: ssh costan@linux.mit.edu
  2. Sign up for scripts.mit.edu and MySQL: add scripts; signup-web; signup-sql
  3. Generate the Rails application: add scripts; scripts-rails app_name
    1. When prompted to enter 1 or 2, enter 1 (you're a user, not a group).
    2. Add the application name to the Desired address: field. For example: http://costan.scripts.mit.edu/app_name
    3. When asked to press Enter to continue, check your application's directory. It should be something along the lines of ~/web_scripts/app_name
  4. Verify that the application works by pointing Firefox to http://costan.scripts.mit.edu/app_name/
Move to the Latest Rails Release
The scripts-rails tool uses the rails command on the server,
  1. Open a command shell in your Eclipse workspace: cd ~/workspace
  2. Copy the application: scp -r costan@linux.mit.edu:~/web_scripts/app_name .
  3. Go into the application: cd app_name
  4. Hack around a configuration bug: ln -s . public/app_name
  5. Install the latest version of Rails: sudo gem install rails
  6. See what the latest version of Rails is (this example assumes 2.3.5): rails -v
  7. Edit config/environment.rb to say RAILS_GEM_VERSION='2.3.5'
  8. Update the configuration files: rake rails:update
  9. Add a requirement (the line below) for the Rack gem to your app's config/environment.rb:
      config.gem 'rack', :version => '1.0.1'
  10. Vendor Rails: rake rails:freeze:gems VERSION=2.3.5
  11. Vendor any other gems that your application uses: rake gems:unpack:dependencies
  12. Follow the steps below to deploy the application back to scripts.mit.edu, and verify that it still works.
Deploy Back on Scripts
The command sequence below will update your application in an almost safe way.
  1. Go into the application directory: cd ~/workspace/app_name
  2. Copy the application to scripts: scp -r . costan@linux.mit.edu:~/web_scripts/app_name
    NOTE: I'm using scp for simplicity. You'll probably want to put sixprep under version control, then remove web_scripts/app_name and check it out from the repository.
  3. SSH into a scripts server:
    ssh costan@linux.mit.edu
    ssh -k scripts

  4. Go into the application directory: cd app_name
  5. Build any new native gems you might have added: rake gems:build
  6. Apply database migrations: RAILS_ENV=production rake db:migrate
NOTE: earlier versions of this post included an extra step for killing FastCGI workers that are caching stale code. The scripts.mit.edu software improved, and this step is not necessary anymore.

The upgrade process is almost safe. The following things can go wrong:
  • a FastCGI worker is spawned with new code, works on the old database schema, and trashes the data (window of opportunity: HTTP requests between steps 2 and 6, assuming the request makes FastCGI spawn a new worker process)
  • a FastCGI worker caching the old code works on the new database schema, and trashes the data (window of opportunity: HTTP request between steps 6 and 7, assuming the requests hits a live FastCGI worker)
It shouldn't be too hard to figure out a solution to these problems, but I decided the windows of opportunity are small enough that I don't care. If you do find a nice and easy solution, please let me know in the comments.

Discussion
This is my method for hosting Rails applications on scripts.mit.edu. It is simple, and it allows me to use the latest version of Rails. There are other strategies that work well.

For example, you could use the version of Rails on scripts.mit.edu (rails -v to find the version on scripts, then gem install rails --version 2.3.2 to install a specific version of Rails). The scripts.mit.edu team back-ported the fixes for security vulnerabilities since 2.3.2 came out, and intends to do so in the future. This might be a better solution, if you don't plan to be proactive about testing and updating your application whenever a new Rails release comes out.

One more thing - in case you're wondering, you will not be able to use scripts.mit.edu with its current software with Rails 3. The reason is that the scripts server use Ruby 1.8.6, and Rails 3 requires Ruby 1.8.7 or above. Unfortunately, Ruby cannot be vendored :)

I hope you found this post useful, and I'm looking forward to your questions and suggestions in the comments.

Acknowledgements
Alice Li and Christopher Luna helped me iron out some bugs in this post by following earlier versions of it.

Sunday, January 10, 2010

Automated Backup for Picasa Web Albums

This post describes a script for downloading all the photos in a Picasa Web account. My script is mainly intended for Linux users, because Windows and Mac users can use the Picasa desktop software to download Web albums easily.

Usage
The script is written in Ruby, and uses RubyGems. You will need to install the appropriate packages for your distribution. For example, in Debian and Ubuntu:
sudo apt-get install ruby rubygems libopenssl-ruby

The script uses Google's gdata RubyGem, so you need to install it:
sudo gem install gdata

Download and unpack the script from my GitHub gist. Create an account.yml file in the same folder, using the following template:
username: costan
password: "my secret password"

Run the script:
ruby picasa_downloader.rb

Find your photos, nicely grouped by album, in the picasa_username folder.

For Developers
I used Google's gdata RubyGem, and the Picasa Web Albums protocol guide and protocol reference.

The code is pretty straight-forward. There is one subtlety: for photos bigger than 1600x1200, the Web Albums API returns the URL for 1600x1200 thumbnails by default. To get the original uploaded file, I use the imgmax=d parameter in album feed URIs.

Motivation and Conclusion
I wrote this script because the Picasa desktop software does not seem to be able to download from Picasa Web Albums on Linux.

I hope you found my post useful. I welcome your comments, as well as your pull requests on GitHub.

Saturday, January 9, 2010

Ruby on Rails Development Setup

This post documents my development setup for Ruby on Rails on Linux, MacOS X, and Windows. The Linux-specific instructions are geared towards Ubuntu, but should be easy to translate to other distributions.

Platform-Dependent Steps
The first part of the setup is installing the Ruby programming language, the Rubygems package management library, and the MySQL and SQLite database software. Due to the binary nature of the packages, the instructions are different for each OS.

Keep in mind that Windows and MacOS X differ in various ways from Linux, which will likely be your production environment. For best results, I strongly recommend installing Ubuntu as a dual-boot, or using Virtual Box to get an Ubuntu VM.

Ubuntu Linux
The steps below have been tested on a fresh installation of Ubuntu 9.10 Desktop.
  1. Update your package repository: sudo apt-get update
  2. Install Ruby: sudo apt-get install build-essential ruby irb rubygems libopenssl-ruby
  3. Install MySQL: sudo apt-get install mysql-client mysql-server libmysqlclient-dev
  4. When prompted, leave the MySQL root password blank. This will make it easy to share a database.yml file with the rest of your team.
  5. Install SQLite:  sudo apt-get install sqlite3 libsqlite3-dev
  6. Find out the system-wide RubyGems binary path (pick the one that is not in your home directory): gem env gempath
  7. Add the path to the system-wide binary path: sudo echo "PATH=/var/lib/gems/1.8/bin:$PATH" > /etc/profile.d/rubygems1.8.sh
  8. Reboot the system so the path change goes into effect.

MacOS X
The steps below have been tested on a fresh installation of MacOS X 10.6: Snow Leopard.
  1. Install Xcode from Apple's Developer site, or from the MacOS installation DVD.
  2. Go to the MySQL download site, and download the Mac OS X (package format) for your OS (Leopard: 10.5 x86, Snow Leopard: 10.5 x86_64). 
    1. You don't have to register, there is a No thanks, just take me to the downloads! link on the mirror page.
    2. Install the mysql- pkg file.
    3. Install MySQLStartupItem.pkg.
    4. Install (by double-clicking) MySQL.prefPane, and choose to Install for all users. Start the MySQL server, and check the Automatically Start MySQL Server on Startup box.
  3. In the Platform-Independent Steps, substitute gem install mysql with the following command, taken from the Riding Rails blog: sudo env ARCHFLAGS="-arch x86_64" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Windows
The steps below have been tested on a fresh Windows XP SP3 32-bit installation.
  1. Install Firefox: www.getfirefox.com
  2. Install the latest stable release of 7-Zip: http://www.7-zip.org/
  3. Launch the 7-Zip File Manager (in Start > All Programs > 7-Zip). From the menu, select Tools > Options, and check .7z under Associate 7-Zip with. Click Ok to dismiss the dialog, and exit the file manager.
  4. Go to the OneClick Ruby download site.
    1. From the latest RubyInstaller, download the rubyinstaller-1.8.6 executable. Install in the default path c:\Ruby and "add Ruby executable to your path".
    2. From the latest Development Kit, download the devkit- archive. Extract its contents in c:\Ruby
  5. Go to the MySQL download site, and download the Windows MSI Installer (x86)
    1. You don't have to register, there is a No thanks, just take me to the downloads! link on the mirror page.
    2. After downloading, do a Complete Install.
    3. At the end of installation, check Configure the MySQL server now, and uncheck Register the MySQL server now.
    4. When configuring the server, select Detailed Configuration, then Developer Machine, then Multifunctional Database. Accept the defaults, until the Install as Windows Service option shows up, then select both Install as Windows Service and Include Bin Directory in Windows PATH. For ease of development, don't change the root password (so it stays blank).
  6. Go to the SQLite download site, under the Precompiled Binaries For Windows section. Download the sqlite- and the sqlitedll- zip files, and extract them into c:\windows\system32.
  7. Restart your computer so the PATH changes go into effect.
  8. You can go into a Ruby-enabled command prompt via Start > All Programs > Ruby 1.8.6 > Start Command Prompt with Ruby.
  9. In the Platform-Independent Steps section, substitute gem install mysql with the following command. gem install mysql --source http://gems.rubyinstaller.org
Platform-Independent Steps
The second part of the development environment setup is identical in all OSes, with one small exception. The gem command should be executed with superuser privileges on Unix systems (Linux, MacOS X). To achieve that, all gem commands should be prefixed by sudo. For example, gem update would become sudo gem update.

Issue the following commands, and you're done setting up!
gem update --system
gem update
gem install rdoc 
gem install rails
gem install mysql
gem install sqlite3-ruby
gem install json mongrel ruby-debug-ide

Motivation
My post is intended to help the students in MIT's Web Programming Competition (6.470) who choose Rails as their back-end platform.

I hope you have found this post to be useful. Please leave a comment if you have trouble using the instructions above, or if you have a suggestion for improving these steps.

Saturday, January 2, 2010

Advice for 6.470 Teams Using Ruby on Rails

This post contains a suggested strategy for students competing in 6.470 (MIT's yearly Web programming competition) who are planning to use Ruby on Rails.

Motivation
Ruby on Rails will be covered in 6.470 on Friday. The competing back-end platform, PHP, will be covered on Tuesday. This post addresses students or teams who are considering using Ruby on Rails, but are concerned by the disadvantage of not being exposed to the platform earlier in the course.

Advice
My advice is based on my experience as a 6.470 contestant in 2008. My team won the 2nd place. The points below are the parts of our strategy that went well, and what I wish we would have done better.

  1. Don't agonize over your idea. Like in a startup, you will end up revising your idea as you learn what you can and can't accomplish in a month. Start prototyping, and start learning about the data that you can obtain.
  2. Research integration points (other sites' APIs) that you will use early. For some APIs, your developer account needs to be approved manually, and that can take a few days. It's a good idea to start as early as possible.
  3. Put your infrastructure in place. Set up version control (I recommend git, but I know svn is easy to set up on Athena). Set up story tracking (see Pivotal Tracker) so you don't forget about bugs. Set up regular meeting and coding times.
  4. Finalize your team staffing as quickly as possible. Either compete solo, or get a team of the maximum size allowed. Solo developers might impress the judges. That aside, nobody cares about your team size, and you don't want to miss the big prize because your work isn't polished enough.
  5. Set ground rules. 6.470 brings together competition, pride, and money, which are big potentials for drama. That can be mitigated
  6. Spend most of your time until Friday on the front-end. Make sure you learn HTML, CSS, JavaScript and Photoshop.
  7. Start mocking the UI using HTML, CSS and JavaScript. Get used to Firebug. Both the skills and the digital assets you'll develop are easily transferable into a Rails application.
  8. Pay particular attention to jQuery. Rails recommends Prototype for JavaScript, but jQuery is better at keeping your JavaScript out of your HTML. At the very least, you'll have an easier time learning Prototype if you know the principles of jQuery.
  9. Don't worry too much about SQL, but pay attention to database design.
  10. Think about whether you'll have testers. Our testers gave us great feedback and helped us make the site more usable. On the other hand, one of the early adopters showed us a security vulnerability by crashing our production database right before the deadline. So choose your testers wisely.
Relax
Rails is the most productive well-known Web development framework. There's a very good reason why the Rails book has a hammock on its cover. You can totally catch up with your competitors' back-ends over the first weekend.

Good luck!