Ruby Gem conflicts between local gem and system installed gem - ruby

I have a Gemfile that looks something like this:
source 'http://rubygems.org'
gemspec
gem "parent_gem", "0.1.3", :path => "#{File.expand_path(__FILE__)}/../vendor/gems/parent_gem-0.1.3"
parent_gem depends on child_gem-1.4.1, which we have packaged in the /vender/gems folder in the package. If a user installs child_gem-1.6.0 on their host on Ubuntu, they will get a Gem::ConflictError because it will try to use 1.6 and log child_gem-1.6.0 conflicts with child_gem (~> 1.4.1). However, if a user runs it on Amazon Linux, it works fine and uses the packaged child gem.
If I run gem list child_gem, I get the following output on both systems:
*** LOCAL GEMS ***
child_gem (1.6.1)
gem list parent_gem returns nothing because it's not installed separated and only exists in the package's vendor directory.
In the files that initiates the script, there is this line:
Gem.use_paths(nil, Gem.path << "/opt/my-app/vendor")
And the /opt/my-app/vendor directory includes child_gem-1.4.1.
Ultimately, I know that setting up the package properly with Bundler should resolve the issue, but I'm trying to understand why this is an issue.
How does Ruby decide where to look for a gem on a system? And where do I look to understand why it works on Amazon Linux but not Ubuntu?

Related

Can't install gem using Bundler's Rakefile install task when developing a custom gem

I'm developing a couple of private gems and I think I don't understand correctly the PATH/GEM_PATH and/or Bundler/RVM installation flow, would love if someone could chip in.
I have a repository with two gems (A & B for simplicity sake). I've developed the gems using the scaffolding + following the guidelines provided by this bundler tutorial.
Thanks to the Bundler project I have a few Rakefile tasks like rake build, rake install, rake install:local and rake release. Because of the private nature of these gems I can't release them to RubyGems (and we haven't looked into hosting our rubygems).
My machines are using RVM to manage ruby versions and Bundler version 1.15.1
What I want to do: Assuming a new machine/developer trying out the project, ideally we would cd into each of the subfolders (currently 2, gem A and gem B), run rake install and after that we should have the gems available system wide for the current user.
What is happening: The gems are built and work properly, but they are only available inside the subfolder of each gem i.e. gem A is only available inside the subfolder A and gem B is only available inside subfolder B.
What I've tried: So, after rake build/install/install:local a new .gem file is generated under pkg. I've tried to manually install the "compiled" file using gem install pkg/A.gem, gem install --local pkg/A.gem and gem install --local --user-install pkg/A.gem without success. (there are plenty of SO questions/answers about this)
I believe this has something to do with the PATH variables, but like I said before I don't fully understand the way they are managed. I get the following results from these commands:
# Our gem
> gem which A
/home/ubuntu/.rvm/gems/ruby-2.4.0/gems/A-0.1.8/lib/A.rb
# Pry, available globally
> gem which pry
/home/ubuntu/.rvm/gems/ruby-2.4.0/gems/pry-0.11.1/lib/pry.rb
I've been lost and frustrated for far too long now, any help is appreciated. Also open to hear suggestions of better private gem installation flows :)
Yes, it has something to do with your PATH variables. Your installation seems to be good.
I advise you to first affirm your gems installation path with:
echo $GEM_HOME
The double check your PATH to ensure its present and also confirm that the GEM home is also where the gem got installed into from the rake install
echo $PATH
If not, put it in your path and you should be fine with something like this:
echo PATH=$PATH:$GEM_HOME >> ~/.bashrc
source ~/.bashrc
Build your gem as per that guide you linked. You should end up with a gem file. Distribute this as you see fit (I use rsync/crontab to download newer gem versions but anything goes). User can install the gem as follows:
gem install --user-install /path/to/your/file.gem
This will install the gem in the user's ~/.gem/ruby/<version>/gems/<your-gem-name> directory.
Tried it with an empty gem (foodie, as in that example guide) and it works fine. But if you don't specify the --user-install parameter it will try to install in the system ruby dir (/usr/lib/ruby/gems...)

How to pick gems from a specific directory

