MetaGreg

writes code that writes code for food

Archive for the ‘Geekiness’ Category

Deploy a Rails 3, Sqlite3 application in Tomcat using JRuby

with 4 comments

and have a Ruby version running side-by-side.

A few months ago I got interested in JRuby while researching for text mining algorithms. I found some gems but they are either unmaintained or inadequate while the mature libraries I found were written in Java. No problem! JRuby to the rescue. Thank God.

Next stop, I decided to take Rails 3 and JRuby for a spin. Incidentally, I will be on a 3-city Rails tour in the Philippines this September and since there are many Filipino Java developers, they might find it interesting to see their favorite Java platform works nicely with Ruby on Rails.

Setup

I will be using the following for this tutorial:

java 1.6 + JDK
tomcat 7.0.2
rvm 1.0.1
jruby 1.5.0
ruby 1.9.2p0

Further below, I outline how to install these software. First, let’s see my current environment.

$ more /etc/issue
Ubuntu 9.10 \n \l

$ java -version
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) Server VM (build 16.3-b01, mixed mode)

$ rvm -v
rvm 1.0.1 by Wayne E. Seguin (wayneeseguin@gmail.com) [http://rvm.beginrescueend.com/]

$ jruby -v
jruby 1.5.0 (ruby 1.8.7 patchlevel 249) (2010-05-12 6769999) (Java HotSpot(TM) Client VM 1.6.0_20) [i386-java]

$ TOMCAT/bin/version.sh
Using CATALINA_BASE:   /usr/local/apache-tomcat-7.0.2
Using CATALINA_HOME:   /usr/local/apache-tomcat-7.0.2
Using CATALINA_TMPDIR: /usr/local/apache-tomcat-7.0.2/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /usr/local/apache-tomcat-7.0.2/bin/bootstrap.jar:/usr/local/apache-tomcat-7.0.2/bin/tomcat-juli.jar
Server version: Apache Tomcat/7.0.2
Server built:   Aug 4 2010 12:23:47
Server number:  7.0.2.0
OS Name:        Linux
OS Version:     2.6.31-22-generic
Architecture:   i386
JVM Version:    1.6.0_20-b02
JVM Vendor:     Sun Microsystems Inc.

$ ruby -v
ruby 1.9.2p0 (2010-08-18 revision 29036) [i686-linux]

Install JDK and Tomcat

$ aptitude install curl sun-java6-bin sun-java6-jre sun-java6-jdk
$ wget  http://apache.mobiles5.com/tomcat/tomcat-7/v7.0.2-beta/bin/apache-tomcat-7.0.2.tar.gz
$> tar zxvf apache-tomcat-7.0.2.tar.gz
$> mv apache-tomcat-7.0.2 /usr/local

Of course, these assume you want to use 7.0.2 and you want it installed at your /usr/local.

Install JRuby, Rails 3

I assume you already have rvm installed. If not, I highly recommend that you do. I can’t imagine a Ruby developer not using rvm :)

$ rvm install jruby
$ rvm jruby
$ rvm gemset create railsjam
$ rvm jruby@railsjam
$ gem install rails

Try a sample app

I’ve created sample app for the RailsJam tour. This have several functionalities already and better than creating a Rails app from scratch.

$ git clone git://github.com/gregmoreno/railsjam.git

Update the Gemfile

You need a separate set of gems to make your Rails 3 application work with JRuby. For learning purposes, I want my Rails 3 application to work other than JRuby. To accomplish that, we need to specify what gems are needed solely by JRuby.

source 'http://rubygems.org'

gem 'rails', '3.0.0'

if defined?(JRUBY_VERSION)
  gem 'jdbc-sqlite3'
  gem 'activerecord-jdbc-adapter'
  gem 'activerecord-jdbcsqlite3-adapter'
  gem 'jruby-openssl'
  gem 'jruby-rack'
  gem 'warbler'
else
  gem 'sqlite3-ruby', :require => 'sqlite3'
end

