versioning best practices for ruby gems - ruby-on-rails

I'm adding new gems and functionality to it often. Before my last release some of my code broke in my dev environment and I found out it was because some of my gems (CarrierWave and jQuery in particular) had been updated and didn't work with some code.
What's the best way to manage gems regarding versioning? Some seem to say that you should always specify the version number in your Gemfile...but for all gems? Just some?
I know that for some gems you may have to store the version numbers because of bugs, etc. But these aside, in development there are times where I'm adding new gems and may need to do a bundle update to get the new stuff working but then don't want to break old stuff.
I have good tests to hopefully catch a lot of errors before pushing to production. How are other users ensuring gem update may not break completely unrelated functionality when in development?

Unfortunately, if you dont' want you app to break because of backward-incompatible gem updates, you do have to specify gem versions. What I found to be a good practice is using the pessimistic operator ~> to specify gem versions. For example:
gem carrierwave, '~>0.6.0'
This means the carrierwave gem will be frozen at version 0.6, but bundle will install any minor, backward-compatible updates and bug fixes, which are usually increments of the last number (0.6.1, 0.6.2...). That means you can update your bundle without running the risk of breaking something (no more flinching when running bundle update).
You can also use the pessimistic operator on major versions:
gem devise, '~>2.0'
Meaning bundle will update to versions 2.1.0, 2.2.0, 2.2.1, 2.3.0, but never to 3.x.
Some considerations:
You don't have to specify all gem versions, but it's good practice. I don't specify versions of my own gems, for example. But every third party gem has its version specified. Otherwise, I'd be trusting my code to things beyond my control.
You still need to have a certain amount of trust in the gem maintainers to use the pessimistic operator. A reckless maintainer still could release backwards-incompatible changes in a minor version. In those cases, I lock the minor version (no pessimistic operator).
If you specify gem versions, you'll be making bundle's work of resolving gem dependencies a lot easier, meaning it'll do it much faster.

Related

update gem version that is specified in another gem dependency

In my Gemfile, I'm including a gem that has a .gemspec that uses the pessimistic operator ~> to specify a version of the gem. However, I need to use a newer version of the gem. In my case I'm using spree 3-0-stable that has activemerchant set to '~> 1.47.0' https://github.com/spree/spree/blob/3-0-stable/core/spree_core.gemspec#L22 but I need use active merchant '~> 1.59.0'
I tried to specify version 1.59.0 in my Gemfile but bundle complains with:
You have requested:
activemerchant ~> 1.59.0
The bundle currently has activemerchant locked at 1.47.0. Try running
bundle update activemerchant
If you are updating multiple gems in your Gemfile at once, try passing
them all to bundle update
But the command bundle update activemerchant will only update to 1.47.x.
What's the rails-y way of accomplishing this? Aside from forking spree myself and updating the .gemspec? Obviously updates to the activemerchant gem might break the spree gem. But I can test with my app.
The maintainers of Bundler have recommended forking the relevant gem, then making a PR back onto the gem. Then link directly to the branch in that PR in your Gemfile. This encourages the community to rally around a solution.
I've documented the process in detail here: https://github.com/rubygems/rfcs/pull/13#issuecomment-770416467
You can't do it, the rubygems system doesn't allow that. You have to file an issue for the upstream gem dependency, and ask them to loosen their requirements.
It may be that the version of activemerchant you want actually won't work with the version of spree you want, it will break it. If so, spree code would have to be fixed to work with the new version of activemerchant. Or, it could work fine with the version you want, the restriction in spree is accidentally unneccesarily strict, in which case the spree gemspec would just have to be changed to allow it.
But there's no way for you to override it without changing the spree gemspec.
You could make your own fork of spree in which you change the gemspec -- but without knowing whether or not spree will really work with the version of activemerchant you want, that could be dangerous. And now you're on your own fork, so you won't be able to upgrade to new versions of spree without merging in their changes to your fork, etc.
Another thing is, it's a bit odd to be running off a spree 3-0-stable branch, instead of an actual released version of the gem. But I'm not familiar with spree's release management practices, perhaps this is what they encourage? At any rate, changing this probably won't fix your problem.
If your app is relatively new or simple, you could consider switching from spree to solidus, a fork of spree. Solidus forked off at spree 2.x though, so migration from spree 3.x might not be straightforward, especially if your app is established and somewhat complex. Although the solidus_core gem is also locked to activemerchant ~> 1.48.0, so it also won't allow anything but activemerchant 1.48.x. The solidus team is pretty responsive on their Slack channel, if you wanted to ask them what's up with that, if they know if later versions of activemerchant will work, if the dependency can be loosened. I have found the solidus maintainers to be very responsive, and likely to be interested in allowing later versions of activemerchant if it's feasible to do. I'm not familiar with the current Spree team and how they communicate with users (if they have a slack, or listserv, or are responsive on github issues, or what), but you could of course have the same conversation with them.

Is there a way to package all of the gem's my gem depends on WITH my gem?

