computable 0.1.0 → 1.1.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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.github/workflows/ci.yml +44 -0
- data/.travis.yml +5 -0
- data/README.md +3 -0
- data/Rakefile +1 -1
- data/computable.gemspec +3 -1
- data/lib/computable/version.rb +1 -1
- data/lib/computable.rb +204 -68
- data/test/helper.rb +16 -0
- data/test/test_backtrace.rb +33 -0
- data/test/test_candy.rb +8 -5
- data/test/test_debug.rb +40 -0
- data/test/test_expired_recalc.rb +40 -0
- data/test/test_format.rb +6 -3
- data/test/test_freeze.rb +6 -3
- data/test/test_inheritance.rb +31 -0
- data/test/test_input_value.rb +6 -3
- data/test/test_parallel.rb +126 -0
- data/test/test_recursion_detection.rb +6 -3
- data/test/test_unknown.rb +47 -0
- data.tar.gz.sig +0 -0
- metadata +52 -18
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ec5682e86b9081039947fc2718c8cdc42d2407af8cb0da30244a41b994d917f5
|
|
4
|
+
data.tar.gz: f8471eed0b3144951e4bb834e63013339e1af67842278e1bac7368c82070e76d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9be1797f3865b5ed1f0cbf6b98c3a8ceca6342b689212a801e55e2ad1e3499618a1439746744260d719bda4e9895c0ff080b2a2e43a971414f06f18a60f9cb33
|
|
7
|
+
data.tar.gz: b3852ade5823458ff7a5da0e6e89416c3c9edd326fa3466781ad52cdf876aaa8ee032949b98a52b6646f37b8b8d7256a57ff41d1a5c01c041be4b53b88738232
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
job_test:
|
|
7
|
+
name: CI
|
|
8
|
+
strategy:
|
|
9
|
+
fail-fast: false
|
|
10
|
+
matrix:
|
|
11
|
+
include:
|
|
12
|
+
- os: windows
|
|
13
|
+
ruby: "head"
|
|
14
|
+
- os: ubuntu
|
|
15
|
+
ruby: "head"
|
|
16
|
+
- os: ubuntu
|
|
17
|
+
ruby: "3.1"
|
|
18
|
+
- os: ubuntu
|
|
19
|
+
ruby: "2.5"
|
|
20
|
+
- os: ubuntu
|
|
21
|
+
ruby: "truffleruby"
|
|
22
|
+
NO_TIMING_TESTS: true
|
|
23
|
+
- os: ubuntu
|
|
24
|
+
ruby: "jruby"
|
|
25
|
+
- os: macos
|
|
26
|
+
ruby: "head"
|
|
27
|
+
NO_TIMING_TESTS: true
|
|
28
|
+
|
|
29
|
+
runs-on: ${{ matrix.os }}-latest
|
|
30
|
+
env:
|
|
31
|
+
NO_TIMING_TESTS: ${{ matrix.NO_TIMING_TESTS }}
|
|
32
|
+
|
|
33
|
+
steps:
|
|
34
|
+
|
|
35
|
+
- uses: actions/checkout@v2
|
|
36
|
+
- name: Set up Ruby
|
|
37
|
+
uses: ruby/setup-ruby@v1
|
|
38
|
+
with:
|
|
39
|
+
ruby-version: ${{ matrix.ruby }}
|
|
40
|
+
|
|
41
|
+
- run: bundle install
|
|
42
|
+
|
|
43
|
+
- name: Run tests
|
|
44
|
+
run: bundle exec rake test
|
data/.travis.yml
ADDED
data/README.md
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
[](https://github.com/larskanis/computable/actions/workflows/ci.yml)
|
|
2
|
+
[](https://app.travis-ci.com/larskanis/computable)
|
|
3
|
+
|
|
1
4
|
# Computable
|
|
2
5
|
|
|
3
6
|
TODO: Write a gem description
|
data/Rakefile
CHANGED
|
@@ -3,5 +3,5 @@ require "bundler/gem_tasks"
|
|
|
3
3
|
task :gem => :build
|
|
4
4
|
|
|
5
5
|
task :test do
|
|
6
|
-
sh "ruby -w -W2 -I. -Ilib -e \"#{Dir["test/test_*.rb"].map{|f| "require '#{f}';"}.join}\" -- -v"
|
|
6
|
+
sh "ruby -w -W2 -I. -Ilib -e \"#{Dir["test/test_*.rb"].map{|f| "require '#{f}';"}.join}\" -- -v #{ENV["TESTOPT"]}"
|
|
7
7
|
end
|
data/computable.gemspec
CHANGED
|
@@ -12,12 +12,14 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.summary = %q{Define computation tasks with automatic caching and dependency tracking.}
|
|
13
13
|
spec.homepage = "https://github.com/larskanis/computable"
|
|
14
14
|
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = ">= 2.4"
|
|
15
16
|
|
|
16
17
|
spec.files = `git ls-files`.split($/)
|
|
17
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
20
|
spec.require_paths = ["lib"]
|
|
20
21
|
|
|
21
|
-
spec.add_development_dependency "bundler", "
|
|
22
|
+
spec.add_development_dependency "bundler", ">= 1.4", "< 3.0"
|
|
22
23
|
spec.add_development_dependency "rake"
|
|
24
|
+
spec.add_development_dependency "minitest", "~> 5.6"
|
|
23
25
|
end
|
data/lib/computable/version.rb
CHANGED
data/lib/computable.rb
CHANGED
|
@@ -16,29 +16,41 @@ class Computable
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
class Variable
|
|
19
|
-
attr_accessor :name, :calc_method, :used_for, :expired_from, :value, :value_calced, :count
|
|
20
|
-
|
|
19
|
+
attr_accessor :name, :calc_method, :used_for, :expired_from, :value, :value_calced, :count, :in_process, :recalc_error
|
|
20
|
+
|
|
21
|
+
def initialize(name, calc_method, comp, mutex)
|
|
21
22
|
@name = name
|
|
22
23
|
@calc_method = calc_method
|
|
24
|
+
@comp = comp
|
|
25
|
+
@mutex = mutex
|
|
23
26
|
@used_for = {}
|
|
24
27
|
@expired_from = {}
|
|
25
28
|
@count = 0
|
|
26
29
|
@value = Unknown
|
|
30
|
+
@in_process = false
|
|
31
|
+
@recalc_error = nil
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
def inspect
|
|
30
|
-
|
|
35
|
+
has = @recalc_error ? "error!" : "value:#{Unknown!=value}"
|
|
36
|
+
"<Variable #{name} used_for:#{used_for.keys} expired_from:#{expired_from.keys} has #{has} value_calced:#{value_calced.inspect}>"
|
|
31
37
|
end
|
|
32
38
|
|
|
33
39
|
def calc!
|
|
34
40
|
self.count += 1
|
|
35
|
-
|
|
41
|
+
self.value_calced = true
|
|
42
|
+
@mutex.unlock
|
|
43
|
+
begin
|
|
44
|
+
calc_method.call(self)
|
|
45
|
+
ensure
|
|
46
|
+
@mutex.lock
|
|
47
|
+
end
|
|
36
48
|
end
|
|
37
49
|
|
|
38
50
|
def expire_value
|
|
39
51
|
return if used_for.empty?
|
|
40
52
|
|
|
41
|
-
puts "expire #{inspect}" if
|
|
53
|
+
puts "expire #{inspect}" if @comp.computable_debug
|
|
42
54
|
used_for.each do |name2, v2|
|
|
43
55
|
if v2.value_calced && !v2.expired_from[name]
|
|
44
56
|
v2.expire_value
|
|
@@ -50,7 +62,7 @@ class Computable
|
|
|
50
62
|
def revoke_expire
|
|
51
63
|
return if used_for.empty?
|
|
52
64
|
|
|
53
|
-
puts "revoke expire #{inspect}" if
|
|
65
|
+
puts "revoke expire #{inspect}" if @comp.computable_debug
|
|
54
66
|
used_for.each do |name2, v2|
|
|
55
67
|
if v2.value_calced && v2.expired_from.delete(name) && v2.expired_from.empty?
|
|
56
68
|
v2.revoke_expire
|
|
@@ -58,49 +70,177 @@ class Computable
|
|
|
58
70
|
end
|
|
59
71
|
end
|
|
60
72
|
|
|
73
|
+
def process_recalced_value(recalced_value, err)
|
|
74
|
+
if err
|
|
75
|
+
self.recalc_error = err
|
|
76
|
+
self.value = Unknown
|
|
77
|
+
used_for.clear
|
|
78
|
+
elsif self.value == recalced_value
|
|
79
|
+
revoke_expire
|
|
80
|
+
else
|
|
81
|
+
self.recalc_error = nil
|
|
82
|
+
self.value = recalced_value
|
|
83
|
+
used_for.clear
|
|
84
|
+
end
|
|
85
|
+
expired_from.clear
|
|
86
|
+
end
|
|
87
|
+
|
|
61
88
|
def recalc_value
|
|
62
89
|
return if !value_calced || expired_from.empty?
|
|
63
90
|
|
|
64
|
-
puts "recalc #{inspect}" if
|
|
91
|
+
puts "recalc #{inspect}" if @comp.computable_debug
|
|
65
92
|
expired_from.each do |name2, v2|
|
|
66
93
|
v2.recalc_value
|
|
67
94
|
end
|
|
68
95
|
|
|
69
96
|
unless expired_from.empty?
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
97
|
+
begin
|
|
98
|
+
recalced_value = self.calc!
|
|
99
|
+
rescue Exception => err
|
|
100
|
+
end
|
|
101
|
+
process_recalced_value(recalced_value, err)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def new_worker(from_workers, to_workers)
|
|
106
|
+
Thread.new do
|
|
107
|
+
while v = to_workers.pop
|
|
108
|
+
puts "recalc parallel #{v.inspect}" if @comp.computable_debug
|
|
109
|
+
err = nil
|
|
110
|
+
begin
|
|
111
|
+
recalced_value = v.calc_method.call(v)
|
|
112
|
+
rescue Exception => err
|
|
113
|
+
end
|
|
114
|
+
from_workers.push([v, recalced_value, err])
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def recalc_parallel(max_threads)
|
|
120
|
+
workers = []
|
|
121
|
+
from_workers = Queue.new
|
|
122
|
+
to_workers = Queue.new
|
|
123
|
+
|
|
124
|
+
master_loop(max_threads, workers, from_workers, to_workers)
|
|
125
|
+
|
|
126
|
+
to_workers.close
|
|
127
|
+
workers.each { |t| t.join }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def master_loop(max_threads, workers, from_workers, to_workers)
|
|
131
|
+
num_working = 0
|
|
132
|
+
loop do
|
|
133
|
+
if num_working == max_threads || !(node = find_recalcable)
|
|
134
|
+
#
|
|
135
|
+
# maxed out or no nodes available -- wait for results
|
|
136
|
+
#
|
|
137
|
+
return if num_working == 0
|
|
138
|
+
|
|
139
|
+
puts "recalc join" if @comp.computable_debug
|
|
140
|
+
@mutex.unlock
|
|
141
|
+
begin
|
|
142
|
+
node, recalced_value, err = from_workers.pop
|
|
143
|
+
ensure
|
|
144
|
+
@mutex.lock
|
|
145
|
+
end
|
|
146
|
+
node.in_process = false
|
|
147
|
+
num_working -= 1
|
|
148
|
+
|
|
149
|
+
if err
|
|
150
|
+
# Add the backtrace of the caller to the small in-thread backtrace for better debugging
|
|
151
|
+
err.set_backtrace(err.backtrace + caller)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
node.process_recalced_value(recalced_value, err)
|
|
73
155
|
else
|
|
74
|
-
|
|
75
|
-
|
|
156
|
+
#
|
|
157
|
+
# not maxed out and found a node -- compute it
|
|
158
|
+
#
|
|
159
|
+
if (max_threads && workers.size < max_threads) ||
|
|
160
|
+
(!max_threads && num_working == workers.size)
|
|
161
|
+
workers << new_worker(from_workers, to_workers)
|
|
162
|
+
end
|
|
163
|
+
node.in_process = true
|
|
164
|
+
node.count += 1
|
|
165
|
+
node.value_calced = true
|
|
166
|
+
num_working += 1
|
|
167
|
+
to_workers.push(node)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def find_recalcable
|
|
173
|
+
if !value_calced || expired_from.empty? || in_process
|
|
174
|
+
nil
|
|
175
|
+
elsif expired_from.all?{ |_, v2| !v2.value_calced || v2.expired_from.empty? }
|
|
176
|
+
self
|
|
177
|
+
else
|
|
178
|
+
expired_from.each do |_, v2|
|
|
179
|
+
node = v2.find_recalcable and return node
|
|
76
180
|
end
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def assign_value(value)
|
|
186
|
+
unless self.value == value
|
|
187
|
+
expire_value
|
|
77
188
|
expired_from.clear
|
|
189
|
+
used_for.clear
|
|
190
|
+
self.value = value
|
|
191
|
+
end
|
|
192
|
+
self.value_calced = false
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def query_value(kaller)
|
|
196
|
+
if kaller
|
|
197
|
+
v2 = used_for[kaller.name]
|
|
198
|
+
if v2
|
|
199
|
+
if Unknown==value && Unknown==v2.value && value_calced && v2.value_calced
|
|
200
|
+
raise RecursionDetected, "#{v2.name} depends on #{name}, but #{name} could not be computed without #{v2.name}"
|
|
201
|
+
end
|
|
202
|
+
else
|
|
203
|
+
used_for[kaller.name] = kaller
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
max_threads = @comp.computable_max_threads
|
|
208
|
+
if !max_threads || max_threads > 0
|
|
209
|
+
recalc_parallel(max_threads)
|
|
210
|
+
else
|
|
211
|
+
recalc_value
|
|
78
212
|
end
|
|
213
|
+
|
|
214
|
+
raise recalc_error if recalc_error
|
|
215
|
+
self.value = calc! if Unknown==value
|
|
216
|
+
self.value
|
|
79
217
|
end
|
|
80
218
|
end
|
|
81
219
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
220
|
+
def computable_debug=(v)
|
|
221
|
+
@computable_debug = v
|
|
222
|
+
end
|
|
223
|
+
def computable_debug
|
|
224
|
+
@computable_debug
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def computable_max_threads=(v)
|
|
228
|
+
@computable_max_threads = v
|
|
85
229
|
end
|
|
86
|
-
def
|
|
87
|
-
|
|
230
|
+
def computable_max_threads
|
|
231
|
+
@computable_max_threads
|
|
88
232
|
end
|
|
89
233
|
|
|
90
|
-
def computable_display_dot(
|
|
234
|
+
def computable_display_dot(**kwargs)
|
|
91
235
|
IO.popen("dot -Tpng | display -", "w") do |fd|
|
|
92
|
-
fd.puts computable_to_dot(
|
|
236
|
+
fd.puts computable_to_dot(**kwargs)
|
|
93
237
|
end
|
|
94
238
|
end
|
|
95
239
|
|
|
96
|
-
def computable_to_dot(
|
|
97
|
-
rankdir = params.delete(:rankdir){ "TB" }
|
|
98
|
-
multiline = params.delete(:multiline){ true }
|
|
99
|
-
raise ArgumentError, "invalid params #{params.inspect}" unless params.empty?
|
|
100
|
-
|
|
240
|
+
def computable_to_dot(rankdir: "TB", multiline: true)
|
|
101
241
|
dot = "digraph #{self.class.name.inspect} {\n"
|
|
102
242
|
dot << "graph [ dpi = 45, rankdir=#{rankdir} ];\n"
|
|
103
|
-
@
|
|
243
|
+
@computable_variables.each do |name, v|
|
|
104
244
|
col = case
|
|
105
245
|
when !v.value_calced then "color = red,"
|
|
106
246
|
when !v.used_for.empty? then "color = green,"
|
|
@@ -120,82 +260,78 @@ class Computable
|
|
|
120
260
|
end
|
|
121
261
|
|
|
122
262
|
def initialize
|
|
123
|
-
@
|
|
124
|
-
@
|
|
263
|
+
@computable_debug = false
|
|
264
|
+
@computable_max_threads = 0
|
|
265
|
+
@computable_variables = {}
|
|
266
|
+
@computable_caller = nil
|
|
267
|
+
@computable_mutex = Mutex.new
|
|
125
268
|
end
|
|
126
269
|
|
|
127
270
|
|
|
128
271
|
def self.verify_format(name, value, format)
|
|
129
|
-
if format && value
|
|
272
|
+
if format && !(Unknown==value) && !(format === value)
|
|
130
273
|
raise InvalidFormat, "variable '#{name}': value #{value.inspect} is not in format #{format.inspect}"
|
|
131
274
|
end
|
|
132
275
|
end
|
|
133
276
|
|
|
134
|
-
def
|
|
135
|
-
|
|
136
|
-
|
|
277
|
+
private def improve_backtrace(err, block, text)
|
|
278
|
+
fpath, lineno = block.source_location
|
|
279
|
+
bt = err.backtrace
|
|
280
|
+
myloc = err.backtrace_locations.select.with_index{|loc, i| loc.path == fpath && loc.lineno >= lineno && !bt[i].include?("#") }.min{|a,b| a.lineno <=> b.lineno }
|
|
281
|
+
idx = err.backtrace_locations.index(myloc)
|
|
282
|
+
bt[idx] += " ##{text}"
|
|
283
|
+
raise err
|
|
284
|
+
end
|
|
137
285
|
|
|
286
|
+
def self.calc_value name, format=nil, freeze: true, &block
|
|
138
287
|
calc_method_id = "calc_#{name}".intern
|
|
139
288
|
define_method(calc_method_id, &block)
|
|
140
289
|
|
|
141
290
|
calc_method2_id = "calc_#{name}_with_tracking".intern
|
|
142
291
|
define_method(calc_method2_id) do |v|
|
|
292
|
+
old_caller = Thread.current.thread_variable_get("Computable #{object_id}")
|
|
293
|
+
Thread.current.thread_variable_set("Computable #{self.object_id}", v)
|
|
143
294
|
begin
|
|
144
|
-
v.
|
|
145
|
-
@caller, old_caller = v, @caller
|
|
295
|
+
puts "do calc #{v.inspect}" if @computable_debug
|
|
146
296
|
begin
|
|
147
|
-
puts "do calc #{v.inspect}" if @@debug
|
|
148
297
|
res = send(calc_method_id)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
res
|
|
152
|
-
ensure
|
|
153
|
-
@caller = old_caller
|
|
298
|
+
rescue Exception => err
|
|
299
|
+
improve_backtrace(err, block, name)
|
|
154
300
|
end
|
|
301
|
+
Computable.verify_format(name, res, format)
|
|
302
|
+
res.freeze if freeze
|
|
303
|
+
res
|
|
304
|
+
ensure
|
|
305
|
+
Thread.current.thread_variable_set("Computable #{self.object_id}", old_caller)
|
|
155
306
|
end
|
|
156
307
|
end
|
|
157
308
|
|
|
158
309
|
define_method("#{name}=") do |value|
|
|
159
310
|
Computable.verify_format(name, value, format)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
v.expire_value
|
|
166
|
-
v.expired_from.clear
|
|
167
|
-
v.used_for.clear
|
|
311
|
+
@computable_mutex.synchronize do
|
|
312
|
+
v = @computable_variables[name]
|
|
313
|
+
puts "set #{name}: #{value.inspect} #{v.inspect}" if @computable_debug
|
|
314
|
+
v = @computable_variables[name] = Variable.new(name, method(calc_method2_id), self, @computable_mutex) unless v
|
|
315
|
+
|
|
168
316
|
value.freeze if freeze
|
|
169
|
-
v.value
|
|
317
|
+
v.assign_value(value)
|
|
170
318
|
end
|
|
171
|
-
v.value_calced = false
|
|
172
319
|
end
|
|
173
320
|
|
|
174
321
|
define_method(name) do
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
322
|
+
@computable_mutex.synchronize do
|
|
323
|
+
v = @computable_variables[name]
|
|
324
|
+
puts "called #{name} #{v.inspect}" if @computable_debug
|
|
325
|
+
v = @computable_variables[name] = Variable.new(name, method(calc_method2_id), self, @computable_mutex) unless v
|
|
178
326
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if v2
|
|
182
|
-
if v.value==Unknown && v2.value==Unknown && v.value_calced && v2.value_calced
|
|
183
|
-
raise RecursionDetected, "#{v2.name} depends on #{name}, but #{name} could not be computed without #{v2.name}"
|
|
184
|
-
end
|
|
185
|
-
else
|
|
186
|
-
v.used_for[@caller.name] = @caller
|
|
187
|
-
end
|
|
327
|
+
kaller = Thread.current.thread_variable_get("Computable #{object_id}")
|
|
328
|
+
v.query_value(kaller)
|
|
188
329
|
end
|
|
189
|
-
|
|
190
|
-
v.recalc_value
|
|
191
|
-
|
|
192
|
-
v.value = v.calc! if v.value==Unknown
|
|
193
|
-
v.value
|
|
194
330
|
end
|
|
195
331
|
end
|
|
196
332
|
|
|
197
|
-
def self.input_value name, format=nil,
|
|
198
|
-
calc_value name, format,
|
|
333
|
+
def self.input_value name, format=nil, **kwargs
|
|
334
|
+
calc_value name, format, **kwargs do
|
|
199
335
|
raise UndefinedValue, "input variable '#{name}' is not assigned"
|
|
200
336
|
end
|
|
201
337
|
end
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require_relative 'helper'
|
|
2
|
+
|
|
3
|
+
class TestBacktrace < Minitest::Test
|
|
4
|
+
class MyBuilder < Computable
|
|
5
|
+
calc_value :a do
|
|
6
|
+
b
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
calc_value :b do
|
|
10
|
+
raise "my error"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def setup
|
|
15
|
+
@b = MyBuilder.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def teardown
|
|
19
|
+
# @b.computable_display_dot
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_backtrace_decoration
|
|
23
|
+
err = assert_raises(StandardError){ @b.a }
|
|
24
|
+
bt = err.backtrace.join("\n")
|
|
25
|
+
assert_match(/my error/, err.to_s)
|
|
26
|
+
assert_match(/test_backtrace.rb:10:.* #b$/, bt)
|
|
27
|
+
assert_match(/test_backtrace.rb:6:.* #a$/, bt)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class TestBacktraceParallel < TestBacktrace
|
|
32
|
+
include Helper::EnableParallel
|
|
33
|
+
end
|
data/test/test_candy.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
require 'computable'
|
|
1
|
+
require_relative 'helper'
|
|
3
2
|
|
|
4
|
-
class TestCandy <
|
|
3
|
+
class TestCandy < Minitest::Test
|
|
5
4
|
class MyBuilder < Computable
|
|
6
5
|
attr_reader :counters
|
|
7
6
|
|
|
@@ -10,9 +9,9 @@ class TestCandy < MiniTest::Unit::TestCase
|
|
|
10
9
|
@counters = {}
|
|
11
10
|
end
|
|
12
11
|
|
|
13
|
-
def self.counted_value name, format=nil,
|
|
12
|
+
def self.counted_value name, format=nil, **kwargs, &block
|
|
14
13
|
define_method "#{name}_counted", &block
|
|
15
|
-
calc_value name, format do
|
|
14
|
+
calc_value name, format, **kwargs do
|
|
16
15
|
@counters[name] ||= 0
|
|
17
16
|
@counters[name] += 1
|
|
18
17
|
send "#{name}_counted"
|
|
@@ -102,3 +101,7 @@ class TestCandy < MiniTest::Unit::TestCase
|
|
|
102
101
|
assert_equal [2,2,2,1,2,2,1,1], @b.counters.values_at(:top, :l0, :r0, :l1, :r1, :bot, :fl, :fr)
|
|
103
102
|
end
|
|
104
103
|
end
|
|
104
|
+
|
|
105
|
+
class TestCandyParallel < TestCandy
|
|
106
|
+
include Helper::EnableParallel
|
|
107
|
+
end
|
data/test/test_debug.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative 'helper'
|
|
2
|
+
|
|
3
|
+
class TestDebug < Minitest::Test
|
|
4
|
+
class MyBuilder < Computable
|
|
5
|
+
input_value :inp
|
|
6
|
+
calc_value :cal do
|
|
7
|
+
inp * 2
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def setup
|
|
12
|
+
@b = MyBuilder.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def teardown
|
|
16
|
+
# @b.computable_display_dot
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_enable_debug
|
|
20
|
+
refute @b.computable_debug
|
|
21
|
+
@b.computable_debug = true
|
|
22
|
+
assert @b.computable_debug
|
|
23
|
+
@b.computable_debug = false
|
|
24
|
+
refute @b.computable_debug
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_prints_debug
|
|
28
|
+
@b.computable_debug = true
|
|
29
|
+
|
|
30
|
+
assert_output(/set inp/){ @b.inp = 3 }
|
|
31
|
+
cal = nil
|
|
32
|
+
assert_output(/do calc/){ cal = @b.cal }
|
|
33
|
+
|
|
34
|
+
assert_equal 6, cal
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class TestDebugParallel < TestDebug
|
|
39
|
+
include Helper::EnableParallel
|
|
40
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require_relative 'helper'
|
|
2
|
+
|
|
3
|
+
class TestExpiredRecalc < Minitest::Test
|
|
4
|
+
class MyBuilder < Computable
|
|
5
|
+
input_value :enable
|
|
6
|
+
|
|
7
|
+
calc_value :a do
|
|
8
|
+
b if enable
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
calc_value :b do
|
|
12
|
+
raise StandardError, "not enabled" unless enable
|
|
13
|
+
:x
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def setup
|
|
18
|
+
@b = MyBuilder.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def teardown
|
|
22
|
+
# @b.computable_display_dot
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_on_off
|
|
26
|
+
@b.enable = true
|
|
27
|
+
assert_equal :x, @b.a
|
|
28
|
+
|
|
29
|
+
@b.enable = false
|
|
30
|
+
assert_nil @b.a # this shouldn't raise an error although b is internally recalced
|
|
31
|
+
assert_raises(StandardError){ @b.b }
|
|
32
|
+
|
|
33
|
+
@b.enable = true
|
|
34
|
+
assert_equal :x, @b.a
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class TestExpiredRecalcParallel < TestExpiredRecalc
|
|
39
|
+
include Helper::EnableParallel
|
|
40
|
+
end
|
data/test/test_format.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
require 'computable'
|
|
1
|
+
require_relative 'helper'
|
|
3
2
|
|
|
4
|
-
class TestFormat <
|
|
3
|
+
class TestFormat < Minitest::Test
|
|
5
4
|
class MyBuilder < Computable
|
|
6
5
|
def self.verify_uniq_array(a)
|
|
7
6
|
a.uniq.length == a.length
|
|
@@ -66,3 +65,7 @@ class TestFormat < MiniTest::Unit::TestCase
|
|
|
66
65
|
assert_raises(Computable::InvalidFormat){ @b.sqrt }
|
|
67
66
|
end
|
|
68
67
|
end
|
|
68
|
+
|
|
69
|
+
class TestFormatParallel < TestFormat
|
|
70
|
+
include Helper::EnableParallel
|
|
71
|
+
end
|
data/test/test_freeze.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
require 'computable'
|
|
1
|
+
require_relative 'helper'
|
|
3
2
|
|
|
4
|
-
class TestFreeze <
|
|
3
|
+
class TestFreeze < Minitest::Test
|
|
5
4
|
class MyBuilder < Computable
|
|
6
5
|
input_value :no_freeze1, String, freeze: false
|
|
7
6
|
calc_value :no_freeze2, String, freeze: false do
|
|
@@ -34,3 +33,7 @@ class TestFreeze < MiniTest::Unit::TestCase
|
|
|
34
33
|
assert @b.freeze2.frozen?, "should be frozen"
|
|
35
34
|
end
|
|
36
35
|
end
|
|
36
|
+
|
|
37
|
+
class TestFreezeParallel < TestFreeze
|
|
38
|
+
include Helper::EnableParallel
|
|
39
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require_relative 'helper'
|
|
2
|
+
|
|
3
|
+
class TestInheritance < Minitest::Test
|
|
4
|
+
class MyBuilder < Computable
|
|
5
|
+
calc_value :a do |*d|
|
|
6
|
+
"x#{d.inspect}"
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class MyChild < MyBuilder
|
|
11
|
+
calc_value :a do |*d|
|
|
12
|
+
super() + "y#{d.inspect}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def setup
|
|
17
|
+
@b = MyChild.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def teardown
|
|
21
|
+
# @b.computable_display_dot
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_super
|
|
25
|
+
assert_equal "x[]y[]", @b.a
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class TestInheritanceParallel < TestInheritance
|
|
30
|
+
include Helper::EnableParallel
|
|
31
|
+
end
|
data/test/test_input_value.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
require 'computable'
|
|
1
|
+
require_relative 'helper'
|
|
3
2
|
|
|
4
|
-
class TestInputValue <
|
|
3
|
+
class TestInputValue < Minitest::Test
|
|
5
4
|
class MyBuilder < Computable
|
|
6
5
|
input_value :i
|
|
7
6
|
|
|
@@ -46,3 +45,7 @@ class TestInputValue < MiniTest::Unit::TestCase
|
|
|
46
45
|
assert_equal 6, @b.o
|
|
47
46
|
end
|
|
48
47
|
end
|
|
48
|
+
|
|
49
|
+
class TestInputValueParallel < TestInputValue
|
|
50
|
+
include Helper::EnableParallel
|
|
51
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
require_relative 'helper'
|
|
2
|
+
|
|
3
|
+
class TestParallel < Minitest::Test
|
|
4
|
+
class MyBuilder < Computable
|
|
5
|
+
input_value :i1, Integer
|
|
6
|
+
input_value :i2, Integer
|
|
7
|
+
|
|
8
|
+
def self.p(idx)
|
|
9
|
+
calc_value "p#{idx}" do
|
|
10
|
+
sleep 0.1
|
|
11
|
+
i1
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.q(idx)
|
|
16
|
+
calc_value "q#{idx}" do
|
|
17
|
+
sleep 0.1
|
|
18
|
+
send("p#{idx}") + i2
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
0.upto(4) do |idx|
|
|
23
|
+
p idx
|
|
24
|
+
q idx
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
calc_value :o do
|
|
28
|
+
0.upto(3).map do |idx|
|
|
29
|
+
send("q#{idx}")
|
|
30
|
+
end.inject(:+)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
input_value :offs
|
|
34
|
+
|
|
35
|
+
100.times do |idx|
|
|
36
|
+
calc_value "m0-#{idx}" do
|
|
37
|
+
#sleep 0.001
|
|
38
|
+
idx + offs
|
|
39
|
+
end
|
|
40
|
+
calc_value "m1-#{idx}" do
|
|
41
|
+
#sleep 0.001
|
|
42
|
+
idx + offs
|
|
43
|
+
end
|
|
44
|
+
calc_value "m-#{idx}" do
|
|
45
|
+
send("m#{offs}-#{idx}")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
calc_value "n" do
|
|
50
|
+
100.times.sum do |idx|
|
|
51
|
+
send("m-#{idx}")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MyError < RuntimeError
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
input_value :enable_error
|
|
60
|
+
calc_value :raise_error do
|
|
61
|
+
raise MyError if enable_error
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def setup
|
|
66
|
+
@b = MyBuilder.new
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def teardown
|
|
70
|
+
# @b.computable_display_dot
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_many_recalcs
|
|
74
|
+
@b.computable_max_threads = 10
|
|
75
|
+
@b.offs = 0
|
|
76
|
+
assert_equal 4950, @b.n
|
|
77
|
+
|
|
78
|
+
#@b.computable_debug = true
|
|
79
|
+
@b.offs = 1
|
|
80
|
+
assert_equal 5050, @b.n
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_error
|
|
84
|
+
@b.enable_error = false
|
|
85
|
+
assert_nil @b.raise_error
|
|
86
|
+
@b.enable_error = true
|
|
87
|
+
assert_raises(MyBuilder::MyError){ @b.raise_error }
|
|
88
|
+
assert_raises(MyBuilder::MyError){ @b.raise_error }
|
|
89
|
+
@b.enable_error = false
|
|
90
|
+
assert_nil @b.raise_error
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
unless ENV["NO_TIMING_TESTS"]=="true"
|
|
94
|
+
def diff_time
|
|
95
|
+
st = Time.now
|
|
96
|
+
res = yield
|
|
97
|
+
[Time.now - st, res]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_full_parallel
|
|
101
|
+
@b.computable_max_threads = nil
|
|
102
|
+
@b.i1, @b.i2 = 2, 3
|
|
103
|
+
dt, res = diff_time{ @b.o }
|
|
104
|
+
assert_in_delta 0.8, dt, 0.1
|
|
105
|
+
assert_equal 20, res
|
|
106
|
+
|
|
107
|
+
@b.i1, @b.i2 = 4, 5
|
|
108
|
+
dt, res = diff_time{ @b.o }
|
|
109
|
+
assert_in_delta 0.2, dt, 0.1
|
|
110
|
+
assert_equal 36, res
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def test_2_threads
|
|
114
|
+
@b.computable_max_threads = 2
|
|
115
|
+
@b.i1, @b.i2 = 2, 3
|
|
116
|
+
dt, res = diff_time{ @b.o }
|
|
117
|
+
assert_in_delta 0.8, dt, 0.1
|
|
118
|
+
assert_equal 20, res
|
|
119
|
+
|
|
120
|
+
@b.i1, @b.i2 = 4, 5
|
|
121
|
+
dt, res = diff_time{ @b.o }
|
|
122
|
+
assert_in_delta 0.4, dt, 0.1
|
|
123
|
+
assert_equal 36, res
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
require 'computable'
|
|
1
|
+
require_relative 'helper'
|
|
3
2
|
|
|
4
|
-
class TestRecursionDetection <
|
|
3
|
+
class TestRecursionDetection < Minitest::Test
|
|
5
4
|
class MyBuilder < Computable
|
|
6
5
|
calc_value :recursion1 do
|
|
7
6
|
recursion1
|
|
@@ -34,3 +33,7 @@ class TestRecursionDetection < MiniTest::Unit::TestCase
|
|
|
34
33
|
assert_raises(Computable::RecursionDetected){ @b.recursion2 }
|
|
35
34
|
end
|
|
36
35
|
end
|
|
36
|
+
|
|
37
|
+
class TestRecursionDetectionParallel < TestRecursionDetection
|
|
38
|
+
include Helper::EnableParallel
|
|
39
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require_relative 'helper'
|
|
2
|
+
|
|
3
|
+
class TestUnknown < Minitest::Test
|
|
4
|
+
class MyTruth
|
|
5
|
+
def ==(obj)
|
|
6
|
+
true
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class MyFault
|
|
11
|
+
def ==(obj)
|
|
12
|
+
false
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class MyBuilder < Computable
|
|
17
|
+
input_value :v1
|
|
18
|
+
|
|
19
|
+
calc_value :v2 do
|
|
20
|
+
v1
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def setup
|
|
25
|
+
@b = MyBuilder.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def teardown
|
|
29
|
+
# @b.computable_display_dot
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_eq_true
|
|
33
|
+
@b.v1 = v = MyTruth.new
|
|
34
|
+
assert_same @b.v1, v
|
|
35
|
+
assert_same @b.v2, v
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_eq_false
|
|
39
|
+
@b.v1 = v = MyFault.new
|
|
40
|
+
assert_same @b.v1, v
|
|
41
|
+
assert_same @b.v2, v
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class TestUnknownParallel < TestUnknown
|
|
46
|
+
include Helper::EnableParallel
|
|
47
|
+
end
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: computable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lars Kanis
|
|
@@ -10,9 +10,9 @@ bindir: bin
|
|
|
10
10
|
cert_chain:
|
|
11
11
|
- |
|
|
12
12
|
-----BEGIN CERTIFICATE-----
|
|
13
|
-
|
|
13
|
+
MIIDLjCCAhagAwIBAgIBCjANBgkqhkiG9w0BAQsFADA9MQ4wDAYDVQQDDAVrYW5p
|
|
14
14
|
czEXMBUGCgmSJomT8ixkARkWB2NvbWNhcmQxEjAQBgoJkiaJk/IsZAEZFgJkZTAe
|
|
15
|
-
|
|
15
|
+
Fw0yMjA0MTExMTMwNTNaFw0yMzA0MTExMTMwNTNaMD0xDjAMBgNVBAMMBWthbmlz
|
|
16
16
|
MRcwFQYKCZImiZPyLGQBGRYHY29tY2FyZDESMBAGCgmSJomT8ixkARkWAmRlMIIB
|
|
17
17
|
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApop+rNmg35bzRugZ21VMGqI6
|
|
18
18
|
HGzPLO4VHYncWn/xmgPU/ZMcZdfj6MzIaZJ/czXyt4eHpBk1r8QOV3gBXnRXEjVW
|
|
@@ -20,32 +20,37 @@ cert_chain:
|
|
|
20
20
|
lJi4+ENAVT4MpqHEAGB8yFoPC0GqiOHQsdHxQV3P3c2OZqG+yJey74QtwA2tLcLn
|
|
21
21
|
Q53c63+VLGsOjODl1yPn/2ejyq8qWu6ahfTxiIlSar2UbwtaQGBDFdb2CXgEufXT
|
|
22
22
|
L7oaPxlmj+Q2oLOfOnInd2Oxop59HoJCQPsg8f921J43NCQGA8VHK6paxIRDLQID
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
MlGgwc//cCsBG8sa
|
|
23
|
+
AQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUvgTdT7fe
|
|
24
|
+
x17ugO3IOsjEJwW7KP4wDQYJKoZIhvcNAQELBQADggEBAILiaB/unSVBfX5n7uL8
|
|
25
|
+
veGGCOHuGYhCGqspb6mYiCx0dmV3RPRiEfGDLfzcXbHNx/3AjygcxH4Slr+pmaxr
|
|
26
|
+
04Xli3WurocnjoANSWqCwpHH3OhSVxFgBNrCa3OMWcIr0xKH+I7PXA80SXe0pzfg
|
|
27
|
+
ePjpzTY71j+rcyRJqWiU5/zwdUaCCelBJscxh/0IaNcz67ocCEMRj0n4m5HFEmZL
|
|
28
|
+
9zKkMZFoOjxRQjcL84QU7ZXnnFR5HG8nLw+NqWjo49W6MBQ9HGFda2tk3OpBhyWS
|
|
29
|
+
sc3NyOkGUGdfiee5VRG31Sh3LLON3YGED+zZAS+ZF6598y4vhv8MBLa1Oy357byC
|
|
30
|
+
tTg=
|
|
32
31
|
-----END CERTIFICATE-----
|
|
33
|
-
date:
|
|
32
|
+
date: 2022-06-01 00:00:00.000000000 Z
|
|
34
33
|
dependencies:
|
|
35
34
|
- !ruby/object:Gem::Dependency
|
|
36
35
|
name: bundler
|
|
37
36
|
requirement: !ruby/object:Gem::Requirement
|
|
38
37
|
requirements:
|
|
39
|
-
- - "
|
|
38
|
+
- - ">="
|
|
40
39
|
- !ruby/object:Gem::Version
|
|
41
40
|
version: '1.4'
|
|
41
|
+
- - "<"
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '3.0'
|
|
42
44
|
type: :development
|
|
43
45
|
prerelease: false
|
|
44
46
|
version_requirements: !ruby/object:Gem::Requirement
|
|
45
47
|
requirements:
|
|
46
|
-
- - "
|
|
48
|
+
- - ">="
|
|
47
49
|
- !ruby/object:Gem::Version
|
|
48
50
|
version: '1.4'
|
|
51
|
+
- - "<"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.0'
|
|
49
54
|
- !ruby/object:Gem::Dependency
|
|
50
55
|
name: rake
|
|
51
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -60,6 +65,20 @@ dependencies:
|
|
|
60
65
|
- - ">="
|
|
61
66
|
- !ruby/object:Gem::Version
|
|
62
67
|
version: '0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: minitest
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '5.6'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '5.6'
|
|
63
82
|
description: Define computation tasks with automatic caching and dependency tracking.
|
|
64
83
|
email:
|
|
65
84
|
- lars@greiz-reinsdorf.de
|
|
@@ -67,7 +86,9 @@ executables: []
|
|
|
67
86
|
extensions: []
|
|
68
87
|
extra_rdoc_files: []
|
|
69
88
|
files:
|
|
89
|
+
- ".github/workflows/ci.yml"
|
|
70
90
|
- ".gitignore"
|
|
91
|
+
- ".travis.yml"
|
|
71
92
|
- Gemfile
|
|
72
93
|
- LICENSE.txt
|
|
73
94
|
- README.md
|
|
@@ -75,11 +96,18 @@ files:
|
|
|
75
96
|
- computable.gemspec
|
|
76
97
|
- lib/computable.rb
|
|
77
98
|
- lib/computable/version.rb
|
|
99
|
+
- test/helper.rb
|
|
100
|
+
- test/test_backtrace.rb
|
|
78
101
|
- test/test_candy.rb
|
|
102
|
+
- test/test_debug.rb
|
|
103
|
+
- test/test_expired_recalc.rb
|
|
79
104
|
- test/test_format.rb
|
|
80
105
|
- test/test_freeze.rb
|
|
106
|
+
- test/test_inheritance.rb
|
|
81
107
|
- test/test_input_value.rb
|
|
108
|
+
- test/test_parallel.rb
|
|
82
109
|
- test/test_recursion_detection.rb
|
|
110
|
+
- test/test_unknown.rb
|
|
83
111
|
homepage: https://github.com/larskanis/computable
|
|
84
112
|
licenses:
|
|
85
113
|
- MIT
|
|
@@ -92,21 +120,27 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
92
120
|
requirements:
|
|
93
121
|
- - ">="
|
|
94
122
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '
|
|
123
|
+
version: '2.4'
|
|
96
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
125
|
requirements:
|
|
98
126
|
- - ">="
|
|
99
127
|
- !ruby/object:Gem::Version
|
|
100
128
|
version: '0'
|
|
101
129
|
requirements: []
|
|
102
|
-
|
|
103
|
-
rubygems_version: 2.2.0
|
|
130
|
+
rubygems_version: 3.3.7
|
|
104
131
|
signing_key:
|
|
105
132
|
specification_version: 4
|
|
106
133
|
summary: Define computation tasks with automatic caching and dependency tracking.
|
|
107
134
|
test_files:
|
|
135
|
+
- test/helper.rb
|
|
136
|
+
- test/test_backtrace.rb
|
|
108
137
|
- test/test_candy.rb
|
|
138
|
+
- test/test_debug.rb
|
|
139
|
+
- test/test_expired_recalc.rb
|
|
109
140
|
- test/test_format.rb
|
|
110
141
|
- test/test_freeze.rb
|
|
142
|
+
- test/test_inheritance.rb
|
|
111
143
|
- test/test_input_value.rb
|
|
144
|
+
- test/test_parallel.rb
|
|
112
145
|
- test/test_recursion_detection.rb
|
|
146
|
+
- test/test_unknown.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|