Programmatically check if gem in bundle? - ruby

At runtime, after bundler setup is complete, and groups are applied, what is the best way to programmatically check if a given gem_name is in the bundle?
By reading the source, I've discovered Bundler.definition, e.g.
gem_name = 'banana'
Bundler.definition.dependencies.map(&:name).include?(gem_name)
but unable to find documentation1, I don't know if this is the recommended usage.
Update: It looks like Bundler::Definition#dependencies returns all dependencies, irrespective of groups. As an alternative, I've discovered Bundler::Runtime#dependencies_for which takes groups as arguments.
Bundler.load.dependencies_for(:default, Rails.env).map(&:name).include?(gem_name)
However, it seems like a bad idea to duplicate the "group lists" at every call site. Ideally, I'd like a method in Bundler that doesn't require me to specify the current groups.
1 The bundler website and man page are focused on command-line usage. I have not found any documentation on the gem's public ruby API. Comments in the source are helpful, but focused on data types, etc.

Bundler is used to set up the applications gems, so you can use the Gem API rather than Bundler:
Gem.loaded_specs.has_key? gem_name
Bundler will have set things up so that any gems in the bundle (in the appropriate groups) have been activated (so they will have entries in loaded_specs) and any other (non-bundle) gems will be prevented from being loaded.

Related

What are the practical advantages of using `add_development_dependency` in .gemspec?

..vs just listing gems used for the gem development in Gemfile (while everyone uses bundler anyway)..
..except for giving your fingers extra work?
It's not clear there is any.
In theory, RubyGems has the information necessary to run tests with add_development_dependency and test_files (see this question).
Some believe that this should be outside the scope of RubyGems (see this huge thread).
In practice, there is currently no real advantage, and the fact that RubyGems still installs test files by default is a drawback, as might be the lack of flexibility that Gemfile offers.
One benefit of add_development_dependency is that if you publish your Gem to rubygems.org, those dependencies can be listed on the gem's details page. Thus if anyone finds your gem via rubygems.org, they have an idea right away what will be required if they want to contribute to the gem.

Bundler: Allow user added gems in an UserGemfile

I'm developing a ruby/rails application which should be highly extensible by adding plugins. A plugin is - obviously - a gem, which follows specific roules and contains library code, a rails engine with assets, routes, controllers, views and whatever. Additionally there wille be some kind of API which allows the plugin developer to register custom entries to abstract concepts of the app like adding cronjobs (theres some kind of cron in the main system), adding commands to the CLI and so on.
There will be some 'builtin' plugins containted in the Gemfile shipped with the app due they're part of the app and always activated. And there will be a pool of community written gems which are optional.
Now I'm searching for a possibility to let the user, who installs the app on his machine, addional gems (containing plugins) to the app.
The simplest (but not best) solution would be to say "Just add the plugin gems you want to the Gemfile before running bundle install)". But if there comes an update of the app, the Gemfile would be potentially conflicting and either be resetted or the user have to resolve those conflicts. That's something I don't want actually.
A nicer way would be to say "Hey, here's a UserGemfile, just add the plugin gems you want to that file and run bundle install". That UserGemfile would be listed in the .gitignore and everything would be nice.
Unfortunately it seems like bundler doesn't support something like that.
Do you have any advice how to tackle that issue?
You can do something like this, in, say, import_gems:
puts `cat Gemfile UserGemfile > EffectiveGemfile`
puts `bundle install --gemfile=EffectiveGemfile`
Then, when you run your application, specify the Gemfile:
BUNDLE_GEMFILE=EffectiveGemfile bundle list
Make sure to add EffectiveGemfile and EffectiveGemfile.lock to .gitignore as well.

How can I find gems & groups using Bundler in Ruby?

I would like to load a Gemfile using the Bundler object in ruby and be able to view the gems, dependencies, and various groups. Since this obviously isn't documented, does anyone with a knowledge of the bundler source know how to do this?
After poking around the Bundler source code... I found you can do this...
require 'bundler'
b = Bundler::Dsl.new
b.eval_gemfile('Gemfile') # point to your Gemfile path
b.dependencies.each do |g|
puts g.name
puts g.groups
puts g.requirements_list
end
#sevenseacat You might have to poke around a bit more to lookup other gems a particular gem is dependent on, but hopefully that gets you started. It certainly accomplished what I was looking to do.
The only way, that I'm aware of, to get a representation of what groups hold what dependencies at which versions would be through bundle viz.
Via the bundle viz documentation, or by $ bundle help viz, you can see that the -V option will output the versions in the graph generated.
Note, you'll need to install dependencies (typically ruby-graphviz). You'll get an image which looks something similar to:
Note, this may not be the easiest way to parse this data; but, it is the available method.

How to make a gem available that isn't listed in Gemfile

I made a gem for myself to use in the console. I want to be able to use it all the time everywhere.
I can't figure out how to get Bundler to recognize it without putting it into my Gemfile, but that requires the rest of the people on my team to know about it and have it installed.
Is there a way to get Bundler to overlay a Gemfile of my choosing? Or to somehow generally make my gem available to me even though it is not listed in my Gemfile?
You could circumvent bundler by putting this at the end of the Gemfile and gitignore the Gemfile.dev.rb:
dev_gemfile = File.expand_path("Gemfile.dev.rb", File.dirname(__FILE__))
instance_eval(File.read(dev_gemfile)) if File.exists?(dev_gemfile)
Your Gemfile.lock will not match on different developers' machines if their Gemfile.dev.rb didn't match but I'm only concerned with exact matches in production. Minor changes between developers and production are already an issue if you have multiple platforms and can be worked around in the same way.

Ruby: just-in-time gem installation?

Would it be possible to override the default "require" by adding automatic download-and-install code for any missing includes (provided that the missing include is published as a ruby gem).
Which would not work in situations where Ruby is not interfaced with a shell. But still I think it would be an interesting idea.
Is there such a mechanism in existence today?
Edit:Removed portion about password check. I just checked and gem install doesn't seem to require me to type my password.
You would be able to hijack require method so as gems are installed when an attempt is made to require them, but still you won't have access to newly installed gem in current process, because gem index has to be reloaded.
I understand the intentions but I think exercise might not be worth it.
When installing a fresh gem the gem will be installed in the GEM_HOME. If that is not writable then it will try in the user's home .gem directory (on *NIX at least).
You could certainly script this. In a way Rail's rake gems:build is just this. Just not on demand.
But, I would recommend against this. You could run into build, versioning, dependency and network issues. And probably security issues as well.
PS: Francis Hwang did something related a while ago, although only as a require, not a require gems.
http://fhwang.net/2005/11/01/urirequire-I-got-yer-Web-2-0-right-here
A better option would be to use bundler and distribute the required gems with the application.
It is also quite simple to write a script to bootstrap the installation of gems if you didn't
want to distribute them with your code (using the bundle install/check commands)

Resources