GemIsolator
Allows running commands in an isolated set of gems.
Useful for testing gem dependencies in your project and reproducing related bugs.
NOTE: It currently requires Bundler to setup the isolated environment, but Bundler isn't necessary to run your Ruby code in isolation.
Requirements
For supported Ruby versions, check the Travis build status
(For supporting older versions, open an issue - with some info about why you are unable to upgrade).
Installation
Add this line to your application's Gemfile:
gem 'gem_isolator'
And then execute:
$ bundle
Usage
E.g. in an RSpec test:
cmd = "ruby #{File.expand_path('lib/foo.rb')}"
GemIsolator.isolate(gems: [%w(bar >=3.2.1)]) do |env, isolation|
expect(isolation.system(env, cmd)).to eq(true)
end
-
lib/foo.rb
is relative to your project's current directory. -
system
acts just likeKernel.system
. - You can pass custom environment variables, e.g
env.merge('FOO' => '1')
-
:gems
is a list of gems you want installed
To use your project sources:
cmd = "bundle exec ruby #{File.expand_path('lib/foo.rb')}" # bundler is needed to turn project dir into gem source
source_def = ['myproject', path: Dir.pwd] # expands to use `:path` parameter in `Gemfile`
GemIsolator.isolate(gems: [source_def, %w(bar >=3.2.1)]) do |env, isolation|
expect(isolation.system(env, cmd)).to eq(true)
end
Features
- sets up temp dir with bundled gems
- allows defining which gems you need installed in the sandbox
- allows overriding environment
- allows running commands via Bundler or plain RubyGems
- allows multiple commands within same sandbox
- fails if sandbox can't be initialized
- seemlessly allows you to run same tests using Docker
- smart caching and/or proxy for faster gem installations
- allows reusing same sandbox object between tests
- release a 1.x as soon as API is useful and comfortable enough
Things to be careful about
You can run commands in 3 ways:
-
system('foo')
will search for 'foo' among the binaries of installed isolated gems first -
system('bin/foo')
a binstubbed version of binary from isolated gems (uses Bundler) -
system('bundle exec foo')
uses Bundler instead of RubyGems
Capturing output is not yet supported (open a PR if you're interested).
NOTE: Inside the block, the current directory is set to a temporary directory, so it's best to expand your paths outside the block.
NOTE: You might prefer to use caching, by e.g. setting a gem source or proxy. Open a feature request if you're interested in something automatic/generic.
NOTE: :gems
option is an array (for each gem) of arrays (Gemfile gem
keyword arguments). .inspect
is used to stringify, so things should work as expected, even if you use a hash.
Debugging
Just use system() commands to find out what's going on, e.g.:
cmd = "ruby #{File.expand_path('lib/foo.rb')}"
GemIsolator.isolate(gems: [%w(bar ~>1.2)]) do |env, isolation|
# debugging
isolation.system("pwd")
isolation.system("gem env")
isolation.system("gem list")
isolation.system("cat Gemfile")
isolation.system("cat Gemfile.lock")
isolatior.system("ls -l")
isolatior.system("find bundle -name 'foo'")
expect(isolation.system(env, cmd)).to eq(true)
end
Development
Run rake spec
to run the tests.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/e2/gem_isolator.
If there are no other major open pull requests (or active branches), feel free to refactor/reorganize the project as you like.
License
The gem is available as open source under the terms of the MIT License.