-
Creating a gem that works on both Rails 3.0.x and Rails 3.1.x
Today I was trying to create a Rubygem that can be used with Rails 3.0.x or Rails 3.1.x. I
bundle gemcreated the gem, and got busy figuring out how to specify my dependency on two version numbers.Introducting the spermy operator
The Spermy operator in a
.gemspecfile lets you specify a dependancy where you don’t care about certain levels of version numbers.For example,
s.add_dependancy "rails", "~> 3.0.1"says, “I depend on Rails 3.0.x being installed”.The spermy operator is pretty intelligent, and “masks” off the last digit you pass to it. For example, if you pass
s.add_dependancy "rails", "~> 3.0", this says, “I depend on Rails 3.x being installed”.(Source: Pessimistic Version Control specification in the RubyGems manual).
Why the spermy operator (by itself) is not what I want
Iideally I do NOT want to just say “~> 3.0”, because I don’t know if my gem will break in Rails version 3.4 or something. I want to be able to tell my users, “I have tested this gem under Rails 3.0.x and it works, as well as Rails 3.1.x. If you’re running something newer, or older, I bet this gem is not compatible.”
Specifying multiple versions in the
add_dependancylistI learned that you can specify a list of versions to
add_dependancy, like so:s.add_dependancy "rails", ["3.0.1", "3.0.2"]Of course, those version numbers are just for illustration purposes, it makes little sense to force one of two minor versions of a gem. But that is how you specify multiple version requirements for a gem dependancy.
“Ahh, just tie the spermy operator into the multiple requirements operator, duh!”
Tried that:
s.add_dependency 'rails', '~> 3.0.7', '~> 3.1.0.rc'This doesn’t work: Rubygems itself gives an error:
Unable to resolve dependencies: mygem requires rails (~> 3.0.7, ~> 3.1.0.rc)“What, what? Why doesn’t it work?”
I went to #bundler on freenode, and asked my question there. Chatroom participant indirect said this, which makes sense:
it cannot BOTH be less than 3.1 and greater than 3.1 at the same time
Ok, great, how do I get this working?
Let’s revisit the requirements:
- My gem requires at least Rails 3.0. It uses Rails 3.0 specific APIs.
- I assume that Rails 3.2, or Rails 4.0, will break my gem. I want to avoid installing bad software into some user’s app, 3 years from now.
The solution is simple
After banging my head against this problem for an hour, the solution was obvious: forget the spermy operator. When every problem you have looks like a nail, the only solution you can think of is a hammer.
The solution
s.add_dependency 'activerecord', '>= 3.0.0', 'This will require a Rails greater than or equal to Rails 3.0, and less than Rails 3.2. So, this will pass for any Rails 3.0.x app, and any Rails 3.1.x app.
Conclusion
I hope you have learned a bit about how Ruby gem dependancies work, and how to set up your gem so it works with Rails 3.0 and Rails 3.1.