gem_isolator 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []