comp_tree 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,6 +1,11 @@
1
1
 
2
2
  = CompTree ChangeLog
3
3
 
4
+ == Version 0.7.1
5
+
6
+ * rename README to README.rdoc for github display
7
+ * internal cleanup
8
+
4
9
  == Version 0.7.0
5
10
 
6
11
  * remove fork and discard_result options
@@ -54,7 +54,7 @@ function you define must explicitly depend on the data it uses.
54
54
  }
55
55
 
56
56
  Unless <em>offset</em> is really a constant, the result of
57
- <tt>driver.compute(:area, :num_threads => n)</tt> is not well-defined
57
+ <tt>driver.compute(:area, :threads => n)</tt> is not well-defined
58
58
  for _n_ > 1.
59
59
 
60
60
  Just as depending on some changeable state is bad, it is likewise bad
@@ -64,12 +64,12 @@ to affect a state (to produce a <em>side effect</em>).
64
64
  # BAD example: affecting state
65
65
  #
66
66
  driver.define(:area, :width, :height) { |width, height|
67
- ACCUMULATOR.add "more data"
67
+ accumulator.add "more data"
68
68
  width*height
69
69
  }
70
70
 
71
- Given a tree where nodes are modifying _ACCUMULATOR_, the end state of
72
- _ACCUMULATOR_ is not well-defined. Moreover if _ACCUMULATOR_ is not
71
+ Given a tree where nodes are modifying _accumulator_, the end state of
72
+ _accumulator_ is not well-defined. Moreover if _accumulator_ is not
73
73
  thread-safe, the result will be even worse.
74
74
 
75
75
  Note however it is OK affect a state as long as <em>no other function
@@ -80,7 +80,7 @@ depends on that state</em>. This is the principle under which
80
80
 
81
81
  % gem install comp_tree
82
82
 
83
- Or for the regular (non-gem) .tgz package,
83
+ Or for the (non-gem) .tgz package,
84
84
 
85
85
  % ruby install.rb [--uninstall]
86
86
 
