isolate 1.10.2 → 2.0.0.pre.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,31 @@
1
+ === 2.0.0.pre.0 / 2010-04-27
2
+
3
+ * Remove cache thawing until I fix RubyGems.
4
+ * Add deprecation warning for Isolate.gems.
5
+ * Fix now! test to properly disable system gems.
6
+ * Fix failing file installation test.
7
+ * Move Isolate::Test to where it belongs.
8
+ * Require Rake tasks automatically if Rake exists.
9
+ * Make sure it's possible to install a local .gem file.
10
+ * Make additive ENV changes idempotent. [Eric Wong]
11
+ * Isolate is a module now, not a class.
12
+ * Use tmp/isolate as the default, replacing tmp/gems.
13
+ * Sanity updates to README, still needs lots of work.
14
+ * Minimize public API on Isolate.
15
+ * Add isolate:debug task, remove isolate:dotgems.
16
+ * Separate isolated gem homes by ruby executable/version.
17
+ * Allow options changes in Isolate files or blocks.
18
+ * Extract instance bits of Isolate into Isolate::Sandbox.
19
+ * Allow path to be set in blocks/files.
20
+ * Add support for local override files.
21
+ * Make the directory path when enabled, not in the ctor.
22
+ * Clean up tests, provide a helper for default options.
23
+ * Move installation inside Isolate::Entry.
24
+ * Make entries additive. Multiple calls to Isolate#gem is fine.
25
+ * Extract, refactor, and test Isolate::Entry.
26
+ * Lock down required Ruby and RubyGems versions.
27
+ * Move Isolate::Entry to its very own file.
28
+
1
29
  === 1.10.2 / 2010-04-24
2
30
 
3
31
  Add 'isolate/now' convenience require in prep. for 2.x.
data/Manifest.txt CHANGED
@@ -5,10 +5,18 @@ README.rdoc
5
5
  Rakefile
6
6
  lib/hoe/isolate.rb
7
7
  lib/isolate.rb
8
+ lib/isolate/entry.rb
8
9
  lib/isolate/now.rb
9
10
  lib/isolate/rake.rb
11
+ lib/isolate/sandbox.rb
12
+ test/fixtures/blort-0.0.gem
10
13
  test/fixtures/isolate.rb
14
+ test/fixtures/override.rb
15
+ test/fixtures/override.rb.local
11
16
  test/fixtures/with-hoe/specifications/hoe-2.3.3.gemspec
12
17
  test/fixtures/with-hoe/specifications/rake-0.8.7.gemspec
13
18
  test/fixtures/with-hoe/specifications/rubyforge-1.0.4.gemspec
19
+ test/isolate/test.rb
14
20
  test/test_isolate.rb
21
+ test/test_isolate_entry.rb
22
+ test/test_isolate_sandbox.rb
data/README.rdoc CHANGED
@@ -37,12 +37,12 @@ same name. Version specifiers are optional.
37
37
  require "rubygems"
38
38
  require "isolate"
39
39
 
40
- Isolate.gems "tmp/gems" do
40
+ Isolate.now! do
41
41
  gem "johnson", "~> 1.1" # or maybe...
42
42
  gem "jbarnette-johnson"
43
43
  end
44
44
 
45
- At the end of the <tt>Isolate.gems</tt> block, you're completely
45
+ At the end of the <tt>Isolate.now!</tt> block, you're completely
46
46
  isolated. <tt>GEM_PATH</tt> and <tt>GEM_HOME</tt> are set, and all
47
47
  your specified gems have been activated.
48
48
 
@@ -60,7 +60,7 @@ Sometimes different sets of gems are appropriate at different
60
60
  times. Isolate allows you to restrict gems by 'environment' (which is
61
61
  really just a string passed in when things are activated).
62
62
 
63
- Isolate.gems "tmp/gems" do
63
+ Isolate.now! do
64
64
  gem "intercession"
65
65
 
66
66
  environment :test, :cucumber do
@@ -75,9 +75,9 @@ your environment.
75
75
 
76
76
  === Options
77
77
 
78
- Any trailing hash args to <tt>gem</tt> are passed to
79
- <tt>Gem::DependencyInstaller</tt> as options. There are two special
80
- exceptions, <tt>:source</tt> and <tt>:args</tt>.
78
+ Any trailing hash args to <tt>gem</tt> are attached to Isolate's gem
79
+ entry as options. There are two well-known keys, <tt>:source</tt>
80
+ and <tt>:args</tt>.
81
81
 
