using bundler to load different versions of gems for different platforms - ruby

So I am developing a Sinatra for both windows and linux. The problem is I'm using Thin instead of Webrick and eventmachine for windows only works with a pre-release version while linux uses the latest stable. in the gemfile you of course cannot include the same gem with different versions like so:
gem "eventmachine", "~> 1.0.0.beta.4.1", :group => :development_win
gem "eventmachine", group => :development_linux
gem "thin
I was wondering if there was a way to work around this, maybe using one gemfile for windows and one gemfile for linux, what would the command be to load one or the other.
Alternatively is there a way to perhaps in git manage just the gemfile for two different platforms, perhaps through a branch for just the file (don't know if that's possible from what I've read of git branches).

You can do it like that:
# Windows
gem "eventmachine", "~> 1.0.0.beta.4.1", :platform => [:mswin, :mingw]
# C Ruby (MRI) or Rubinius, but NOT Windows
gem "eventmachine", :platform => :ruby
Full list of available platforms:
ruby C Ruby (MRI) or Rubinius, but NOT Windows
ruby_18 ruby AND version 1.8
ruby_19 ruby AND version 1.9
ruby_20 ruby AND version 2.0
mri Same as ruby, but not Rubinius
mri_18 mri AND version 1.8
mri_19 mri AND version 1.9
mri_20 mri AND version 2.0
rbx Same as ruby, but only Rubinius (not MRI)
jruby JRuby
mswin Windows
mingw Windows 'mingw32' platform (aka RubyInstaller)
mingw_18 mingw AND version 1.8
mingw_19 mingw AND version 1.9
mingw_20 mingw AND version 2.0
You can find more information in Gemfile(5) man page here (see 'Platforms' section).
Another approach is to use RUBY_PLATFORM constant:
if RUBY_PLATFORM =~ /win32/
gem "eventmachine", "~> 1.0.0.beta.4.1"
else
gem "eventmachine"
end
I haven't seen full list of available values for RUBY_PLATFORM but you can run
ruby -e 'puts RUBY_PLATFORM'
on both your platforms and see the difference.

You can use the --gemfile option to use different gemfiles for different platforms. See the documentation here
http://gembundler.com/man/bundle-config.1.html

You need multiple versions (all with the same name) of a gem. Therefore, currently with Bundler, you need multiple, simultaneous Bundler dependency snapshot 'lock' files. This is possible, if your developers make use of Bundler's gemfile configuration setting. They might do this either:
By making use of environment variable BUNDLE_GEMFILE (on the command line or in .bash_profile); or
(Probably less desirably) in .bundle/config (globally, in their home directories).
Thus, safely, Bundler can create (and presumably automatically later use, given the same configuration settings) e.g. Gemfile-linux.lock and Gemfile-windows.lock.
Although this basic approach seems workable, it's not very DRY. However, this improves if, e.g., both Gemfile-linux and Gemfile-windows automatically incorporate whatever Gemfile statements they share in common: i.e., if they include the statement:
::Kernel.eval(File.open('Gemfile-common','r'){|f| f.read},::Kernel.binding)

Related

Lock gem dependency versions for different rubies

Rubygems doesn't provide a proper way to specify different dependencies for different ruby versions. If we put a conditional in the gemspec, it can switch what gets installed:
if RUBY_VERSION < "1.9"
# ruby 1.8 only supports nokogiri < 1.6
s.runtime_dependency "nokogiri", ">= 1.4.0", "< 1.6"
else
s.runtime_dependency "nokogiri", ">= 1.4.0"
end
But, this doesn't control what gets loaded. If there's a newer version in the load path, that would get loaded even if it's incompatible with ruby 1.8.
I'm not sure if this is actually a problem: If you're using rbenv/rvm etc. then you have different gem paths for each ruby, so hopefully you'd never have both installed in the same place. I think even the standard gem paths are divided by compatibility versions (1.8 / 1.9.1). Is that intended to take care of this, or could you ever get into a situation where both versions are installed together?
The other approach would be to leave the gemspec open-ended (without the conditional), and warn users to set the correct version constraint in their Gemfile if they need it.
Which way is preferable?
I believe this is just the nature of how bundler and requiring gems works. It's one of the great benefits to tools such as rvm.

how to set a ruby application to run on multiple environments

I have developed a simple ruby application without any MVC, and runs on command prompt using ruby 1.9.2 . It also has a Gemfile. I want this application to run on machines running ruby 1.8.7 and 1.9.3 as well. So how can I set it right for multiple environments.
Please Guide.
Thanks
At first you have to make sure that your code is compatible with the syntax of ruby 1.8 and 1.9 https://stackoverflow.com/a/21621/335523
Then make sure that the gems you used support both versions of ruby. (Read through their github page or documentation)
If you are using gems that do not suport both versions of ruby you have to find alternative gems that provide the same functionality in each environment and specify them in your gem file like this:
gem 'rcov', :platforms => :ruby_18
gem 'simplecov', :platforms => :ruby_19

How to use bundler for a project that works in jruby and ruby

How do you use bundler with a project that works for both jruby and ruby?
Specifically, some gems are required in one but not the other, as well as different versions.
Reference: http://github.com/docwhat/iated
You can use:
platforms :ruby do
gem 'gem_that_only_works_with_regular_ruby'
end
platforms :jruby do
gem 'gem_that_only_works_with_jruby'
end

How do I add conditional rubygem requirements to a gem specification?

Is it possible to add a gem dependency only if the person is using a certain version of ruby?
Background: I'm working on a fork of a project that uses Test::Unit::Autorunner and the like. They are part of the standard library in ruby 1.8, but aren't part of the standard library in 1.9.1, and is instead in the "test-unit" gem. I want to add a dependency that says that if someone's using ruby 1.9.1 or later, install the "test-unit" gem, but if they're using 1.8 or earlier, they don't need to install anything.
If you look at the gemspec documentation for add_dependency, there isn't an option for a ruby version. Perhaps you could use the post_install_message attribute to tell the user to install the gem if they're using ruby 1.9.
I did this exact thing for a project. The trick is to add the script as an extension, which will then get executed at install time on the user's machine.
Here are code snippets and links to our github:
First, when in the gemspec (we're actually using a Rakefile to generate it, but the result ends up the same) source
# This file *needs* to be named this, there are other files
# for other extension processors
s.extensions << 'ext/mkrf_conf.rb'
And then the relevant lines in that mkrf_conf.rb source
require 'rubygems/dependency_installer.rb'
inst = Gem::DependencyInstaller.new
inst.install "test-unit" if RUBY_VERSION > "1.9"
Gem doesn't support conditional dependencies (except on gem builder's environment -as noted above), and bundler is not a viable option to solve this either - see https://github.com/carlhuda/bundler/issues/1281
hay ... i'm kind of a ruby newbie ... if this is the best way to do it.
any way ... i wish i can do that using only Ruby .... though u can use your operating system shell to do that, by using this in your program installer, just execute (works for Linux based operating systems):
$ruby --version
(u can execute that from a ruby installer file, just like: ruby --version)
and put a possibility according to output, if it's 1.9.1 add an extra line to execute:
$ sudo gem install gem_name
else, just leave it be.
Checkout this tutorial in the Ruby Programming wikibook.
Tt shows how to install different versions of dependencies depending on what version of ruby the installee is using.
(short answer--it ain't as easy as it should be)
You can't. You need to build two gems, one with
spec.required_ruby_version = '~> 1.8.6'
and one with
spec.required_ruby_version = '~> 1.9.1'
spec.add_dependency 'test-unit', '~> 2.0.5'
Gemspecs are just ruby files anyway, so you can execute any ruby code inside them, so:
spec.add_dependency = 'test-unit', '>= 2.0' if RUBY_VERSION =~ '1.9'
EDIT: Specs run only on the builders machine.

Do you have to do duplicate gem installs for JRuby & MRI?

I have JRuby and Ruby (MRI) installed. It seems that I need to install gems twice - once for each of these platforms. Is this necessary or am I doing it wrong? After I installed the rails gem for MRI, should I have pointed JRuby to it, or was it necessary for me to also call: "jruby -S gem install rails"
You need to install gems for each different install of ruby that you have.
If you set GEM_HOME you can share your gem installations.
Some gems target specific platforms, e.g. Mongrel (there's a MRI one and a JRuby one). Also, JRuby cannot use gems that have native extensions (i.e. C code) unless they use the FFI (which most do not - yet).
Personally I have separate gem repos for MRI and JRuby. The little bit of extra hassle is worth the peace of mind when trying to track down a problem.
It's pretty easy to see what each repo has installed:
jruby -S gem list --local
vs.
gem list --local
You could even write a ruby script to sync one gem list to the other, but you'd have to be careful about platform specific gems....
I hit this problem when creating my gem, jimmy_jukebox, but made my gem work with both.
First, JRuby doesn't handle fork...exec (and even replies incorrectly to Process.respond_to?(:fork)), so I had to rescue NotImplementedError and use Spoon.spawnp instead.
I then created (in my gem's /bin directory) paired executables -- play_jukebox and jplay_jukebox; and load_jukebox and jload_jukebox -- each with the correct shebang line (/usr/bin/env ruby or /usr/bin/env jruby).
I'd love to know a better way. But I'd rather handle everything within a single gem than maintain and distribute multiple gems.

Resources