23 September 2008
Installing Rails Plugins from Local Sources

I’m usually reluctant to go change “big things”, almost always preferring to work around any issues I encounter. On the rare occasion though, I find I really must make changes or I’d forget what I did and struggle with it again later.

The script/plugin install command in Rails is a wonderful thing. It fetches code from repositories (like subversion or github) and installs it into your Rails application. Plugins generally extend Rails’ capabilities, much in the way that Ruby is extended through Rubygems. However, the plugin installer assumes a repository, or at least the web. If there is a local copy of the plugin in your environment that you’d like to use as the source, you’re out of luck. You have to do the install by hand, and remember to set up some environment specification. There’s just no provision for local installs.

Necessity being the mother of invention, when I found myself needing to install a plugin from local sources, I tried to find a workaround. When I could find nothing satisfying, I did some editing. A very slight amount of editing.

In ruby/lib/ruby/gems/1.8/gems/rails-2.1.0/lib/commands/plugin.rb

class Plugin
# test for local
  def file_url?
    @uri =~ /^\//
  end

  def install(method=nil, options = {})
    method ||= rails_env.best_install_method?
    if :http == method
      method = :export if svn_url?
      method = :clone  if git_url?
# if it's local...
      method = :file   if file_url?
    end

    uninstall if installed? and options[:force]

    unless installed?
      send("install_using_#{method}", options)
      run_install_hook
    else
      puts "already installed: #{name} (#{uri}).  pass --force to reinstall"
    end
  end

private
# install local
  def install_using_file(options = {})
    root = rails_env.root
    mkdir_p "#{root}/vendor/plugins"
    Dir.chdir "#{root}/vendor/plugins" do
      cp_r @uri, @name
    end
  end
end

That is, if the specified plugin being installed starts with a slash, assume it’s an absolute path to the directory that contains the plugin and fetch it by copying. Then the install can go along it’s merry way.

Now I can just fire off a

$ ruby script/plugin install /Downloads/rails/plugins/foo

and it will install the foo plugin in the specified directory into my Rails project, just as if it were out on the net.

This certainly may not be perfect, and if I were polishing it I’d add a check for a file: protocol and include relative paths, but this was just enough for my needs.