Find out binary dependencies of gem - ruby

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.

Related

How to create interdependent gems with bundler, and use repositories as the source?

How is it possible to create interdependent gems, and still use Bundler?
Right now, if the Gemfile for library-a reads like this:
gem 'library-b'
gem 'library-c'
And the Gemfile for library-b reads like this:
gem 'library-a'
gem 'library-c'
And the Gemfile for library-c reads:
gem 'library-a', github: 'library-root/library-a', branch: 'master'
gem 'library-b', github: 'library-root/library-b', branch: 'master'
When you try to run bundle update and/or bundle install for library-c, this is the output:
Your Gemfile requires gems that depend depend on each other,
creating an infinite loop. Please remove either gem 'library-a'
or gem 'library-b' and try again.
A variation of the same message will happen for library-a or library-b.
But if you use the following for library-c, and use the same style for library-b and library-a, this will work:
gem.add_dependency 'library-a'
gem.add_dependency 'library-b'
However now, you will need to cut and push gems to RubyGems to test each revision, especially when using Travis CI. Whereas if you'd used Gemfile definitions, even if you also used gemspec definitions, the code being run or tested would have been pulled from a git repository, and could be updated up-to-the-minute without cutting and pushing gems.
I've posted two issues about this.
https://github.com/bundler/bundler/issues/3594
https://github.com/bundler/bundler/issues/3597
I need to have interdependent gems, and use git repositories as the source of the gem code.
For those who say just don't do it ... "it's Circular Dependency", etc. I understand, but let's think about this. Is a gem really that complicated? It's just files with a certain structure. True, it has version information, and "dependencies" associated -- but those dependencies just need to be present. If a gem of the appropriate version exists already, it ought to just break out of a "circular dependency" and say, "hey, the gem's already here, let's just move forward" then.
Bundler is a package management system; the above is not Circular Dependency of objects it is Interdependency of files ... if it were Objects inheriting from each other, definitely... I surrender. But this is a point where one package assumes another exists in the filesystem.
You could change the word depends to expects the presence of not requires specifically sequential instantiation of.
Again gemspec works. If it were Circular Dependency that would be impossible. But gemspec is clearer on its purpose as part of a package management system. It lets files be files.
gemspec behavior? Both gems need each other? Cool! Download both gems. Easy, done.
Gemfile behavior? Both gems need each other? Wait, what? Implode.
I want gemspec "dependency" behavior, with Gemfile source specificity power. Otherwise I'd just keep using gemspec only.
Bundler is warning you that your gems have a circular dependancy. Namely library-a depends on library-b and the other way around. This is a situation that should be avoided.
If 2 libraries can't exist without each other it's best to just make them one.

How do I make a gem with a binary, when it depends on bundler to find its dependencies?