82
82
  # explicitly specify gem source
83
83
  gem "jbarnette-johnson", :source => "http://gems.github.com"
@@ -87,16 +87,16 @@ exceptions, <tt>:source</tt> and <tt>:args</tt>.
87
87
 
88
88
  === An External File
89
89
 
90
- If you'd like to have a separate file that *just* has your +gem+ and
91
- +environment+ calls in it, you can pass an optional <tt>:file</tt>
92
- option instead of (or in addition to) providing a block.
90
+ It's nice to play use <tt>Isolate.now!</tt> inline, but it's better to
91
+ keep all your stuff in an external file. This is supported by default:
93
92
 
94
- # keepin' things clean
95
- Isolate.gems "tmp/gems", :file => "config/isolate.rb"
93
+ Isolate.now!
96
94
 
97
- If you pass <tt>:file => true</tt> instead, Isolate will try to load
98
- from <tt>./Isolate</tt> and <tt>./config/isolate.rb</tt>, in that
99
- order.
95
+ This will look for `Isolate` and `config/isolate.rb` files relative to
96
+ the current directory, and `instance_eval` the first one it finds. If
97
+ there's another matching file with a `.local` extension, Isolate will
98
+ grab that one too. This makes it easy to have additional config that's
99
+ specific to a deployment or developer.
100
100
 
101
101
  === Installing Isolated Gems
102
102
 
@@ -105,19 +105,17 @@ automatically. You can pass the <tt>:cleanup</tt>, <tt>:install</tt>,
105
105
  and <tt>:verbose</tt> options to control things:
106
106
 
107
107
  # don't remove unnecesary gems
108
- Isolate.gems "tmp/gems", :cleanup => false do
109
- ...
110
- end
108
+ Isolate.now!, :cleanup => false
111
109
 
112
110
  # install, but quietly
113
- Isolate.gems "tmp/gems", :verbose => false do
114
- ...
115
- end
111
+ Isolate.now! :verbose => false
116
112
 
117
113
  # don't install
118
- Isolate.gems "tmp/gems", :install => false do
119
- ...
120
- end
114
+ Isolate.now! :install => false
115
+
116
+ All of these options can be set inside your `Isolate` file, too:
117
+
118
+ options :cleanup => false, :verbose => false
121
119
 
122
120
  === Interaction and Rake
123
121
 
@@ -138,29 +136,22 @@ by any of your gems are available on your PATH.
138
136
  # run a new isolated subshell
139
137
  $ rake isolate:sh
140
138
 
141
- ==== Exporting
142
-
143
- Isolate can generate a <tt>.gems</tt> manifest file in the root
144
- directory of your project. <tt>.gems</tt> is used (among other things)
145
- to specify project dependencies on Heroku.
139
+ ==== What's up?
146
140
 
147
- $ rake isolate:dotgems
141
+ $ rake isolate:debug
148
142
 
149
- === A Rails Example
143
+ === A Rails 2 Example
150
144
 
151
145
  Here's a quick example (extracted from a real project) of how to use
152
146
  Isolate with Rails. This project doesn't use vendored Rails, and
153
147
  doesn't want to depend on any system gems (except isolate, of course).
154
148
 
155
- Gem dependencies are defined in <tt>config/preinitializer.rb</tt>. If
156
- you want, you could just as easily put them in
157
- <tt>config/{gems,deps,isolate}.rb</tt>, just make sure it's loaded in
158
- the preinitializer:
149
+ Let's edit <tt>config/preinitializer.rb</tt>:
159
150
 
160
151
  require "rubygems"
161
152
  require "isolate"
162
153
 
163
- Isolate.gems "tmp/gems", :file => "config/isolate.rb"
154
+ Isolate.now!
164
155
 
165
156
  In <tt>config/isolate.rb</tt>:
166
157
 
@@ -195,9 +186,8 @@ In <tt>config/isolate.rb</tt>:
195
186
  gem "webrat" # integration tests
196
187
  end
197
188
 
198
- You could specify all this inside an <tt>Isolate.gems</tt> block in
199
- <tt>config/preinitializer.rb</tt>, of course, but sometimes it's nicer
200
- to have an external file that's just your dependency manifest.
189
+ You could specify all this inside the <tt>Isolate.now!</tt> block, of
190
+ course, but keeping everything in an external file is nicer.
201
191
 
