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!

Using the Redis addon on Heroku

I am always playing around with new addons offered by Heroku. My latest discovery was the Redis addon that is provided by Redistogo. The addon is probably in private beta (docs are still on beta), but since they put up a link to it on their site, I managed to install it to my personal website that runs in the cloud.

Redis is “an advanced key-value store” and has some features that make it a perfect match for a cache! I use caching extensively on my site and keep on trying out new ways to do it to circumvent Heroku’s readonly filesystem.

Like Memcache, Redis provides the ability to set a time to live (ttl) on a key. This comes in handy, if you have data that expires in a short period of time, like 3rd party data from Twitter etc.

Caching with Redis

Accessing Redis is very simple, since it is a text based protocol. The command reference is straight forward and there is a simple Ruby wrapper available:

require "redis"
redis = Redis.new
redis.set "foo", "bar"
# => "OK"
redis.get "foo"
# => "bar"

The redis-store gem already provides a Rails 3 compatible Cache Store implementation, but I needed some more configuration points, especially the ttl.

That’s why I wrote my own Rails 3 Redis Cache, also a great way to get used to the way of working with Redis and the Redistogo addon.

Using Rails Redis Cache

There is some configuration needed for Rails to pick up the new cache store. If you want to use different or no caching for test, development and production, you should put the config in your environment files:

# config/environemnts/production.rb
config.action_controller.perform_caching = true
config.cache_store = ActiveSupport::Cache::RailsRedisCache.new(:url => ENV['REDISTOGO_URL'])

If there is a Redis server available in all environments, you can put it in your environment file:

# config/environment.rb
ActionController::Base.cache_store = ActiveSupport::Cache::RailsRedisCache.new(:url => ENV['REDISTOGO_URL'])

The caching parts are mostly in my controllers:

@tweets = cache("tweets", :expires_in => 30.seconds){ Twitter::Search.new(...) }

The store is using the basic Rails cache store implementation which is broken in the Rails 3.0.0.beta1 version that runs on Heroku, so I added a monkey-patch for that using edge Rails.

Redis on localhost

Installing and running Redis on Mac OS X is really simple:

brew install redis
redis-server

There is also a commandline client available for direct access:

redis-cli
redis> set "foo" "bar"
OK
redis> get "foo"
"bar"

It’s key value stores, stupid!

Mongo Ruby Driver, Mongoid and MongoMapper

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

Update Aug. 2010

On Whyday, I created a live demo of the examples, that is running on Heroku.

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

I am constantly looking around for different storage mechanisms on Heroku that can be used for caching 3rd party data. A recent update of their platform offered an MongoDB addon to access the MongoHQ service that drew my attention, so I started to evaluate this noSQL document database…

MongoDB on OS X

It’s always a good starting point to have a local installation of a technology, here is how you get it running on your Mac with Homebrew:

brew install mongodb
# create a place for MongoDB to store the data
mkdir -p /data/db
# run server with default config (adapt to the right version)
mongod run --config /usr/local/Cellar/mongodb/1.4.4-x86_64/mongod.conf

Using MongoHQ requires a user-authentication, so it’s nice to have the same credentials on your local MongoDB instance:

# start the client
mongo
> use test
> db.addUser("test", "test")

evaluating different APIs

A very basic approach, that basically wraps the MongoDB API into Ruby code is the Mongo Ruby Driver, but there are two higher level APIs close to ActiveRecord called Mongoid and MongoMapper.

Mongo Ruby Driver

It’s pretty easy to connect to your MongoDB with the right connection string:

conn = Mongo::Connection.from_uri("mongodb://user:pass@host:port/db")
db = conn.db("db")

The Mongo Ruby Driver is very simple and close to the MongoDB API:

coll = db.collection('test')
coll.insert('a' => 1)
coll.find().each { |row| p row }

MongoMapper

MongoMapper can also be accessed with a connection string:

Mongo::Connection.from_uri(MONGO_URL)

Instead of using ActiveRecord::Base MongoMapper provides the MongoMapper::Document module to handle the object document mapping. Since the structure of a document in MongoDB is open and not static like in a SQL database, you have to define the structure in code, so MongoMapper knows how to map the document to your Ruby objects:

class Person
  include MongoMapper::Document

  key :name, String
  key :age, Integer
  key :born_at, Time
  key :active, Boolean
  key :fav_colors, Array

  connection Mongo::Connection.from_uri(MONGO_URL)
  set_database_name 'basement'
end

person = Person.create({
  :name => 'Nunemaker',
  :age => 27,
  :born_at => Time.mktime(1981, 11, 25, 2, 30),
  :active => true,
  :fav_colors => %w(red green blue)
})

person.save

Person.all.each do |p|
  ...
end

Mongoid

