How to Gemify your Rails Plugins
Ever since Rails added support for declaring gem dependencies, there is really no (good) reason to use plain ol’ plugins. We’ve been slowly gemifying all of our plugins as we need them. There’s a few hoops you have to jump through to get Rake tasks and Capistrano recipes working, but it’s fairly straight forward.
First, you need something that will help you generate the gemspec and build the gem. You can do this by hand, but there’s several great plugins out there that make it easy. We recommend Jeweler. Follow the directions in the Jeweler README for “Using in an existing project”.
1. Move init.rb to rails/init.rb
Rails plugins have the magical init.rb that gets loaded when the plugin is initialized. To make this work in a gem, all you have to do is move it to rails/init.rb. Recent versions of Rails will look there whether you install it as a plugin or a gem, so you can just move it to the new location if you don’t care about ancient versions of Rails.
2. Move rake tasks to lib/
Rails will load tasks/*.rake defined in any plugins. Unfortunately, these don’t get loaded from Gems. To make your rake tasks work from a plugin, you will need to move them into the lib directory, and explicitly require them from your app’s Rakefile:
require 'mygem/tasks'If you want your tasks to still be available when your code is installed as a plugin, you can just explicitly require the task from task/mygem.rake:
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'mygem', 'tasks'))
3. Move Capistrano tasks to lib/
Capistrano recipes defined at recipes/*.rb in your plugin are also automatically loaded by Rails. Unfortunately, they suffer the same fate as Rake tasks and have to be move to the lib directory and be explicitly required from config/deploy.rb.
When moving the recipes to the lib directory, we have to jump through a hoop to get Capistrano to load it properly.
Capistrano::Configuration.instance.load do # put cap recipes here end
And in your config/deploy.rb:
require 'mygem/recipes'As with Rake tasks, if you want your recipes to still work when installed as a plugin, add the following to a file in the recipes/ directory of your plugin:
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'mygem', 'recipes'))
4. Generators
You don’t have to do anything, they just work.
That’s it
Publish your gem and go buy yourself a drink. Check out one of our gems if you need more examples.
5 Comments
Really? I’ve been putting gems into my Rails projects as plugins. I always ran into problems if Project A required version X of a gem and Project B required version Y. Problems went away if the gem was installed a plugin.
Joe: I know what you mean about gem versions being an annoyance, but that was the past. Now by specifying the gem version, you always know what you’re getting and installing other versions won’t have any effect.
Personally, I’ve had all sorts of problems with gems, especially since Rails 2.2. Something about the way the environment is invoked has cause me tons of grief whenever using rake to install/unpack gems – dependencies are not loaded properly, or sometimes the dev environment is run BEFORE requiring gems, which in turn forces me to comment out gem-specific code in my app, just to install the damn things.
Plugins have NEVER given me any kind of problems – pretty damn rock-solid here. Gem integration in Rails is inherently broken, and Yehuda Katz knows it, which is why Bundler will be the new standard – hopefully it will iron out all of the kinks, cause I’m pretty damn tired of this ghetto setup!
Wow you actually deleted my comment? All I said was that gems are a pain since Rails 2.2, but if you want to stick your head in the sand and pretend they are perfect knock yourself out. Plugins FTW till Rails 3!
Anonymous: I do not delete legitimate comments. Akismet must have thought it was spam. Although, it’s curious that you posted your comment anonymously.
I’m surprised this is such a heated topic and that people have had so many issues. We’ve used gems as much as possible since Rails 2.2, and haven’t really had any issues.
Post a Comment