I forked a gem I use a lot in order to code some enhancements on it. I want it to be installed in a subfolder called ~/codebase/ruby, where I keep all my Ruby projects, arranged in subdirectories.
To do that, I built and installed the gem with the following commands:
gem build my_gem.gemspec
gem install mygem-x.x.gem -i./mygem
mygem is installed in ~/codebase/ruby/mygem. I can't get my client code (which is in another directory) to grab mygem from there.
I've tried all of the following without success:
Running ruby with argument -I<path_to_mygem>.
Adding <path_to_mygem> to PATH.
Putting :gempath: <path_to_mygem> into ~/.gemrc.
I know I could put the gem in ~/.gem/ruby/<version> (since it appears in gem env) and that would probably work, but that would break my existing directory structure of Ruby code, forcing me to code in a different directory only for mygem, which is something I want to avoid unless it's the only option.
Thoughts?
Hopefully you are using Bundler to load the required gems. Then you just specify the path to the gem file in your Gemfile:
gem 'my_gem', :path => '~/codebase/ruby/mygem'

Bundler: using shared gem when it exists, rather than downloading from gem server

Say, I have Gemfile like following.
source "GEM_REPOSITORY"
gem 'gem_A'
# gem_A has no additional dependency
gem 'gem_B'
# gem_B depends on gem_B_1 and gem_B_2
When I run bundle install, I want Bundler to do the following.
If a gem already exists in "local system-wide gems", it copies the gem from local.
If a gem doesn't exist in local, it looks for GEM_REPOSITORY.
I looked for some related articles, and found some of likely-answers like
Ruby Bundler multiple sources in Gemfile
SOURCE PRIORITY
But none of the above looks like the answer for me.
Using source repository priority does't work. Because in the example above, if dependent gem (say, gem_B_1) exits in local but the target gem (gem_B) doesn't exist in local, it'll download both of above from the remote repository.
Are there any work around for doing this?
If not, don't you guys think it's necessary considering the cost of the implementation and the effect?
This is the current behavior.
When running gem install, directly or via bundle install, gem will first build a dependency graph with all the needed gems. If the gem is found locally it will use it, otherwise it will try to download it from the specified source.
If you want, try it yourself.
bundle gem gem_a
bundle gem gem_b
cd gem_a
vim gem_a.gemspec
add
spec.add_dependency 'multi_json', '~> 1.10.1'
or any dependency you want to the gem and run bundle install.
cd ../gem_b
vim Gemfile
and add
gem 'gem_a', path: '../gem_a'
then run
bundle install --verbose
you will see that the multi_json or whatever dependency of gem_a uses the local version and does not download anything.
This is of course also true for gems from remote sources.

Local gem repository? Install a ruby gem without 'gem'?

Is it possibile to have something like a 'local' gem repository for Ruby?
I'm working on a custom Linux distribution without admin rights. Ruby is installed on the machine (v.1.8.7) but apparently no 'gem' or 'bundle' or whatever are installed. I need to use some Ruby gems like Nokogiri.
Is it possible to use Ruby gems without installing them through gem install?
Yes. Any gem can be used standalone. You just have to either download the source from github, or download the gem and extract its contents manually.
After you've done that you have to add the lib folder of the gem into the load path ($:) of Ruby. For example:
$:.unshift(File.expand_path("nokogiri-1.6.1/lib"))
require 'nokogiri'
Assuming you are running Ruby in the current directory and the Nokogiri source is in the folder nokogiri-1.6.1.
But remember that first you have to do the same with all Nokogiri prerequisites. I.e. all the libraries Nokogiri depends on.
Another option, which is what I would do, is to install RubyGems in your home directory. That can get a little bit tricky though, but it's possible by downloading the RubyGems package and running something like:
ruby setup.rb --prefix=/home/my_user_name/rubygems
Then you need to set up the ENV variables GEM_HOME and GEM_PATH to point to a location in your home directory where you want all your gems to be installed. See "What's the difference between GEM_HOME and GEM_PATH?" for a description of what they do.
You will also need to add something like /home/my_user_name/rubygems/bin to your PATH so that the gem command can be found by the shell.

Copy compiled gem to other computer, avoiding compiling again

Using RVM, how can I copy gems between two different computers? The gem is compiled and I want to avoid a compilation on the target machine.
I tried to copy the .rvm/gems/ruby-x.x.x-pxxx#gemset/gems/gem_name directory, and also the /bin and /cache, but in the target computer I can't see the gem in the gem list output.
Is there anything else I need to do?
You have to copy also the gem specification file you find in .rvm/gems/ruby-x.x.x-pxxx#gemset/specifications/gem_name.gemspec.
Keep in mind that the binary code could not run on the destination platform.
You could use gem-compiler from Luis Lavena.
It should be as easy as (replace <gem-*> with actual gem information):
gem install gem-compiler
gem fetch <gem-name> --platform=ruby
gem compile <gem-name>-<gem-version>.gem
This commands will create the *.gem file for your platform which can be then moved to another machine and installed with gem install --local <gem-file>.
For more details check https://github.com/luislavena/gem-compiler#usage

Resources