Rails, getting started without the hassle

I just changed jobs and am now a Rails developer at tolingo.com, which is an online translation broker. When I started out working on my new desk, I had to setup my iMac development environment. There are tons of articles of how to compile/install/run stuff like MySQL, to get you started on OS X, but I think all one really needs is Homebrew and RVM.

Homebrew

Homebrew is a Ruby based packaging tool for Mac and once you start using it, you immediately hate yourself for having wasted time on MacPorts

“Homebrew is the easiest and most flexible way to install the UNIX tools Apple didn’t include with OS X.”

This quote is from the official website and I guess they are absolutely right!

Formula

Homebrew is build around formulas. They describe how a package should be loaded from the web and installed on your system. It also cares about package dependencies, paths and all the other ugly stuff:

require 'formula'

class Wget < Formula
  homepage 'http://www.gnu.org/wget/'
  url 'http://ftp.gnu.org/wget-1.12.tar.gz'
  md5 '308a5476fc096a8a525d07279a6f6aa3'

  def install
    system "./configure --prefix=#{prefix}"
    system 'make install'
  end
end

You can easily install packages from the shell with brew:

brew install wget

Homebrew puts all the packages into ‘/usr/local’, so that it won’t interfer with other components of your system. To get your packages working, you need to include it into your $PATH. If you have any problems running something, Homebrew comes with the doctor command, that scans for problems in your setup!

Installation

Just download Homebrew to your system and update once a while:

# install homebrew via curl
sudo mkdir -p /usr/local && sudo chown -R $USER /usr/local && curl -Lsf http://bit.ly/9H4NXH | tar xvz -C/usr/local --strip 1

# update homebrew
brew update

Git, MySQL, Sphinx and more

What else do you need? Just search for it or get more infos with info!

These are the packages that I needed for development:

# install mysql and set it up
brew install mysql
mysql_install_db
# add mysqld as launch agent
cp /usr/local/Cellar/mysql/#{MYSQL_VERSION}/com.mysql.mysqld.plist ~/Library/LaunchAgents
launchctl load -w ~/Library/LaunchAgents/com.mysql.mysqld.plist

# install git
brew install git git-flow

# add git bash completion (find path to your git with 'brew info git')
ln -s /usr/local/Cellar/git/#{GIT_VERSION}/etc/bash_completion.d/git-completion.bash ~/.git-completion.bash
source .git-completion.bash

# install sphinx search-deamon
brew install sphinx

# aspell with all spellings
brew install aspell --all

# libxml and imagemagick for sprites
brew install libxml2 imagemagick

RVM the Ruby Version Manager

RVM is a command line tool for managing your local Ruby environments, you can get some more information on the RVM homepage and in earlier articles.

Quick start with installing RVM to your machine:

# install rvm via curl !!! FOLLOW RVM INSTRUCTIONS !!!
bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )

# download and compile latest 1.8.7
rvm install 1.8.7

# create a .rvmrc file in your app's base directory
echo "rvm use 1.8.7@#{YOUR_APP} --create" > #{YOUR_APP}/.rvmrc
# execute it by cd-ing to your app's directory
cd #{YOUR_APP}

Now you can work on your app with a custom gem environment. Unless you are using Bundler, this is probably what you want for installing and removing gems painlessly.

Cucumber with Celerity

Behavior driven development with Cucumber works nicely with Celerity, a JRuby implementation of a headless browser using HtmlUnit and it’s companion a Ruby wrapper called Culerity. Culerity has recently been updated with some configuration points for registering your local JRuby environment:

# jruby config für culerity (from http://rvm.beginrescueend.com/integration/culerity/)
rvm install jruby
rvm use jruby@celerity --create
gem install celerity
rvm wrapper jruby@celerity celerity jruby
# add to .profile
export JRUBY_INVOCATION="$(readlink "$(which celerity_jruby)")"

If you are experiencing any weird Broken Pipe errors (like me), have a look at this issue.

This is just an example of how you can setup your Rails development environment. Comments on this topic are appreciated!

Distinguish Ruby Runtimes with WhichRuby

Nowadays there are several decent Ruby runtimes available besides MRI ranging from alpha-versions to production-ready status. Using RVM these different interpreters become more and more interchangeable.

current problems

Since switching between runtimes became as easy rvm use x more care has to be taken to support a wide range of interpreters and versions. This is especially true for shared code like gems.

Some engines like JRuby have limitations that prevent the usage of some Ruby features. In most cases it’s possible to work around these limitations and provide a different solution that works, but might be somewhat less performant.

checking runtimes

Ruby is great at introspection, but especially 1.8 misses some key information like RUBY_ENGINE to determine the current interpreter at runtime and one has to extract it from the RUBY_DESCRIPTION constant.

WhichRuby aims at simplifying this tedious task and providing a simple API:

# irb@jruby
jruby-1.4.0 > require 'which_ruby'
 => true 
jruby-1.4.0 > include WhichRuby
 => Object 
jruby-1.4.0 > jruby?
 => true

Executing different code fragments becomes as easy as defining a scope:

ruby_scope(:jruby) do
  # custom jruby code here
end