202
192
  Since this is loaded in the preinitializer, Isolate will install and
203
193
  activate the all gems before Rails loads. The current environment is
@@ -223,7 +213,7 @@ as putting:
223
213
  before the <tt>Hoe.spec</tt> call in your <tt>Rakefile</tt>.
224
214
 
225
215
  If you're not using Hoe, you can just do a regular
226
- <tt>Isolate.gems</tt> block at the top of your Rakefile.
216
+ <tt>Isolate.now!</tt> block at the top of your Rakefile.
227
217
 
228
218
  == Installation
229
219
 
data/Rakefile CHANGED
@@ -7,6 +7,9 @@ Hoe.spec "isolate" do
7
7
  developer "John Barnette", "jbarnette@rubyforge.org"
8
8
  developer "Ryan Davis", "ryand-ruby@zenspider.com"
9
9
 
10
+ require_ruby_version ">= 1.8.7"
11
+ require_rubygems_version ">= 1.3.6"
12
+
10
13
  self.extra_rdoc_files = Dir["*.rdoc"]
11
14
  self.history_file = "CHANGELOG.rdoc"
12
15
  self.readme_file = "README.rdoc"
data/lib/hoe/isolate.rb CHANGED
@@ -18,18 +18,18 @@ class Hoe # :nodoc:
18
18
 
19
19
  module Isolate
20
20
 
21
- # Where should Isolate, um, isolate? [default: <tt>"tmp/gems"</tt>]
21
+ # Where should Isolate, um, isolate? [default: <tt>"tmp/isolate"</tt>]
22
22
 
23
23
  attr_accessor :isolate_dir
24
24
 
25
25
  def initialize_isolate # :nodoc:
26
26
  # Tee hee! Move ourselves to the front to beat out :test.
27
27
  Hoe.plugins.unshift Hoe.plugins.delete(:isolate)
28
- self.isolate_dir ||= "tmp/gems"
28
+ self.isolate_dir ||= "tmp/isolate"
29
29
  end
30
30
 
31
31
  def define_isolate_tasks # HACK
32
- i = ::Isolate.new self.isolate_dir, :cleanup => true
32
+ i = ::Isolate::Sandbox.new :path => isolate_dir
33
33
 
34
34
  (self.extra_deps + self.extra_dev_deps).each do |name, version|
35
35
  i.gem name, *Array(version)
data/lib/isolate.rb CHANGED
@@ -1,71 +1,46 @@
1
- require "rubygems/dependency_installer"
2
- require "rubygems/uninstaller"
3
- require "rubygems/requirement"
1
+ require "isolate/sandbox"
4
2
 
5
3
  # Restricts +GEM_PATH+ and +GEM_HOME+ and provides a DSL for
6
4
  # expressing your code's runtime Gem dependencies. See README.rdoc for
7
5
  # rationale, limitations, and examples.
8
6
 
9
- class Isolate
7
+ module Isolate
10
8
 
11
- VERSION = "1.10.2" # :nodoc:
9
+ # Duh.
12
10
 
13
- # An isolated Gem, with requirement, environment restrictions, and
14
- # installation options. Internal use only.
15
-
16
- class Entry < Struct.new(:name, :requirement, :environments, :options)
17
- def matches? environment # :nodoc:
18
- environments.empty? || environments.include?(environment)
19
- end
20
-
21
- def matches_spec? spec
22
- name == spec.name and requirement.satisfied_by? spec.version
23
- end
24
- end
25
-
26
- attr_reader :entries # :nodoc:
27
- attr_reader :path # :nodoc:
11
+ VERSION = "2.0.0.pre.0"
28
12
 
29
13
  # Disable Isolate. If a block is provided, isolation will be
30
14
  # disabled for the scope of the block.
31
15
 
32
16
  def self.disable &block
33
- instance.disable(&block)
17
+ sandbox.disable(&block)
34
18
  end
35
19
 
