Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Wednesday, April 30, 2014

Restoring Ruby 2.0 on Ubuntu 14.04

For some reason, Ubuntu 14.04 sets up MRI 1.9.1 as the default Ruby (/usr/bin/ruby and friends) version, even if Ruby was installed using the ruby2.0 package. This post outlines a quick and dirty method for restoring Ruby 2.0 as the default Ruby version.

Method

Issue the following commands in a shell.

sudo rm /usr/bin/ruby /usr/bin/gem /usr/bin/irb /usr/bin/rdoc /usr/bin/erb
sudo ln -s /usr/bin/ruby2.0 /usr/bin/ruby
sudo ln -s /usr/bin/gem2.0 /usr/bin/gem
sudo ln -s /usr/bin/irb2.0 /usr/bin/irb
sudo ln -s /usr/bin/rdoc2.0 /usr/bin/rdoc

sudo ln -s /usr/bin/erb2.0 /usr/bin/erb
sudo gem update --system
sudo gem pristine --all

Discussion

The underlying issue is that the ruby2.0 package in Ubuntu 14.04 depends on the ruby package, which sets up Ruby 1.9.1. The ruby package file list shows that it contains the /usr/bin symlinks used to run ruby and manual pages. The fact that the files are architecture-independent is a pretty good hint that they're not binaries, and merely point to other files. The ruby2.0 package file list (for amd64) shows the binaries that the symlinks need to point to for Ruby 2.0 to become the default version on the system.

The commands above restore the symlinks in the ruby package and rebuild all the gems, to ensure that their /usr/bin stubs are set up correctly as well. Regenerating the gems is a good idea after a system upgrade anyway, because this causes extensions to be re-compiled against the updated libraries. 

Conclusion

The ruby packaging in Ubuntu 14.04 has a nasty surprise for upgraders who use ruby2.0 as the default Ruby interpreter. This post provides a quick workaround for the problem. I hope you found this useful, and look forward to your comments and feedback!

Tuesday, October 18, 2011

Getting MRI 1.9.2 on Ubuntu 11.10

The release of Ubuntu 11.10 (Oneiric) brings much-needed updates to the Ruby packaging. We can now get a sane Ruby environment in Ubuntu with a few commands.

sudo apt-get install ruby1.9.1-full
sudo update-alternatives --set ruby /usr/bin/ruby1.9.1

sudo env REALLY_GEM_UPDATE_SYSTEM=1 gem update --system

This sets up a fully working MRI 1.9.2 environment, with an up-to-date version of Rubygems.

Enjoy!

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!

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, November 14, 2009

Quick Way to See Your Gems' Documentation

