comp_tree 0.7.0 → 0.7.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.
- data/CHANGES +5 -0
- data/{README → README.rdoc} +5 -5
- data/comp_tree.gemspec +5 -4
- data/lib/comp_tree.rb +7 -5
- data/lib/comp_tree/algorithm.rb +19 -25
- data/lib/comp_tree/driver.rb +25 -45
- data/lib/comp_tree/error.rb +6 -8
- data/lib/comp_tree/node.rb +2 -2
- data/rakelib/jumpstart.rake +365 -0
- data/test/test_basic.rb +1 -11
- metadata +7 -6
data/CHANGES
CHANGED
data/{README → README.rdoc}
RENAMED
@@ -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, :
|
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
|
-
|
67
|
+
accumulator.add "more data"
|
68
68
|
width*height
|
69
69
|
}
|
70
70
|
|
71
|
-
Given a tree where nodes are modifying
|
72
|
-
|
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
|
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.
|
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
|
-
#
|
33
|
+
# :call-seq:
|
34
|
+
# build { |driver| ... }
|
34
35
|
#
|
35
|
-
# A Driver instance is passed to the
|
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> --
|
40
|
-
#
|
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|
|
data/lib/comp_tree/algorithm.rb
CHANGED
@@ -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
|
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
|
-
|
21
|
-
|
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
|
-
|
32
|
-
|
33
|
+
num_threads_ready += 1
|
34
|
+
condition.wait(tree_mutex)
|
33
35
|
}
|
34
36
|
|
35
|
-
loop_with(
|
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
|
-
|
41
|
-
throw
|
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
|
-
|
53
|
-
throw
|
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
|
-
|
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 {
|
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
|
111
|
+
until num_threads_ready == 0
|
110
112
|
#trace "Main: waking threads"
|
111
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/comp_tree/driver.rb
CHANGED
@@ -14,21 +14,15 @@ module CompTree
|
|
14
14
|
include Algorithm
|
15
15
|
|
16
16
|
#
|
17
|
-
#
|
17
|
+
# See CompTree.build.
|
18
18
|
#
|
19
|
-
|
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
|
-
#
|
35
|
+
# _name_ -- unique node identifier (for example a symbol).
|
42
36
|
#
|
43
|
-
#
|
44
|
-
#
|
37
|
+
# _child_names_ -- unique node identifiers for children.
|
38
|
+
#
|
39
|
+
# Define a computation node.
|
45
40
|
#
|
46
|
-
#
|
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(*
|
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 =
|
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
|
-
#
|
79
|
+
# _name_ -- unique node identifier (for example a symbol).
|
93
80
|
#
|
94
|
-
#
|
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
|
-
#
|
108
|
+
# :call-seq:
|
109
|
+
# compute(name, threads)
|
110
|
+
# compute(name, :threads => threads)
|
126
111
|
#
|
127
|
-
#
|
112
|
+
# _name_ -- unique node identifier (for example a symbol).
|
128
113
|
#
|
129
|
-
#
|
114
|
+
# _threads_ -- number of threads.
|
130
115
|
#
|
131
|
-
#
|
116
|
+
# Compute this node, returning its result.
|
132
117
|
#
|
133
|
-
#
|
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
|
-
|
128
|
+
compute_parallel(root, threads)
|
149
129
|
end
|
150
130
|
end
|
151
131
|
end
|
data/lib/comp_tree/error.rb
CHANGED
@@ -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 =
|
10
|
+
# driver.nodes[name].function = lambda { ... }
|
17
11
|
#
|
18
12
|
class RedefinitionError < Error ; end
|
19
13
|
|
20
|
-
#
|
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
|
data/lib/comp_tree/node.rb
CHANGED
@@ -69,8 +69,8 @@ module CompTree
|
|
69
69
|
end
|
70
70
|
|
71
71
|
#
|
72
|
-
# Force
|
73
|
-
#
|
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(
|
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.
|
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-
|
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
|
-
-
|
65
|
+
- rakelib/jumpstart.rake
|
65
66
|
- --exclude
|
66
67
|
- rakelib/jumpstart/ruby.rb
|
67
68
|
- --exclude
|