(A copy of this Gemfile is available at the ‘jruby’ folder of the railsjam application.)

Now, it’s time to intall the gems.

# Must do this. Otherwise,  bundle picks up wrong version of jdbc
$ rm Gemfile.lock
$ jruby -S bundle install

Prepare the database.

The first time I worked on this tutorial, I needed to specify the jdbcsqlite3 as the database adapter. However, when I tried the tutorial on the same machine with a fresh gemset, it worked pretty well with just ‘sqlite3’. Just to be sure, I modified ‘database.yml’ to check for JRuby.

development:
  adapter: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
  database: db/development.sqlite3
  pool: 5
  timeout: 5000

production:
  adapter: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
  database: /home/greg/dev/railsjam/db/development.sqlite3
  pool: 5
  timeout: 5000

When you deploy to Tomcat, it will be on ‘production’ mode by default. Since sqlite3 is file based and for simplicity, I used the same development database.

Now, do the migration.

$ jruby -S rake db:migrate

Deploy to Tomcat

We use ‘warble’ which is an excellent tool for packaging your Rails application. It packages everything you need to run your Rails application inside a Java container.

$ warble
$ cp railsjam.war  $TOMCAT/webapps

# start Tomcat
# assuming you arein $TOMCAT dir
$ sudo ./startup.sh

Check your Rails 3 application

# You should see the famous Rails welcome
localhost:3000/railsjam

# Play around with your application
localhost:3000/railsjam/users

Deploy Rails 3 using Ruby 1.9.2

Without shutting down your JRuby and Tomcat version, let’s try to run our app using Ruby 1.9.2

# In a new console
$ rvm 1.9.2
$ rvm gemset create railsjam
$ rvm 1.9.2@railsjam
$ gem install rails

# Assuming you are in the ‘railsjam’ folder
# This will install sqlite3-ruby gem
$ bundle install

$ rails server

Now, go play with your Rails 3 applications

# jruby + tomcat

http://localhost:8080/railsjam/users

# ruby 1.9.2

http://localhost:3000/users

In case you encountered some problems, here are some ways to solve them. If your problem is not listed here, you can email me. I only accept Paypal :)

JRuby does not support native extensions

You did not update the Gemfile to use the jdbc version of sqlite3. You will encounter this error when you install the gems.

$ bundle install
....
Installing sqlite3-ruby (1.3.1) with native extensions /home/greg/.rvm/rubies/jruby-1.5.2/lib/ruby/site_ruby/1.8/rubygems/installer.rb:482:in `build_extensions': ERROR: Failed to build gem native extension. (Gem::Installer::ExtensionBuildError)

/home/greg/.rvm/rubies/jruby-1.5.2/bin/jruby extconf.rb
WARNING: JRuby does not support native extensions or the `mkmf' library.
         Check http://kenai.com/projects/jruby/pages/Home for alternatives.
extconf.rb:9: undefined method `dir_config' for main:Object (NoMethodError)

undefined method `attributes_with_quotes’ for class `ActiveRecord::Base’

I first encountered this problem when doing migration.

$ rake db:migrate
rake aborted!
undefined method `attributes_with_quotes' for class `ActiveRecord::Base'

This is caused by an old version of your jdbc gems. In my case, sometimes bundler installs the old versions:

Installing activerecord-jdbc-adapter (0.9.2)
Installing activerecord-jdbcsqlite3-adapter (0.9.2)

As of this writing, the latest version is 0.9.7

Installing activerecord-jdbc-adapter-0.9.7-java
Installing activerecord-jdbcsqlite3-adapter-0.9.7-java

Bundler keeps installing 0.9.2

$ rm Gemfile.lock
$ jruby -S bundle install

no such file to load — sqlite3

$ rake db:migrate
(in /home/greg/dev/projects/jruby/railsjam)
rake aborted!
no such file to load -- sqlite3

‘sqlite3’ is the default name of the database adapter but with jruby, it should be ‘jdbcsqlite3’. (another) But, when I tried ‘sqlite3’ with a fresh gemset and a new machine, it went well. Anyway, just in case you run into the same problem in the future, add a condition in your database.yml

