Connecting Ruby on Rails to Oracle on an Intel Mac in Leopard (Mac OSX 10.5)
Nov 19 ’07
NOTE: This tutorial has been superseded by a newer version that takes advantage of the newly-released Intel Mac version of the Oracle InstantClient. The new version is much, much simpler, and causes far fewer headaches.
Updated (12/11/07): The ruby-oci8 library just went to full 1.0.0 release. I've updated that section to reflect the new file names.
Updated (5/5/08): The oracle adapter installation has been on-again-off-again with successive Rails releases, but there's an easy fix for it. I've updated the relevant section.
At my new job, I'm using Ruby on Rails to connect to multiple databases — multiple Oracle (10g) instances, as well as MySQL and FileMaker. There's a lot of challenges to doing this, so I'm going to post some of the less obvious solutions as they come up.
The first challenge I had was to get RoR to talk to Oracle. There isn't a lot of information about this online, because the people who tend to use Oracle are not the people who tend to use open source software like Rails.
Upgrade Rails to version 2.0
To start out, I wanted to use the most recent release candidate of Rails: version 1.99. I figured that I might as well upgrade before writing my first application, so that the codebase is all Rails 2.0 and up. Leopard comes with both Ruby and Rails, and upgrading is actually very easy:
sudo gem install rails --source http://gems.rubyonrails.org
Then enter "y" at each prompt, to install all of the dependencies. That's it! Check yourself with
You should see Rails 1.99.0 or Rails 2.0.
Getting Rails to talk to Oracle
Now, install the oracle adapter:
sudo gem install activerecord-oracle-adapter --source http://gems.rubyonrails.org
This doesn't, however, install the Ruby oci8 driver. We'll do that now, to let the Oracle adapter talk to Oracle.
Make sure you've got the Oracle Instant Client SDK installed in your Oracle Instant Client directory (/Library/Oracle/instantclient/10.1.0.3)
There's a problem on new Intel Macs with this library. Oracle hasn't seen fit to release an Intel version of the library in the past year and a half, and that's going to cause problems since the Ruby binary is going to run as Intel-native, but the Instant Client will run under Rosetta. To solve this, we have to make a PPC version of the Ruby binary.
Make PPC and fat versions of Ruby
This is easier than it sounds. We don't have to recompile, since the Ruby binary in /user/bin is fat. You can check this:
file `which ruby`
This should show you a PPC and a i386 version:
/usr/bin/ruby_fat: Mach-O universal binary with 2 architectures /usr/bin/ruby_fat (for architecture ppc7400): Mach-O executable ppc /usr/bin/ruby_fat (for architecture i386): Mach-O executable i386
All we have to do is extract the PPC version as a standalone binary, and trick the system into using that only. It will make our Ruby slower, but it will get Ruby to talk to Oracle, so I'm willing to take the performance hit. It's so fast on the Intel Macs anyway, unless your Rails apps are doing massive amounts of processing, you probably won't notice.
We'll use ditto to extract the PPC version:
sudo ditto -arch ppc7400 /usr/bin/ruby /usr/bin/ruby_ppc sudo mv /usr/bin/ruby /usr/bin/ruby_fat
Now we've got two versions of the Ruby binary — the original fat version, and a PPC only version.
There's a catch here, though: we actually have two copies of the ruby binary on our system. The second binary is a part of the Ruby Framework, and that's the binary that's used by irb, rake and other very useful Rails helpers, so let's fix this one too:
sudo ditto -arch ppc7400 /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby_ppc sudo mv /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby_fat
Now we'll need a facility for switching between the fat and PPC only binaries. Create /usr/bin/ppc_ruby.sh:
#!/bin/bash ln -fs /usr/bin/ruby_ppc /usr/bin/ruby ln -fs /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby_ppc /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
#!/bin/bash ln -fs /usr/bin/ruby_fat /usr/bin/ruby ln -fs /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby_fat /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
Now, make them both executable:
sudo chmod 0711 /usr/bin/ppc_ruby.sh sudo chmod 0711 /usr/bin/fat_ruby.sh
Run ruby_ppc.sh to switch to using the PPC version:
Verify this worked:
file `which ruby`
This should return /usr/bin/ruby: Mach-O executable PPC.
We're getting closer. Now we need the ruby-oci8 library.
Compile the ruby-oci8 library
The most recent stable version of the oci8 library is 1.0.0. Download it and unpack the file in the finder: it should unzip into ~/Downloads/ruby-oci8-1.0.0.
Before we can compile this library, we have to fool the system into making it PPC only, or it's not going to work. With your favorite text editor, edit /usr/lib/ruby/1.8/universal-darwin9.0/rbconfig.rb. Comment out line 17 ('-arch ppc -arch i386' and replace it with '-arch ppc'. Save your changes. Leave the file open in your text editor, because we'll come back to it.
Now we can finish configuring the environment before we compile the library.
cd ~/Downloads/ruby-oci8-1.0.0 export DYLD_LIBRARY_PATH=/Library/Oracle/instantclient/10.1.0.3 export SQLPATH=/Library/Oracle/instantclient/10.1.0.3 ruby setup.rb config make sudo make install
Now you can go back to your text editor and restore line 17, save your changes and exit.
At this point, we're done. We've fooled our system into running a PPC version of Ruby under Rosetta, and we've installed the ruby-oci8 library. Now it's a question of making sure that it works:
In the IRb console, type:
If the console returns true, you're in business.
Configure your database.yml
The last step is to make your application use the Oracle connection. In your database.yml, use the following to make it work:
development: adapter: oracle database: your_instance_name username: your_user_name password: your_password
The database name comes straight out of your /Library/Oracle/instantclient/10.1.0.3/network/admin/tnsnames.ora file. You don't need to specify any other connection information in database.yml, since the tnsnames.ora file has everything you need.
Your application is now talking to Oracle!
Start the conversation
* indicates a required field