gem_isolator 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 09ed7c8a98c30e7514975a626310801132f33458
4
+ data.tar.gz: ac418d8c8c303f4463a1de38bb3132591c4c08ff
5
+ SHA512:
6
+ metadata.gz: 1eda65173f7419182d89c00ffe2a3d71004e9d66f8fa6d27c482d11cce5035859d31b721c4e4a419991fc1dd3ad36f3faba0ac17034deaedd631f5db0a8627da
7
+ data.tar.gz: b3f5df4b52ba9d37910e4ea8f4d5e63693b187b5dc2e35e0dc1babb0ac41c860c1c2aeadd8516a4db3c04cc17ff7f14c75041fa5de2c0e41bdc6bc53f885a414
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Cezary Baginski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,114 @@
1
+ [![Gem Version](https://badge.fury.io/rb/gem_isolator.png)](http://badge.fury.io/rb/gem_isolator) [![Build Status](https://secure.travis-ci.org/e2/gem_isolator.png?branch=master)](http://travis-ci.org/e2/gem_isolator)
2
+
3
+ # GemIsolator
4
+
5
+ Allows running commands in an isolated set of gems.
6
+
7
+ Useful for testing gem dependencies in your project and reproducing related bugs.
8
+
9
+ NOTE: It currently requires Bundler to setup the isolated environment, but
10
+ Bundler isn't necessary to run your Ruby code in isolation.
11
+
12
+ ## Requirements
13
+
14
+ For supported Ruby versions, check the .travis.yml file.
15
+
16
+ (For supporting older versions, open an issue - with some info about why you are unable to upgrade).
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'gem_isolator'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ ## Usage
31
+
32
+ E.g. in an RSpec test:
33
+
34
+ ```ruby
35
+ cmd = "ruby #{File.expand_path('lib/foo.rb')}"
36
+ GemIsolator.isolate(gems: %w(bar >=3.2.1)) do |env, isolation|
37
+ expect(isolation.system(env, cmd)).to eq(true)
38
+ end
39
+ ```
40
+
41
+ 1. 'lib/foo.rb' is relative to your project's current directory.
42
+ 2. `system` acts just like `Kernel.system`.
43
+ 3. you can pass custom environment variables, e.g `env.merge('FOO' => '1')`
44
+ 4. `:gems` is a list of gems you want installed
45
+
46
+ ## Features
47
+
48
+ - [x] sets up temp dir with bundled gems
49
+ - [x] allows defining which gems you need installed in the sandbox
50
+ - [x] allows overriding environment
51
+ - [x] allows running commands via Bundler or plain RubyGems
52
+ - [x] allows multiple commands within same sandbox
53
+ - [x] fails if sandbox can't be initialized
54
+ - [ ] seemlessly allows you to run same tests using Docker
55
+ - [ ] smart caching and/or proxy for faster gem installations
56
+ - [ ] allows reusing same sandbox object between tests
57
+ - [ ] release a 1.x as soon as API is useful and comfortable enough
58
+
59
+ ## Things to be careful about
60
+
61
+ You can run commands in 3 ways:
62
+
63
+ 1. `system('foo')` will search for 'foo' among the binaries of installed isolated gems first
64
+ 2. `system('bin/foo')` a binstubbed version of binary from isolated gems (uses Bundler)
65
+ 3. `system('bundle exec foo')` uses Bundler instead of RubyGems
66
+
67
+ Capturing output is not yet supported (open a PR if you're interested).
68
+
69
+ NOTE: Inside the block, the current directory is set to a temporary directory,
70
+ so it's best to expand your paths outside the block.
71
+
72
+ NOTE: Variables other than those reset by `Bundler.with_clean_env` are left
73
+ alone, so it's up to you to reset them. (Open an issue if you want to have a
74
+ "clean slate").
75
+
76
+ NOTE: You might prefer to use caching, by e.g. setting a gem source or proxy.
77
+ Open a feature request if you're interested.
78
+
79
+ 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.
80
+
81
+
82
+ ## Debugging
83
+
84
+ Just use system() commands to find out what's going on, e.g.:
85
+
86
+ ```ruby
87
+ cmd = "ruby #{File.expand_path('lib/foo.rb')}"
88
+ GemIsolator.isolate(gems: [%w(bar ~>1.2)]) do |env, isolation|
89
+ # debugging
90
+ isolation.system("pwd")
91
+ isolation.system("gem env")
92
+ isolation.system("gem list")
93
+ isolation.system("cat Gemfile")
94
+ isolation.system("cat Gemfile.lock")
95
+ isolatior.system("ls -l")
96
+ isolatior.system("find bundle -name 'foo'")
97
+
98
+ expect(isolation.system(env, cmd)).to eq(true)
99
+ end
100
+ ```
101
+
102
+ ## Development
103
+
104
+ Run `rake spec` to run the tests.
105
+
106
+ ## Contributing
107
+
108
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/gem_isolator.
109
+
110
+ If there are no other major open pull requests (or active branches), feel free to refactor/reorganize the project as you like.
111
+
112
+ ## License
113
+
114
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ require 'gem_isolator/version'
3
+ require 'gem_isolator/isolation'
4
+
5
+ # Namespace
6
+ module GemIsolator
7
+ def self.isolate(options, &block)
8
+ Isolation.new(options, &block)
9
+ end
10
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Show warnings about vulnerabilities, bugs and outdated Rubies, since previous
4
+ # versions aren't tested or officially supported.
5
+ require 'ruby_dep/warning'
6
+ RubyDep::Warning.new.show_warnings
7
+
8
+ require 'gem_isolator/isolation/environment'
9
+ require 'gem_isolator/isolation/sandbox'
10
+
11
+ module GemIsolator
12
+ # Main class for setting up sandbox/isolation environment
13
+ class Isolation
14
+ def initialize(options)
15
+ gem_defs = options.fetch(:gems, [[]])
16
+ within_sandbox do
17
+ @sandbox = Sandbox.new(gems: gem_defs)
18
+ sandbox.setup
19
+ environment = Environment.new(gem_home: sandbox.gem_home).to_hash
20
+ yield(environment, self)
21
+ end
22
+ end
23
+
24
+ def system(*args)
25
+ sandbox.system(*args)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :sandbox
31
+
32
+ def within_sandbox
33
+ Bundler.with_clean_env do
34
+ Dir.mktmpdir do |path|
35
+ Dir.chdir(path) do
36
+ yield
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module GemIsolator
3
+ class Isolation
4
+ # RubyGems isolating environment
5
+ class Environment
6
+ def initialize(options)
7
+ @gem_home = options.fetch(:gem_home)
8
+ end
9
+
10
+ def to_hash
11
+ @environment ||= {
12
+ 'GEM_HOME' => gem_home.to_s,
13
+ 'GEM_PATH' => gem_home.to_s,
14
+ 'PATH' => "#{bin_path}:#{ENV['PATH']}"
15
+ }.freeze
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :gem_home
21
+
22
+ def bin_path
23
+ @bin_path ||= gem_home + 'bin'
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ module GemIsolator
3
+ class Isolation
4
+ # Creates a Gemfile
5
+ class Gemfile
6
+ def initialize(options)
7
+ gem_defs = options.fetch(:gems)
8
+ @content = generate(gem_defs)
9
+ end
10
+
11
+ def create
12
+ IO.write('Gemfile', @content)
13
+ end
14
+
15
+ private
16
+
17
+ def generate(gem_defs)
18
+ content = "source 'https://rubygems.org';\n"
19
+ gem_defs.each do |requirements|
20
+ content += "gem #{requirements.map(&:inspect).join(', ')};\n"
21
+ end
22
+ content
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ require 'gem_isolator/isolation/gemfile'
3
+
4
+ module GemIsolator
5
+ class Isolation
6
+ # Class for installing gems into the sandbox in the current directory
7
+ class Sandbox
8
+ class Error < RuntimeError
9
+ class BundlerFailed < Error; end
10
+ end
11
+
12
+ def initialize(options)
13
+ @gem_defs = options.fetch(:gems)
14
+ end
15
+
16
+ def setup
17
+ Gemfile.new(gems: @gem_defs).create
18
+ case Kernel.system(*%w(bundle install --standalone --binstubs))
19
+ when false
20
+ raise Error::BundlerFailed, 'sandbox setup with bundler failed'
21
+ when nil
22
+ raise Errno::ENOENT, "cannot run command 'bundle'"
23
+ end
24
+ end
25
+
26
+ def gem_home
27
+ @gem_home ||= gem_base_path + ruby_dir
28
+ end
29
+
30
+ def system(*args)
31
+ Kernel.system(*args)
32
+ end
33
+
34
+ private
35
+
36
+ def gem_base_path
37
+ @gem_base_path ||= Pathname.pwd + 'bundle/ruby'
38
+ end
39
+
40
+ def ruby_dir
41
+ subdirs = gem_base_path.children
42
+ unknown_dirs = subdirs[1..-1]
43
+ return subdirs.first if unknown_dirs.empty?
44
+ raise ArgumentError, "Unknown gem home subdirs: #{unknown_dirs.inspect}"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module GemIsolator
3
+ VERSION = '0.2.1'
4
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem_isolator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Cezary Baginski
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby_dep
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.3.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.3.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '11.1'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '11.1'
47
+ description: Good for testing dependencies of a gem and/or different gem version combinations
48
+ email:
49
+ - cezary@chronomantic.net
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - LICENSE.txt
55
+ - README.md
56
+ - lib/gem_isolator.rb
57
+ - lib/gem_isolator/isolation.rb
58
+ - lib/gem_isolator/isolation/environment.rb
59
+ - lib/gem_isolator/isolation/gemfile.rb
60
+ - lib/gem_isolator/isolation/sandbox.rb
61
+ - lib/gem_isolator/version.rb
62
+ homepage: https://github.com/e2/gem_isolator
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.2'
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 2.2.5
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.6.4
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: Use an isolated set of gems in your tests
89
+ test_files: []