RubyRedGrapefruit
RubyRedGrapefruit

Reputation: 12214

How can I conditionally load a local gem in my Rails 4.2 Gemfile?

I am using a gem that is only distributed in binary form. As such, we have two versions (not gem version numbers, just two different binaries) that we must somehow load conditionally in our environments since we develop on OS X and deploy on Linux (AWS).

I have these gems extracted to <app_root>/vendor/gems like so:

chilkat-9.5.0.69-x86_64-darwin/
chilkat-9.5.0.69-x86_64-linux/

I realize that I can set a development group and a production group in Gemfile:

group :development do
  gem 'chilkat', path: 'vendor/gems/chilkat-9.5.0.69-x86_64-darwin'
end

group :production do
  gem 'chilkat', path: 'vendor/gems/chilkat-9.5.0.69-x86_64-linux'
end

But that fails:

[!] There was an error parsing `Gemfile`: You cannot specify the same gem twice coming from different sources.
You specified that chilkat (>= 0) should come from source at `vendor/gems/chilkat-9.5.0.69-x86_64-darwin` and source at `vendor/gems/chilkat-9.5.0.69-x86_64-linux`
. Bundler cannot continue.

What's more is that I can't get the Bundler to run correctly on the production one by itself, maybe because it can't recognize the platform:

Could not find chilkat-9.5.0.69 in any of the sources

I don't know much about gemspec files, but maybe the last line:

--- !ruby/object:Gem::Specification
name: chilkat
version: !ruby/object:Gem::Version
  version: 9.5.0.69
platform: x86_64-linux

tells Bundler to skip it if it specifies a different platform than the one on which it's running.

Initial Solution

by user ulferts below.

if RUBY_PLATFORM =~ /darwin/
  gem 'chilkat', path: 'vendor/gems/chilkat-9.5.0.69-x86_64-darwin'
elsif RUBY_PLATFORM =~ /x86_64-linux/
  gem 'chilkat', path: 'vendor/gems/chilkat-9.5.0.69-x86_64-linux'
end

I had been trying to check Rails.env but I wasn't aware of the RUBY_PLATFORM constant.

However

Bundler appears to be deficient and lacks flexibility here. This fails upon attempting to deploy into production:

You are trying to install in deployment mode after changing
your Gemfile. Run `bundle install` elsewhere and add the
updated Gemfile.lock to version control.

So even though the conditional is there, it appears that the Gemfile.lock file is causing there to be some kind of a problem. This is really unfortunate, because I don't think this legitimate use is that much of an edge case.

In short, even with a conditional, you can not have the same gem listed in your Gemfile twice. Whether for a different source, different version, or both.

I tried something else: changing the name of the gem in production. I changed the directory name, the gem name, and the references in the gemspec file. The result:

You have added to the Gemfile:
* source: source at `vendor/gems/chilkatprod-9.5.0.69-x86_64-linux`
* chilkatprod

You have deleted from the Gemfile:
* source: source at `vendor/gems/chilkat-9.5.0.69-x86_64-darwin`
* chilkat

You have changed in the Gemfile:
* chilkat from `no specified source` to `source at

`vendor/gems/chilkat-9.5.0.69-x86_64-darwin``

So suddenly it looks like it doesn't even like the conditional now. I had a put a conditional in the code that did the require to facilitate this, but I can't even get there if the code can't be deployed.

Upvotes: 1

Views: 1861

Answers (2)

ulferts
ulferts

Reputation: 2242

The Gemfile is just another ruby file. If you can figure out the architecture you are on, you can simply wrap it in an if ... else e.g.

if architecture_is_os_x?
  gem 'chilkat', path: 'vendor/gems/chilkat-9.5.0.69-x86_64-darwin'
else
  gem 'chilkat', path: 'vendor/gems/chilkat-9.5.0.69-x86_64-linux'
end

One possibility to differentiate would be setting an env variable in production.

Upvotes: 2

Chilkat Software
Chilkat Software

Reputation: 1659

Maybe you can use Install_if?

See http://bundler.io/man/gemfile.5.html#INSTALL_IF

Upvotes: 1

Related Questions