Monday, May 5, 2008

Freezing and Autoloading Gems - Not just for Rails

Now this practice in part is counterintuitive to the beauty of ruby gems. After all, the DRY concept of gems means removing code from your application. Especially that code that is shared between multiple applications on the same server. Gems are easy to intstall, easy to share, and easy to maintain.



But sometimes you want to reduce the number of steps required to deploy your application to a new server or perhaps your application required a one time customization to a gem library or maybe for whatever reason you want to keep all code dependencies for your application in one tree.



There is a straightforward way to make this happen.



Create a place in your application tree to store the gems your app uses. I chose to create a vendor/gems/ directory. So create the directory and issue the commands below




cd vendor/gems/
unpack gemName


Now these gems are part of our application source tree but aren’t yet visible to our application environment. We need to add these gems load paths to our application load path. Open config/environment.rb and add the following code (place it inside the Rails::Initializer.run do |config| code block):




config.load_paths += Dir["#{RAILS_ROOT}/vendor/gems/**"].map do |dir|
File.directory?(lib = "#{dir}/lib") ? lib : dir
end


With this done.. you can go about your business and use gem ‘genName’ as you typically do, however, I like the sledgehammer approach.. it’s fun.. and i want to be lazy and have all the gems in my vendor/gems directory loaded auto-magically. Add the following code to your environment.rb or one of your initializers if you’re running Rails 2..




Dir[File.dirname(__FILE__) + "/../vendor/*"].each do |path|
gem_name = File.basename(path.gsub(/-\d+.\d+.\d+$/, ''))
gem_path = path + "/lib/" + gem_name + ".rb"
require gem_path if File.exists? gem_path
end


With this you can freeze any gem into your application and have that gem automatically load.