Configuring Mongoid is somewhat different but easy:

Mongoid.database = Mongo::Connection.new(host, port).db(db)
Mongoid.database.authenticate(user, pass)

The DSL for defining Mongoid Documents is similar to MongoMapper and works mostly the same way. Querying the database is also similar to the API provided by ActiveRecord:

class Tweeter 
  include Mongoid::Document 
  field :user 
  embeds_many :tweets 
end 

class Tweet 
  include Mongoid::Document 
  field :status, :type => String 

  embedded_in :tweeter, :inverse_of => :tweets 
end

tweet = Tweet.new(:status => "This is a tweet!") 
tweet.tweeter = Tweeter.new(:user => 'ted') 
tweet.save

Tweeter.all.each do |tweeter| 
  ...
end

You can get the complete code and some more links from the GitHub project created for testing.

MongoDB is a great way to store document focused data and it’s simple to use with these great libraries!

Creating a Maven webapp from scratch

Doing a little research for an article about log4j configuration in Java webapps, I decided to use Maven as a sidekick. I already pointed out earlier that my favourite build tool is buildr. The reason I chose Maven is that it has archetypes and that there is a deprecated guide for using such an archetype to create a blank webapp that would be valuable for me.

installing Maven on OS X

The Maven documentation in general and the installation instructions in particular SUCK. They lack essential information as most Java documentation does. So here is a step by step guide for OS X:

# go to the place you want to install Maven to
cd ~/Library
# download latest Maven version
curl -O http://apache.autinity.de/maven/binaries/apache-maven-2.2.1-bin.zip
# unzip the archive
unzip apache-maven-2.2.1-bin.zip
# add a symlink for convenience
ln -s apache-maven-2.2.1 maven
# add executables to the PATH
echo export PATH="~/Library/maven/bin":\$PATH >> ~/.profile

# open a new bash and check Maven is running
mvn --version
=> Apache Maven 2.2.1 (r801777; 2009-08-06 21:16:01+0200)

creating the webapp

If Maven is running on the system, you can use the archetype to create a blank webapp:

# go to the workspace
cd ~/Documents/workspace/
# let Maven create a webapp
mvn archetype:generate \ 
   -DgroupId=de.nofail \
   -DartifactId=tomcat-logging \
   -Dversion=1.0.0-SNAPSHOT \
   -DarchetypeArtifactId=maven-archetype-webapp

This takes several minutes, as it requires a thousand some broken dependencies for whatever is necessary to create some files and folders! But after that, you have a fresh, apache conform webapp at hand:

# go to the webapp
cd tomcat-logging/
# let Maven create a distributable war
mvn clean package

This is very nice for a Java application! Zero configuration and you get a running webapp that you can start after another two thousand downloads instantly from the command line:

mvn jetty:run &
open http://localhost:8080/tomcat-logging/	

all by myself

There is just one big problem with this so far: it won’t work bejond that! Maven lacks sensible defaults everywhere! Examples? Here you go:

Java compiler

Maven uses Java 1.4 as the default compliance level for the Java compiler. So if you start adding some Annotations or Generics to your code, which are Java 1.5 features, your build will fail until configuring the compiler plugin properly:

	[...]
	<build>
		<plugins>
			[...]
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			[...]
		</plugins>
	</build>
	[...]

This should be a top level configuration!

Jetty plugin

Making changes to the code won’t change anything in the webapp run by the Jetty plugin. You need to configure the plugin in order to pick up changes. Since the plugin can not do any hot code replacement, it has to restart the context after every change. A no-go for most webapps:

	[...]
	<build>
		<plugins>
			[...]
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>maven-jetty-plugin</artifactId>
				<version>6.1.10</version>
				<configuration>
					<scanIntervalSeconds>10</scanIntervalSeconds>
				</configuration>
			</plugin>
			[...]
		</plugins>
	</build>
	[...]

Why is this soooo much XML?

Eclipse integration

Maven has support for integrating a project into Eclipse via the Maven-Eclipse-Plugin:

# configure your workspace (do not use ~ to point to your home!)
mvn eclipse:configure-workspace -Declipse.workspace=../
# create Eclipse files
mvn eclipse:eclipse

These tasks add a M2_REPO classpath variable to your Eclipse environment that points to your local Maven repository and creates a .project and .classpath file from the existing pom. Just import the project with Import > General > Existing Projects into Workspace and your done.

better with Eclipse plugins

Since there are mature Eclipse plugins for Maven and Jetty, you should consider installing these from their update sites:

It’s never easy doing stuff from scratch, but Maven should help flatten the rocky path to Java projects. Instead it piles up another Mount Everest of complexity to climb for a Java developer…

additional information

Check out my github profile for a working example project that was created using these steps.

Maven - development for morons made easy!

(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…