data/comp_tree.gemspec CHANGED
@@ -6,11 +6,11 @@ Gem::Specification.new { |g|
6
6
  g.name = "comp_tree"
7
7
  g.rubyforge_project = "comptree"
8
8
  g.homepage = "comptree.rubyforge.org"
9
- g.version = "0.7.0"
9
+ g.version = "0.7.1"
10
10
  g.description =
11
11
  "Build a computation tree and execute it with N parallel threads."
12
12
 
13
- readme = "README"
13
+ readme = "README.rdoc"
14
14
 
15
15
  g.files = %W[
16
16
  CHANGES
@@ -19,14 +19,15 @@ Gem::Specification.new { |g|
19
19
  #{g.name}.gemspec
20
20
  install.rb
21
21
  ] + %w[lib rakelib test].inject(Array.new) { |acc, dir|
22
- acc + Dir[dir + "/**/*.rb"]
22
+ acc + Dir[dir + "/**/*.{rake,rb}"]
23
23
  }
24
24
  g.has_rdoc = true
25
25
  rdoc_files = [
26
26
  readme,
27
27
  "lib/comp_tree.rb",
28
28
  "lib/comp_tree/driver.rb",
29
- "lib/comp_tree/error.rb"
29
+ "lib/comp_tree/error.rb",
30
+ "lib/comp_tree/node.rb",
30
31
  ]
31
32
  g.extra_rdoc_files += [readme]
32
33
 
data/lib/comp_tree.rb CHANGED
@@ -25,19 +25,21 @@ require 'comp_tree/driver'
25
25
  #
26
26
  # CompTree -- Parallel Computation Tree.
27
27
  #
28
- # See README.
28
+ # See README.rdoc.
29
29
  #
30
30
  module CompTree
31
31
  class << self
32
32
  #
33
- # Build and run a new computation tree.
33
+ # :call-seq:
34
+ # build { |driver| ... }
34
35
  #
35
- # A Driver instance is passed to the given block.
36
+ # Build a new computation tree. A Driver instance is passed to the
37
+ # given block.
36
38
  #
37
39
  # Options hash:
38
40
  #
39
- # <tt>:node_class</tt> -- (Class) CompTree::Node subclass from
40
- # which nodes are created.
41
+ # <tt>:node_class</tt> -- CompTree::Node subclass from which nodes
42
+ # are created.
41
43
  #
42
44
  # Example:
43
45
  # CompTree.build do |driver|
@@ -1,6 +1,9 @@
1
1
 
2
2
  module CompTree
3
3
  module Algorithm
4
+ LEAVE = :__comp_tree_leave
5
+ AGAIN = :__comp_tree_again
6
+
4
7
  module_function
5
8
 
6
9
  def loop_with(leave, again)
@@ -13,13 +16,12 @@ module CompTree
13
16
  }
14
17
  end
15
18
 
16
- def compute_multithreaded(root, num_threads)
19
+ def compute_parallel(root, num_threads)
17
20
  #trace "Computing #{root.name} with #{num_threads} threads"
18
21
  finished = nil
19
22
  tree_mutex = Mutex.new
20
- node_finished_condition = ConditionVariable.new
21
- thread_wake_condition = ConditionVariable.new
22
- num_threads_in_use = 0
23
+ condition = ConditionVariable.new
24
+ num_threads_ready = 0
23
25
 
24
26
  threads = (0...num_threads).map { |thread_index|
25
27
  Thread.new {
@@ -28,17 +30,17 @@ module CompTree
28
30
  #
29
31
  tree_mutex.synchronize {
30
32
  #trace "Thread #{thread_index} waiting to start"
31
- num_threads_in_use += 1
32
- thread_wake_condition.wait(tree_mutex)
33
+ num_threads_ready += 1
34
+ condition.wait(tree_mutex)
33
35
  }
34
36
 
35
- loop_with(:leave, :again) {
37
+ loop_with(LEAVE, AGAIN) {
36
38
  node = tree_mutex.synchronize {
37
39
  #trace "Thread #{thread_index} acquired tree lock; begin search"
38
40
  if finished
39
41
  #trace "Thread #{thread_index} detected finish"
40
- num_threads_in_use -= 1
41
- throw :leave
42
+ num_threads_ready -= 1
43
+ throw LEAVE
42
44
  else
43
45
  #
44
46
  # Find a node. The node we obtain, if any, will be locked.
@@ -49,8 +51,8 @@ module CompTree
49
51
  node
50
52
  else
51
53
  #trace "Thread #{thread_index}: no node found; sleeping."
52
- thread_wake_condition.wait(tree_mutex)
53
- throw :again
54
+ condition.wait(tree_mutex)
55
+ throw AGAIN
54
56
  end
55
57
  end
56
58
  }
@@ -92,7 +94,7 @@ module CompTree
92
94
  #
93
95
  # Tell the main thread that another node was computed.
94
96
  #
95
- node_finished_condition.signal
97
+ condition.signal
96
98
  }
97
99
  }
98
100
  #trace "Thread #{thread_index} exiting"
@@ -100,15 +102,15 @@ module CompTree
100
102
  }
101
103
 
102
104
  #trace "Main: waiting for threads to launch and block."
103
- until tree_mutex.synchronize { num_threads_in_use == num_threads }
105
+ until tree_mutex.synchronize { num_threads_ready == num_threads }
104
106
  Thread.pass
105
107
  end
106
108
 
107
109
  tree_mutex.synchronize {
108
110
  #trace "Main: entering main loop"
109
- until num_threads_in_use == 0
111
+ until num_threads_ready == 0
110
112
  #trace "Main: waking threads"
111
- thread_wake_condition.broadcast
113
+ condition.broadcast
112
114
 
113
115
  if finished
114
116
  #trace "Main: detected finish."
@@ -116,21 +118,13 @@ module CompTree
116
118
  end
117
119
 
118
120
  #trace "Main: waiting for a node"
119
- node_finished_condition.wait(tree_mutex)
121
+ condition.wait(tree_mutex)
120
122
  #trace "Main: got a node"
121
123
  end
122
124
  }
123
125
 
124
126
  #trace "Main: waiting for threads to finish."
125
- loop_with(:leave, :again) {
126
- tree_mutex.synchronize {
127
- if threads.all? { |thread| thread.status == false }
128
- throw :leave
129
- end
130
- thread_wake_condition.broadcast
131
- }
132
- Thread.pass
133
- }
127
+ threads.each { |t| t.join }
134
128
 
135
129
  #trace "Main: computation done."
136
130
  if finished.is_a? Exception
@@ -14,21 +14,15 @@ module CompTree
14
14
  include Algorithm
15
15
 
16
16
  #
17
- # Build and run a new computation tree.
17
+ # See CompTree.build.
18
18
  #
19
- # Options hash:
20
- #
21
- # <tt>:node_class</tt> -- (Class) CompTree::Node subclass from
22
- # which nodes are created.
23
- #
24
- def initialize(opts = nil)
25
- @node_class = (
19
+ def initialize(opts = nil) #:nodoc:
20
+ @node_class =
26
21
  if opts and opts[:node_class]
27
22
  opts[:node_class]
28
23
  else
29
24
  Node
30
25
  end
31
- )
32
26
  @nodes = Hash.new
33
27
  end
34
28
 
@@ -38,13 +32,15 @@ module CompTree
38
32
  attr_reader :nodes
39
33
 
40
34
  #
41
- # Define a computation node.
35
+ # _name_ -- unique node identifier (for example a symbol).
42
36
  #
43
- # The first argument is the name of the node to define.
44
- # Subsequent arguments are the names of this node's children.
37
+ # _child_names_ -- unique node identifiers for children.
38
+ #
39
+ # Define a computation node.
45
40
  #
46
- # The values of the child nodes are passed to the block. The
47
- # block returns the result of this node.
41
+ # During a computation, the results of the child nodes are passed
42
+ # to the block. The block returns the result of this node's
43
+ # computation.
48
44
  #
49
45
  # In this example, a computation node named +area+ is defined
50
46
  # which depends on the nodes +width+ and +height+.
@@ -53,27 +49,18 @@ module CompTree
53
49
  # width*height
54
50
  # }
55
51
  #
56
- def define(*args, &block)
57
- parent_name = args.first
58
- children_names = args[1..-1]
59
-
60
- unless parent_name
61
- raise ArgumentError, "No name given for node"
62
- end
63
-
52
+ def define(name, *child_names, &block)
64
53
  #
65
54
  # retrieve or create parent and children
66
55
  #
67
- parent = @nodes[parent_name] || (
68
- @nodes[parent_name] = @node_class.new(parent_name)
69
- )
70
56
 
57
+ parent = @nodes[name] || (@nodes[name] = @node_class.new(name))
71
58
  if parent.function
72
59
  raise RedefinitionError, "Node `#{parent.name.inspect}' redefined."
73
60
  end
74
61
  parent.function = block
75
62
 
76
- children = children_names.map { |child_name|
63
+ children = child_names.map { |child_name|
77
64
  @nodes[child_name] || (
78
65
  @nodes[child_name] = @node_class.new(child_name)
79
66
  )
@@ -89,25 +76,21 @@ module CompTree
89
76
  end
90
77
 
91
78
  #
92
- # Mark this node and all its children as uncomputed.
79
+ # _name_ -- unique node identifier (for example a symbol).
93
80
  #
94
- # Arguments:
95
- #
96
- # +name+ -- unique node identifier (usually a symbol).
81
+ # Mark this node and all its children as uncomputed.
97
82
  #
98
83
  def reset(name)
99
84
  @nodes[name].reset
100
85
  end
101
86
 
87
+ #
88
+ # _name_ -- unique node identifier (for example a symbol).
102
89
  #
103
90
  # Check for a cyclic graph below the given node. If found,
104
91
  # returns the names of the nodes (in order) which form a loop.
105
92
  # Otherwise returns nil.
106
93
  #
107
- # Arguments:
108
- #
109
- # +name+ -- unique node identifier (usually a symbol).
110
- #
111
94
  def check_circular(name)
112
95
  helper = Proc.new { |root, chain|
113
96
  if chain.include? root
@@ -122,30 +105,27 @@ module CompTree
122
105
  end
123
106
 
124
107
  #
125
- # Compute this node.
108
+ # :call-seq:
109
+ # compute(name, threads)
110
+ # compute(name, :threads => threads)
126
111
  #
127
- # Arguments:
112
+ # _name_ -- unique node identifier (for example a symbol).
128
113
  #
129
- # +name+ -- unique node identifier (usually a symbol).
114
+ # _threads_ -- number of threads.
130
115
  #
131
- # +threads+ -- (Integer) number of threads.
116
+ # Compute this node, returning its result.
132
117
  #
133
- # compute(:volume, :threads => 4) syntax is also accepted.
118
+ # Any uncomputed children are computed first.
134
119
  #
135
120
  def compute(name, opts)
136
121
  threads = opts.is_a?(Hash) ? opts[:threads] : opts
137
122
  root = @nodes[name]
138
-
139
- if threads < 1
140
- raise ArgumentError, "threads is #{threads}"
141
- end
142
-
143
123
  if root.computed
144
124
  root.result
145
125
  elsif threads == 1
146
126
  root.result = root.compute_now
147
127
  else
148
- compute_multithreaded(root, threads)
128
+ compute_parallel(root, threads)
149
129
  end
150
130
  end
151
131
  end
@@ -3,20 +3,18 @@ module CompTree
3
3
  # Base class for CompTree errors.
4
4
  class Error < StandardError ; end
5
5
 
6
- # Internal error inside CompTree. Please send a bug report.
7
- class AssertionFailedError < Error ; end
8
-
9
- # Bad arguments were passed to a method.
10
- class ArgumentError < Error ; end
11
-
12
6
  #
13
7
  # Attempt to redefine a Node.
14
8
  #
15
9
  # If you wish to only replace the function, set
16
- # driver.nodes[name].function = some_new_lambda
10
+ # driver.nodes[name].function = lambda { ... }
17
11
  #
18
12
  class RedefinitionError < Error ; end
19
13
 
20
- # No function was defined for this node.
14
+ # Encountered a node without a function during a computation.
21
15
  class NoFunctionError < Error ; end
16
+
17
+ #debug {
18
+ # class AssertionFailedError < Error ; end
19
+ #}
22
20
  end
@@ -69,8 +69,8 @@ module CompTree
69
69
  end
70
70
 
71
71
  #
72
- # Force computation of all children; intended for
73
- # single-threaded mode.
72
+ # Force all children and self to be computed; no locking required.
73
+ # Intended to be used outside of parallel computations.
74
74
  #
75
75
  def compute_now #:nodoc:
76
76
  unless @children_results
@@ -0,0 +1,365 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/sshpublisher'
5
+ require 'rake/clean'
6
+ require 'rdoc/rdoc'
7
+
8
+ require "jumpstart/simple_installer"
9
+ require "jumpstart/ruby"
10
+
11
+ ######################################################################
12
+ # constants
13
+
14
+ unless defined?(RUBYFORGE_USER)
15
+ RUBYFORGE_USER = "quix"
16
+ end
17
+
18
+ GEMSPEC = eval(File.read(Dir["*.gemspec"].last))
19
+
20
+ DOC_DIR = "documentation"
21
+ SPEC_FILES = Dir['spec/*_spec.rb'] + Dir['examples/*_example.rb']
22
+ TEST_FILES = Dir['test/test_*.rb']
23
+ RCOV_DIR = "coverage"
24
+ SPEC_OUTPUT = "spec.html"
25
+
26
+ RCOV_OPTIONS = Dir["*"].select { |file|
27
+ File.directory?(file) and file != "lib"
28
+ }.inject(Array.new) { |acc, file|
29
+ acc + ["--exclude", file + "/"]
30
+ }
31
+
32
+ ######################################################################
33
+ # spec
34
+
35
+ unless SPEC_FILES.empty?
36
+ require 'spec/rake/spectask'
37
+
38
+ desc "run specs"
39
+ Spec::Rake::SpecTask.new('spec') do |t|
40
+ t.spec_files = SPEC_FILES
41
+ end
42
+
43
+ desc "run specs with text output"
44
+ Spec::Rake::SpecTask.new('text_spec') do |t|
45
+ t.spec_files = SPEC_FILES
46
+ t.spec_opts = ['-fs']
47
+ end
48
+
49
+ desc "run specs with html output"
50
+ Spec::Rake::SpecTask.new('full_spec') do |t|
51
+ t.spec_files = SPEC_FILES
52
+ t.rcov = true
53
+ t.rcov_opts = RCOV_OPTIONS
54
+ t.spec_opts = ["-fh:#{SPEC_OUTPUT}"]
55
+ end
56
+
57
+ desc "run full_spec then open browser"
58
+ task :show_spec => :full_spec do
59
+ open_browser(SPEC_OUTPUT, RCOV_DIR + "/index.html")
60
+ end
61
+
62
+ desc "run specs individually"
63
+ task :spec_deps do
64
+ run_ruby_on_each(*SPEC_FILES)
65
+ end
66
+
67
+ task :prerelease => :spec_deps
68
+
69
+ task :default => :spec
70
+
71
+ CLEAN.include(SPEC_OUTPUT)
72
+ end
73
+
74
+ ######################################################################
75
+ # test
76
+
77
+ unless TEST_FILES.empty?
78
+ desc "run tests"
79
+ task :test do
80
+ TEST_FILES.each { |file|
81
+ require file
82
+ }
83
+ end
84
+
85
+ desc "run tests with rcov"
86
+ task :full_test do
87
+ previous = RakeFileUtils.verbose_flag
88
+ begin
89
+ sh("rcov", "-o", RCOV_DIR, *(TEST_FILES + RCOV_OPTIONS))
90
+ ensure
91
+ RakeFileUtils.verbose_flag = previous
92
+ end
93
+ end
94
+
95
+ desc "run full_test then open browser"
96
+ task :show_test => :full_test do
97
+ open_browser(RCOV_DIR + "/index.html")
98
+ end
99
+
100
+ desc "run tests individually"
101
+ task :test_deps do
102
+ run_ruby_on_each(*TEST_FILES)
103
+ end
104
+
105
+ task :prerelease => :test_deps
106
+ task :default => :test
107
+
108
+ CLEAN.include RCOV_DIR
109
+ end
110
+
111
+ ######################################################################
112
+ # clean
113
+
114
+ task :clean do
115
+ Rake::Task[:clobber].invoke
116
+ end
117
+
118
+ ######################################################################
119
+ # package
120
+
121
+ task :package => :clean
122
+ task :gem => :clean
123
+
124
+ Rake::GemPackageTask.new(GEMSPEC) { |t|
125
+ t.need_tar = true
126
+ }
127
+
128
+ ######################################################################
129
+ # doc
130
+
131
+ #
132
+ # Try to mimic the gem documentation
133
+ #
134
+ desc "run rdoc"
135
+ task :doc => :clean_doc do
136
+ args = (
137
+ GEMSPEC.rdoc_options +
138
+ GEMSPEC.require_paths.clone +
139
+ GEMSPEC.extra_rdoc_files +
140
+ ["-o", DOC_DIR]
141
+ ).flatten.map { |t| t.to_s }
142
+ RDoc::RDoc.new.document args
143
+ end
144
+
145
+ task :clean_doc do
146
+ # normally rm_rf, but mimic rake/clean
147
+ rm_r(DOC_DIR) rescue nil
148
+ end
149
+
150
+ desc "run rdoc then open browser"
151
+ task :show_doc => :doc do
152
+ open_browser(DOC_DIR + "/index.html")
153
+ end
154
+
155
+ task :rdoc => :doc
156
+ task :clean => :clean_doc
157
+
158
+ ######################################################################
159
+ # publisher
160
+
161
+ desc "upload docs"
162
+ task :publish => [:clean_doc, :doc] do
163
+ Rake::SshDirPublisher.new(
164
+ "#{RUBYFORGE_USER}@rubyforge.org",
165
+ "/var/www/gforge-projects/#{GEMSPEC.rubyforge_project}",
166
+ DOC_DIR
167
+ ).upload
168
+ end
169
+
170
+ ######################################################################
171
+ # install/uninstall
172
+
173
+ desc "direct install (no gem)"
174
+ task :install do
175
+ Jumpstart::SimpleInstaller.new.run([])
176
+ end
177
+
178
+ desc "direct uninstall (no gem)"
179
+ task :uninstall do
180
+ Jumpstart::SimpleInstaller.new.run(["--uninstall"])
181
+ end
182
+
183
+ ######################################################################
184
+ # debug
185
+
186
+ def comment_src_dst(on)
187
+ on ? ["", "#"] : ["#", ""]
188
+ end
189
+
190
+ def comment_regions(on, contents, start)
191
+ src, dst = comment_src_dst(on)
192
+ contents.gsub(%r!^(\s+)#{src}#{start}.*?^\1#{src}(\}|end)!m) { |chunk|
193
+ indent = $1
194
+ chunk.gsub(%r!^#{indent}#{src}!, "#{indent}#{dst}")
195
+ }
196
+ end
197
+
198
+ def comment_lines(on, contents, start)
199
+ src, dst = comment_src_dst(on)
200
+ contents.gsub(%r!^(\s*)#{src}#{start}!) {
201
+ $1 + dst + start
202
+ }
203
+ end
204
+
205
+ def debug_info(enable)
206
+ Find.find("lib", "test") { |path|
207
+ if path =~ %r!\.rb\Z!
208
+ replace_file(path) { |contents|
209
+ result1 = comment_regions(!enable, contents, "def trace_")
210
+ result2 = comment_regions(!enable, result1, "debug")
211
+ comment_lines(!enable, result2, "trace")
212
+ }
213
+ end
214
+ }
215
+ end
216
+
217
+ desc "enable debug and trace calls"
218
+ task :debug_on do
219
+ debug_info(true)
220
+ end
221
+
222
+ desc "disable debug and trace calls"
223
+ task :debug_off do
224
+ debug_info(false)
225
+ end
226
+
227
+ ######################################################################
228
+ # check columns
229
+
230
+ desc "check for columns > 80"
231
+ task :check_columns do
232
+ Dir["**/*.rb"].each { |file|
233
+ File.read(file).scan(%r!^.{81}!) { |match|
234
+ unless match =~ %r!http://!
235
+ raise "#{file} greater than 80 columns"
236
+ end
237
+ }
238
+ }
239
+ end
240
+
241
+ task :prerelease => :check_columns
242
+
243
+ ######################################################################
244
+ # comments
245
+
246
+ task :comments do
247
+ write_file("comments") {
248
+ Array.new.tap { |result|
249
+ (["Rakefile"] + Dir["**/*.{rb,rake}"]).each { |file|
250
+ File.read(file).scan(%r!\#[^\{].*$!) { |match|
251
+ result << match
252
+ }
253
+ }
254
+ }.join("\n")
255
+ }
256
+ end
257
+
258
+ ######################################################################
259
+ # release
260
+
261
+ def git(*args)
262
+ sh("git", *args)
263
+ end
264
+
265
+ task :prerelease => :clean do
266
+ unless `git status` =~ %r!nothing to commit \(working directory clean\)!
267
+ raise "Directory not clean"
268
+ end
269
+ %w[github.com rubyforge.org].each { |server|
270
+ cmd = "ping " + (
271
+ if Config::CONFIG["host"] =~ %r!darwin!
272
+ "-c2 #{server}"
273
+ else
274
+ "#{server} 2 2"
275
+ end
276
+ )
277
+ unless `#{cmd}` =~ %r!0% packet loss!
278
+ raise "No ping for #{server}"
279
+ end
280
+ }
281
+ end
282
+
283
+ def rubyforge(command, file)
284
+ sh(
285
+ "rubyforge",
286
+ command,
287
+ GEMSPEC.rubyforge_project,
288
+ GEMSPEC.rubyforge_project,
289
+ GEMSPEC.version.to_s,
290
+ file
291
+ )
292
+ end
293
+
294
+ task :finish_release do
295
+ gem, tgz = %w(gem tgz).map { |ext|
296
+ "pkg/#{GEMSPEC.name}-#{GEMSPEC.version}.#{ext}"
297
+ }
298
+
299
+ gem_md5, tgz_md5 = [gem, tgz].map { |file|
300
+ "#{file}.md5".tap { |md5|
301
+ sh("md5sum #{file} > #{md5}")
302
+ }
303
+ }
304
+
305
+ rubyforge("add_release", gem)
306
+ [gem_md5, tgz, tgz_md5].each { |file|
307
+ rubyforge("add_file", file)
308
+ }
309
+
310
+ git("tag", "#{GEMSPEC.name}-" + GEMSPEC.version.to_s)
311
+ git(*%w(push --tags origin master))
312
+ end
313
+
314
+ task :release =>
315
+ [
316
+ :prerelease,
317
+ :package,
318
+ :publish,
319
+ :finish_release,
320
+ ]
321
+
322
+ ######################################################################
323
+ # util
324
+
325
+ def open_browser(*files)
326
+ if Config::CONFIG["host"] =~ %r!darwin!
327
+ sh("open", "/Applications/Firefox.app", *files)
328
+ else
329
+ sh("firefox", *files)
330
+ end
331
+ end
332
+
333
+ unless respond_to? :tap
334
+ class Object
335
+ def tap
336
+ yield self
337
+ self
338
+ end
339
+ end
340
+ end
341
+
342
+ def replace_file(file)
343
+ old_contents = File.read(file)
344
+ yield(old_contents).tap { |new_contents|
345
+ if old_contents != new_contents
346
+ File.open(file, "wb") { |output|
347
+ output.print(new_contents)
348
+ }
349
+ end
350
+ }
351
+ end
352
+
353
+ def write_file(file)
354
+ yield.tap { |contents|
355
+ File.open(file, "wb") { |out|
356
+ out.print(contents)
357
+ }
358
+ }
359
+ end
360
+
361
+ def run_ruby_on_each(*files)
362
+ files.each { |file|
363
+ Jumpstart::Ruby.run_or_raise("-w", file)
364
+ }
365
+ end
data/test/test_basic.rb CHANGED
@@ -51,7 +51,7 @@ class TestBasic < Test::Unit::TestCase
51
51
 
52
52
  def test_malformed
53
53
  CompTree.build { |driver|
54
- assert_raise(CompTree::ArgumentError) {
54
+ assert_raise(ArgumentError) {
55
55
  driver.define {
56
56
  }
57
57
  }
@@ -61,16 +61,6 @@ class TestBasic < Test::Unit::TestCase
61
61
  driver.define(:a) {
62
62
  }
63
63
  }
64
- assert_raise(CompTree::ArgumentError) {
65
- driver.define(:b) {
66
- }
67
- driver.compute(:b, 0)
68
- }
69
- assert_raise(CompTree::ArgumentError) {
70
- driver.define(:c) {
71
- }
72
- driver.compute(:c, -1)
73
- }
74
64
  }
75
65
  end
76
66
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: comp_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James M. Lawrence
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-09 00:00:00 -04:00
12
+ date: 2009-04-12 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -20,10 +20,10 @@ executables: []
20
20
  extensions: []
21
21
 
22
22
  extra_rdoc_files:
23
- - README
23
+ - README.rdoc
24
24
  files:
25
25
  - CHANGES
26
- - README
26
+ - README.rdoc
27
27
  - Rakefile
28
28
  - comp_tree.gemspec
29
29
  - install.rb
@@ -32,6 +32,7 @@ files:
32
32
  - lib/comp_tree/error.rb
33
33
  - lib/comp_tree/node.rb
34
34
  - lib/comp_tree.rb
35
+ - rakelib/jumpstart.rake
35
36
  - rakelib/jumpstart/ruby.rb
36
37
  - rakelib/jumpstart/simple_installer.rb
37
38
  - test/common.rb
@@ -47,7 +48,7 @@ homepage: comptree.rubyforge.org
47
48
  post_install_message:
48
49
  rdoc_options:
49
50
  - --main
50
- - README
51
+ - README.rdoc
51
52
  - --title
52
53
  - "comp_tree: Parallel Computation Tree"
53
54
  - --exclude
@@ -61,7 +62,7 @@ rdoc_options:
61
62
  - --exclude
62
63
  - lib/comp_tree/algorithm.rb
63
64
  - --exclude
64
- - lib/comp_tree/node.rb
65
+ - rakelib/jumpstart.rake
65
66
  - --exclude
66
67
  - rakelib/jumpstart/ruby.rb
67
68
  - --exclude