cdo 1.3.6 → 1.4.0
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 +4 -4
- data/gemspec +1 -1
- data/lib/cdo.rb +176 -73
- data/test/test_cdo.rb +233 -151
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2085dbda68e564e4449b83e69c840dcfc817b60adfe2010c597eeb795eade50d
|
4
|
+
data.tar.gz: 34c2082c1ed7de54cb889dde3cd8fe3d3e8e9ba7abd6f20f655bd6d5478e0905
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38f8689cf9674697d64c9958df35c85177e86c08d6becaa808cc13a56c460fcbce6a6f57c43ca376c129c531c26a1b09dbae38a07c1557e36ec1746f4365ca50
|
7
|
+
data.tar.gz: 71d527c68c39531e5b3c8007a05fa0b46989efa4b49da2a4e3738ced45d60137d32071bfe77a2da11273a6a716f0c9e17484a4676680825f480633289521f921
|
data/gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.unshift File.join(File.dirname(__FILE__),"..","lib")
|
|
3
3
|
|
4
4
|
spec = Gem::Specification.new do |s|
|
5
5
|
s.name = "cdo"
|
6
|
-
s.version = '1.
|
6
|
+
s.version = '1.4.0'
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.files = ["lib/cdo.rb","gemspec","LICENSE"]
|
9
9
|
s.test_file = "test/test_cdo.rb"
|
data/lib/cdo.rb
CHANGED
@@ -2,6 +2,8 @@ require 'pp'
|
|
2
2
|
require 'open3'
|
3
3
|
require 'logger'
|
4
4
|
require 'stringio'
|
5
|
+
require 'json'
|
6
|
+
require 'tempfile'
|
5
7
|
|
6
8
|
class Hash
|
7
9
|
alias :include? :has_key?
|
@@ -12,17 +14,25 @@ class Cdo
|
|
12
14
|
|
13
15
|
# hardcoded fallback list of output operators - from 1.8.0 there is an
|
14
16
|
# options for this: --operators_no_output
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
# this list works for cdo-1.6.4
|
18
|
+
NoOutputOperators = %w[cdiread cmor codetab conv_cmor_table diff diffc diffn
|
19
|
+
diffp diffv dump_cmor_table dumpmap filedes gmtcells gmtxyz gradsdes griddes
|
20
|
+
griddes2 gridverify info infoc infon infop infos infov map ncode ndate
|
21
|
+
ngridpoints ngrids nlevel nmon npar ntime nvar nyear output outputarr
|
22
|
+
outputbounds outputboundscpt outputcenter outputcenter2 outputcentercpt
|
23
|
+
outputext outputf outputfld outputint outputkey outputsrv outputtab outputtri
|
24
|
+
outputts outputvector outputvrml outputxyz pardes partab partab2 seinfo
|
25
|
+
seinfoc seinfon seinfop showattribute showatts showattsglob showattsvar
|
26
|
+
showcode showdate showformat showgrid showlevel showltype showmon showname
|
27
|
+
showparam showstdname showtime showtimestamp showunit showvar showyear sinfo
|
28
|
+
sinfoc sinfon sinfop sinfov spartab specinfo tinfo vardes vct vct2 verifygrid
|
29
|
+
vlist xinfon zaxisdes]
|
30
|
+
TwoOutputOperators = %w[trend samplegridicon mrotuv eoftime
|
31
|
+
eofspatial eof3dtime eof3dspatial eof3d eof complextorect complextopol]
|
32
|
+
MoreOutputOperators = %w[distgrid eofcoeff eofcoeff3d intyear scatter splitcode
|
33
|
+
splitday splitgrid splithour splitlevel splitmon splitname splitparam splitrec
|
34
|
+
splitseas splitsel splittabnum splitvar splityear splityearmon splitzaxis]
|
35
|
+
|
26
36
|
|
27
37
|
attr_accessor :cdo, :returnCdf, :forceOutput, :env, :debug, :logging, :logFile
|
28
38
|
attr_reader :operators, :filetypes
|
@@ -32,6 +42,7 @@ class Cdo
|
|
32
42
|
returnFalseOnError: false,
|
33
43
|
forceOutput: true,
|
34
44
|
env: {},
|
45
|
+
tempdir: Dir.tmpdir,
|
35
46
|
logging: false,
|
36
47
|
logFile: StringIO.new,
|
37
48
|
debug: false,
|
@@ -41,20 +52,30 @@ class Cdo
|
|
41
52
|
@cdo = ENV.has_key?('CDO') ? ENV['CDO'] : cdo
|
42
53
|
|
43
54
|
@operators = getOperators(@cdo)
|
55
|
+
@noOutputOperators = @operators.select {|op,io| 0 == io}.keys
|
56
|
+
|
44
57
|
@returnCdf = returnCdf
|
45
58
|
@forceOutput = forceOutput
|
46
59
|
@env = env
|
47
60
|
@debug = ENV.has_key?('DEBUG') ? true : debug
|
48
|
-
@noOutputOperators = getNoOuputOperators(@cdo) # operators without output files (write to stdout)
|
49
61
|
@returnNilOnError = returnNilOnError
|
50
62
|
|
51
|
-
@filetypes = getFiletypes
|
52
63
|
@returnFalseOnError = returnFalseOnError
|
53
64
|
|
65
|
+
@tempStore = CdoTempfileStore.new(tempdir)
|
54
66
|
@logging = logging
|
55
67
|
@logFile = logFile
|
56
68
|
@logger = Logger.new(@logFile,'a')
|
57
69
|
@logger.level = Logger::INFO
|
70
|
+
|
71
|
+
@config = getFeatures
|
72
|
+
|
73
|
+
# create methods to descibe what can be done with the binary
|
74
|
+
@config.each {|k,v|
|
75
|
+
self.class.send :define_method, k.tr('-','_') do
|
76
|
+
v
|
77
|
+
end
|
78
|
+
}
|
58
79
|
end
|
59
80
|
|
60
81
|
private # {{{
|
@@ -73,9 +94,13 @@ class Cdo
|
|
73
94
|
end
|
74
95
|
|
75
96
|
# collect the complete list of possible operators
|
76
|
-
def getOperators(path2cdo)
|
97
|
+
def getOperators(path2cdo) #{{{
|
98
|
+
operators = {}
|
99
|
+
|
100
|
+
# little side note: the option --operators_no_output works in 1.8.0 and
|
101
|
+
# 1.8.2, but not in 1.9.0, from 1.9.1 it works again
|
77
102
|
case
|
78
|
-
when version
|
103
|
+
when version < '1.7.2' then
|
79
104
|
cmd = path2cdo + ' 2>&1'
|
80
105
|
help = IO.popen(cmd).readlines.map {|l| l.chomp.lstrip}
|
81
106
|
if 5 >= help.size
|
@@ -84,38 +109,77 @@ class Cdo
|
|
84
109
|
exit
|
85
110
|
end
|
86
111
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
112
|
+
_operators = help[(help.index("Operators:")+1)..help.index(help.find {|v|
|
113
|
+
v =~ /CDO version/
|
114
|
+
}) - 2].join(' ').split
|
115
|
+
|
116
|
+
# build up operator inventory
|
117
|
+
# default is 1 output stream
|
118
|
+
_operators.each {|op| operators[op] = 1 }
|
119
|
+
operators.each {|op,_|
|
120
|
+
operators[op] = 0 if NoOutputOperators.include?(op)
|
121
|
+
operators[op] = 2 if TwoOutputOperators.include?(op)
|
122
|
+
operators[op] = -1 if MoreOutputOperators.include?(op)
|
123
|
+
}
|
124
|
+
|
125
|
+
when (version < '1.8.0' or '1.9.0' == version) then
|
126
|
+
cmd = "#{path2cdo} --operators"
|
127
|
+
_operators = IO.popen(cmd).readlines.map {|l| l.split(' ').first }
|
128
|
+
|
129
|
+
_operators.each {|op| operators[op] = 1 }
|
130
|
+
operators.each {|op,_|
|
131
|
+
operators[op] = 0 if NoOutputOperators.include?(op)
|
132
|
+
operators[op] = 2 if TwoOutputOperators.include?(op)
|
133
|
+
operators[op] = -1 if MoreOutputOperators.include?(op)
|
134
|
+
}
|
135
|
+
|
136
|
+
|
137
|
+
when version < '1.9.3' then
|
138
|
+
|
139
|
+
cmd = "#{path2cdo} --operators"
|
140
|
+
_operators = IO.popen(cmd).readlines.map {|l| l.split(' ').first }
|
141
|
+
cmd = "#{path2cdo} --operators_no_output"
|
142
|
+
_operatorsNoOutput = IO.popen(cmd).readlines.map {|l| l.split(' ').first }
|
143
|
+
|
144
|
+
# build up operator inventory
|
145
|
+
_operators.each {|op| operators[op] = 1 }
|
146
|
+
_operatorsNoOutput.each {|op| operators[op] = 0}
|
147
|
+
operators.each {|op,_|
|
148
|
+
operators[op] = 0 if _operatorsNoOutput.include?(op)
|
149
|
+
operators[op] = 2 if TwoOutputOperators.include?(op)
|
150
|
+
operators[op] = -1 if MoreOutputOperators.include?(op)
|
151
|
+
}
|
152
|
+
|
91
153
|
else
|
92
154
|
cmd = "#{path2cdo} --operators"
|
93
155
|
operators = {}
|
94
156
|
IO.popen(cmd).readlines.map {|line|
|
95
|
-
lineContent
|
96
|
-
name
|
97
|
-
iCounter, oCounter = lineContent[-1].
|
98
|
-
operators[name]
|
157
|
+
lineContent = line.chomp.split(' ')
|
158
|
+
name = lineContent[0]
|
159
|
+
iCounter, oCounter = lineContent[-1][1..-1].split('|')
|
160
|
+
operators[name] = oCounter.to_i
|
99
161
|
}
|
100
162
|
end
|
101
163
|
return operators
|
102
|
-
end
|
164
|
+
end #}}}
|
103
165
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
else
|
109
|
-
NoOutputOperators
|
110
|
-
end
|
111
|
-
end
|
166
|
+
# get meta-data about the CDO installation
|
167
|
+
def getFeatures
|
168
|
+
config = {}
|
169
|
+
config.default(false)
|
112
170
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
171
|
+
if version > '1.9.3' then
|
172
|
+
config.merge!(JSON.parse(IO.popen(@cdo + " --config all").read.chomp))
|
173
|
+
config.each {|k,v| config[k] = ('yes' == v) ? true : false}
|
174
|
+
else
|
175
|
+
_, _, stderr, _ = Open3.popen3(@cdo + " -V")
|
176
|
+
supported = stderr.readlines.map(&:chomp)
|
117
177
|
|
118
|
-
|
178
|
+
supported.grep(/(Filetypes)/)[0].split(':')[1].split.map(&:downcase).each {|ftype|
|
179
|
+
config["has-#{ftype}"] = true
|
180
|
+
}
|
181
|
+
end
|
182
|
+
config
|
119
183
|
end
|
120
184
|
|
121
185
|
# Execute the given cdo call and return all outputs
|
@@ -174,18 +238,25 @@ class Cdo
|
|
174
238
|
autoSplit: nil)
|
175
239
|
options = options.to_s
|
176
240
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
241
|
+
# switch netcdf output if data of filehandles are requested as output
|
242
|
+
options << ' -f nc' if ( \
|
243
|
+
( returnCdf ) or \
|
244
|
+
( not returnArray.nil? ) or \
|
245
|
+
( not returnMaArray.nil?) \
|
246
|
+
)
|
247
|
+
|
183
248
|
# setup basic operator execution command
|
184
249
|
cmd = "#{@cdo} -O #{options} -#{operatorName}#{operatorParameters} #{input} "
|
185
250
|
|
186
251
|
# use an empty hash for non-given environment
|
187
252
|
env = {} if env.nil?
|
188
253
|
|
254
|
+
# list of all output streams
|
255
|
+
outputs = []
|
256
|
+
|
257
|
+
# just collect given output(s)
|
258
|
+
outputs << output unless output.nil?
|
259
|
+
|
189
260
|
case output
|
190
261
|
when $stdout
|
191
262
|
retvals = _call(cmd,env)
|
@@ -204,13 +275,18 @@ class Cdo
|
|
204
275
|
end
|
205
276
|
end
|
206
277
|
else
|
278
|
+
# if operators was not called with output-forcing given, take the global switch
|
207
279
|
force = @forceOutput if force.nil?
|
280
|
+
|
208
281
|
if force or not File.exists?(output.to_s)
|
209
|
-
# create
|
210
|
-
output
|
282
|
+
# create tempfile(s) according to the number of output streams needed
|
283
|
+
# if output argument is missing
|
284
|
+
if output.nil? then
|
285
|
+
operators[operatorName].times { outputs << @tempStore.newFile}
|
286
|
+
end
|
211
287
|
|
212
288
|
#finalize the execution command
|
213
|
-
cmd << "#{
|
289
|
+
cmd << "#{outputs.join(' ')}"
|
214
290
|
|
215
291
|
retvals = _call(cmd,env)
|
216
292
|
|
@@ -222,20 +298,25 @@ class Cdo
|
|
222
298
|
end
|
223
299
|
end
|
224
300
|
else
|
225
|
-
warn "Use existing file '#{
|
301
|
+
warn "Use existing file(s) '#{outputs.join(' ')}'" if @debug
|
226
302
|
end
|
227
303
|
end
|
228
304
|
|
305
|
+
# return data arrays instead - this is for now limitted to fields of the
|
306
|
+
# first output file. number from the second need only one addition line, so
|
307
|
+
# I think this is sufficient
|
229
308
|
if not returnArray.nil?
|
230
|
-
readArray(
|
309
|
+
readArray(outputs[0],returnArray)
|
231
310
|
elsif not returnMaArray.nil?
|
232
|
-
readMaArray(
|
311
|
+
readMaArray(outputs[0],returnMaArray)
|
233
312
|
elsif returnCdf or @returnCdf
|
234
|
-
readCdf(
|
313
|
+
retval = outputs.map{|f| readCdf(f)}
|
314
|
+
return 1 == retval.size ? retval[0] : retval
|
235
315
|
elsif /^split/.match(operatorName)
|
236
316
|
Dir.glob("#{output}*")
|
237
317
|
else
|
238
|
-
|
318
|
+
return outputs[0] if outputs.size == 1
|
319
|
+
return outputs
|
239
320
|
end
|
240
321
|
end
|
241
322
|
|
@@ -295,17 +376,20 @@ class Cdo
|
|
295
376
|
return File.open(@logFile).readlines
|
296
377
|
end
|
297
378
|
end
|
379
|
+
|
298
380
|
# print the loggin messaged
|
299
381
|
def showLog
|
300
382
|
puts collectLogs
|
301
383
|
end
|
302
384
|
|
385
|
+
# check if the CDO binary is present and works
|
303
386
|
def hasCdo(path=@cdo)
|
304
387
|
executable = system("#{path} -V >/dev/null 2>&1")
|
305
388
|
fullpath = File.exists?(path) and File.executable?(path)
|
306
389
|
|
307
390
|
return (executable or fullpath)
|
308
391
|
end
|
392
|
+
|
309
393
|
# check if cdo backend is working
|
310
394
|
def check
|
311
395
|
return false unless hasCdo
|
@@ -316,6 +400,7 @@ class Cdo
|
|
316
400
|
return true
|
317
401
|
end
|
318
402
|
|
403
|
+
# return CDO version string
|
319
404
|
def version(verbose=false)
|
320
405
|
info = IO.popen("#{@cdo} -V 2>&1").readlines
|
321
406
|
if verbose then
|
@@ -359,8 +444,9 @@ class Cdo
|
|
359
444
|
end
|
360
445
|
end
|
361
446
|
|
362
|
-
|
363
|
-
|
447
|
+
# remove tempfiles created from this or previous runs
|
448
|
+
def cleanTempDir
|
449
|
+
@tempStore.cleanTempDir
|
364
450
|
end
|
365
451
|
# }}}
|
366
452
|
|
@@ -392,33 +478,50 @@ class Cdo
|
|
392
478
|
|
393
479
|
end
|
394
480
|
#
|
395
|
-
# Helper module for easy temp file handling
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
481
|
+
# Helper module for easy temp file handling {{{
|
482
|
+
class CdoTempfileStore
|
483
|
+
# base for persitent temp files - just for debugging
|
484
|
+
N = 10000000
|
485
|
+
|
486
|
+
def initialize(dir=Dir.tmpdir)
|
487
|
+
@dir = dir
|
488
|
+
@tag = 'Cdorb'
|
489
|
+
@persistent_tempfiles = false
|
490
|
+
|
491
|
+
# storage for filenames in order to prevent too early removement
|
492
|
+
@_tempfiles = []
|
493
|
+
|
494
|
+
# make sure the tempdir ie really there
|
495
|
+
Dir.mkdir(@dir) unless Dir.exists?(@dir)
|
404
496
|
end
|
405
497
|
|
406
|
-
def
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
498
|
+
def setPersist(value)
|
499
|
+
@persistent_tempfiles = value
|
500
|
+
end
|
501
|
+
|
502
|
+
def newFile
|
503
|
+
unless @persistent_tempfiles
|
504
|
+
t = Tempfile.new(@tag,@dir)
|
505
|
+
@_tempfiles << t
|
506
|
+
@_tempfiles << t.path
|
411
507
|
t.path
|
412
508
|
else
|
413
|
-
t = "_"+rand(
|
414
|
-
|
509
|
+
t = "_"+rand(N).to_s
|
510
|
+
@_tempfiles << t
|
415
511
|
t
|
416
512
|
end
|
417
513
|
end
|
418
514
|
|
419
|
-
def
|
420
|
-
|
515
|
+
def showFiles
|
516
|
+
@_tempfiles.each {|f| print(f+" ") if f.kind_of? String}
|
421
517
|
end
|
422
|
-
end
|
423
518
|
|
424
|
-
|
519
|
+
def cleanTempDir
|
520
|
+
# filter by name, realfile and ownership
|
521
|
+
Dir.entries(@dir).map {|f| "#@dir/#{f}"}.find_all {|file|
|
522
|
+
File.file?(file) and File.owned?(file) and file.include?(@tag)
|
523
|
+
}.each {|f| File.unlink(f)}
|
524
|
+
end
|
525
|
+
end #}}}
|
526
|
+
|
527
|
+
# vim: fdm=marker
|
data/test/test_cdo.rb
CHANGED
@@ -18,6 +18,7 @@ class TestCdo < Minitest::Test
|
|
18
18
|
|
19
19
|
def setup
|
20
20
|
@cdo = Cdo.new
|
21
|
+
@tempStore = CdoTempfileStore.new
|
21
22
|
end
|
22
23
|
|
23
24
|
def test_cdo
|
@@ -82,7 +83,7 @@ class TestCdo < Minitest::Test
|
|
82
83
|
end
|
83
84
|
def test_CDO_version
|
84
85
|
assert("1.4.3.1" < @cdo.version,"Version too low: #{@cdo.version}")
|
85
|
-
assert("1.
|
86
|
+
assert("1.6.3" < @cdo.version,"Version too low: #{@cdo.version}")
|
86
87
|
assert("3.0" > @cdo.version,"Version too high: #{@cdo.version}")
|
87
88
|
end
|
88
89
|
def test_args
|
@@ -264,23 +265,25 @@ class TestCdo < Minitest::Test
|
|
264
265
|
assert_nil(@cdo.diffv(:input => ["-stdatm,0",fileB]).last)
|
265
266
|
end
|
266
267
|
|
267
|
-
def
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
assert_raises ArgumentError do
|
272
|
-
@cdo.filetypes("foo")
|
273
|
-
end
|
268
|
+
def test_features
|
269
|
+
assert_equal(true,@cdo.has_srv)
|
270
|
+
assert_equal(true,@cdo.has_ieg)
|
271
|
+
assert_equal(true,@cdo.has_ext)
|
274
272
|
end
|
275
273
|
|
276
274
|
def test_noOutputOps
|
277
|
-
|
278
|
-
|
275
|
+
operators = @cdo.operators
|
276
|
+
%w[griddes griddes2 gridverify info infoc infon infop infos infov map
|
279
277
|
outputarr outputbounds outputboundscpt outputcenter outputcenter2
|
280
278
|
outputcentercpt outputext outputf outputfld outputint outputkey outputsrv
|
281
279
|
outputtab outputtri outputts outputvector outputvrml outputxyz pardes partab].each {|op|
|
282
|
-
assert(
|
280
|
+
assert(operators.include?(op),"Operator '#{op}' cannot be found!")
|
281
|
+
assert_equal(0,operators[op],"Operator '#{op}' has a non-zero output counter!")
|
283
282
|
}
|
283
|
+
# just a rought estimation
|
284
|
+
opsCounf = @cdo.operators.select {|_,c| 0 == c}.size
|
285
|
+
assert(opsCounf > 50)
|
286
|
+
assert(opsCounf < 200)
|
284
287
|
end
|
285
288
|
|
286
289
|
def test_output_set_to_nil
|
@@ -311,59 +314,226 @@ class TestCdo < Minitest::Test
|
|
311
314
|
}
|
312
315
|
end
|
313
316
|
|
317
|
+
def test_operators_with_multiple_output_files
|
318
|
+
assert_equal(1,@cdo.operators['topo'],'wrong output counter for "topo"')
|
319
|
+
assert_equal(0,@cdo.operators['sinfo'],'wrong output counter for "sinfo"')
|
320
|
+
assert_equal(0,@cdo.operators['ngridpoints'],'wrong output counter for "sinfo"') if @cdo.version > '1.6.4'
|
321
|
+
|
322
|
+
assert_equal(-1,@cdo.operators['splitsel'],'wrong output counter for "splitsel"')
|
323
|
+
assert_equal(2,@cdo.operators['trend'],'wrong output counter for "trend"')
|
324
|
+
# create input for eof
|
325
|
+
#
|
326
|
+
# check automatic generation ot two tempfiles
|
327
|
+
aFile, bFile = @cdo.trend(input: "-addc,7 -mulc,44 -for,1,100")
|
328
|
+
assert_equal(51.0,@cdo.outputkey('value',input: aFile)[-1].to_f)
|
329
|
+
assert_equal(44.0,@cdo.outputkey('value',input: bFile)[-1].to_f)
|
330
|
+
# check usage of 'returnCdf' with these operators
|
331
|
+
aFile, bFile = @cdo.trend(input: "-addc,7 -mulc,44 -for,1,100",returnCdf: true)
|
332
|
+
assert_equal(51.0, aFile.var('for').get.flatten[0],"got wrong value from cdf handle")
|
333
|
+
assert_equal(44.0, bFile.var('for').get.flatten[0],"got wrong value from cdf handle")
|
334
|
+
|
335
|
+
avar = @cdo.trend(input: "-addc,7 -mulc,44 -for,1,100",returnArray: 'for').flatten[0]
|
336
|
+
assert_equal(51.0, avar,"got wrong value from narray")
|
337
|
+
end
|
338
|
+
def test_tempdir
|
339
|
+
# manual set path
|
340
|
+
tag = 'tempRb'
|
341
|
+
tempPath = Dir.pwd+'/'+tag
|
342
|
+
pp Dir.glob("#{tempPath}/*").size
|
343
|
+
assert_equal(0,Dir.glob("#{tempPath}/*").size)
|
344
|
+
cdo = Cdo.new(tempdir: tempPath)
|
345
|
+
cdo.topo('r10x10',options: '-f nc')
|
346
|
+
assert_equal(1,Dir.glob("#{tempPath}/*").size)
|
347
|
+
cdo.topo('r10x10',options: '-f nc')
|
348
|
+
cdo.topo('r10x10',options: '-f nc')
|
349
|
+
assert_equal(3,Dir.glob("#{tempPath}/*").size)
|
350
|
+
cdo.topo('r10x10',options: '-f nc')
|
351
|
+
cdo.topo('r10x10',options: '-f nc')
|
352
|
+
assert_equal(5,Dir.glob("#{tempPath}/*").size)
|
353
|
+
cdo.cleanTempDir
|
354
|
+
assert_equal(0,Dir.glob("#{tempPath}/*").size)
|
355
|
+
end
|
356
|
+
|
357
|
+
def test_returnArray
|
358
|
+
temperature = @cdo.stdatm(0,:returnCdf => true).var('T').get.flatten[0]
|
359
|
+
assert(1.7 < temperature,"Temperature to low!")
|
360
|
+
assert_raises ArgumentError do
|
361
|
+
@cdo.stdatm(0,:returnArray => 'TT')
|
362
|
+
end
|
363
|
+
temperature = @cdo.stdatm(0,:returnArray => 'T')
|
364
|
+
assert_equal(288.0,temperature.flatten[0])
|
365
|
+
pressure = @cdo.stdatm(0,1000,:options => '-b F64',:returnArray => 'P')
|
366
|
+
assert_equal("1013.25 898.543456035875",pressure.flatten.to_a.join(' '))
|
367
|
+
end
|
368
|
+
def test_returnMaArray
|
369
|
+
@cdo.debug = @@debug
|
370
|
+
topo = @cdo.topo(:returnMaArray => 'topo')
|
371
|
+
assert_equal(-1890.0,topo.mean.round)
|
372
|
+
bathy = @cdo.setrtomiss(0,10000,
|
373
|
+
:input => @cdo.topo(:options => '-f nc'),:returnMaArray => 'topo')
|
374
|
+
assert_equal(-3386.0,bathy.mean.round)
|
375
|
+
oro = @cdo.setrtomiss(-10000,0,
|
376
|
+
:input => @cdo.topo(:options => '-f nc'),:returnMaArray => 'topo')
|
377
|
+
assert_equal(1142.0,oro.mean.round)
|
378
|
+
bathy = @cdo.remapnn('r2x2',:input => @cdo.topo(:options => '-f nc'), :returnMaArray => 'topo')
|
379
|
+
assert_equal(-4298.0,bathy[0,0])
|
380
|
+
assert_equal(-2669.0,bathy[1,0])
|
381
|
+
ta = @cdo.remapnn('r2x2',:input => @cdo.topo(:options => '-f nc'))
|
382
|
+
tb = @cdo.subc(-2669.0,:input => ta)
|
383
|
+
withMask = @cdo.div(:input => ta+" "+tb,:returnMaArray => 'topo')
|
384
|
+
assert(-8.0e+33 > withMask[1,0])
|
385
|
+
assert(0 < withMask[0,0])
|
386
|
+
assert(0 < withMask[0,1])
|
387
|
+
assert(0 < withMask[1,1])
|
388
|
+
end
|
389
|
+
def test_combine
|
390
|
+
ofile0, ofile1 = @tempStore.newFile, @tempStore.newFile
|
391
|
+
@cdo.fldsum(:input => @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:options => "-f nc"),:output => ofile0)
|
392
|
+
@cdo.fldsum(:input => "-stdatm,25,100,250,500,875,1400,2100,3000,4000,5000",:options => "-f nc",:output => ofile1)
|
393
|
+
@cdo.returnCdf = true
|
394
|
+
@tempStore.showFiles
|
395
|
+
diff = @cdo.sub(:input => [ofile0,ofile1].join(' ')).var('T').get
|
396
|
+
assert_equal(0.0,diff.min)
|
397
|
+
assert_equal(0.0,diff.max)
|
398
|
+
@cdo.returnCdf = false
|
399
|
+
end
|
400
|
+
|
401
|
+
def test_returnCdf
|
402
|
+
ofile = rand(0xfffff).to_s + '_test_returnCdf.nc'
|
403
|
+
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
404
|
+
assert_equal(ofile,vals)
|
405
|
+
@cdo.returnCdf = true
|
406
|
+
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
407
|
+
assert_equal(["lon","lat","level","P","T"],vals.var_names)
|
408
|
+
assert_equal(276,vals.var("T").get.flatten.mean.floor)
|
409
|
+
@cdo.returnCdf = false
|
410
|
+
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
411
|
+
assert_equal(ofile,vals)
|
412
|
+
FileUtils.rm(ofile)
|
413
|
+
end
|
414
|
+
def test_simple_returnCdf
|
415
|
+
ofile0, ofile1 = @tempStore.newFile, @tempStore.newFile
|
416
|
+
sum = @cdo.fldsum(:input => @cdo.stdatm(0,:options => "-f nc"),
|
417
|
+
:returnCdf => true).var("P").get
|
418
|
+
assert_equal(1013.25,sum.min)
|
419
|
+
sum = @cdo.fldsum(:input => @cdo.stdatm(0,:options => "-f nc"),:output => ofile0)
|
420
|
+
assert_equal(ofile0,sum)
|
421
|
+
test_returnCdf
|
422
|
+
end
|
423
|
+
def test_readCdf
|
424
|
+
input = "-settunits,days -setyear,2000 -for,1,4"
|
425
|
+
cdfFile = @cdo.copy(:options =>"-f nc",:input=>input)
|
426
|
+
cdf = @cdo.readCdf(cdfFile)
|
427
|
+
assert_empty(['lon','lat','for','time'] - cdf.var_names)
|
428
|
+
end
|
429
|
+
def test_combine
|
430
|
+
ofile0, ofile1 = @tempStore.newFile, @tempStore.newFile
|
431
|
+
@cdo.fldsum(:input => @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:options => "-f nc"),:output => ofile0)
|
432
|
+
@cdo.fldsum(:input => "-stdatm,25,100,250,500,875,1400,2100,3000,4000,5000",:options => "-f nc",:output => ofile1)
|
433
|
+
@cdo.returnCdf = true
|
434
|
+
@tempStore.showFiles
|
435
|
+
diff = @cdo.sub(:input => [ofile0,ofile1].join(' ')).var('T').get
|
436
|
+
assert_equal(0.0,diff.min)
|
437
|
+
assert_equal(0.0,diff.max)
|
438
|
+
@cdo.returnCdf = false
|
439
|
+
end
|
440
|
+
def test_readArray
|
441
|
+
@cdo.debug = @@debug
|
442
|
+
assert_equal([40,80],@cdo.readArray(@cdo.sellonlatbox(-10,10,-20,20,:input => '-topo',:options => '-f nc'), 'topo').shape)
|
443
|
+
end
|
444
|
+
def test_env
|
445
|
+
oTag = 'test_env_with_splitlevel_'
|
446
|
+
levels = [0,10,100]
|
447
|
+
expected = levels.map {|l| "test_env_with_splitlevel_000#{l.to_s.rjust(3,'0')}"}
|
448
|
+
# clean up first
|
449
|
+
rm(Dir.glob(oTag+'*'))
|
450
|
+
|
451
|
+
# oType = grb (default)
|
452
|
+
ofiles = expected.map {|f| f += '.grb'}
|
453
|
+
@cdo.splitlevel(input: "-stdatm,0,10,100",output: oTag)
|
454
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
455
|
+
rm(ofiles)
|
456
|
+
|
457
|
+
# oType = nc, from cdo options
|
458
|
+
ofiles = expected.map {|f| f += '.nc'}
|
459
|
+
@cdo.splitlevel(input: "-stdatm,0,10,100",output: oTag,options: '-f nc')
|
460
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
461
|
+
rm(ofiles)
|
462
|
+
|
463
|
+
# oType = nc, from input type
|
464
|
+
ofiles = expected.map {|f| f += '.nc'}
|
465
|
+
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag)
|
466
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
467
|
+
rm(ofiles)
|
468
|
+
|
469
|
+
# oType = nc, from input ENV
|
470
|
+
ofiles = expected.map {|f| f += '.nc2'}
|
471
|
+
@cdo.env = {'CDO_FILE_SUFFIX' => '.nc2'}
|
472
|
+
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag)
|
473
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
474
|
+
rm(ofiles)
|
475
|
+
|
476
|
+
# oType = nc, from input ENV setting for each call
|
477
|
+
ofiles = expected.map {|f| f += '.nc4'}
|
478
|
+
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag,env: {'CDO_FILE_SUFFIX' => '.nc4'})
|
479
|
+
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
480
|
+
rm(ofiles)
|
481
|
+
end
|
482
|
+
def test_log
|
483
|
+
cmd = '-fldmean -mul -random,r20x20 -topo,r20x20'
|
484
|
+
# logging without a real file
|
485
|
+
@cdo = Cdo.new( returnNilOnError: true)
|
486
|
+
@cdo.debug = false
|
487
|
+
@cdo.logging = true
|
488
|
+
@cdo.topo
|
489
|
+
@cdo.temp
|
490
|
+
@cdo.sinfov(input: cmd)
|
491
|
+
puts @cdo.showLog
|
492
|
+
@cdo.sinfov(input: '-top')
|
493
|
+
@cdo.topo
|
494
|
+
puts @cdo.showLog
|
495
|
+
# use a use definded file for looging
|
496
|
+
@cdo = Cdo.new(logFile: 'test.log',logging: true, returnNilOnError: true)
|
497
|
+
@cdo.topo
|
498
|
+
@cdo.temp
|
499
|
+
@cdo.sinfov(input: cmd)
|
500
|
+
puts @cdo.showLog
|
501
|
+
end
|
314
502
|
if @@maintainermode then
|
315
503
|
require 'unifiedPlot'
|
316
504
|
|
505
|
+
def test_system_tempdir
|
506
|
+
# automatic path
|
507
|
+
tempPath = Dir.tmpdir
|
508
|
+
tag = 'Cdorb'
|
509
|
+
pattern = "#{tempPath}/#{tag}*"
|
510
|
+
cdo = Cdo.new
|
511
|
+
assert_equal(0,Dir.glob(pattern).size)
|
512
|
+
cdo.topo('r10x10')
|
513
|
+
assert_equal(1,Dir.glob(pattern).size)
|
514
|
+
cdo.topo('r10x10')
|
515
|
+
cdo.topo('r10x10',options: '-f nc')
|
516
|
+
assert_equal(3,Dir.glob(pattern).size)
|
517
|
+
cdo.topo('r10x10',options: '-f nc')
|
518
|
+
cdo.topo('r10x10',options: '-f nc')
|
519
|
+
cdo.topo('r10x10',options: '-f nc')
|
520
|
+
cdo.topo('r10x10')
|
521
|
+
cdo.topo('r10x10',options: '-f nc')
|
522
|
+
cdo.topo('r10x10',options: '-f nc')
|
523
|
+
cdo.topo('r10x10',options: '-f nc')
|
524
|
+
cdo.topo('r10x10')
|
525
|
+
cdo.topo('r10x10',options: '-f nc')
|
526
|
+
assert_equal(12,Dir.glob(pattern).size)
|
527
|
+
cdo.cleanTempDir()
|
528
|
+
assert_equal(0,Dir.glob(pattern).size)
|
529
|
+
end
|
317
530
|
def test_longChain
|
318
531
|
ifile = "-enlarge,global_0.3 -settaxis,2000-01-01 -expr,'t=sin(for*3.141529/180.0)' -for,1,10"
|
319
532
|
t = @cdo.fldmax(input: "-div -sub -timmean -seltimestep,2,3 #{ifile} -seltimestep,1 #{ifile} -gridarea #{ifile}",returnArray: "t")
|
320
533
|
assert_equal(8.981299259858133e-09,t[0])
|
321
534
|
end
|
322
|
-
def test_returnArray
|
323
|
-
temperature = @cdo.stdatm(0,:options => '-f nc',:returnCdf => true).var('T').get.flatten[0]
|
324
|
-
assert_raises ArgumentError do
|
325
|
-
@cdo.stdatm(0,:options => '-f nc',:returnArray => 'TT')
|
326
|
-
end
|
327
|
-
temperature = @cdo.stdatm(0,:options => '-f nc',:returnArray => 'T')
|
328
|
-
assert_equal(288.0,temperature.flatten[0])
|
329
|
-
pressure = @cdo.stdatm(0,1000,:options => '-f nc -b F64',:returnArray => 'P')
|
330
|
-
assert_equal("1013.25 898.543456035875",pressure.flatten.to_a.join(' '))
|
331
|
-
end
|
332
|
-
def test_returnMaArray
|
333
|
-
@cdo.debug = @@debug
|
334
|
-
topo = @cdo.topo(:options => '-f nc',:returnMaArray => 'topo')
|
335
|
-
assert_equal(-1890.0,topo.mean.round)
|
336
|
-
bathy = @cdo.setrtomiss(0,10000,
|
337
|
-
:input => @cdo.topo(:options => '-f nc'),:returnMaArray => 'topo')
|
338
|
-
assert_equal(-3386.0,bathy.mean.round)
|
339
|
-
oro = @cdo.setrtomiss(-10000,0,
|
340
|
-
:input => @cdo.topo(:options => '-f nc'),:returnMaArray => 'topo')
|
341
|
-
assert_equal(1142.0,oro.mean.round)
|
342
|
-
bathy = @cdo.remapnn('r2x2',:input => @cdo.topo(:options => '-f nc'), :returnMaArray => 'topo')
|
343
|
-
assert_equal(-4298.0,bathy[0,0])
|
344
|
-
assert_equal(-2669.0,bathy[1,0])
|
345
|
-
ta = @cdo.remapnn('r2x2',:input => @cdo.topo(:options => '-f nc'))
|
346
|
-
tb = @cdo.subc(-2669.0,:input => ta)
|
347
|
-
withMask = @cdo.div(:input => ta+" "+tb,:returnMaArray => 'topo')
|
348
|
-
assert(-8.0e+33 > withMask[1,0])
|
349
|
-
assert(0 < withMask[0,0])
|
350
|
-
assert(0 < withMask[0,1])
|
351
|
-
assert(0 < withMask[1,1])
|
352
|
-
end
|
353
|
-
def test_combine
|
354
|
-
ofile0, ofile1 = MyTempfile.path, MyTempfile.path
|
355
|
-
@cdo.fldsum(:input => @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:options => "-f nc"),:output => ofile0)
|
356
|
-
@cdo.fldsum(:input => "-stdatm,25,100,250,500,875,1400,2100,3000,4000,5000",:options => "-f nc",:output => ofile1)
|
357
|
-
@cdo.returnCdf = true
|
358
|
-
MyTempfile.showFiles
|
359
|
-
diff = @cdo.sub(:input => [ofile0,ofile1].join(' ')).var('T').get
|
360
|
-
assert_equal(0.0,diff.min)
|
361
|
-
assert_equal(0.0,diff.max)
|
362
|
-
@cdo.returnCdf = false
|
363
|
-
end
|
364
|
-
|
365
535
|
def test_tempfile
|
366
|
-
ofile0, ofile1 =
|
536
|
+
ofile0, ofile1 = @tempStore.newFile, @tempStore.newFile
|
367
537
|
assert(ofile0 != ofile1, "Found equal tempfiles!")
|
368
538
|
# Tempfile should not disappeare even if the GC was started
|
369
539
|
puts ofile0
|
@@ -371,35 +541,6 @@ class TestCdo < Minitest::Test
|
|
371
541
|
GC.start
|
372
542
|
assert(File.exist?(ofile0))
|
373
543
|
end
|
374
|
-
|
375
|
-
def test_returnCdf
|
376
|
-
ofile = rand(0xfffff).to_s + '_test_returnCdf.nc'
|
377
|
-
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
378
|
-
assert_equal(ofile,vals)
|
379
|
-
@cdo.returnCdf = true
|
380
|
-
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
381
|
-
assert_equal(["lon","lat","level","P","T"],vals.var_names)
|
382
|
-
assert_equal(276,vals.var("T").get.flatten.mean.floor)
|
383
|
-
@cdo.returnCdf = false
|
384
|
-
vals = @cdo.stdatm(25,100,250,500,875,1400,2100,3000,4000,5000,:output => ofile,:options => "-f nc")
|
385
|
-
assert_equal(ofile,vals)
|
386
|
-
FileUtils.rm(ofile)
|
387
|
-
end
|
388
|
-
def test_simple_returnCdf
|
389
|
-
ofile0, ofile1 = MyTempfile.path, MyTempfile.path
|
390
|
-
sum = @cdo.fldsum(:input => @cdo.stdatm(0,:options => "-f nc"),
|
391
|
-
:returnCdf => true).var("P").get
|
392
|
-
assert_equal(1013.25,sum.min)
|
393
|
-
sum = @cdo.fldsum(:input => @cdo.stdatm(0,:options => "-f nc"),:output => ofile0)
|
394
|
-
assert_equal(ofile0,sum)
|
395
|
-
test_returnCdf
|
396
|
-
end
|
397
|
-
def test_readCdf
|
398
|
-
input = "-settunits,days -setyear,2000 -for,1,4"
|
399
|
-
cdfFile = @cdo.copy(:options =>"-f nc",:input=>input)
|
400
|
-
cdf = @cdo.readCdf(cdfFile)
|
401
|
-
assert_empty(['lon','lat','for','time'] - cdf.var_names)
|
402
|
-
end
|
403
544
|
def test_selIndexListFromIcon
|
404
545
|
input = "~/data/icon/oce.nc"
|
405
546
|
end
|
@@ -456,71 +597,12 @@ class TestCdo < Minitest::Test
|
|
456
597
|
{:y => vFm1s,:style => 'points',:title => 'fillmiss2'}],
|
457
598
|
plotConf: {:yrange => '[0:1]'},title: 'r10x1') if @@show
|
458
599
|
end
|
459
|
-
end
|
460
|
-
|
461
|
-
def test_env
|
462
|
-
oTag = 'test_env_with_splitlevel_'
|
463
|
-
levels = [0,10,100]
|
464
|
-
expected = levels.map {|l| "test_env_with_splitlevel_000#{l.to_s.rjust(3,'0')}"}
|
465
|
-
# clean up first
|
466
|
-
rm(Dir.glob(oTag+'*'))
|
467
|
-
|
468
|
-
# oType = grb (default)
|
469
|
-
ofiles = expected.map {|f| f += '.grb'}
|
470
|
-
@cdo.splitlevel(input: "-stdatm,0,10,100",output: oTag)
|
471
|
-
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
472
|
-
rm(ofiles)
|
473
|
-
|
474
|
-
# oType = nc, from cdo options
|
475
|
-
ofiles = expected.map {|f| f += '.nc'}
|
476
|
-
@cdo.splitlevel(input: "-stdatm,0,10,100",output: oTag,options: '-f nc')
|
477
|
-
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
478
|
-
rm(ofiles)
|
479
|
-
|
480
|
-
# oType = nc, from input type
|
481
|
-
ofiles = expected.map {|f| f += '.nc'}
|
482
|
-
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag)
|
483
|
-
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
484
|
-
rm(ofiles)
|
485
|
-
|
486
|
-
# oType = nc, from input ENV
|
487
|
-
ofiles = expected.map {|f| f += '.nc2'}
|
488
|
-
@cdo.env = {'CDO_FILE_SUFFIX' => '.nc2'}
|
489
|
-
@cdo.splitlevel(input: @cdo.stdatm(0,10,100,options: '-f nc'),output: oTag)
|
490
|
-
assert_equal(ofiles,Dir.glob(oTag+'*').sort)
|
491
|
-
rm(ofiles)
|
492
600
|
|
493
|
-
#
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
end
|
499
|
-
def test_log
|
500
|
-
cmd = '-fldmean -mul -random,r20x20 -topo,r20x20'
|
501
|
-
# logging without a real file
|
502
|
-
@cdo = Cdo.new( returnNilOnError: true)
|
503
|
-
@cdo.debug = false
|
504
|
-
@cdo.logging = true
|
505
|
-
@cdo.topo
|
506
|
-
@cdo.temp
|
507
|
-
@cdo.sinfov(input: cmd)
|
508
|
-
puts @cdo.showLog
|
509
|
-
@cdo.sinfov(input: '-top')
|
510
|
-
@cdo.topo
|
511
|
-
puts @cdo.showLog
|
512
|
-
# use a use definded file for looging
|
513
|
-
@cdo = Cdo.new(logFile: 'test.log',logging: true, returnNilOnError: true)
|
514
|
-
@cdo.topo
|
515
|
-
@cdo.temp
|
516
|
-
@cdo.sinfov(input: cmd)
|
517
|
-
puts @cdo.showLog
|
518
|
-
end
|
519
|
-
def test_noOutputOps
|
520
|
-
c = Cdo.new
|
521
|
-
opsCounf = c.noOutputOps.size
|
522
|
-
assert(opsCounf > 50)
|
523
|
-
assert(opsCounf < 200)
|
601
|
+
# opendap test - broken since 1.9.0
|
602
|
+
def test_opendap
|
603
|
+
ifile = 'https://www.esrl.noaa.gov/psd/thredds/dodsC/Datasets/cpc_global_precip/precip.1979.nc'
|
604
|
+
@cdo.sinfov(input: ifile)
|
605
|
+
end if @@debug
|
524
606
|
end
|
525
607
|
end
|
526
608
|
|
@@ -584,10 +666,10 @@ end
|
|
584
666
|
# @cdo.expr("'t=#{TEMP_EXPR['geopotheight']}'", :input => ifile, :output => tempFile)
|
585
667
|
#
|
586
668
|
#
|
587
|
-
# # TIPS: I often work with temporary files and for getting rid of handling them manually the
|
669
|
+
# # TIPS: I often work with temporary files and for getting rid of handling them manually the CdoTempfileStore module can be used:
|
588
670
|
# # Simply include the following methods into you scripts and use tfile for any temporary variable
|
589
671
|
# def tfile
|
590
|
-
#
|
672
|
+
# CdoTempfileStore.path
|
591
673
|
# end
|
592
674
|
# # As an example, the computation of simple atmospherric density could look like
|
593
675
|
# presFile, tempFile = tfile, tfile
|
@@ -596,6 +678,6 @@ end
|
|
596
678
|
# @cdo.chainCall("setname,#{rho} -divc,#{C_R} -div",in: [presFile,tempFile].join(' '), out: densityFile)
|
597
679
|
#
|
598
680
|
# # For debugging, it is helpfull, to avoid the automatic cleanup at the end of the scripts:
|
599
|
-
#
|
681
|
+
# CdoTempfileStore.setPersist(true)
|
600
682
|
# # creates randomly names files. Switch on debugging with
|
601
683
|
# @cdo.Debug = true
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cdo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ralf Mueller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: unifiedPlot
|