Summary
The fastest way to see the RDoc for your installed gems is to type the following command into a terminal, and point your Web browser to the address it returns (usually http://localhost:8808)
gem server

Details
The Web server created by the gem server command contains the RDocs for all the gems you have installed, unless you disabled RDoc generation when you installed your gems.


Motivation
I was used to either searching via Google, or going to a gem's RubyForge page to see the RDoc for the gem.

Initially, it seemed that the bit of effort is worth not having to learn some way to generate and bring up the RDocs myself. To my surprise, the procedure is very simple, and it's totally faster than browsing to some Web site that has the RDocs.

That aside, using a local server has the advantage that you'll see the RDocs corresponding to the exact versions of your gems. And, last but not least, RubyForge seems to be slowly falling into oblivion, and newer gems don't seem to bother publishing their RDocs.

I hope this post saves you some time.

Saturday, July 25, 2009

Rebuild Your Ruby Gems If You Update To Snow Leopard

This post contains a command that you must absolutely issue if you are a Ruby developer upgrading to Snow Leopard.

The Commands
Update: Type the following command in terminal:
sudo gem update --system; sudo gem pristine --all
rubygems will produce some errors which are safe to ignore

For historical reasons, here's my initial solution to re-compiling all the native gems.
Fire up irb, and type the following command:
`gem list`.each_line {|line| system "sudo gem install #{line.split.first}"}

Motivation
Snow Leopard introduces a disrupting change: everything runs in 64-bit mode by default. Most importantly to me, ruby is now 64-bit. This is a problem when upgrading to Snow Leopard, as opposed to doing a fresh install, because the Ruby extensions in your old gems are probably 32-bit.

A quick solution to this problem is getting all the extensions rebuilt, which is done by reinstalling all the gems.

In case you're wondering, gem update won't do the trick, because it will not rebuild all your gems.

Symptoms
If you're lucky, you'll get a library loading error when trying to use some gem with an extension (example: json), and you'll figure out rather quickly that you need to reinstall the gem.

A subtle symptom of the same bug is experiencing slowdowns running a Rails development server. In my case, webrick was really slow - start-up took about 30 seconds. For this reason, it's better to re-compile all the gems, as oppose to fire-fighting load error messages.

I hope this post saves you some time.

Wednesday, April 15, 2009

App Engine supports Ruby! Sort-of.

This post is a follow-up to my Great Time To Be a Web Programmer post, where I assert that HTML / CSS / JavaScript are the technology to learn in 2009, if you don't know them already. In that post, I said that Google's App Engine only supports Python, and that has changed. I am writing this quick update so my blog's readers are aware of the change in the Web application hosting landscape.

Java leads the way to Ruby
As of early April, Google's App Engine supports Java. The really good news here, if you don't care for low-productivity languages, is that the Java 6 VM provided by the App Engine has near-native performance, and most high-level languages have interpreters written in Java.

This opens the route to my favorite language, Ruby, being available on Google's App Engine. appengine-jruby is an experimental open-source project aimed at making Jruby available for the App Engine, and at implementing Ruby-esque abstractions over Google's APIs. At the same time, Ola Bini from ThoughtWorks took the time to get Rails to run on the App Engine, and wrote a blog post documenting his method.

There is still a devil in the details, however. According to Ola Bini, performance is nothing to write home about, and developers still have to zip up their source code to work around App Engine's 1,000 file limit.

Why this matters 
I think Google's App Engine is an important cloud-hosting platform because of its generous free tier. It is the best solution that I know for hosting hobby projects, or for a project's incubating phase

Conclusion
Sooner or later, Rails applications will run seamlessly on Google's App Engine. I believe it will happen sooner rather than later. Once Rails 3 shows up in the horizon and delivers on its promise of modularity, developers will be in a good position to rewrite the right parts for the App Engine.

In the bigger picture, the reality is shifting towards my guess that the cloud hosting platforms will soon support all the high-level programming languages. So the last programming language that you will have to learn for Web development is JavaScript, because the browser is still tied to it.

I hope that you have found this post useful, and look forward to your comments.

Wednesday, March 25, 2009

Removing Default Ruby Gems on OSX Leopard

This post describes a quick way to remove the gems that come pre-installed on OSX Leopard.

Method
First, you should update your gems, so you have newer versions for all the gems you're about to remove. While you're at it, update rubygems as well.
sudo gem update --system
sudo gem update

Now blast the directory containing the gems that came with OSX.
sudo rm -r /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8

If, for some reason, that directory does not exist on your system, you can see rubygems stores its gems by running gem env paths. Most likely, the old gems have already been cleaned.

Enjoy being able to clean up all the old gems on your system.
sudo gem clean

Warning
Removing the gems this way is permanent. If you don't like that thought, rename the 1.8 directory to 1.8.dead, and create an empty 1.8.  This way, rubygems doesn't see the old gems, but they are still around, if you need them for some reason. So, instead of rming,
sudo mv /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8.dead
sudo mkdir /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/gems/1.8

Motivation
The pre-installed gems have been released 2 years ago, so they're really old by now. They need to go away. Doing a gem clean will fail to remove them. (tested with Rubygems 1.3.1 and below) What's worse, gem clean will fail to remove other old gems that you have installed so, after a while, you'll have a lot of cruft on your system.

I wrote this post because, up until now, I've been too lazy to figure out the gem cleanup situation. Now that I finally did, I want to make it easy for others to get their systems clean.

Conclusion
I've described a quick way to remove the old ruby gems that come preinstalled with OSX Leopard. This is useful because gem clean is non-functional in the presence of those gems. I hope you have found the post useful. Please comment if you have better or quicker solutions to this problem.

Sunday, January 11, 2009

Date, time, and datetime validation in Rails 2.x

This is a very short post pointing to a good plug-in for validating time stuff in Rails. One-line summary: use validates_timeliness.

I wanted to validate the format of a datetime field in one of my models, so I tried validates_date_time and saw that Rails doesn't have it installed. I googled validates_date_time, and stumbled upon the plug-in with the same name by Jonathan Viney.

I installed the plugin, fixed the unit tests to run on Rails 2.2 with sqlite3, and then proceeded to write my validation, complete with unit tests. All the code is included, for your pleasure. After some booring debugging, I came to the conclusion the plugin can't handle invalid formats on new models, because of how it's using _before_type_cast to determine whether its parser failed or succeeded.

I googled for datetime validation in rails 2.2, hoping that someone fixed the bug. I didn't find a fix, but instead stumbled over the validates_timeliness plugin, written by Adam Meehan. In case you're seeing this in plain text, the link to the plug-in is http://github.com/adzap/validates_timeliness/

I hope Google indexes my post, and that it helps you avoid going through the same trouble that I have.

The code I used for this post:

Sunday, November 23, 2008

Some Tricks for Background Processing in Rails

This post describes a bunch of tricks that I'm using to do background processing in my Rails applications, Movie Nights and MIT's 6.006 Course Site.

If you want the whole picture, read ahead. If you just want to see the tricks, skip the next section. There's also code at the end, for your copy-pasting pleasure. And it all works with the newly released Rails 2.2.

Big Picture
I'm doing my background processing in one or more long-lived processes. I keep their code in script/background, and use simple-daemon to make them... daemons. I use Starling to pass messages between my Rails front-end processes and the background processors. I don't like starting anything by hand, so I use daemonz to start up Starling and my daemons.

Trick 1: ActiveRecord + Long-Running Process
When using ActiveRecord in long-lived processes, you'll see their connections drop. You know you're experiencing this if you see the following in your logs
Error processing task - ActiveRecord::StatementInvalid: Mysql::Error: MySQL server has gone away:

I fixed this problem by having ActiveRecord re-check its connections every time a Starling request is processed:

ActiveRecord::Base.verify_active_connections!


If your background tasks are really long, and you're working with your database (for instance, updating some status when a task completes) you might need to run the line above several times during a task.

Trick 2: ActiveRecord + Fork
I know I'm not supposed to fork. But I need to. My course website runs student-submitted programs, and then processes their stdouts. So my background processor daemon needs to fork/exec to be able to run those programs.

The problem is, forking copies the parent's memory. So ActiveRecord's connection pools will get copied. And when the child exits, ActiveRecord will close the database connections, and screw over the parent process. spawn used to handle this for me, then it stopped working in Rails 2.2. So, after some experimentation, the following seems to be the most concise fix:

ActiveRecord::Base.connection_handler.instance_variable_set :@connection_pools, {}


Keep in mind that my forked children run non-rails code, or exec something else right away. I haven't tried using ActiveRecord in them, and I suspect it would break.

Code
My daemonz configuration (daemonz.yml) for Starling and the background processor is below. I have 4 task processors, because the production server has 4 cores and therefore can run 4 student submissions in parallel.



My background processor boilerplate code is below. It uses two queues, pulls and pushes, and tasks in pulls have priority over tasks in pushes. It also works with daemonz.yml to get multiple instances of the same daemon, which is non-trivial when using simple-daemon.



The code above calls into OfflineTasks, which I define in lib/offline_tasks.rb. The code for my course site is below. Each background task has a method that pushes it into Starling, and a case branch that executes it when it's popped from the Starling queue.



And finally, a snippet of code that I use to fork:



Thanks for reading! I hope you found this useful.

Wednesday, November 19, 2008

Post-install / post-update scripts for ruby gems

This post outlines a hack that allows a gem to run Ruby code when the gem is installed or updated, which in effect gives post-install / post-update hooks. The method described here works with any reasonable version of ruby and rubygems (I only tested as far back as ruby 1.8.4 and rubygems 0.9.4, and everything looked good.)

Summary
Add a fake extension, use extconf.rb to run your code and then simulate successful compilation. Use the links at the end for example code.


Detailed Description
RubyGems isn't supposed to run arbitrary code during gem installations, but it supports building extensions for gems. This is a really sweet feature, and makes rubygems a nice tool for cross-platform package management. That's good and all, but the part that we care about is that the process of building an extension starts by running extconf.rb in the extension's directory, which is responsible for producing a Make file that will orchestrate the building process.

Knowing this, the first thing that comes to one's mind is - let's add an extension to the gem, and put the hook code in extconf.rb. However, there's one more issue left. If rubygems believes that your gem's extension hasn't been built properly, it will not finish installing the gem, and it will spit out a nasty error message.

In order to work around that, we need to trick rubygems' build process, so we need to bypass 3 checks:
  1. a Make file is generated - create an empty Makefile
  2. make all and make install run successfully - generate a make file that contains empty all and install targets; create fake make binaries to cover the case when the user doesn't have a build environment (Linux/Mac: an executable make with a /bin/sh shebang should work; Windows: an empty nmake.bat should trick the Windows port of rubygems)
  3. an extension binary is generated - create empty files your_extension_name.so and your_extension_name.dll
You can implement this yourself, or you can depend on my zerg_support gem, and use the method there, like the example code below does. I promise I won't mind if you copy-paste the code, so you don't have to take an extra dependency :)

