isolate 2.0.0.pre.1 → 2.0.0.pre.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,12 @@
1
+ === 2.0.0.pre.2 / 2010-05-07
2
+
3
+ * Update docs.
4
+ * Add event hooks for better extension/integration.
5
+ * Add ISOLATED env var when Isolate is activated.
6
+ * Teach the Hoe plugin to recognize Isolate files.
7
+ * Add `env` as an alias for `environment`.
8
+ * Clean up Rake tasks.
9
+
1
10
  === 2.0.0.pre.1 / 2010-05-02
2
11
 
3
12
  * Don't provide a special way to set path, options is enough.
data/Manifest.txt CHANGED
@@ -6,6 +6,7 @@ Rakefile
6
6
  lib/hoe/isolate.rb
7
7
  lib/isolate.rb
8
8
  lib/isolate/entry.rb
9
+ lib/isolate/events.rb
9
10
  lib/isolate/now.rb
10
11
  lib/isolate/rake.rb
11
12
  lib/isolate/sandbox.rb
@@ -19,4 +20,5 @@ test/fixtures/with-hoe/specifications/rubyforge-1.0.4.gemspec
19
20
  test/isolate/test.rb
20
21
  test/test_isolate.rb
21
22
  test/test_isolate_entry.rb
23
+ test/test_isolate_events.rb
22
24
  test/test_isolate_sandbox.rb
data/README.rdoc CHANGED
@@ -5,18 +5,13 @@
5
5
  == Description
6
6
 
7
7
  Isolate is a very simple RubyGems sandbox. It provides a way to
8
- express and install your code's Gem dependencies.
8
+ express and automatically install your project's Gem dependencies.
9
9
 
10
- == How?
10
+ == Wha?
11
11
 
12
12
  When Isolate runs, it uses GEM_HOME, GEM_PATH, and a few other tricks
13
- to completely separate your code from the system's RubyGems
14
- configuration, leaving it free to run in blissful solitude.
15
-
16
- While Isolate doesn't make any assumptions about what sort of code
17
- you're writing, it was extracted from a few Rails apps, so it's
18
- naturally going to be most useful with stuff like Rails, Merb, or
19
- Sinatra.
13
+ to separate your code from the system's RubyGems configuration,
14
+ leaving it free to run in blissful solitude.
20
15
 
21
16
  Isolate is very, very, very stupid simple. For a much more
22
17
  full-featured Gem bundler, check out Yehuda Katz and Carl Lerche's