development:
  adapter: <%= defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3' %>
  database: db/development.sqlite3
  pool: 5
  timeout: 5000

We’re sorry, but something went wrong.

If you see the famous Rails error message, you need to dig in Tomcat’s log files.

$ cd /usr/local/apache-tomcat-7.0.2/logs
$ ls -al localhost*

-rw-r--r-- 1 root root 1181 2010-09-01 00:17 localhost.2010-09-01.log
-rw-r--r-- 1 root root 1062 2010-09-01 00:18 localhost_access_log.2010-09-01.txt

$ tail -f localhost.2010-09-01.log

In the log file, you will see the errors like missing database.

org.jruby.rack.RackInitializationException: The driver encountered an error: java.sql.SQLException: path to ‘/home/greg/dev/tmp/apache-tomcat-7.0.2/webapps/railsjam/WEB-INF/db/production.sqlite3′: ‘/home/greg/dev/tmp/apache-tomcat-7.0.2/webapps/railsjam/WEB-INF/db’ does not exist

Written by Greg Moreno

September 1st, 2010 at 5:08 pm

Posted in Geekiness

Tagged with , , ,

Rails 3 upgrade part 4: Prototype helpers and Javascript

with one comment

Rails 3 is embracing the unobtrusive Javascript (or UJS) mantra which is good because it is the right way; at the same time, it is bad because many applications will break when they upgrade to Rails 3. On the other hand, who’s expecting a smooth upgrade anyway :)

In my test application, I used jrails because I am more interested in jQuery than Prototype. But since jrails doesn’t work with Rails 3, I removed it.

When jrails was removed, I received this error:

undefined method `observe_field' for #<#<Class:0xb6867e58>:0xb6865b6c>

Install Prototype helper plugin

observe_field’ is a Prototype helper and Rails 3 removed the the link between its Javascript helpers and Prototype. The goal in Rails 3 is for developers to use their preferred Javascript library. Also note that remote_#{method} helpers have been removed from Rails and moved to Prototype Legacy Helper plugin . To install this plugin, just do:

rails plugin install git://github.com/rails/prototype_legacy_helper

Remove jQuery

Once the prototype_legacy_helper is installed, the missing method is gone but observe_field is not triggering. Removing jQuery fixes this problem.

Now what if you want to use jQuery instead of Prototype? It depends how dependent your application is to Prototype. I have not found a jQuery equivalent for Prototype helper plugin yet so that would be an issue like in my case. Based on this jQuery and Rails 3 tutorial, using the jQuery UJS driver looks very easy.

Previous: Rails 3 upgrade part 3: Code fixes, views, and forms

Written by Greg Moreno

August 24th, 2010 at 6:44 pm

Posted in Geekiness

Tagged with , ,

Rails 3 upgrade part 2: Routes

with 3 comments

In the previous post, I outlined the steps I took to upgrade and boot a Rails 3 application. This time, I share my experience upgrading the routes file. By the way, I forgot to mention in the last post that I’m using Rails 3 Upgrade Handbook by Jeremy McAnally.

The task rails:upgrade:routes (comes with the rails_upgrade plugin) converts your Rails 2 routes into Rails 3 format. It handles most cases but you may still need to edit the generated routes depending on your setup.

map.root

Below, I show the old route and the generated version.

# Rails 2
map.root :controller => 'search'

# Rails 3
match '/' => 'search#index'

The conversion is correct but since I use the named route ‘root_path’ in my application, I had to change it:

root :to => 'search#index'

:as, :member, :any, :path_names

# Rails 2
map.resources :workspaces, :as => 'b', :member => { :widget => :get } do |workspace|
  # ...
end

# Rails 3
resources :workspaces do
  # ...
end

In Rail 3, :as is for overriding the normal naming for named routes witout affecting the path. For example, the code below will recognize the path ‘/workspaces’ and the named route becomes offices_path.

resources :workspaces, :as => "offices"

In Rails 2, :as affects the path. In my example, ‘/b’ routes the request to WorkspacesController. So for Rails 3 to recognize the path ‘/b’, I need to add another route.

match 'b' => 'workspaces#index'

The rails:upgrade:routes did not convert the following member route and had to be added.

:member => { :widget => :get }

The new route becomes:

resources :workspaces do
  get :widget, :on => :member
end

In Rails 2, you can use the :any option to define a custom route that responds to any request method.

# Rails 2
workspace.resource :twitter_account, :member => { :authorize => :any }, :path_names => { :edit => 'request_authorization' }

# generated by rails:upgrade:routes
resource :twitter_account do
  member do
    any :authorize
  end
end

The rails:upgrade:routes converted the :any option. However, when I booted the application, it raised an exception:

undefined method `any' for #<ActionDispatch::Routing::Mapper:0xb71b6fcc> (NoMethodError)

