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 [email protected]#{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 [email protected] --create
gem install celerity
rvm wrapper [email protected] 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:[email protected]: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!

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!

noSQL – Rails models with SOAP

Using a DB is a natural thing for a Rails developer. Since Rails is a database driven application framework, that does not come as a big surprise. But there are times where environmental constraints do not allow the freedom to use the weapon of choice…

Imagine a legacy Java SOA landscape that provides tons of webservices but does not permit access to a transaction DB. Sounds phoney? Ask your local J2EE consultant!

Working around this constraint, it would be great if one could just wire a SOAP service into Rails as a backing of model data. Using Rails without a database is a little bit tricky, especially if you don’t want to forego the power of ActiveRecord!

so why use Rails then?

There are a lot of people that would say “Why don’t you use Sinatra instead?”.

First of all, most Ruby developers know how to use Rails. The Rails community is large, lively and a great resource for knowledge. Features like REST come for free and nobody want’s to miss model validations. In general, Rails plugins are lazy programmers best friend!

working with ActiveForm

A simple way to get your SOAP backed noSQL model working with ActiveRecord::Validations is probably by using ActiveForm. It provides validations for non ActiveRecord models and is available on github.

You can install the Rails plugin via:

# (re)install from git as a plugin
script/plugin install --force git://github.com/remvee/active_form.git

Using the plugin in your code is simple. Inherit from ActiveForm instead of ActiveRecord::Base:

# app/models/blog.rb
class Blog < ActiveForm
  column :title
  column :message, :type => :text
  
  validates_presence_of :title, :message
  [...]
end

It’s possible to remove all evidence of database connectivity. Just kick ActiveRecord from the list of Rails frameworks and re-add it as a gem (this step is not necessary, so you might skip it and work with Rails sqlite3 default):

# config/environment.rb
Rails::Initializer.run do |config|
  [...]
  config.gem "activerecord", :version => '2.3.5'
  [...]
  config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
  [...]
end

Doing so will allow you to delete the database.yml file in your application.

Savon for multi-tier persistence

Accessing an enterprise SOAP service with Savon is easy and integrating Savon into a Rails model requires just two steps:

  • implementing a to_hash method
  • implementing a save hook

Since Savon communication is based on data hashes, you have to provide a thin mapping layer to convert your model into a request hash that matches your SOAP interface:

# app/models/blog.rb
  def to_hash
    { :data => {:title=>title, :message=>message} }
  end

Pushing the data to the webservice requires some custom ‘persistence’ code to be implemented. A good place for that code should be in one of the model’s save hooks:

# app/models/blog.rb
  def after_save
    client = Savon::Client.new "http://localhost:8080/"

    client.post! do |soap|
      soap.namespace = "urn:savon:blog"
      soap.body = to_hash
    end
  end

Overwriting the after_save method is a neat way to let the model code be readable for other Rails developers. Sticking to conventions is a best practice and reduces complexity greatly!

more information?

There is a working example using a local soap4r server available on github.

no SQL - no problem!