This comes in very handy for stuff like accessing Java code natively via JRuby instead of using RJB.

I don’t like Rubbae - I love it!

Ruby in Java, Java in Ruby, JRuby or Ruby Java Bridge?

Hosting (J)Rails applications on a high availability Java infrastructure with clusters, loadbalancers and all that shit stuff is great, if you already have it in place.

But does running an app on the JRE make it a JRuby application by default? Do you really want to be stuck on JRuby?

I really like the idea of running code with the most appropriate runtime available, that is why I use RVM all the time. JRuby is still very slow on startup and tools like autotest/autospec take minutes for just a handfull of tests…

So I would like to host Rails applications on a Tomcat, but on the other hand I want the tests to be executed with MRI! This should not be a problem as long as you don’t want to share a common codebase within Ruby and Java.

Integration

Is there a best practice for combining Java and Ruby?

Since every project is different, I think that you’ve got to evaluate the possible solutions to pick the one that fits best.

The next sections cover some approaches on integration of Java and Ruby code. Make sure to have RVM installed if you want to execute the provided example code. I assume that a JRE is provided with every OS nowadays…

Java in Ruby

There are two common solutions for embeding Java into a Ruby application. The first and obvious one is via JRuby, which can only be run with the JRuby runtime:

require 'java'

puts "access java.util.UUID via JRuby"
puts java.util.UUID.randomUUID().toString()
rvm use jruby 
=> Using jruby 1.5.0.RC1

jruby lib/jruby.rb 
=> access java.util.UUID via JRuby
=> a16fda6a-c57d-43b9-8376-801e48fe56b3

The same thing is possible using the Ruby Java Bridge from MRI:

require 'rjb'

puts "access java.util.UUID via RJB"
puts Rjb::import('java.util.UUID').randomUUID().toString()
rvm use 1.8.7
=> Using ruby 1.8.7 p249

ruby lib/rjb.rb 
=> access java.util.UUID via RJB
=> 1db8298c-5486-4933-be00-cdb180388e38

But it is also possible to do it the other way around!

Ruby in Java

Evaluating Ruby code in Java is dead simple with help of the JRuby library. You just need to set up a scripting container that executes your scripts:

  @Test
  public void execute_jruby_scriptlet() {
    new ScriptingContainer().runScriptlet("puts 'hello jruby world'");
  }

A more advanced example is to wire a Ruby class as a Spring bean. You need to provide some configuration and a Java interface that can be used as the basis for the bean:


<lang:jruby id="identifier" script-interfaces="de.nofail.Identifier" script-source="classpath:/ruby/identifier.rb">
    <lang:property name="uuid" value="#{ T(java.util.UUID).randomUUID().toString() }" />
</lang:jruby>
# identifier.rb
class Identifier
  def setUuid(uuid)
    @uuid = uuid
  end
  def getUuid()
    @uuid
  end
  def to_s()
    "Identifier[#{@uuid}]"
  end
end

# don't forget to return an instance as a bean
Identifier.new
// Identifier.java
public interface Identifier {
  String getUuid();
}
// SpringJRubyBeanTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationContext.xml")
public class SpringJRubyBeanTest {

  @Resource(name = "identifier")
  Identifier identifier;

  @Test
  public void generateUuid_success() {
    System.out.println("uuid: " + identifier);
  }
}

The code is currently not running with JRuby 1.5.0.RC1 so you need the latest stable release for testing.

checking Ruby enginges

It is even possible to mix and match all those approches! You just need to keep track of the Ruby engine that is evaluating your code. I created a little helper called which_ruby (available on rubygems) as a sidekick for this article, but more on that in the next week.

Some Ruby sugar in your cup of Java!

Migrating to Rails 3 for Heroku Bamboo

Recently there were some interesting updates to the Heroku infrastructure, giving the opportunity to migrate my personal Rails 2 website to Rails 3.

Having an app with only a single model for caching data, there is no worry about database migration. A nice opportunity for starting out new:

rvm use 1.9.1
gem install rails --pre
rails basement-rails3
cd basement-rails3
heroku create basement-rails3 --stack bamboo-mri-1.9.1

business as usual?

Not really… Having Yehuda Katz as a core developer of Rails 3, it’s no surprise they adopted the Merb approach of just using one executable for everything. So the ‘script’ folder now contains just a ‘rails’ script. Creating controllers, running the server, jumping into the console - all through the ‘rails’ command:

rails -h
=> [...]
=>  generate    Generate new code (short-cut alias: "g")
=>  console     Start the Rails console (short-cut alias: "c")
=>  server      Start the Rails server (short-cut alias: "s")
=> [...]

I appreciate the shortcuts! No more discussions about what shortcut to use for ‘script/server’ (ss is not an option in germany…)!

dependency management

Rails 3 has changed the way of working with gems. It uses bundler to deal with dependencies. Beeing a big fan of Java’s dependency management tools like Ivy or Maven, I think that separating out the dependency issue is good idea.

All dependencies are now defined in a separate ‘Gemfile’ using an easy dsl to manage the gems:

gem "rails", "3.0.0.beta"
[...]
gem "sqlite3-ruby", :require => "sqlite3"
[...]
group :test do
  gem "test-unit", "1.2.3"