@@ -24,111 +19,116 @@ Bundler[http://github.com/carlhuda/bundler]: It does a lot of fancy
24
19
  AOT dependency resolution, supports non-gem (including git) resources,
25
20
  and is probably a better fit for you.
26
21
 
27
- YMMV, but I haven't tried Isolate with anything older than RubyGems
28
- 1.3.5.
22
+ == Requirements
23
+
24
+ RubyGems 1.3.6 or better, Ruby 1.8.7 or better.
29
25
 
30
- == Examples
26
+ == Getting Started
31
27
 
32
- === Defining Your Isolated Environment
28
+ === Rails 2
33
29
 
34
- It's pretty easy: <tt>gem</tt> is similar to RubyGems' method of the
35
- same name. Version specifiers are optional.
30
+ In <tt>config/preinitializer.rb</tt>:
36
31
 
37
32
  require "rubygems"
38
- require "isolate"
33
+ require "isolate/now"
39
34
 
40
- Isolate.now! do
41
- gem "johnson", "~> 1.1" # or maybe...
42
- gem "jbarnette-johnson"
43
- end
35
+ In <tt>Isolate</tt>:
44
36
 
45
- At the end of the <tt>Isolate.now!</tt> block, you're completely
46
- isolated. <tt>GEM_PATH</tt> and <tt>GEM_HOME</tt> are set, and all
47
- your specified gems have been activated.
37
+ gem "rails", "2.3.5"
38
+ gem "aasm", "2.0.0"
48
39
 
49
- If you need access to the original un-isolated environment for any
50
- reason, you can temporarily disable Isolate:
40
+ env :development, :test do
41
+ gem "sqlite3-ruby", "1.2.5"
42
+ end
51
43
 
52
- Isolate.disable { `heroku config` }
44
+ env :production do
45
+ gem "memcached", "0.19.2"
46
+ end
53
47
 
54
- I occasionally use this to call out to gems or tools that don't belong
55
- in my project.
48
+ Try running <tt>rake environment</tt>. Before anything else happens,
49
+ Isolate will make sure you have copies of every gem you need (extend
50
+ the example above to cover all your dependencies). If they're already
51
+ installed on your system Isolate will use them, otherwise a private
52
+ copy will be installed under <tt>tmp/isolate</tt>.
56
53
 
57
- === Conditionals
54
+ === Rails 3
58
55
 
59
- Sometimes different sets of gems are appropriate at different
60
- times. Isolate allows you to restrict gems by 'environment' (which is
61
- really just a string passed in when things are activated).
56
+ In <tt>config/boot.rb</tt>:
62
57
 
63
- Isolate.now! do
64
- gem "intercession"
58
+ require "rubygems"
59
+ require "isolate/now"
65
60
 
66
- environment :test, :cucumber do
67
- gem "mocha"
68
- end
69
- end
61
+ Construct your <tt>Isolate</tt> file as above. Be sure to remove any
62
+ references to <tt>Bundler.setup</tt> and <tt>Bundler.require</tt> from
63
+ <tt>config/boot.rb</tt> and <tt>config/application.rb</tt>.
70
64
 
71
- Unsurprisingly, the <tt>mocha</tt> gem will only be activated in the
72
- <tt>test</tt> and <tt>cucumber</tt> environments. See the Rails
73
- example below for an example of how to use <tt>RAILS_ENV</tt> to set
74
- your environment.
65
+ === Sinatra, Rack, and Anything Else
75
66
 
76
- === Options
67
+ There's nothing special about Rails, it's just an easy first
68
+ example. You can use Isolate with any library or framework by simply
69
+ putting an <tt>Isolate</tt> file in the root of your project and
70
+ requiring <tt>isolate/now</tt> as early as possible in the startup
71
+ process.
77
72
 
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>.
73
+ When you're starting up, Isolate tries to determine its environment by
74
+ looking at the <tt>ISOLATE_ENV</tt>, <tt>RACK_ENV</tt>, and
75
+ <tt>RAILS_ENV</tt> env vars. If none are set, it defaults to
76
+ <tt>development</tt>.
81
77
 
82
- # explicitly specify gem source
83
- gem "jbarnette-johnson", :source => "http://gems.github.com"
78
+ === Library Development
84
79
 
85
- # pass gem install args (the part after the '--')
86
- gem "agem", :args => "--no-blah"
80
+ If you're using Hoe[http://blog.zenspider.com/hoe] to manage your
81
+ library, you can use Isolate's Hoe plugin to automatically install
82
+ your lib's development, runtime, and test dependencies without
83
+ polluting your system RubyGems, and run your tests/specs in total
84
+ isolation.
87
85
 
88
- === An External File
86
+ Assuming you have a recent Hoe and isolate's installed, it's as simple
87
+ as putting:
89
88
 
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:
89
+ Hoe.plugin :isolate
92
90
 
93
- Isolate.now!
91
+ before the <tt>Hoe.spec</tt> call in your <tt>Rakefile</tt>.
94
92
 
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.
93
+ If you're not using Hoe, you can use an <tt>Isolate.now!</tt> block at
94
+ the top of your Rakefile. See the RDoc for details.
100
95
 
101
- === Installing Isolated Gems
96
+ == Rake
102
97
 
103
- By default, Isolate will install and clean up your gems
104
- automatically. You can pass the <tt>:cleanup</tt>, <tt>:install</tt>,
105
- and <tt>:verbose</tt> options to control things:
98
+ Isolate provides a few useful Rake tasks.
106
99
 
107
- # don't remove unnecesary gems
108
- Isolate.now!, :cleanup => false
100
+ === isolate:env
109
101
 
110
- # install, but quietly
111
- Isolate.now! :verbose => false
102
+ This task shows you the current Isolate settings and gems.
112
103
 
113
- # don't install
114
- Isolate.now! :install => false
104
+ $ rake isolate:env
115
105
 
116
- All of these options can be set inside your `Isolate` file, too:
106
+ path: tmp/isolate/ruby-1.8
107
+ env: development
108
+ files: Isolate
117
109
 
118
- options :cleanup => false, :verbose => false
110
+ cleanup? true
111
+ enabled? true
112
+ install? true
113
+ multiruby? true
114
+ system? true
115
+ verbose? true
119
116
 
120
- === Interaction and Rake
117
+ [all environments]
118
+ gem rails, = 2.3.5
119
+ gem aasm, = 2.0.0
121
120
 
122
- You don't strictly need them, but Isolate provides a set of Rake tasks
123
- that make a few common thing easier. To use them, just drop a
124
- <tt>require</tt> in your <tt>Rakefile</tt>:
121
+ [development, test]
122
+ gem sqlite3-ruby, = 1.2.5
125
123
 
126
- require "isolate/rake"
124
+ [production]
125
+ gem memcached, = 0.19.2
127
126
 
128
- ==== Running Shell Commands
127
+ === isolate:sh
129
128
 
130
- When you're in an isolated subshell, the command-line tools provided
131
- by any of your gems are available on your PATH.
129
+ This task allows you to run a subshell or a command in the isolated
130
+ environment, making any command-line tools available on your
131
+ <tt>PATH</tt>.
132
132
 
133
133
  # run a single command in an isolated subshell
134
134
  $ rake isolate:sh['gem list']
@@ -136,92 +136,37 @@ by any of your gems are available on your PATH.
136
136
  # run a new isolated subshell
137
137
  $ rake isolate:sh
138
138
 
139
- ==== What's up?
140
-
141
- $ rake isolate:debug
142
-
143
- === A Rails 2 Example
144
-
145
- Here's a quick example (extracted from a real project) of how to use
146
- Isolate with Rails. This project doesn't use vendored Rails, and
147
- doesn't want to depend on any system gems (except isolate, of course).
148
-
149
- Let's edit <tt>config/preinitializer.rb</tt>:
150
-
151
- require "rubygems"
152
- require "isolate"
153
-
154
- Isolate.now!
155
-
156
- In <tt>config/isolate.rb</tt>:
157
-
158
- gem "rails", "= 2.2.2"
139
+ === isolate:stale
159
140
 
160
- # async emails!
161
- gem "ar_mailer", "~> 1.3", '>= 1.3.3'
141
+ This task lists gems that have a more recent released version than the
142
+ one you're using.
162
143
 
163
- # Facebook integration
164
- gem "facebooker", ">= 1.0.31"
144
+ $ rake isolate:stale
145
+ aasm (2.0.0 < 2.1.5)
165
146
 
166
- # Google contacts integration
167
- gem "gmail_contacts", "~> 1.7"
147
+ == Further Reading
168
148
 
169
- # View templates
170
- gem "haml", "~> 2.0"
149
+ <tt>require "isolate/now"</tt> is sugar for <tt>Isolate.now!</tt>,
150
+ which creates, configures, and activates a singleton version of
151
+ Isolate's sandbox. <tt>Isolate.now!</tt> takes a few useful options,
152
+ and lets you define an entire environment inline without using an
153
+ external file.
171
154
 
172
- # Session as model
173
- gem "intercession", "~> 1.0"
174
-
175
- # XML/HTML parsing in Facebooker and tests
176
- gem "nokogiri", ">= 1.2.3"
177
-
178
- # Twitter authentication
179
- gem "oauth", "~> 0.3"
180
-
181
- environment :cucumber, :development, :test do
182
- gem "cucumber" # stories!
183
- gem "modelizer" # easy model factories
184
- gem "sqlite3-ruby" # database support
185
- gem "vlad" # deployment
186
- gem "webrat" # integration tests
187
- end
188
-
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.
191
-
192
- Since this is loaded in the preinitializer, Isolate will install and
193
- activate the all gems before Rails loads. The current environment is
194
- determined by looking at the <tt>ISOLATE_ENV</tt>, <tt>RAILS_ENV</tt>,
195
- or <tt>RACK_ENV</tt> environment variable. If none are set,
196
- <tt>"development"</tt> is the default.
197
-
198
- Pow. Isolated!
199
-
200
- === A Library Example
201
-
202
- If you're using Hoe[http://blog.zenspider.com/hoe] to manage your
203
- library, you can use Isolate's Hoe plugin to automatically install
204
- your lib's development, runtime, and test dependencies without
205
- polluting your system RubyGems, and run your tests/specs in total
206
- isolation.
207
-
208
- Assuming you have a recent Hoe and isolate's installed, it's as simple
209
- as putting:
210
-
211
- Hoe.plugin :isolate
212
-
213
- before the <tt>Hoe.spec</tt> call in your <tt>Rakefile</tt>.
214
-
215
- If you're not using Hoe, you can just do a regular
216
- <tt>Isolate.now!</tt> block at the top of your Rakefile.
155
+ For detailed information on <tt>Isolate.now!</tt> and the rest of the
156
+ public API, please see the RDoc.
217
157
 
218
158
  == Installation
219
159
 
220
160
  $ gem install isolate
221
161
 
162
+ == Meta
163
+
164
+ Bugs:: http://github.com/jbarnette/isolate/issues
165
+ Email:: isolate@librelist.com
166
+
222
167
  == License
223
168
 
224
- Copyright 2009 John Barnette, et al. (jbarnette@rubyforge.org)
169
+ Copyright 2009-2010 John Barnette, et al. (code@jbarnette.com)
225
170
 
226
171
  Permission is hereby granted, free of charge, to any person obtaining
227
172
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -15,5 +15,5 @@ Hoe.spec "isolate" do
15
15
  self.readme_file = "README.rdoc"
16
16
  self.testlib = :minitest
17
17
 
18
- extra_dev_deps << ["minitest", "~> 1.4"]
18
+ extra_dev_deps << ["minitest", "~> 1.6"]
19
19
  end
data/lib/hoe/isolate.rb CHANGED
@@ -19,23 +19,39 @@ class Hoe # :nodoc:
19
19
  module Isolate
20
20
 
21
21
  # Where should Isolate, um, isolate? [default: <tt>"tmp/isolate"</tt>]
22
+ # FIX: consider removing this and allowing +isolate_options+ instead.
22
23
 
23
24
  attr_accessor :isolate_dir
24
25
 
25
- def initialize_isolate # :nodoc:
26
+ def initialize_isolate
26
27
  # Tee hee! Move ourselves to the front to beat out :test.
27
28
  Hoe.plugins.unshift Hoe.plugins.delete(:isolate)
29
+
28
30
  self.isolate_dir ||= "tmp/isolate"
31
+ @sandbox = ::Isolate::Sandbox.new
32
+
33
+ @sandbox.entries.each do |entry|
34
+ dep = [entry.name, *entry.requirement.as_list]
35
+
36
+ if entry.environments.include? "development"
37
+ extra_dev_deps << dep
38
+ elsif entry.environments.empty?
39
+ extra_deps << dep
40
+ end
41
+ end
29
42
  end
30
43
 
31
44
  def define_isolate_tasks # HACK
32
- i = ::Isolate::Sandbox.new :path => isolate_dir
33
45
 
46
+ # reset, now that they've had a chance to change it
47
+ @sandbox.options :path => isolate_dir
48
+
49
+ # allows traditional extra{_dev}_deps calls to override
34
50
  (self.extra_deps + self.extra_dev_deps).each do |name, version|
35
- i.gem name, *Array(version)
51
+ @sandbox.gem name, *Array(version)
36
52
  end
37
53
 
38
- i.activate
54
+ @sandbox.activate
39
55
  end
40
56
  end
41
57
  end
data/lib/isolate.rb CHANGED
@@ -8,7 +8,7 @@ module Isolate
8
8
 
9
9
  # Duh.
10
10
 
11
- VERSION = "2.0.0.pre.1"
11
+ VERSION = "2.0.0.pre.2"
12
12
 
13
13
  # Disable Isolate. If a block is provided, isolation will be
14
14
  # disabled for the scope of the block.
@@ -17,10 +17,17 @@ module Isolate
17
17
  sandbox.disable(&block)
18
18
  end
19
19
 
20
+ # What environment should be isolated? Consults environment
21
+ # variables <tt>ISOLATE_ENV</tt>, <tt>RAILS_ENV</tt>, and
22
+ # <tt>RACK_ENV</tt>. Defaults to <tt>development"/tt> if none are
23
+ # set.
24
+
20
25
  def self.env
21
26
  ENV["ISOLATE_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
22
27
  end
23
28
 
29
+ # Deprecated. See Isolate.now!
30
+
24
31
  def self.gems path, options = {}, &block # :nodoc:
25
32
  warn "Isolate.gems is deprecated, use Isolate.now! instead.\n" +
26
33
  "Isolate.gems will be removed in v3.0."
@@ -28,6 +35,8 @@ module Isolate
28
35
  now! options.merge(:path => path), &block
29
36
  end
30
37
 
38
+ # Deprecated. See Isolate.sandbox.
39
+
31
40
  def self.instance
32
41
  warn "Isolate.instance is deprecated, use Isolate.sandbox instead.\n" +
33
42
  "Isolate.instance will be removed in v3.0."
@@ -37,21 +46,49 @@ module Isolate
37
46
 
38
47
  @@sandbox = nil
39
48
 
49
+ # A singleton instance of Isolate::Sandbox.
50
+
40
51
  def self.sandbox
41
52
  @@sandbox
42
53
  end
43
54
 
44
55
  # Declare an isolated RubyGems environment, installed in +path+. Any
45
- # block given will be <tt>instance_eval</tt>ed, see Isolate#gem and
46
- # Isolate#environment for the sort of stuff you can do.
56
+ # block given will be <tt>instance_eval</tt>ed, see
57
+ # Isolate::Sandbox#gem and Isolate::Sandbox#environment for the sort
58
+ # of stuff you can do.
59
+ #
60
+ # Valid options:
61
+ #
62
+ # :cleanup:: Should obsolete gems be removed? Default is +true+.
63
+ #
64
+ # :file:: Specify an Isolate file to +instance_eval+. Default is
65
+ # <tt>Isolate</tt> or <tt>config/isolate.rb</tt>, whichever
66
+ # is found first. Passing <tt>false</tt> disables file
67
+ # loading.
68
+ #
69
+ # :install:: Should missing gems be installed? Default is +true+.
70
+ #
71
+ # :multiruby:: Should Isolate assume that multiple Ruby versions
72
+ # will be used simultaneously? If so, gems will be
73
+ # segregated by Ruby version. Default is +true+.
74
+ #
75
+ # :path:: Where should isolated gems be kept? Default is
76
+ # <tt>tmp/isolate"</tt>, and a Ruby version specifier suffix
77
+ # will be added if <tt>:multiruby</tt> is +true+.
78
+ #
79
+ # :system:: Should system gems be allowed to satisfy dependencies?
80
+ # Default is +true+.
81
+ #
82
+ # :verbose:: Should Isolate be chatty during installs and nukes?
83
+ # Default is +true+.
47
84
 
48
85
  def self.now! options = {}, &block
49
86
  @@sandbox = Isolate::Sandbox.new options, &block
50
87
  @@sandbox.activate
51
88
  end
52
89
 
53
- # Poke RubyGems, we've probably monkeyed with a bunch of paths and
54
- # suchlike. Clears paths, loaded specs, and source indexes.
90
+ # Poke RubyGems, since we've probably monkeyed with a bunch of paths
91
+ # and suchlike. Clears paths, loaded specs, and source indexes.
55
92
 
56
93
  def self.refresh # :nodoc:
57
94
  Gem.loaded_specs.clear
data/lib/isolate/entry.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "isolate/events"
1
2
  require "rubygems"
2
3
  require "rubygems/command"
3
4
  require "rubygems/dependency_installer"
@@ -7,9 +8,12 @@ require "rubygems/version"
7
8
  module Isolate
8
9
 
9
10
  # An isolated Gem, with requirement, environment restrictions, and
10
- # installation options. Internal use only.
11
+ # installation options. Generally intended for internal use. This
12
+ # class exposes lifecycle events for extension, see Isolate::Events
13
+ # for more information.
11
14
 
12
15
  class Entry
16
+ include Events
13
17
 
14
18
  # Which environments does this entry care about? Generally an
15
19
  # Array of Strings. An empty array means "all", not "none".
@@ -31,9 +35,14 @@ module Isolate
31
35
  attr_reader :requirement
32
36
 
33
37
  # Create a new entry. Takes +sandbox+ (currently an instance of
34
- # Isolate), +name+ (as above), and any number of optional version
35
- # requirements (generally Strings). Options can be passed as a
36
- # trailing hash. FIX: document well-known keys.
38
+ # Isolate::Sandbox), +name+ (as above), and any number of optional
39
+ # version requirements (generally strings). Options can be passed
40
+ # as a trailing hash. Well-known keys:
41
+ #
42
+ # :args:: Command-line build arguments. Passed to the gem at
43
+ # installation time.
44
+ #
45
+ # :source:: An alternative RubyGems source for this gem.
37
46
 
38
47
  def initialize sandbox, name, *requirements
39
48
  @environments = []
@@ -53,26 +62,32 @@ module Isolate
53
62
  update(*requirements)
54
63
  end
55
64
 
65
+ # Activate this entry. Fires <tt>:activating</tt> and
66
+ # <tt>:activated</tt>.
67
+
56
68
  def activate
57
- Gem.activate name, *requirement.as_list
69
+ fire :activating, :activated do
70
+ Gem.activate name, *requirement.as_list
71
+ end
58
72
  end
59
73
 
60
- # Install this entry in the sandbox.
74
+ # Install this entry in the sandbox. Fires <tt>:installing</tt>
75
+ # and <tt>:installed</tt>.
61
76
 
62
77
  def install
63
78
  old = Gem.sources.dup
64
79
 
65
80
  begin
66
- cache = File.join @sandbox.path, "cache"
81
+ fire :installing, :installed do
82
+ installer = Gem::DependencyInstaller.new :development => false,
83
+ :generate_rdoc => false, :generate_ri => false,
84
+ :install_dir => @sandbox.path
67
85
 
68
- installer = Gem::DependencyInstaller.new :development => false,
69
- :generate_rdoc => false, :generate_ri => false,
70
- :install_dir => @sandbox.path
86
+ Gem.sources += Array(options[:source]) if options[:source]
87
+ Gem::Command.build_args = Array(options[:args]) if options[:args]
71
88
 
72
- Gem.sources += Array(options[:source]) if options[:source]
73
- Gem::Command.build_args = Array(options[:args]) if options[:args]
74
-
75
- installer.install @file || name, requirement
89
+ installer.install @file || name, requirement
90
+ end
76
91
  ensure
77
92
  Gem.sources = old
78
93
  Gem::Command.build_args = nil
@@ -92,14 +107,22 @@ module Isolate
92
107
  name == spec.name and requirement.satisfied_by? spec.version
93
108
  end
94
109
 
110
+ # The Gem::Specification for this entry.
111
+
112
+ def specification
113
+ Gem.source_index.find_name(name, requirement).first
114
+ end
115
+
95
116
  # Updates this entry's environments, options, and
96
117
  # requirement. Environments and options are merged, requirement is
97
- # replaced.
118
+ # replaced. Fires <tt>:updating</tt> and <tt>:updated</tt>.
98
119
 
99
120
  def update *reqs
100
- @environments |= @sandbox.environments
101
- @options.merge! reqs.pop if Hash === reqs.last
102
- @requirement = Gem::Requirement.new reqs unless reqs.empty?
121
+ fire :updating, :updated do
122
+ @environments |= @sandbox.environments
123
+ @options.merge! reqs.pop if Hash === reqs.last
124
+ @requirement = Gem::Requirement.new reqs unless reqs.empty?
125
+ end
103
126
 
104
127
  self
105
128
  end
@@ -0,0 +1,42 @@
1
+ module Isolate
2
+
3
+ # A simple way to watch and extend the Isolate lifecycle.
4
+ #
5
+ # Isolate::Events.watch Isolate::Sandbox, :initialized do |sandbox|
6
+ # puts "A sandbox just got initialized: #{sandbox}"
7
+ # end
8
+ #
9
+ # Read the source for Isolate::Sandbox and Isolate::Entry to see
10
+ # what sort of events are fired.
11
+
12
+ module Events
13
+
14
+ # Watch for an event called +name+ from an instance of
15
+ # +klass+. +block+ will be called when the event occurs. Block
16
+ # args vary by event, but usually an instance of the relevant
17
+ # class is passed.
18
+
19
+ def self.watch klass, name, &block
20
+ watchers[[klass, name]] << block
21
+ end
22
+
23
+ def self.fire klass, name, *args #:nodoc:
24
+ watchers[[klass, name]].each do |block|
25
+ block[*args]
26
+ end
27
+ end
28
+
29
+ def self.watchers #:nodoc:
30
+ @watchers ||= Hash.new { |h, k| h[k] = [] }
31
+ end
32
+
33
+ def fire name, after = nil, *args, &block #:nodoc:
34
+ Isolate::Events.fire self.class, name, *args
35
+
36
+ if after && block_given?
37
+ yield self
38
+ Isolate::Events.fire self.class, after, *args
39
+ end
40
+ end
41
+ end
42
+ end
data/lib/isolate/rake.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  namespace :isolate do
2
2
  desc "Show current isolated environment."
3
- task :debug do
3
+ task :env do
4
4
  require "pathname"
5
5
 
6
6
  sandbox = Isolate.sandbox
@@ -9,10 +9,10 @@ namespace :isolate do
9
9
  files = sandbox.files.map { |f| Pathname(f) }
10
10
 
11
11
  puts
12
- puts " sandbox: #{path}"
12
+ puts " path: #{path}"
13
13
  puts " env: #{Isolate.env}"
14
14
 
15
- files.collect! { |f| f.absolute? ? f.relative_path_from(here) : f }
15
+ files.map! { |f| f.absolute? ? f.relative_path_from(here) : f }
16
16
  puts " files: #{files.join ', '}"
17
17
  puts
18
18
 
@@ -45,4 +45,26 @@ namespace :isolate do
45
45
  task :sh, [:command] do |t, args|
46
46
  exec args.command || ENV["SHELL"]
47
47
  end
48
+
49
+ desc "Which isolated gems have updates available?"
50
+ task :stale do
51
+ require "rubygems/source_index"
52
+ require "rubygems/spec_fetcher"
53
+
54
+ index = Gem::SourceIndex.new
55
+ index.add_specs *Isolate.sandbox.entries.map { |e| e.specification }
56
+
57
+ outdated = index.outdated.map do |n|
58
+ Isolate.sandbox.entries.find { |e| e.name == n }
59
+ end
60
+
61
+ outdated.sort_by { |e| e.name }.each do |entry|
62
+ local = entry.specification.version
63
+ dep = Gem::Dependency.new entry.name, ">= #{local}"
64
+ remotes = Gem::SpecFetcher.fetcher.fetch dep
65
+ remote = remotes.last.first.version
66
+
67
+ puts "#{entry.name} (#{local} < #{remote})"
68
+ end
69
+ end
48
70
  end
@@ -1,17 +1,26 @@
1
1
  require "fileutils"
2
2
  require "isolate/entry"
3
+ require "isolate/events"
3
4
  require "rbconfig"
4
5
  require "rubygems/defaults"
5
6
  require "rubygems/uninstaller"
6
7
 
7
8
  module Isolate
9
+
10
+ # An isolated environment. This class exposes lifecycle events for
11
+ # extension, see Isolate::Events for more information.
12
+
8
13
  class Sandbox
14
+ include Events
15
+
9
16
  attr_reader :entries # :nodoc:
10
17
  attr_reader :environments # :nodoc:
11
18
  attr_reader :files # :nodoc:
12
19
 
13
- # Create a new Isolate instance. See Isolate.gems for the public
14
- # API. You probably don't want to use this constructor directly.
20
+ # Create a new Isolate::Sandbox instance. See Isolate.now! for the
21
+ # most common use of the API. You probably don't want to use this
22
+ # constructor directly. Fires <tt>:initializing</tt> and
23
+ # <tt>:initialized</tt>.
15
24
 
16
25
  def initialize options = {}, &block
17
26
  @enabled = false
@@ -22,6 +31,8 @@ module Isolate
22
31
 
23
32
  file, local = nil
24
33
 
34
+ fire :initializing
35
+
25
36
  unless FalseClass === options[:file]
26
37
  file = options[:file] || Dir["{Isolate,config/isolate.rb}"].first
27
38
  local = "#{file}.local" if file
@@ -36,6 +47,7 @@ module Isolate
36
47
  end
37
48
 
38
49
  load local if local && File.exist?(local)
50
+ fire :initialized
39
51
  end
40
52
 
41
53
  # Activate this set of isolated entries, respecting an optional
@@ -44,10 +56,12 @@ module Isolate
44
56
  # everything, and removes any superfluous gem (again, if
45
57
  # necessary). If +environment+ isn't specified, +ISOLATE_ENV+,
46
58
  # +RAILS_ENV+, and +RACK_ENV+ are checked before falling back to
47
- # <tt>"development"</tt>.
59
+ # <tt>"development"</tt>. Fires <tt>:activating</tt> and
60
+ # <tt>:activated</tt>.
48
61
 
49
62
  def activate environment = nil
50
63
  enable unless enabled?
64
+ fire :activating
51
65
 
52
66
  env = (environment || Isolate.env).to_s
53
67
 
@@ -58,38 +72,43 @@ module Isolate
58
72
  end
59
73
 
60
74
  cleanup if cleanup?
75
+ fire :activated
61
76
 
62
77
  self
63
78
  end
64
79
 
65
80
  def cleanup # :nodoc:
81
+ fire :cleaning
82
+
66
83
  activated = Gem.loaded_specs.values.map { |s| s.full_name }
67
84
  available = Gem.source_index.gems.values.sort
68
85
 
69
86
  extra = available.reject do |spec|
70
87
  active = activated.include? spec.full_name
71
- entry = entries.detect { |e| e.matches_spec? spec }
88
+ entry = entries.find { |e| e.matches_spec? spec }
72
89
  system = !spec.loaded_from.include?(path)
73
90
 
74
91
  active or entry or system
75
92
  end
76
93
 
77
- return if extra.empty?
78
-
79
- padding = Math.log10(extra.size).to_i + 1
80
- format = "[%0#{padding}d/%s] Nuking %s."
94
+ unless extra.empty?
95
+ padding = Math.log10(extra.size).to_i + 1
96
+ format = "[%0#{padding}d/%s] Nuking %s."
81
97
 
82
- extra.each_with_index do |e, i|
83
- log format % [i + 1, extra.size, e.full_name]
98
+ extra.each_with_index do |e, i|
99
+ log format % [i + 1, extra.size, e.full_name]
84
100
 
85
- Gem::DefaultUserInteraction.use_ui Gem::SilentUI.new do
86
- Gem::Uninstaller.new(e.name,
87
- :version => e.version,
88
- :ignore => true,
89
- :executables => true,
90
- :install_dir => path).uninstall
101
+ Gem::DefaultUserInteraction.use_ui Gem::SilentUI.new do
102
+ Gem::Uninstaller.new(e.name,
103
+ :version => e.version,
104
+ :ignore => true,
105
+ :executables => true,
106
+ :install_dir => path).uninstall
107
+ end
91
108
  end
92
109
  end
110
+
111
+ fire :cleaned
93
112
  end
94
113
 
95
114
  def cleanup?
@@ -98,9 +117,11 @@ module Isolate
98
117
 
99
118
  def disable &block
100
119
  return self if not enabled?
120
+ fire :disabling
101
121
 
102
122
  ENV["GEM_PATH"] = @old_gem_path
103
123
  ENV["GEM_HOME"] = @old_gem_home
124
+ ENV["ISOLATED"] = @old_isolated
104
125
  ENV["PATH"] = @old_path
105
126
  ENV["RUBYOPT"] = @old_ruby_opt
106
127
 
@@ -109,6 +130,8 @@ module Isolate
109
130
  @enabled = false
110
131
 
111
132
  Isolate.refresh
133
+ fire :disabled
134
+
112
135
  begin; return yield ensure enable end if block_given?
113
136
 
114
137
  self
@@ -116,9 +139,11 @@ module Isolate
116
139
 
117
140
  def enable # :nodoc:
118
141
  return self if enabled?
142
+ fire :enabling
119
143
 
120
144
  @old_gem_path = ENV["GEM_PATH"]
121
145
  @old_gem_home = ENV["GEM_HOME"]
146
+ @old_isolated = ENV["ISOLATED"]
122
147
  @old_path = ENV["PATH"]
123
148
  @old_ruby_opt = ENV["RUBYOPT"]
124
149
  @old_load_path = $LOAD_PATH.dup
@@ -151,10 +176,13 @@ module Isolate
151
176
  ENV["PATH"] = [bin, ENV["PATH"]].join File::PATH_SEPARATOR
152
177
  end
153
178
 
179
+ ENV["ISOLATED"] = path
180
+
154
181
  Isolate.refresh
155
182
  Gem.path.unshift path if system?
156
183
 
157
184
  @enabled = true
185
+ fire :enabled
158
186
 
159
187
  self
160
188
  end
@@ -174,12 +202,14 @@ module Isolate
174
202
  @environments = old
175
203
  end
176
204
 
205
+ alias_method :env, :environment
206
+
177
207
  # Express a gem dependency. Works pretty much like RubyGems' +gem+
178
208
  # method, but respects +environment+ and doesn't activate 'til
179
209
  # later.
180
210
 
181
211
  def gem name, *requirements
182
- entry = entries.detect { |e| e.name == name }
212
+ entry = entries.find { |e| e.name == name }
183
213
  return entry.update(*requirements) if entry
184
214
 
185
215
  entries << entry = Entry.new(self, name, *requirements)
@@ -187,22 +217,26 @@ module Isolate
187
217
  end
188
218
 
189
219
  def install environment # :nodoc:
220
+ fire :installing
221
+
190
222
  installable = entries.select do |e|
191
223
  !Gem.available?(e.name, *e.requirement.as_list) &&
192
224
  e.matches?(environment)
193
225
  end
194
226
 
195
- return self if installable.empty?
227
+ unless installable.empty?
228
+ padding = Math.log10(installable.size).to_i + 1
229
+ format = "[%0#{padding}d/%s] Isolating %s (%s)."
196
230
 
197
- padding = Math.log10(installable.size).to_i + 1
198
- format = "[%0#{padding}d/%s] Isolating %s (%s)."
231
+ installable.each_with_index do |entry, i|
232
+ log format % [i + 1, installable.size, entry.name, entry.requirement]
233
+ entry.install
234
+ end
199
235
 
200
- installable.each_with_index do |entry, i|
201
- log format % [i + 1, installable.size, entry.name, entry.requirement]
202
- entry.install
236
+ Gem.source_index.refresh!
203
237
  end
204
238
 
205
- Gem.source_index.refresh!
239
+ fire :installed
206
240
 
207
241
  self
208
242
  end
@@ -0,0 +1,44 @@
1
+ require "isolate/events"
2
+ require "isolate/test"
3
+
4
+ class TestIsolateEvents < Isolate::Test
5
+ include Isolate::Events
6
+
7
+ def setup
8
+ Isolate::Events.watchers.clear
9
+ super
10
+ end
11
+
12
+ def test_self_watch
13
+ b = lambda {}
14
+ Isolate::Events.watch String, :foo, &b
15
+ assert_equal [b], Isolate::Events.watchers[[String, :foo]]
16
+ end
17
+
18
+ def test_fire
19
+ count = 0
20
+
21
+ Isolate::Events.watch self.class, :increment do
22
+ count += 1
23
+ end
24
+
25
+ fire :increment
26
+ assert_equal 1, count
27
+ end
28
+
29
+ def test_fire_block
30
+ count = 0
31
+
32
+ [:increment, :incremented].each do |name|
33
+ Isolate::Events.watch self.class, name do
34
+ count += 1
35
+ end
36
+ end
37
+
38
+ fire :increment, :incremented do |x|
39
+ assert_same self, x
40
+ end
41
+
42
+ assert_equal 2, count
43
+ end
44
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: isolate
3
3
  version: !ruby/object:Gem::Version
4
- hash: 1923832043
4
+ hash: 1923832045
5
5
  prerelease: true
6
6
  segments:
7
7
  - 2
8
8
  - 0
9
9
  - 0
10
10
  - pre
11
- - 1
12
- version: 2.0.0.pre.1
11
+ - 2
12
+ version: 2.0.0.pre.2
13
13
  platform: ruby
14
14
  authors:
15
15
  - John Barnette
@@ -18,7 +18,7 @@ autorequire:
18
18
  bindir: bin
19
19
  cert_chain: []
20
20
 
21
- date: 2010-05-02 00:00:00 -07:00
21
+ date: 2010-05-07 00:00:00 -07:00
22
22
  default_executable:
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
@@ -45,11 +45,11 @@ dependencies:
45
45
  requirements:
46
46
  - - ~>
47
47
  - !ruby/object:Gem::Version
48
- hash: 7
48
+ hash: 3
49
49
  segments:
50
50
  - 1
51
- - 4
52
- version: "1.4"
51
+ - 6
52
+ version: "1.6"
53
53
  type: :development
54
54
  version_requirements: *id002
55
55
  - !ruby/object:Gem::Dependency
@@ -70,7 +70,7 @@ dependencies:
70
70
  version_requirements: *id003
71
71
  description: |-
72
72
  Isolate is a very simple RubyGems sandbox. It provides a way to
73
- express and install your code's Gem dependencies.
73
+ express and automatically install your project's Gem dependencies.
74
74
  email:
75
75
  - jbarnette@rubyforge.org
76
76
  - ryand-ruby@zenspider.com
@@ -91,6 +91,7 @@ files:
91
91
  - lib/hoe/isolate.rb
92
92
  - lib/isolate.rb
93
93
  - lib/isolate/entry.rb
94
+ - lib/isolate/events.rb
94
95
  - lib/isolate/now.rb
95
96
  - lib/isolate/rake.rb
96
97
  - lib/isolate/sandbox.rb
@@ -104,6 +105,7 @@ files:
104
105
  - test/isolate/test.rb
105
106
  - test/test_isolate.rb
106
107
  - test/test_isolate_entry.rb
108
+ - test/test_isolate_events.rb
107
109
  - test/test_isolate_sandbox.rb
108
110
  has_rdoc: true
109
111
  homepage: http://github.com/jbarnette/isolate
@@ -147,4 +149,5 @@ summary: Isolate is a very simple RubyGems sandbox
147
149
  test_files:
148
150
  - test/test_isolate.rb
149
151
  - test/test_isolate_entry.rb
152
+ - test/test_isolate_events.rb
150
153
  - test/test_isolate_sandbox.rb