A sign of the success of Ruby on Rails is the size of applications being written with it. Currently, we are working on a project with many interconnecting modules that we split up into separate Rails applications.
However, the business domain was the same among the projects, resulting in the different projects sharing the same database. This article discusses the way we’ve accomplished this.
Create a Lib Project
We first need a place to pull our common classes into. This will not be a Rails application; it will just be a Ruby module that your Rails application will require. Our directory structure will look something like this:
1 /projects/my_lib 2 --> lib 3 --> spec
Now we can move on to the actual refactoring.
Pull the Models Out
The first step is the easiest: pull out the models from the app/models directory of your Rails application into the lib directory of your new library.
In the config/environments.rb file within your Rails application, you will need to add this new directory to your Ruby search path by adding this line to the end of the file.
1 $: << File.dirname(__FILE__) + '/your_lib_path'
More on the Ruby search path later.
Pull the Tests Out
Let’s move on to making our specs pass. Now that we don't have the Ruby environments loaded up as part of our test helpers (spec_helper for the rspecers out there), we need to establish an Active Record connection ourselves.
Create a spec helper for our lib specs and start it with the connection to the database like so:
1 require 'rubygems' 2 require 'active_record' 3 4 ActiveRecord::Base.establish_connection( 5 :adapter => "postgresql", 6 :host => "localhost", 7 :username => "rails", 8 :password => "", 9 :database => "<db name>_test" 10 )
This is the same way Rails sets up your database connections for active record when you load the Rails environment. Now your specs just need to require this new spec_helper in order to get Active Record functioning.
Use Models Across Projects
Now you are free to use the same database across many projects. To do this, you just need to either require a single lib file that loads all the models, or you could put the lib directory in your Ruby search path.
Ruby Search Path vs. Lib Require File
The new common library will need to be included in your rails application. There are two methods we have used to do this. The first is to create a library file that does nothing more than requires all of the common lib files, looking something like the following:
1 require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_a' 2 require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_b' 3 require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_c' 4 require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_d' 5 require File.expand_path(File.dirname(__FILE__) + '/my_lib/lib/file_e'
The problem with this approach is it requires you to sequence the require statements in the appropriate order, or else you will get errors about missing objects. The benefit is that you load the entire environment in one place rather than adding require statements at the top of each file to include the needed external dependencies.
These code files that do nothing but require files are no stranger to Ruby; however, they must be managed properly as they become temperamental and irritable when dependencies are mixed up.
The other approach we have used is what I recommended above. In your Rails application’s environment, add the lib directory to your search path, and include the require statements as needed in each model file.
Of course, by including one file you will be implicitly including all of the dependencies. Using this technique, you can eliminate files that just require other files.