How to trace and check dependencies in bundled Ruby gems - ruby

Bundler will automatically install any dependencies for the specified gems, however it doesn't output which dependencies map to which gems in the standard output. That information is useful when one of the dependencies fails the installation.
Is there a way to set Bundler to be more verbose and inform about the dependencies while installing?
I am using Bundler 1.0.2

To see a visual representation of the dependency tree run bundle viz:
apt-get install graphviz && gem install ruby-graphviz && bundle viz
It will generate a PNG file of the tree.

A less exciting, but equally effective way is to just do:
gem dep
which will generate a Gemfile.lock style output with dependency information. You could pipe this output to less:
gem dep | less
Or, if you are searching for a failing dependency, you could grep it with some context. For instance, to find out where my failing Thin dependency was coming from (fails with JRuby), I did:
gem dep | grep -C 15 thin

Related

Ruby Bundler - Multiple Ruby versions in the same gemfile

I am using rbenv and bundler on macos.
I need to maintain multiple versions of Ruby for my application. Specifically 2.2.4 and 2.5.5. Different users will have different versions.
Currently I am only working with 2.2.4 so I do the following. My gemfile looks like this
source "https://rubygems.org"
ruby '2.2.4'
gem "net-ssh", "4.1.0"
gem "net-scp", "2.0.0"
.....
To install I run bundle install and then to deploy with my application I run
bundle install --deployment --path src/mct-tools/ext/gems
This generates a folder src/mct-tools/ext/gems/ruby/2.2.0 in my application that I then distribute.
Now I would like to add ruby 2.5.5 with updated gems so I was wondering whether it would be acceptable to have a gemfile looking like this one
source "https://rubygems.org"
ruby '2.2.4'
gem "net-ssh", "4.1.0"
gem "net-scp", "2.0.0"
.....
ruby '2.5.5'
gem "net-ssh", "6.1.0"
gem "net-scp", "3.0.0"
.....
so that when I deploy with bundle install --deployment --path src/mct-tools/ext/gems the two versions are added to the gems folder.
Is this the correct approach? An alternative would be to specify two different gemfiles for each version and then switch between them.
Gemfiles Declare Dependencies
A Gemfile declares a dependency on a Ruby version, with or without semantic versioning constraints. It is not meant to control multiple build targets for your application. It simply enforces that the Ruby version available to your app/gem is whatever you've defined. For example:
# Will fail if run with a different RUBY_VERSION.
ruby '2.2.4'
# Allows RUBY_VERSION >= 2.2.4, but <= 2.3.
ruby '~> 2.2.4'
# Allows either Ruby 2.2.4+ or 2.5.5+, with
# a minimum (but no maximum) patch version.
ruby '~> 2.2.4', '~> 2.5.5'
However, it won't install a given Ruby, nor do anything other than raise an error and a non-zero exit status when running bundler install. You need to take a different approach to test multiple targets.
Changing Build Targets with Continuous Integration (CI) Tools
If you're using an external CI like TravisCI, you can create a build matrix that targets multiple Ruby versions to test against. Whether you remove the Ruby version constraint altogether, or specify a supported range, is up to you. Leveraging your CI tool to build against the versions of Ruby you plan to support is really the best approach, though, whether or not you constrain your Ruby runtime in a Gemfile.
For example, you might use a matrix in your travis.yml like so:
language: ruby
rvm:
- 2.2.4
- 2.5.5
Switching Gemfiles
If you insist on doing it the way you're doing it, with a singular Ruby version allowed in your Gemfile, then you might consider having two separate gemfiles with different names in your source tree, such as Gemfile-2.2.4 and Gemfile-2.5.5. You can then specify which Gemfile to use with Bundler's --gemfile flag , or by symlinking a custom Gemfile to the canonical Gemfile for your project.
Here are some examples to consider:
# Resolve against a specific Gemfile with
# hard-coded Ruby version.
$ ls Gemfile*
Gemfile-2.2.4 Gemfile-2.5.5
$ bundle install --gemfile="Gemfile-2.2.4"
# Resolve against whatever custom file is
# symlinked to your ./Gemfile.
$ ln -sf Gemfile{-2.5.5,}
$ ls -F Gemfile*
Gemfile# Gemfile-2.2.4 Gemfile-2.5.5
$ bundle install
Both approaches work, but the former is more flexible at the cost of needing to specify your chosen Gemfile each time, while the latter can help when you have a development/testing workflow that doesn't support Bundler's --gemfile flag.
Changing Rubies with Ruby Managers
If you have multiple Ruby versions in development, best practice is to use a version manager such as rvm, rbenv, or chruby. You can use your version manager to change rubies back and forth manually as needed.
You might also check whether your version manager supports auto-switching on .ruby-version or other configuration files. You'd still have to update that file each time you want to build or test against a different Ruby, but you wouldn't have to keep changing your Gemfile contents, re-pointing the Gemfile symlink, or updating a flag on each call to Bundler.
Whether or not any given approach is better than others will depend on your workflow. No technical solution will fit all circumstances, so your mileage may legitimately vary.