I haven't tested out the Windows plan yet, but I have good reasons to believe it should work (I've built gems with real extensions on Windows some time ago).


Code Map
Adding an extension to your gemspec (assumes you're using hoe or echoe):
http://rails-pwnage.rubyforge.org/svn/trunk/zerg/Rakefile

Placing your hook in extconf.rb:
http://rails-pwnage.rubyforge.org/svn/trunk/zerg/ext/zerg_setup_hook/extconf.rb

Tricking rubygems into thinking an extension was built (see emulate_extension_install):
http://rails-pwnage.rubyforge.org/svn/trunk/zerg_support/lib/zerg_support/gems.rb

Monday, September 22, 2008

Daemons in Ruby on Rails applications

Summary
I have created a Rails plug-in, daemonz, that automatically starts and stops the daemons associated with an application, without too much configuration. It works in both production and developer mode. Read below if you care about the motivation behind not using an existing tool.

The Story
In the life of every Ruby on Rails app, there comes a time when the boundaries of a single process are simply too narrow. It might be the need for speed-ups (memcached), full-text indexing (ferret), or background work (simple-daemon), or something entirely different. One thing is certain - daemons will start accompanying your applications.

My application, Movie Nights, desperately needed daemons for all the above reasons. I've been putting that off for as much as possible, because I didn't want to make my developers learn how to start all these processes, and I didn't want the hassle of having to remember all that. When I really had to do this, I knew there is only one right answer: automate.

So I looked into monit and god. They're awesome for the production environment, which didn't really help - I already have a deployment tool, which can handle daemons. I wanted something that would work under the development environment, and would automatically start/stop everything, and I accepted that I'll have to build my own.

Sadly, I don't have time to do this the right way (an integrated system that manages all the dependencies of a Rails app), so I settled for the quick way. Enter daemonz, a bandaid (not really a full solution) for the problem I've described above. Once it is configured, it knows how to start and stop the daemons in most non-trivial circumstances, e.g. having a cluster of app servers, or having the Rails framework brought up by tools. It's not a nice and elegant solution, but it works, and it took a few hours to code.

Enjoy!
(If you're not a Rails programmer, hopefully you can at least enjoy the speed that Movie Nights has gained by performing all its Facebook API calls in a background process.)

Monday, November 19, 2007

My first Ruby gem

This past weekend has a special importance for me. I am finally able to stand by my favorite language, Ruby, with more than just words.

To cut a long story short, I'm playing with smart cards for my research. I didn't find any good library for accessing cards from Ruby, so I was faced with either writing my own extension, or leaving my favorite language for something else. My desire for beauty triumphed over my laziness, and my first Ruby extension was born.

It wasn't as bad as I expected. It took me 4 half-days to figure out PC/SC, ruby's extension mechanism, gems, and Rubyforge. And in the fifth day I rested got it to work under Linux/Windows.

So, yay, I've made my small contribution to the Ruby community. If you're curious to check it out, go to its Rubyforge page.

Wednesday, September 26, 2007

BSD Sockets in Ruby

I love Ruby. Completely. I don't want to write in anything else. However, meeting with the BSD sockets interface left me with a sour taste. Here's what I learned. Hopefully, you'll find this post and won't waste time if you have to work with Ruby's sockets.

You can send packets with send
The method name is good for consistency, but a horrible choice in general, if you ask me, because it overrides send in Object (used to send a message to an object, same thing as calling a method).

send's parameters are a string representing the data (same encoding as in recv), and a number for flags. The number is NOT optional (messed up choice, in my opinion), so my sockets code always has send data, 0 in it.

send fails oddly
If you're seeeing in `send': symbol string may not contain `\0' (ArgumentError) then your socket is object is null. This happens because nil uses Object's send, so it thinks you're passing it a method name.

pack_sockaddr_in doesn't like localhost
Silly me, I looked the example using www.google.com and I thought I can give pack_sockaddr_in any address. If I give it localhost, I get in `connect': Invalid argument - connect(2) (Errno::EINVAL).
Solution: use 127.0.0.1

Using strings for send / recv is annoying
If I'm using sockets, I'm working at the byte level. Using Fixnum arrays to represent bytes in a packet is a lot more convenient than using strings. Here are snippers for converting between the two formats. (I did Scheme at MIT, so I love map-reduce... I meant, collect-inject).

bytes > string:
block_string = block.flatten.map { |value| value.chr }.join('')


string > bytes:
block = (0...block_string.length).map { |i| block_string[i] }


That's about it. Hope this helps someone!
Victor