To fix this, I replaced the offending line with a match method.

resource :twitter_account do
  match :authorize, :on => :member
end

:path_names was also not included in the generated route so has to be added as well.

resource :twitter_account, :path_names => { :edit => 'request_authorization' } do
  match :authorize, :on => :member
end

Specifying a different controller

# Rails 2
map.resource :settings, :controller => 'users' do |settings|
  settings.resource :twitter_account, :name_prefix => nil, :member => { :authorize => :any }, :path_names => { :edit => 'request_authorization' }
end

# generated by rake:upgrade:routes
resource :settings do
  resource :twitter_account do
    member do
      any :authorize
    end
  end
end

To fix, just specify the controller

resource :settings,:controller => :users do
  # ...
end

Undefined named route helper

I encountered this exception while trying the application:

undefined method `edit_twitter_account_path'

In Rails 2, this is the route that created this named route:

map.resource :settings, :controller => 'users' do |settings|
  settings.resource :twitter_account, :name_prefix => nil, :member => { :authorize => :any }, :path_names => { :edit => 'request_authorization' }
end

This is a bit tricky for me because I can’t remember why I nested it :) Nevertheless, to fix the Rails 3 error, I moved :twitter_account outside of :settings. The correct Rails routes now look like these:

resource :settings, :controller => :users
resource :twitter_account, :path_names => { :edit => 'request_authorization' } do
  match :authorize, :on => :member
end

Custom polymorphic named route helper

A long time ago, I played around with polymorphic paths. In hindsight, that is a waste of time but back then it was fun or should I say a time well wasted. I have a named route helper that takes any object and used like this:

# in views
link_to 'invitations', invitations_path(@voteable)

# definition
module RoutesHelper
  def invitations_path(voteable)
    send("#{voteable.class.name.underscore}_invitations_path", voteable)
  end

  def workspace_invitations_path(workspace)
    super(:workspace_id => workspace)
  end

  # ...
end

In Rails 3, my named route helper is not being called. Thus, wrong URL is generated. I know, I know it should have been a simple polymorphic_path call but I still wonder why my method is not called. Moving on, the new ruby is:

link_to 'invitations', polymorphic_path([@voteable, :invitations])

I cheated a bit here because I want this post to focus on routes. Along the way, I had to update non-route related code to discover the route problems. You can learn more about Rails 3 routes from this RailsGuides page.

There are still more updates to be done and I’ll share them in other posts. Just like your favorite late night infomercial, “Wait! There’s more”.

Next: Rails 3 upgrade part 3: Code fixes, views, and forms

Previous: Rails 3 upgrade Part 1: Booting the application

Written by Greg Moreno

August 12th, 2010 at 2:40 pm

Posted in Geekiness

Tagged with , ,

Rails 3 upgrade part 1: Booting the application

with 3 comments

It’s time for another Rails upgrade! We all have our share of bad experiences and frustrations every time we upgrade a piece of software. Even for technical people who live and breath on the edge, upgrades are one of these things we try to avoid as much as possible. Still, there is always a sense of excitement in trying something new even if it adds problems to an already stable piece of code.

For a little background, I am upgrading a Rails app several of friends and I have written last year. The code is available at github.

In this post, I share the steps I did to boot the application. This doesn’t mean the upgrade went fine neither the app is ready to go. It only means all the required initialization are OK. In succeeding posts, I share my experiences in upgrading the app to a green state.

First, my environment.

greg@piccolo:~/dev/projects/propsify3$ rvm info
ruby-1.8.7-p299@propsify:

  system:
    uname:        "Linux piccolo 2.6.31-22-generic #61-Ubuntu SMP Wed Jul 28 01:57:06 UTC 2010 i686 GNU/Linux"
    shell:        "bash"
    version:      "4.0.33(1)-release"

  rvm:
    version:      "rvm 0.1.44 by Wayne E. Seguin (wayneeseguin@gmail.com) [http://rvm.beginrescueend.com/]"

  ruby:
    interpreter:  "ruby"
    version:      "1.8.7"
    date:         "2010-06-23"
    platform:     "i686-linux"
    patchlevel:   "2010-06-23 patchlevel 299"
    full_version: "ruby 1.8.7 (2010-06-23 patchlevel 299) [i686-linux]"

greg@piccolo:~/dev/projects/propsify3$ script/about
About your application's environment
Ruby version              1.8.7 (i686-linux)
RubyGems version          1.3.7
Rack version              1.0 bundled
Rails version             2.3.2
Active Record version     2.3.2
Action Pack version       2.3.2
Active Resource version   2.3.2
Action Mailer version     2.3.2
Active Support version    2.3.2
Application root          /mnt/hgfs/greg-mini/dev/projects/propsify
Environment               development
Database adapter          postgresql
Database schema version   20100113032723

greg@piccolo:~/dev/projects/propsify3$ gem list

*** LOCAL GEMS ***

actionmailer (2.3.2)
actionpack (2.3.2)
activerecord (2.3.2)
activeresource (2.3.2)
activesupport (2.3.2)
geokit (1.5.0)
json (1.4.5)
mime-types (1.16)
oauth (0.4.1)
pg (0.9.0)
rails (2.3.2)
rake (0.8.7)
RedCloth (4.2.2)
twitter_oauth (0.3.2)

greg@piccolo:~/dev/projects/propsify3$ ls vendor/gems/
authlogic-2.1.3  geokit-1.5.0  haml-2.2.16  macaddr-1.0.0  twitter_oauth-0.3.2  uuid-2.1.0

greg@piccolo:~/dev/projects/propsify3$ ls vendor/plugins/
acts_as_commentable        geokit-rails     is_taggable   thinking-sphinx      will_paginate
declarative_authorization  gravatar-plugin  jrails        validates_date_time
exception_notification     haml             subdomain-fu  vote_fu

Step 1: Install rails 3

gem install rails --pre

Step 2: Install the plugin tool

script/plugin install git://github.com/rails/rails_upgrade.git

Step 3: Show upgrade checklist

rake rails:upgrade:check

This task lists the items you should watch out for when doing the upgrade. You don’t need to fix everything right away (some are deprecation notice) but review the checklist nevertheless.

Step 4: Generate the new routes

rake rails:upgrade:routes

This task reads the current config/routes.rb and outputs a Rails 3 version.
Don’t worry, it doesn’t override your routes file. Keep this in a safe place for later use.

IMPORTANT: I actually didn’t realize I did the right thing until after the actual code upgrade. When I tried generating the new routes after the code change, it outputted an empty block. I have no idea if this is unique to my case but just to be sure, generate the routes beforehand and keep a copy.

Step 5: Create Gemfiles

rails:upgrade:gems

Next is to generate the file ‘Gemfile’. In Rails 2, the gems you need are listed in config/environment.rb while in Rails 3 the gems are listed in the Gemfile. Gemfile is used by the program ‘bundler’ to manage the gems required by your application. Unfortunately, this task didn’t include the gems I listed in environment.rb so I have to add it later.

Step 6: Backup your files

rails:upgrade:backup

I hope you are working on another branch (or a copy) but just in case you are not, run this task to make copies of the files that will be affected during the upgrade.

Now comes the juicy part.

Step 7: Generate the Rails 3 app on top of your Rails 2 app

rails new propsify3 -d postgresql

Run this command in your app’s parent folder. In my case, my app’s name and pathname is ‘propsify3′ and I am using postgresql as my database. This command created and replaced a bunch of files. Since you’ve backed-up everything, there’s nothing to worry.

Step 8: Move code from environment.rb to application.rb

Your new config/environment.rb file looks like it went through a rigorous diet. You can leave this file for now. What is important now is you move the initializer code from your config/environment.rb.rails2 to config/application.rb. These are the config.* lines except the config.gem which goes to Gemfile.

Step 9: Convert the new routes

You can still use the existing routes until 3.1 but since there’s a tool to help you migrate, I suggest doing it. At this point, when I tried the rails:upgrade:routes, no routes were generated. So make sure you generate the routes before Step 7.

Step 10: Delete new_rails_defaults.rb

rm config/initializers/new_rails_defaults

Step 11: Upgrade the plugins and gems

Many plugins are now available as gems. Check your plugins and gems at http://railsplugins.org. In my case, the following plugins were converted to gems:

acts_as_commentable
declarative_authorization
haml
will_paginate
thinking-sphinx

Unfortunately, the plugins below are not yet ready for Rails 3. I removed them for now and all code that references them.

jrails
subdomain-fu
vote_fu

IMPORTANT: In your Gemfile, make sure you check specify the right version that is compatible with Rails 3. Some gems are still in the pre-release version and will not be downloaded if you don’t specify a version in your Gemfile. For example, this is a snippet from my Gemfile:

gem 'pg'
gem 'acts_as_commentable'
gem 'declarative_authorization'
gem 'haml'
gem 'thinking-sphinx', '2.0.0.rc1', :require => 'thinking_sphinx'
gem 'will_paginate', '3.0.pre2'
gem 'uuid'
gem 'geokit'

Step 12: Update initialization code

After step 10 you are good to go, if you’re lucky. In my case, I had to remove some patches and change code to boot the application.

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(date_time_formats)

This fails in Rails 3 because core extensions have been moved out of their modules and are now included in classes they extend. For example, to fix the date format problem do:

Date::DATE_FORMATS.merge!(date_time_formats)

Step 13: Boot the app

rails server

Yay! If you are wondering what happened to script/server command, Rails went the “Merb way” and consolidated the script/* commands into the rails script.

By now, you should see the famous Rails’ “Welcome aboard” message in your browser.

Step 14: Remove public/index.html

Now, you can try if your application is working.

There are still more work to do like moving to the ActiveRecord/ActiveRelation API and removing the deprecation notices. Before moving on, I still need to fix the problems in my routes and unsupported gems which I will tackle in my next post.

Next: Rails 3 upgrade Part 2: Routes

Written by Greg Moreno

August 11th, 2010 at 12:53 am

Posted in Geekiness

Tagged with , ,

Coding gems 31-40

without comments

#31 All non-trivial abstractions, to some degree, are leaky. Joel Spolsky

#32 Five different programmers can solve the same problem five different ways

#33 Don’t write 200 lines of code when 10 will do

#34 If the “box” is the boundary of constraints and conditions, don’t think outside the box—find the tbox. Andy Hunt, Dave Thomas

#35 Complexity and communication costs rise with the square of the number of developers, while work done rises linearly. Fred Brooks

#36 Tests only prove the presence of errors – not the absence of them

#37 A metaprogrammer is someone who writes code that writes code for food

#38 The task on a project is not to try for complete communication but to manage the incompleteness of our communications. Cockburn

#39 A good plan violently executed now is better than a perfect plan next week. Patton

#40 Tests first, then code…. or the kitten gets it

Written by Greg Moreno

June 23rd, 2010 at 9:48 pm

Posted in Geekiness

Tagged with