end

I had some trouble getting bundler working on my machine, but after reinstalling Rails 3 AFTER the bundler gem, everything worked fine.

The only Rails plugin in my app is Haml and I was confident that it would play well with the latest Rails version. Never the less I was pleased to find RailsPlugins.org where one can check the compatibility of plugins with Rails 3.

escaping vs. html_safe

There were just very little changes to the existing codebase in my application. Despite one thing though, that forced changes to nearly all of the wrapper objects that are used to encapsulate the data that is coming from external services like twitter. The Problem is that Rails 3 has a strict way of dealing with escaping. Every string rendered into the view will be escaped unless it is ‘html_safe’. Since my application is using a lot of pregenerated content with inline html, adding ‘html_safe’ markers is inevitable:

  def content
    @json["content"]["$t"].html_safe
  end

Ruby 1.9 is different

The biggest pile of migration problems resulted from using Ruby 1.9.1. The latest Ruby version is a lot faster, but it has changed some of the core functionality. The ‘enum_with_index’ method for example is replaced with an ‘each_with_index’ method on a hash.
Using old YAML files resulted in some strange behavior as these files have changed format slightly (because of the new symbol style that Ruby 1.9 is using, I guess):

# old
  id: home
# new
  :id: home

Ruby 1.9 also changed the way of handling unicode characters. Using these in code forces the developer to put a magic comment in the first line of the ruby file:

# coding: utf-8
[...]

beta quirks

Most of the new Rails 3 stuff just works, but there are some reasons why it is still beta:

# rails console won't quit with controll-c but exits without error typing ö.ö
rails c
=> Loading development environment (Rails 3.0.0.beta)
ruby-1.9.1-p378 > ö.ö
^C

# rails help doesn't work for commands
rails -h
=> [...]
=> All commands can be run with -h for more information.
rails generate -h
=> Could not find generator -h.

Beta but running!

(X)Ruby on the Mac

=================================================================== ===================================================================

Update Aug. 2010

The RVM installer should be prefered over installation via gem:

bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
=================================================================== ===================================================================

If you are a Mac user and a Ruby developer you probably ran into issues with custom Ruby installations and had troubles with Gems like these:

Using MacPorts

There are a lot of people that recommend using MacPorts for installing a custom Ruby version, but there are a lot of issues with this approach too. If you did manage installing the Ruby version of choice, you might run into issues using TextMate or other tools, that depend on the default OS X Ruby (I think that I had ALL the problems one could have, but this might be an exaggeration).

Custom (X)Ruby Versions

The next problem arises if you want to use different versions of Ruby or even different implementations/runtimes (EVIL666) like JRuby, MacRuby or Rubinius.

I am currently using:

  • Ruby 1.8.6 for integration with Heroku
  • Ruby 1.8.7 for daily usage
  • Ruby 1.9.1 for a fast Ruby experience
  • MacRuby for playing around and avoid looking at verbose Objective-C code
  • JRuby for Buildr

There are some simple approaches to fix this mess. MacPorts and JRuby provide different binaries to run like ruby18, ruby19 or jgem, but this is error prone and confusing. I was always installing Gems to the wrong Ruby environment and there were always conflicts with scripts and tools looking for the actual Ruby binary.

Since this did not work out quite well I started to wire the path tightly in the .profile file to include just the right Ruby and Rubygems binary. This had the drawback that I had to close terminals for every switch. So I started writing bash scripts manipulating the path directly, which worked well, but was unconvenient.

Most of the Ruby versions are not stable. Ruby 1.9 is under development, JRuby has frequent compatibility and performance releases and MacRuby is kind of an an “Alpha”.
As far as I am concerned, I always want to use the latest stable release of these distributions! Using MacPorts you often just get some release, so you have to compile stuff yourself. Doing so is time consuming and painful…

RVM to the rescue

Some weeks ago I stumbled over RVM, which can be installed as a Gem to the standard preinstalled OS X Ruby. RVM provides some neat features and does exactly what I want. It provides a simple interface to manage Ruby distributions and does some clever stuff organizing local Gems.

Right now I am using the preinstalled OS X Snow Leopard Ruby distribution with RVM and no ports:

# check that you are running default Ruby
ruby -v
=> ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0]

# update your OS X Rubygems
sudo gem update --system

# check Gem version
gem -v
=> 1.3.5

# install RVM as a Gem to the defaut OS X Ruby
sudo gem install rvm

# run RVM installation
rvm-install
# follow the installation instructions (add RVM to the $PATH etc)

# open new terminal and check RVM version
rvm --version
=> rvm 0.1.0 by Wayne E. Seguin (wayneeseguin@gmail.com) [http://rvm.beginrescueend.com/]

# have a look at the manual
rvm --help

# install a (X)Ruby version
rvm install jruby

# see what is installed
rvm list

# see some more information
rvm info

# use a (X)Ruby version
rvm use jruby

# install a gem for the current (X)Ruby
gem install buildr

# install a gem for all Rubies under RVM control
rvm gem install -v=0.6.9 savon

# flip back to default Ruby
rvm system

Cut Rubies with ease…