Loading dependencies at runtime with bundler - ruby

I have an application with many optional components, all with their own complex dependencies. For example, some deployments might want to use LDAP functionality and will need to load ldap-related gems. But many will not, and those that don't should not have to install ldap-related gems.
How can I use Bundler to load these dependencies depending on which components users (deployers) have enabled?
I don't want to to force deployers to manually edit their Gemfiles. It has to be possible to enabled/disable components via the application's UI.
Just including every possible dependency in the Gemfile is not ideal. Some of the rarely used components require a lot of complicated native compilation. Another solution might be to have the application edit its own Gemfile. But this is kind of awkward and would likely require a restart every time components are changed.
Is there a way in Bundler to dynamically load gems in runtime? If not, are there alternatives that provide something like Bundler's sandboxing but allow for dynamic loading?

You could provide multiple Gemfiles and use bundle install --gemfile to use the specific gemfile and only install the Gems you need for that deployment.
In your application you could then use Bundle.setup with the appropriate groups of the previously installed Gemfile to just load the appropriate Gems
Sure thats not a nice and easy way, but should give you the functionality you want.
See
Bundler Setup
bundle install

Related

Best way to handle a direct dependency that another dependency uses in a gemspec file

In a Ruby project, I have a dependency that is used by my gem, and also used by a gem that my gem depends on. Specifically, I use sawyer and octokit, so my-gem depends on sawyer, and octokit also depends on sawyer.
Right now, this is what I have:
spec.add_runtime_dependency 'octokit', '~> 6.0'
spec.add_runtime_dependency 'sawyer' # no version constraint
This works fine for now, but I'm wondering if this is the best way to do things. The intent is to use whatever version of sawyer that octokit uses, but I'm wondering if it is perhaps better to have a version constraint, or to just omit sawyer from the gemspec.
TL;DR
Gems are most often meant to be libraries, not applications, although there are exceptions. Libraries should only list gems in their gemspecs that are first class dependencies, and avoid building in unnecessary or overly restrictive constraints on the gem.
What to List and How Much to Constrain
When to List and Constrain Gems
As a rule of thumb, gems (as opposed to applications) shouldn't constrain gem dependencies except when necessary to avoid specific conflicts. Otherwise, you may unnecessarily constrain the use of your gem by others who have other constraints or need later or earlier versions for whatever reason.
So, assuming your gem really needs octokit >= 6.0 and <= 6.1, then go ahead and constrain it that way. Otherwise, drop or reduce the level of constraint to the greatest practical extent. For example, if a dependency follows semantic versioning then you should only constrain its major version (if you constrain it at all) unless there's a known problem with a particular minor or patch version that you need to avoid. In other words, be as liberal as possible with library constraints.
Don't Duplicate the Responsibilities of Bundler or Upstream Libraries
If you aren't calling the sawyer gem directly, then it's really just a dependency of octokit that's unrelated to your gem. In such cases, you should trust Bundler's dependency resolution for octokit to pull in the things it needs to function unless you find a specific problem. Otherwise, you will end up managing dependencies that may change upstream, which creates a needless burden on both maintenance of your library and any developers who use it.
On the other hand, if your gem makes direct calls to sawyer, then it's a first class dependency that should be listed in your gemspec. Again, if needed at all, keep your constraints as loose as you can to avoid excessive constraints or having to continuously update minor or patch versions down the road.
In short, if the only reason to list sawyer is because octokit currently uses it then Bundler should manage the dependency. Ideally, the octokit maintainers should update octokit's dependencies when needed. Only manage third-party dependencies when absolutely necessary, and only as long as it takes to get essential fixes merged into the upstream library (or into a suitable fork or library replacement if the fixes you need won't be merged).

In order to get my Gem published, do I absolutely need to write tests?

I would like to publish a Gem I wrote so it can be installed by users via gem install my-gem. The RubyGems guide on how to Make Your Own Gem has a section on using Test::Unit and rake to test my code.
Writing tests is going to add significant development time to a Gem that (in this case) is somewhat trivial in nature. I'd like to avoid it if possible.
Is it necessary to write tests in order to publish a Gem or just a suggestion?
It is a (good) suggestion, and is in no way required to publish a gem.

When packaging gems with an application, can one run the application one another computer with only installing the correct ruby version?

I have a ruby application I would like to package and distribute. The application is dependent on a few gems. This is a question about user experience.
I want the user to be able to download a folder and run my application. At most, the user will have to download and install the correct version of Ruby as a requirement.
Will running 'bundle package' be enough to cover the gem dependencies?
I have also explored bundling the running environment with ocra but I think that is over kill and to much of a knock on performance.

Verify minimum version requirements with bundler

I maintain a gem with dependencies that are stored in a Gemfile, for example:
gem 'foo', '~> 1.5'
gem 'bar', '~> 2.0.5'
Thanks to pessimistic version constraints, bundler will by default install the latest 1.x version of foo, but can compromise on a lower version if my gem is used in conjunction with another that requires (for example) foo =1.6.2
Question: Is there a simple way to get bundler to install all of the minimum versions of my dependencies (in this case, foo =1.5.0 and bar =2.0.5) so that I can test whether, after I write some new functionality, my gem will still work in combination with other environments that use those lower versions?
Or, is the only way for me to manually reinstall all of the minimum versions and then run my tests?
Because we decided to use Rubygems' Requirement class, there isn't a way to specify the lowest version. I vaguely recall an automated testing tool to help you iterate over dependency versions that you want to test against, but it's extremely hard to automate because there are an exponential number of possible version combinations. I suggest creating a second Gemfile with the oldest versions you want to test against, and using BUNDLE_GEMFILE to run against that Gemfile in an additional CI build.
I saw your question in IRC... from my understanding, there's no way to do it without changing your Gemfile. Sorry. :(
https://github.com/carlhuda/bundler/blob/master/lib/bundler/cli.rb for reference

Are you supposed to include Gemfile.lock in a published gem?

If you publish the source for a ruby gem to github.com, is the Gemfile.lock supposed to included?
This guy has strong opinions.
http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
Namely:
You should include your Gemfile.lock in version control if you're developing an application
You should not include your Gemfile.lock in version control if you're developing a gem
I'm not sure if I'm convinced yet. I think that using the Gemfile.lock in my version control is good. But I think that it is too much for that file to be included for others use. The gemfile is enough for an install for others. I think Gemfile.lock is for development, not deployment, contrary to the previously expressed opinion.
No, the Gemfile.lock file is an aid for deployment, not for development.
It is used to recreate an exact replica of your environment on another systems, something that is not (usually) required for development.

Resources