Connecting Ruby on Rails to Oracle on an Intel Mac in Leopard (Mac OSX 10.5)
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
rails –v
You should see Rails 1.99.0 or Rails 2.0.
Getting Rails to talk to Oracle
Now,
sudo gem install activerecord-oracle-adapter --source http://gems.rubyonrails.org
This installs the oracle adapter. It doesn’t, however, install the Ruby oci8 driver. We’ll do that now.
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
Create /usr/bin/fat_ruby.sh:
#!/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:
sudo /usr/bin/ppc_ruby.sh
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.
Test connectivity
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:
ruby /usr/bin/irb
In the IRb console, type:
require 'oci8'
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!
Fritillaria pudica (Liliaceae); Yellow bells

So you’ve convinced them to go with Ruby on Rails?
Good for you!
Not quite yet, Mr. Vanvox. First step is the proof of concept — I’ve got to show that it’s doable. Then we can say that this is the right way to go.
Worked for me with a few slight modifications. I am very happy with this, well done!
Those who are still on Tiger or who want to compile Ruby by themselves can use very similar guidelines that I have posted previously.
This totally rocks. Thanks a ton.
Great post, Thanks!
Thanks for the walk-through. For posterity, I had to do this:
$ cd /Library/Oracle/instantclient/10.1.0.3
$ ln -s libclntsh.dylib.10.1 libclntsh.dylib
Thanks again.
Thanks man. You probably just saved thousands of wasted man hours around the globe. Too bad there’s no way to convert that to carbon credits or something. I was about to abandon running from OSX and just develop from my Linux VM fusion inside of OSX ( which is where the oracle server is running ). But now I can stay in OSX for my rails dev.
Russell: I just discovered that Oracle has announced that they’re finally porting the libraries to Intel-native. One of their guys said so on the Oracle Mix site (login required). It’s slated for Q1 or Q2 of 2008, which is to say real soon.
Oh cool. We’ll have occasion to exercise the ‘fat_ruby.sh’ script perhaps
The upcoming intel-native instant client is for Intel 10.5.x Leopard only. Would there be a way to use/change/hack that to work on OSX Intel 10.4.10 Tiger so that Instant Client and Ruby are both running Intel native?
Randy: I have no idea. I haven’t seen any news about it since February (when I posted comment #9). Of course, that’s because I haven’t looked.
Really, it’s up to how Oracle is building/compiling/releasing the package. That will determine if there’s a hack to make it work on Tiger.
Brent: I just got oci8 to work on my intel osx 10.4.10 (Dell Inspiron hackintosh) not more than 10 minutes ago!! I feel like I’ve run a marathon.
Now I have to deal with migration errors (can’t create a table… rake aborted! wrong number of arguments 1 for 0). But as long as I can connect with ruby compiled as ppc, I should be able to figure migrations out.
Congrats! I hope you didn’t spend all day working on it, though. That would be quite the Marathon. What did you have to change to get my solution to work? A few path names here and there? If you wouldn’t mind posting some of those, I’m sure future readers would be appreciative.
I opened up rbconfig.rb and found this code at line 17:
if e = ENV['ARCHFLAGS']
e
elsif e = ENV['RC_ARCHS']
e.split.map { |a| “-arch #{a}” }.join(’ ‘)
else
‘-arch ppc -arch i386′
end
If you don’t feel brave enough to edit this file, you can simply add this export to your compilation of the instant client:
cd ~/Downloads/ruby-oci8-1.0.0
export ARCHFLAGS=’-arch ppc’ # ADD THIS LINE!
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
I got stuck on the last part where I’m building the oci library. Typed “ruby setup.rb config” and got this error message:
Get the version of Oracle from SQL*Plus… —————————————————
error messages:
cannot get Oracle version from sqlplus
—————————————————
Any idea how I can solve this?
Hmmm…. I haven’t seen this problem yet. What versions of things are you using? (oci8, oracle, ruby)
@Brent:
My bad, I didn’t adjust the path name of the oracle instant client. My dir. was named differently, so once I fixed that it’s all good.
Thanks a lot for the tutorial!
Excellent!
I’m glad the tutorial helped you out.
I’m on Windows (ugh, at work, Mac guy at home :)) but I’m having difficulty finding good Rails/Oracle resources. Using the activerecord-oracle-adapter with OCI8.
Doing a simple ActiveRecord find I’m getting strange column types returned:
=> #<LRU id: #, name: “name”, volts: #, amps: #,weight:#<BigDecimal:32e2f90,
I also have some elaborate queries in my Rails app so I’m executing the SQL with ActiveRecord::Base.connection().execute(”SQL”)
But when I try to iterate through the returned array of objects I noticed I’m getting an object returned of type OCI8::Cursor.
“undefined method `each’ for #”
Any thoughts?
Matthew: What you’re encountering straight-up OracleAdapter stuff. The strange types are simply how the adapter maps Oracle data types into Ruby data types. They’re a bit weird, but nothing to worry about. I’ve run into the same things. They’re not broken; they’re just annoying because in the console you can’t easily read the numbers. You could write custom my_model.to_s or my_model.inspect methods to make the numbers come out nicer.
The OCI8::Cursor problem is a bit more complicated. If you do any straight-up PL/SQL stuff you use cursors a lot: it’s the preferred way for looping through result sets. The Oracle adapter does the same thing. If your returned object is called “foo”, instead of doing
foo.each{|f| f.do_something}you have to do something like this:For a more formal view of how to approach this, open up the
oracle_adapter.rbfile (in your ruby gems directory) and check out theselectmethod: it’s got a full typecasting process.In fact, since you’re already writing your own SQL, there’s a shortcut you could take. To avoid writing custom code that’s simply duplicating the select method, you could do something like this:
Like that you get your custom SQL, but you don’t have to mess with the Oracle cursors.
Brent, thanks for the response, I was able to drop in the ActiveRecord::Base.connection.send(:select, my_custom_sql_query) statement and everything slid right into place and I didn’t need to modify any of my views or anything or need to fuss with the cursors (this is just simple data I’m returning).
Thanks again for the response and for being a decent resource (Rails/Oracle resources seem to be lacking).
Take care
The Intel version of Oracle Instant Client is now available since April 27!
Can someone give instructions to rollback the changes? Specially the ruby-oci8 PPC compilation. I want to configure a full-Intel environment and need to recompile back ruby-oci8 library to the Intel version.
Thanks!
Gerbi: I put up newer instructions, including a rollback section, in a more recent post.