36
- def self.env # :nodoc:
20
+ def self.env
37
21
  ENV["ISOLATE_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
38
22
  end
39
23
 
40
- # Declare an isolated RubyGems environment, installed in +path+. Any
41
- # block given will be <tt>instance_eval</tt>ed, see Isolate#gem and
42
- # Isolate#environment for the sort of stuff you can do.
43
- #
44
- # If you'd like to specify gems and environments in a separate file,
45
- # you can pass an optional <tt>:file</tt> option.
46
- #
47
- # Option defaults:
48
- #
49
- # {
50
- # :cleanup => true,
51
- # :install => true,
52
- # :system => false,
53
- # :verbose => true
54
- # }
24
+ def self.gems path, options = {}, &block # :nodoc:
25
+ warn "Isolate.gems is deprecated, use Isolate.now! instead.\n" +
26
+ "Isolate.gems will be removed in v3.0."
55
27
 
56
- def self.gems path, options = {}, &block
57
- @@instance = new path, options, &block
58
- @@instance.activate
28
+ now! options.merge(:path => path), &block
59
29
  end
60
30
 
61
- @@instance = nil
31
+ @@sandbox = nil
62
32
 
63
- def self.instance # :nodoc:
64
- @@instance
33
+ def self.sandbox
34
+ @@sandbox
65
35
  end
66
36
 
67
- def self.now! #:nodoc:
68
- gems "tmp/gems", :file => true, :system => true
37
+ # Declare an isolated RubyGems environment, installed in +path+. Any
38
+ # block given will be <tt>instance_eval</tt>ed, see Isolate#gem and
39
+ # Isolate#environment for the sort of stuff you can do.
40
+
41
+ def self.now! options = {}, &block
42
+ @@sandbox = Isolate::Sandbox.new options, &block
43
+ @@sandbox.activate
69
44
  end
70
45
 
71
46
  # Poke RubyGems, we've probably monkeyed with a bunch of paths and
@@ -76,220 +51,4 @@ class Isolate
76
51
  Gem.clear_paths
77
52
  Gem.source_index.refresh!
78
53
  end
79
-
80
- # Create a new Isolate instance. See Isolate.gems for the public
81
- # API. You probably don't want to use this constructor directly.
82
-
83
- def initialize path, options = {}, &block
84
- @enabled = false
85
- @entries = []
86
- @environments = []
87
- @path = File.expand_path path
88
-
89
- @install = options.fetch :install, true
90
- @system = options.fetch :system, false
91
- @verbose = options.fetch :verbose, true
92
- @cleanup = @install && options.fetch(:cleanup, true)
93
-
94
- file = options[:file]
95
- file = Dir["{Isolate,config/isolate.rb}"].first if TrueClass === file
96
-
97
- instance_eval IO.read(file), file if file
98
- instance_eval(&block) if block_given?
99
- end
100
-
101
- # Activate this set of isolated entries, respecting an optional
102
- # +environment+. Points RubyGems to a separate repository, messes
103
- # with paths, auto-installs gems (if necessary), activates
104
- # everything, and removes any superfluous gem (again, if
105
- # necessary). If +environment+ isn't specified, +ISOLATE_ENV+,
106
- # +RAILS_ENV+, and +RACK_ENV+ are checked before falling back to
107
- # <tt>"development"</tt>.
108
-
109
- def activate environment = nil
110
- enable unless enabled?
111
-
112
- env = (environment || self.class.env).to_s
113
-
114
- install env if install?
115
-
116
- entries.each do |e|
117
- Gem.activate e.name, *e.requirement.as_list if e.matches? env
118
- end
119
-
120
- cleanup if cleanup?
121
-
122
- self
123
- end
124
-
125
- def cleanup # :nodoc:
126
- activated = Gem.loaded_specs.values.map { |s| s.full_name }
127
-
128
- extra = Gem.source_index.gems.values.sort.reject { |spec|
129
- !spec.loaded_from.include?(path) or
130
- activated.include? spec.full_name or
131
- entries.any? { |e| e.matches_spec? spec }
132
- }
133
-
134
- return if extra.empty?
135
-
136
- padding = Math.log10(extra.size).to_i + 1
137
- format = "[%0#{padding}d/%s] Nuking %s."
138
-
139
- extra.each_with_index do |e, i|
140
- log format % [i + 1, extra.size, e.full_name]
141
-
142
- Gem::DefaultUserInteraction.use_ui Gem::SilentUI.new do
143
- Gem::Uninstaller.new(e.name,
144
- :version => e.version,
145
- :ignore => true,
146
- :executables => true,
147
- :install_dir => path).uninstall
148
- end
149
- end
150
- end
151
-
152
- def cleanup? # :nodoc:
153
- @cleanup
154
- end
155
-
156
- def disable &block # :nodoc:
157
- return self if not enabled?
158
-
159
- ENV["GEM_PATH"] = @old_gem_path
160
- ENV["GEM_HOME"] = @old_gem_home
161
- ENV["PATH"] = @old_path
162
- ENV["RUBYOPT"] = @old_ruby_opt
163
-
164
- $LOAD_PATH.replace @old_load_path
165
-
166
- @enabled = false
167
-
168
- self.class.refresh
169
- begin; return yield ensure enable end if block_given?
170
-
171
- self
172
- end
173
-
174
- def enable # :nodoc:
175
- return self if enabled?
176
-
177
- @old_gem_path = ENV["GEM_PATH"]
178
- @old_gem_home = ENV["GEM_HOME"]
179
- @old_path = ENV["PATH"]
180
- @old_ruby_opt = ENV["RUBYOPT"]
181
- @old_load_path = $LOAD_PATH.dup
182
-
183
- ENV["GEM_HOME"] = path
184
-
185
- unless system?
186
- $LOAD_PATH.reject! do |p|
187
- p != File.dirname(__FILE__) &&
188
- Gem.path.any? { |gp| p.include?(gp) }
189
- end
190
-
191
- # HACK: Gotta keep isolate explicitly in the LOAD_PATH in
192
- # subshells, and the only way I can think of to do that is by
193
- # abusing RUBYOPT.
194
-
195
- ENV["RUBYOPT"] = "#{ENV['RUBYOPT']} -I#{File.dirname(__FILE__)}"
196
- ENV["GEM_PATH"] = path
197
- end
198
-
199
- bin = File.join path, "bin"
200
- ENV["PATH"] = [bin, ENV["PATH"]].join File::PATH_SEPARATOR
201
-
202
- self.class.refresh
203
- Gem.path.unshift path if system?
204
-
205
- @enabled = true
206
-
207
- self
208
- end
209
-
210
- def enabled? # :nodoc:
211
- @enabled
212
- end
213
-
214
- # Restricts +gem+ calls inside +block+ to a set of +environments+.
215
-
216
- def environment *environments, &block
217
- old = @environments
218
- @environments = @environments.dup.concat environments.map { |e| e.to_s }
219
-
220
- instance_eval(&block)
221
- ensure
222
- @environments = old
223
- end
224
-
225
- # Express a gem dependency. Works pretty much like RubyGems' +gem+
226
- # method, but respects +environment+ and doesn't activate 'til
227
- # later.
228
-
229
- def gem name, *requirements
230
- options = Hash === requirements.last ? requirements.pop : {}
231
-
232
- requirement = if requirements.empty? then
233
- Gem::Requirement.default
234
- else
235
- Gem::Requirement.new requirements
236
- end
237
-
238
- entry = Entry.new name, requirement, @environments, options
239
-
240
- entries << entry
241
-
242
- entry
243
- end
244
-
245
- def install environment # :nodoc:
246
- installable = entries.select do |e|
247
- !Gem.available?(e.name, *e.requirement.as_list) && e.matches?(environment)
248
- end
249
-
250
- return self if installable.empty?
251
-
252
- padding = Math.log10(installable.size).to_i + 1
253
- format = "[%0#{padding}d/%s] Isolating %s (%s)."
254
-
255
- installable.each_with_index do |e, i|
256
- log format % [i + 1, installable.size, e.name, e.requirement]
257
-
258
- old = Gem.sources.dup
259
- options = e.options.merge(:development => false,
260
- :generate_rdoc => false,
261
- :generate_ri => false,
262
- :install_dir => path)
263
- source = options.delete :source
264
- args = options.delete :args
265
- Gem.sources += Array(source) if source
266
- installer = Gem::DependencyInstaller.new options
267
-
268
- Gem::Command.build_args = Array(args) if args
269
- installer.install e.name, e.requirement
270
-
271
- Gem.sources = old
272
- Gem::Command.build_args = nil if args
273
- end
274
-
275
- Gem.source_index.refresh!
276
-
277
- self
278
- end
279
-
280
- def install? # :nodoc:
281
- @install
282
- end
283
-
284
- def log s # :nodoc:
285
- $stderr.puts s if verbose?
286
- end
287
-
288
- def system? # :nodoc:
289
- @system
290
- end
291
-
292
- def verbose? # :nodoc:
293
- @verbose
294
- end
295
54
  end