Is there a preferred tool to edit Gemfiles?

I have a project that depends on Ruby to do something. I need to tell these people to install bundler, create a Gemfile (or update an existing one) and then run bundler install.
To be very clear, these people do not care about Ruby, they don't know what Ruby is and they don't need to know what Ruby is.
Currently my documentation is:
Run this command in terminal:
gem install bundler
Create a new file name Gemfile and add these contents:
source 'https://rubygems.org'
gem 'lightning_sites'
Or if there is already a Gemfile then edit that file and add the line gem 'lightning_sites' at the bottom.
Go back to the terminal and run:
bundle install --path vendor/bundle
I would like to replace the documentation for step 2 and preferable replace it with a command line. Is there a tool that ships by default with Ruby or bundler that accomplishes this?
If you want to avoid bundler you need to force-bundle all your dependencies inside your application. This is only really practical if none of your dependencies have compiled extensions, so if they're all pure Ruby you'll be able to do it.
What you end up doing is a bundle install --path gems/ for example, then package up everything including that directory as a deployable application. You may want to make a script that performs this step and creates a .zip file of the final result for distribution purposes.
This is a heavy-handed approach, so it's best to do this only if absolutely necessary.
You don't have to use Bundler to install gems, Ruby provides the gem command for installing gems individually.
You could simply run: gem install lightning_sites --install-dir lightning_sites and in whatever Ruby script is using the gem, programatically modify your GEM_PATH using Gem.paths before the require statement to include that install directory.

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.

How to see the dependency tree just from Gemfile?

I am getting the following error when doing bundle install
Make sure that `gem install couchbase -v '1.3.3'` succeeds before bundling.
Now, i have not included this gem in the Gemfile, so it's coming from some dependency. How can i figure out which gem is dependent on this couchbase gem?
Since bundle install is failing and I don't have Gemfile.lock to figure out this dependency.
gem dependency (with no args) should show you all gems from current system with their dependencies.
bundle exec gem dependency will show you for the current Gemfile
Edit:
You can also do gem dependency -R (or just dep instead of dependency) if you want to find out which gems use specific (or all) gems.
For deeper dependencies I'd parse output (regex maybe?) of first gem dependencies, pick gem's names and call gem dep on each of them, but that's just a loose idea.
You can also use bundler to create a dependency graph.
Install graphviz:
gem install ruby-graphviz
and then:
bundle viz
Here are an example of a newly created Rails app:
You can also play with the options:
bundle help viz

Find out binary dependencies of gem

I want to build rpm with my Ruby app and its gems. As far as some gems depends on binaries I want to find out them automatically. For example nokogiri gem depends on libxml. Can I fetch all this dependencies for all gems in Gemfile automatically to pack them into rpm?
Now I do it mannualy and this is boring!
No, unfortunately, this is not possible. RubyGems only records Gem dependencies. It obviously cannot record any dependencies it doesn't know about, how would that even work?
There is a requirements attribute in the gemspec, but that is only intended to be human-readable, not machine-readable.

Resources