How can I find gems & groups using Bundler in Ruby? - 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.

Related

Create a completely self-contained gem?

I am writing my first real gem. It depends on three gems, and those gems have roughly a dozen dependencies. I'd like this all to be self-contained so my gem will always use the dependencies from inside the gem, and will not require the other gems to be installed at the system level. Doable?
I played around with bundle install --path vendor --standalone. It did indeed store all the gems' source code into vendor, but I could then not get my 'requires' to work correctly and the code could not be found.
I suspect either that it's not possible to do this with a gem (only and app) or I was missing some piece of configuration to point my gem's code at the vendored dependencies. I played around with $LOADPATH but could not make it work.
Anyone know if this can be done and, if so, how to make it work? Pointers to info welcome.
I think you need to add those gems as a dependency to your .gemspec file. That way when your gem gets installed, its dependencies will also be installed.
More information can be found here

Programmatically check if gem in bundle?

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.

Ruby - Can I use classes from a gem that aren't defined in a module?

As the question might indicate, I am still very new to Ruby. I'm developing my first custom gem as my own tutorial. In this case, it's a few classes for managing a deck of cards. I've successfully built and installed the gem, but when I try to use it in IRB, I'm getting a 'uninitialized constant' error. I'm using Ruby 2.0. Here's the basic run-down:
In my gem I have three classes in lib: Card.rb, Deck.rb, and card_deck.rb (which was auto-generated. card_deck.rb defines the module CardDeck, and the Card and `Deck files define classes named the same thing respectively. After installing my gem, I run irb and get the following:
$> require 'card_deck'
=> true
$> CardDeck
=> CardDeck
This tells me the gem was installed successfully. However, when I try using Card or Deck I get the following:
$> Card
NameError: uninitialized constant Card
So my question is: do any classes that I want to be available from my gem need to be included in a module, or do they need to be required or imported differently? In my gemspec the lib folder is listed in the required_path property. Does this mean that any class I want to be accessible after requiring my gem needs to be defined in a module?
Again, I've done a little Rails work, but am still new to Ruby as a whole and building gems. Thanks for any input and advice.
One of the best things I've found to do in a case where you don't really know the proper way to do something: find a popular gem or library for your language, and note the way that it's structured. Do this for 4-5 gems and see what you notice, it'll have a two-fold effect for you. One, you'll have a better grasp of Ruby itself by just reading code, and two you'll pick up patterns and nuances of the language.
I would be say yes, in my admittedly naive amount of knowledge on Gems. I've tried the class << self bit but it stinks of code smell.
These might be of some help for gem creation:
http://net.tutsplus.com/tutorials/ruby/gem-creation-with-bundler/
The Ruby style guide will help acclimate you to some of the other nuances scattered about:
https://github.com/bbatsov/ruby-style-guide

Ruby beginner - using /modifying existing gems in single project

As the title states, I'm a beginner in Ruby.
My project uses 2 existing gems - which I want to modify.
I've forked the gems on GitHub and put them as modules in my repo and they show up as a subdirectory.
I've tried researching this, but I keep on getting lost - I'm thinking I'm missing some basic concepts/knowledge here.
My questions:
Am I going about this right/wrong?
Is it even possible to include the code of those (forked) gems in my actual project, or should I update them separately and just use them as actual gems with require (this seems very cumbersome)
If these gems are part of my project, how do I use them properly, I assume I don't need the require piece? If not, how do I access/use them?
Thanks!
BTW, using Ruby 1.9.2-p194 on Ubuntu with RubyMine as the IDE.
Probably wrong. Ruby is a very flexible language, and has what are called open classes. This means that you can open up and change classes at run-time. Doing this on an external library is called monkey patching. So instead of having to replicate all of the code you want to stay consistent, you can just modify the classes and override any methods you want.
A simple example:
class Fixnum
def is_multiple_of_three?
self % 3 == 0
end
end
However, if the changes you want are really significant, it could make sense to fork the gem.
I recommend the bundler gem. This will let you make a Gemfile which lists all of your dependencies. You can list a github repository as a source for the gem, like so:
gem 'gem_name_here', :git => 'git://github.com/username_here/gem_name_here.git'
and then run bundle install to install your dependencies.
If you install the gems with bundler, it acts just like any other gem you have installed.

Bundling a forked gem inside a gem

I am trying to build a gem for a project that has a dependency on an unnamed external gem :)
During development I found a small bug in the external project and I added a one line fix that resolves it. I submitted a pull request on github, but I have no response from the maintainer for some time now.
I want to make my project available as a gem but it wont work without this fix. What can I do?
What would be the best way to fix this.
One option I thought about was to create a gem of the forked project and publish it under a convoluted name, and make a dependency on that. But I don't like the idea of polluting the servers with such a stupid gem.
So I was wondering if it is possible to bundle the external gem into my application, and make it install together with my gem. What would be the cleanest and easiest way to do this?
Have you considered overriding the function with your own code? I was having a similar problem with some software at work a few weeks ago and I just redefined the function.
Since it was just one line you found, it seems like this would be the easiest solution, but I am a little bit new to Ruby so maybe there is a problem with this plan that I have not considered.
You could publish it under a different name and once the upstream maintainer accepted your fix, you can yank your version.
It's quite simple, in fact. In your Gemfile add the dependency as:
gem "nokogiri", :git => "git://github.com/tenderlove/nokogiri.git"
To do this you would also need to be using bundler to manage your gem, you can get more info on this here.
The other option is to add the code you changed to a vendor directory in your gem and distribute it with your code, this way you can just add the main directory of this other gem to your load path and you will be able to require it without any issues.
To add something to the load path you simply do:
$LOAD_PATH.unshift( File.join(File.dirname(__FILE__), '..', 'vendor', 'some_gem', 'lib') )
And you will be able to directly require files at some_gem.

Resources