I have a gem which depends on Bundler's Gemfile to find its dependencies (they are hosted on github:enterprise). I want it to be usable as a lib and also to provide a binary.
Currently it works fine as a binary if I am running it from its own directory, where the binary can load the bundled gems. However, if I run it from a different directory, it can't find the dependencies (they are in their own rvm gemset). I can't install it as a gem because Rubygems can't find the dependencies (currently they are listed in the .gemspec, and then told where to be found in the Gemfile).
I am currently trying bundle package, and it placed all the gems in the vendor/cache directory. As far as I can tell, this will mean that I need to remove the dependencies from the .gemspec and instead into the Gemfile (because Rubygems shouldn't install them since they are vendored). This means that any code using this gem will need to know about these dependencies, and add them to their Gemspecs, but I'm okay with that. I think this could work if I modify the .gemspec to include the vendored gems when building, and if I can manage the load path such that the binary sets the vendored gems at the front of the load path, but any code using it as a library will not add these to the load path, so they will get whatever versions they've installed.
Unfortunately, I don't know how to do that (short of reading in all the gemspecs, reflecting on their require_paths to add them to the $LOAD_PATH), and I'm not really sure that this is the correct way to handle this situation.
So, how do I make a gem that can be used as a lib, or just for its binary, when it needs Bundler to find its gems?
if you are hosting the code in your own github enterprise instance, then it might be a good idea to have a custom rubygems server. you can then publish your internal gems to that server and have bundler retrieve them from there.
this should make a transparent workflow like you would be running your binaries in the real world!

Why should I add development dependencies to my gemspec

It seems like a strange feature that rubygems wants to know what my development dependencies are, so far I never saw it used to actually load these when running the gem tests.
If someone decides to hack your gem for whatever reason, e.g. they fork it on Github to add a feature to it (which they might want to contribute), it helps if they know what development dependencies your gem needs (e.g. testing frameworks, mocking tools etc.).
If you're using bundler with the gemspec command, it will hook into your gemspec dependencies and install the development dependencies along with the runtime ones when you run
bundle install
This saves you having to install these gems manually.
The gem command can also list all the dependencies of a gem including the development ones:
gem dependency my_gem
Gem my_gem-0.1.3
activerecord (~> 3.0.0)
json (~> 1.4.3, development)
rake (>= 0, development)
rspec (~> 2.5.0, development)
ruby-openid (~> 2.1.0)
Once again this is probably more for other people rather than for yourself.
My gems have normally unit-test. This test requires sometimes gems, which are not needed to use the gem. Or perhaps you need additional gems to generate parts of the gem.
Once I misused development dependencies to define 'optional dependecies' (dependecies were necessary for some specific features of my gem, but not needed for the 'normal' usage).
Example:
My application offered the posibility to export to a file as text or pdf. The pdf-generation uses prawn. So prawn is a dependecy - but it is not necessary to use the application, only a specific feature needs it.
So I didn't add prawn to gem dependencies (it is not necessary), but to the development dependencies (it is usefull for the gem).
The documented answer to this question at RubyGems is as follows:
Development dependencies are useful for when someone wants to make
modifications to your gem. When you specify development dependencies,
another developer can run gem install --dev your_gem and RubyGems will
grab both sets of dependencies (runtime and development). Typical
development dependencies include test frameworks and build systems.
For example, I may specify spec.add_development_dependency 'minitest'
Adding the --dev flag to the gem install command would then pull in the minitest gem automatically. This feature saves another developer from manually having to identify and install the gems necessary for development.

Managing conflicting versions of ruby gems

I am building a framework that loads user provided ruby code. It is basically a plugin mechanism. I want the user provide ruby code to be able to require gems of its own. I intend to have the "plugin" package include a vendor directory with the gems.
How can I load gems that are required by the plugin without having them conflict with my framework's gems? For example, if my framework uses treetop version 1.3.0, and a plugin uses treetop 1.4.2 I want each to work with their specified version.
Likewise, is there a way to prevent plugins from conflicting with each other?
I have looked at gem_plugin, _why's sandbox, and some other tools. But I don't see any library that specifically handles this case - I assume its been done before.
I have also looked at the internals of Bundler to see how it manages gem versions. I am prepared to do some pretty complex stuff if need be. But I am still uncertain of how to go about it.
I also have a lot of freedom in how I implement this. So if you think I am barking up the wrong tree, please say so.
Thanks for any advice.
SIDE NOTE: It occurred to me while writing this that I need something similar to the Classloaders in a Java servlet container. A WAR file can include jar files, and the web application's class loader will prefer those over the jars that are on the global classpath. Is there any way in ruby to segment the ruby "classpath" (i.e. load_path, require, etc)?
To be blunt, you can't have two versions of the same gem loaded at the same time.
Bundler does a good (ish) job of looking through all of the required gems and finding a solution to the various overlapping dependencies, but even so it is limited to only loading one version of a gem at a time.
This leads to plugin developers constantly having to update to support any changes that are made in dependent gems in order to avoid just the situation you describe.
(Don't get me started on the screw up that results from the various competing JSON implementations and the pain you have to go through when you have several gem dependencies all requiring different ones.)
Respectfully disagree with the answer above. Here is how I do it:
ruby -S gem list my_gem
`*** LOCAL GEMS ***
my_gem (1.0.1, 1.0.0, 0.0.2)
`
ruby -S gem lock my_gem-1.0.0 > locklist.rb
which generates list of dependencies for a specific version into locklist
require 'rubygems'
gem 'my_gem', '= 1.0.0'
gem 'gem_base', '= 1.0.0'
gem 'rest-client', '= 1.7.2'
gem 'savon', '= 1.1.0'
gem 'addressable', '= 2.3.6'
gem 'mime-types', '= 1.25.1'
gem 'netrc', '= 0.11.0'
now you can do load('locklist.rb') which will load a specific version of a gem along with its dependencies. Look ma, no Bundler.

Questions about building a new gem using Wycats template

I'm writing a new gem I'm basing off of Yehuda's new gem template and I'm slightly confused. Having a Gemfile with dependencies, and also specifying a gemspec with dependencies seems redundant to me. Can someone explain why this is desirable and if it's truly necessary?
Note, this is the first gem I've ever written so I'm new to all of this.
The .gemspec dependencies tell rubygems what it needs to grab to resolve the dependencies when a user installs your gem. The Gemfile is to manage the dependencies while you develop the gem. Rubygems and Bundler aren't connected, at least not yet.
The gemspec is required to build the gem. The Gemfile is more of a convenience, so that people who are working on your gem can easily pull in all the dependencies via Bundler. In the case that you're developing multiple related gems at once, you may also want to put git sources in the Gemfile, so all of the HEAD versions can be tested against.

Resources