I'm working on writing a gem that has several gem dependencies, one of which depends on a gem that broke backwards compatibility in a newer version. This got me to thinking - I don't want the gem that I'm building to become "that gem" that is making it difficult for people to update their application. I also don't want to force people using my gem to have to use specific versions of the gems it depends on in the rest of their application.
On one hand, I could just rewrite all of the code from those dependencies to bundle them with my gem and remove the dependency all together, but that seems a little tedious. Is there any way for me to simply include the gem dependencies directly in my gem, then wrap them in a module so my packaged versions don't conflict with the versions used by the rest of the application?
I don't think what you want is possible via existing Ruby tools. However, if backwards-compatibility for you when relying on your dependency is purely a syntax/usage issue, and not a low-level difference between versions, then you don't have to import and maintain the old gem's code. You have another option: Create a "shim" layer in your gem that provides the functions you need from either new or old interface of the dependency.
In practice it might look like this, assuming by way of example that it was the constructor to the Thingy class that has changed:
module DependencyShim
def new_Thingy( new_style_args )
if thingy_is_new
Thingy.new( new_style_args )
else
Thingy.new( convert_args_to_old_style( new_style_args ) )
end
end
# convert_args_to_old_style() not shown
private
def thingy_is_old
Thingy::VERSION < '1.3.4'
end
def thingy_is_new
Thingy::VERSION >= '1.3.4'
end
end
Better abstractions are very likely possible, but are difficult to predict as I don't know the nature of difference between old and new, and how tightly bound your code is to your dependency.
This is obviously painful if there are radical changes between versions, and your own gem uses the dependency heavily. But even then it may still be less painful than re-implementing and maintaining the dependency inside your own gem.
I would recommend looking at making your gem the most compatible with latest versions of your dependencies, and over the long term moving to deprecate support for the old dependencies, based on what you know of your user base. There may be good reasons for all involved to move away from the old dependency.
Assuming you are using bundler, you can specify versions for dependencies in your Gemfile like this:
gem "my_dependency", "0.6.1"
There's a whole load of other options too such as 'version greater than X' etc. Read the bundler docs for more info.

Specify gem version or always use the latest one?

I code a RoR webapp on my sparetime. Like anybody (I guess), I use gems (about 20).
A few times, I had errors when deploying the app in production environment because I wasn't specifying the exact version for all my gems, so some of them were updated which led to bugs.
I'm worried about staying in a particular version for all my gems and when I need to update one (or some) of them, the whole thing break down (I had problems making gems for flying-sphinx and twitter to work together after an update).
My question is : should a part-time coder fix once-for-all the version of the gems used or is it worth the time to check each update ? How do you manage your Gemfile ?
Also I think that when you're full-time, you should always keep to date (security, performance) but is it really true ?
I mostly stick with this kind of version specification:
gem "blah", "~> 1.4.2"
which means a version number >= 1.4.2 but < 1.5.0
Most gems follow a semantic versioning scheme or at least only break stuff when doing a version jump. This way, I am 'safe', but still get the good parts (bug fixes etc.).
Locking down the exact version numbers is the idea behind Gemfile.lock, so I check this into version control, too. This approach has worked perfectly for me so far and I have never had problems with this approach.

What kind of things should go into a Ruby gem as opposed to a Rails plugin?

I have a set of functionality that I am considering packaging so as to use them in multiple projects, but I can't decide whether to choose a gem or a plugin. What is the difference? Which one should I choose?
Gem is currently acknowledged as the 'best practice' for Rails. (You can also package as a gem and include an install.rb so that your project can be optionally be installed as a plugin - see this Rails dispatch article).
Basically the only reason to go with a plugin is if your users will want to be able to modify the code more often than not, as it stores a copy in vendor/plugins. However, with the advent of bundler it's pretty simple to store your gems per repository as well and modify them.
If you go with gems, you get the advantages of dependencies, versions, and the functionality that rubygems.org offers for searching, alerts and so on.
Definitely make it a gem!

Should I create a Ruby gem or plugin

I have some logic code that is literally cut and pasted across several different sites I own (it's code that interacts with a web API I've created). Considering that this isn't DRY and that when I update one thing I want it to update across all my sites I wanted to move this to a gem or a plugin. My main question is which one is the best to use in this scenario?
One big sticking point is that this code is private and shouldn't be made available to just anyone.
Thanks
Gems are the de-facto standard for packaging and releasing Ruby libraries.
There used to be two major drawbacks to using gems instead of plugins in Rails applications. Gems did not have access to all the functionality that plugins had. For example, in Rails 2, gems could not directly add rake tasks to your application. In Rails 3, plugins and gems are completely equal.
The second drawback was that gems were harder to bundle with your application. This has been resolved a while ago by rake gems:unpack:dependencies. It copies all gems your application depends on to your app's vendor/gems application. If you deploy these with your application, there is no need to install them on remote servers. In Rails 3, the new gem bundler further improves this mechanism. It even allows you to bundle Ruby C-extensions!
Also consider the things that cannot be done with plugins: versioning, packaging, dependency management, extending Ruby, etc.
In my opinion there is no serious reason to use plugins instead of gems anymore. The last advantage of plugins will be void when Rails 3 is released. Spread the word, and help everyone convert their plugins to gems!
Let's see Pros / Cons:
Gems
Pros
Easier to update the version (gem update)
Code encapsulated and available for all applications on the machine
Cons
Webapp code and gem code aren't encapsulated together.
You have to install every gem on each pc the application will run.
You have to install rubygems on each pc, too.
Maybe it's nedeed to be sudoer to install the gem.
Plugins
Pros
Code encapsulated within the application
You don't need special rights on the machine to work.
You don't need rubygems.
Cons
More difficult to update the code than gems
You have to install the plugin on every application that will need it.
I think this are all the differences. The decission now is yours :)
For me both solutions are OK, you have to think about your needs.
Hope that helps.
Will you ever have more than one version of this API being used in production at the same time?
If not, the simplicity and flexibility of a plugin is probably easier, especially if you use a tool like Piston to install/update the plugin from a centralized code repository.
Gems are not too convenient for private stuff, imo, with the way they're distributed. And distributing them by hand avoiding the gem hosts is an overhead. If it was me and I had a piece of private code I need to use in a bunch of places, I'd simply throw it into lib/ dir as a fake git submodule. Then pull once in a while.

Resources