Sunday, January 11, 2009

Freezing activerecord-jdbc Gems into a Rails Project

Over the Christmas break a Slovak friend of mine (Hi Martin!) asked me to build a simple book library management app for a school in the Philippines where he's been volunteering for the past year. I thought to my self that if someone can volunteer one year of his life in such an amazing way, I could spend a few hours to help him out too.

Since from his description it was obvious that he was looking for a low maintenance solution, I though that a rails application with an embedded database would be a good choice. I worked with derby (JavaDB) in the past and I knew that derby drivers were already available as an active-record adapter gem, so I thought that it would be pretty simple to set up dev environment using Rails, JRuby, and embedded derby db. Surprisingly there were a few issues along the way.

I started with defining the database config in config/database.yml:
adapter: jdbcderby
database: db/library_development
pool: 5
timeout: 5000
The database files for the dev db will be stored under RAILS_ROOT/db/library_development

Secondly I specified the gem dependency in config/environment.rb (you gotta love this Rails 2.1+ feature): do |config|
config.gem "activerecord-jdbcderby-adapter", :version => '0.9', :lib => 'active_record/connection_adapters/jdbcderby_adapter'
Note that you must specify the :lib parameter, otherwise Rails won't be able to initialize the gem and you'll end up with:
no such file to load -- activerecord-jdbcderby-adapter
So far so good. Now let's install the gems we depend on:
$ jruby -S rake gems:install
(in /Users/me3x/Development/library)
rake aborted!
Please install the jdbcderby adapter: `gem install activerecord-jdbcderby-adapter` (no such file to load -- active_record/connection_adapters/jdbcderby_adapter)

(See full trace by running task with --trace)
Huh? I asked rake to install gems and I get an error that I need to install gems first? It turns out that this error comes from ActiveRecord, which tries to initialize db according to database.yml, and only then environment.rb gets to be read.

Ok, so let's install the db dependencies manually:
$ sudo jruby -S gem install activerecord-jdbcderby-adapter
JRuby limited openssl loaded. gem install jruby-openssl for full support.
Successfully installed activerecord-jdbc-adapter-0.9
Successfully installed jdbc-derby-
Successfully installed activerecord-jdbcderby-adapter-0.9
3 gems installed
Installing ri documentation for activerecord-jdbc-adapter-0.9...
Installing ri documentation for jdbc-derby-
Installing ri documentation for activerecord-jdbcderby-adapter-0.9...
Installing RDoc documentation for activerecord-jdbc-adapter-0.9...
Installing RDoc documentation for jdbc-derby-
Installing RDoc documentation for activerecord-jdbcderby-adapter-0.9...
Cool, let's check if all the dependencies are available:
$ jruby -S rake gems
(in /Users/me3x/Development/library)
- [I] activerecord-jdbcderby-adapter = 0.9
- [I] activerecord-jdbc-adapter = 0.9
- [I] jdbc-derby =

I = Installed
F = Frozen
R = Framework (loaded before rails starts)
Yay, all dependencies are installed.

In the past when dependencies couldn't be declared in environment.rb, I found developing with frozen rails and gems much more manageable, especially when the app is being developed by more than one person. This also made for less deployment surprises. With the config.gem defined dependencies, the situation changes quite a bit, but there are situations when it still makes sense to freeze gems into the project. So let's freeze the gems:
$ jruby -S rake gems:unpack:dependencies
(in /Users/me3x/Development/library)
WARNING:  Installing to ~/.gem since /usr/local/jruby/jruby-1.1.6/lib/ruby/gems/1.8 and
   /usr/local/jruby/current/bin aren't both writable.
Unpacked gem: '/Users/me3x/Development/library/vendor/gems/activerecord-jdbcderby-adapter-0.9'
Unpacked gem: '/Users/me3x/Development/library/vendor/gems/activerecord-jdbc-adapter-0.9'
Unpacked gem: '/Users/me3x/Development/library/vendor/gems/jdbc-derby-'
Looks good, let's check it:
$ jruby -S rake gems
(in /Users/me3x/Development/library)
- [F] activerecord-jdbcderby-adapter = 0.9
- [F] activerecord-jdbc-adapter = 0.9
- [F] jdbc-derby =

I = Installed
F = Frozen
R = Framework (loaded before rails starts)
Nice all the dependencies are now frozen!


sb said...

Thank you so much. This was a clean hint to my problem. And now it works.

rweeks said...

Hey Igor - thanks for this tip - it helped me today at my new digs.

Robert B. Weeks