cdo 1.2.7 → 1.3.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.
Files changed (6) hide show
  1. data/gemspec +2 -2
  2. data/lib/cdo.rb +204 -279
  3. data/lib/cdo_lib.rb +415 -0
  4. data/test/test_cdo.rb +180 -181
  5. metadata +3 -3
  6. data/lib/cdo_oo.rb +0 -301
data/gemspec CHANGED
@@ -3,9 +3,9 @@ $:.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.2.7'
6
+ s.version = '1.3.0'
7
7
  s.platform = Gem::Platform::RUBY
8
- s.files = ["lib/cdo.rb","lib/cdo_oo.rb"] + ["gemspec","LICENSE"]
8
+ s.files = ["lib/cdo.rb","lib/cdo_lib.rb"] + ["gemspec","LICENSE"]
9
9
  s.test_file = "test/test_cdo.rb"
10
10
  s.description = "Easy access to the Climate Data operators"
11
11
  s.summary = "Easy access to the Climate Data operators"
data/lib/cdo.rb CHANGED
@@ -3,116 +3,97 @@ require 'open3'
3
3
  require 'logger'
4
4
  require 'stringio'
5
5
 
6
- # Copyright (C) 2011-2013 Ralf Mueller, ralf.mueller@zmaw.de
7
- # See COPYING file for copying and redistribution conditions.
8
- #
9
- # This program is free software; you can redistribute it and/or modify
10
- # it under the terms of the GNU General Public License as published by
11
- # the Free Software Foundation; version 2 of the License.
12
- #
13
- # This program is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
-
18
- # ==============================================================================
19
- # CDO calling mechnism
20
- module Cdo
21
-
22
- VERSION = "1.2.5"
23
- @@file = StringIO.new
24
-
25
- State = {
26
- :debug => false,
27
- :returnCdf => false,
28
- :operators => [],
29
- :forceOutput => true,
30
- :env => {},
31
- :log => false,
32
- :logger => Logger.new(@@file),
33
- }
34
- State[:debug] = ENV.has_key?('DEBUG')
35
- State[:logger].formatter = proc do |serverity, time, progname, msg|
36
- msg
6
+ class Cdo
7
+ OutputOperatorsPattern = /(diff|info|output|griddes|zaxisdes|show|ncode|ndate|nlevel|nmon|nvar|nyear|ntime|npar|gradsdes|pardes)/
8
+
9
+ attr_accessor :cdo, :returnCdf, :forceOutput, :env, :debug, :logging, :logFile
10
+ attr_reader :operators, :filetypes
11
+
12
+ def initialize(cdo: 'cdo',
13
+ returnCdf: false,
14
+ returnFalseOnError: false,
15
+ forceOutput: true,
16
+ env: {},
17
+ logging: false,
18
+ logFile: StringIO.new,
19
+ debug: false,
20
+ returnNilOnError: false)
21
+
22
+ # setup path to cdo executable
23
+ @cdo = ENV.has_key?('CDO') ? ENV['CDO'] : cdo
24
+
25
+ @operators = getOperators(binary=@cdo)
26
+ @returnCdf = returnCdf
27
+ @forceOutput = forceOutput
28
+ @env = env
29
+ @debug = ENV.has_key?('DEBUG') ? true : debug
30
+ @returnNilOnError = returnNilOnError
31
+
32
+ @filetypes = getFiletypes
33
+ @returnFalseOnError = returnFalseOnError
34
+
35
+ @logging = logging
36
+ @logFile = logFile
37
+ @logger = Logger.new(@logFile,'a')
38
+ @logger.level = Logger::INFO
37
39
  end
38
40
 
39
- @@CDO = ENV['CDO'].nil? ? 'cdo' : ENV['CDO']
40
-
41
- # Since cdo-1.5.4 undocumented operators are given with the -h option. For
42
- # earlier version, they have to be provided manually
43
- @@undocumentedOperators = %w[anomaly beta boxavg change_e5lsm change_e5mask
44
- change_e5slm chisquare chvar cloudlayer cmd com command complextorect
45
- covar0 covar0r daycount daylogs del29feb delday delete deltap deltap_fl
46
- delvar diffv divcoslat dumplogo dumplogs duplicate eca_r1mm enlargegrid
47
- ensrkhistspace ensrkhisttime eof3d eof3dspatial eof3dtime export_e5ml
48
- export_e5res fc2gp fc2sp fillmiss fisher fldcovar fldrms fourier fpressure
49
- gather gengrid geopotheight ggstat ggstats globavg gp2fc gradsdes
50
- gridverify harmonic hourcount hpressure import_e5ml import_e5res
51
- import_obs imtocomplex infos infov interpolate intgrid intgridbil
52
- intgridtraj intpoint isosurface lmavg lmean lmmean lmstd log lsmean
53
- meandiff2test mergegrid mod moncount monlogs mrotuv mrotuvb mulcoslat ncode
54
- ncopy nmltest normal nvar outputbounds outputboundscpt outputcenter
55
- outputcenter2 outputcentercpt outputkey outputtri outputvector outputvrml
56
- pardup parmul pinfo pinfov pressure_fl pressure_hl read_e5ml remapcon1
57
- remapdis1 retocomplex scalllogo scatter seascount select selgridname
58
- seloperator selvar selzaxisname setrcaname setvar showvar sinfov smemlogo
59
- snamelogo sort sortcode sortlevel sortname sorttaxis sorttimestamp sortvar
60
- sp2fc specinfo spectrum sperclogo splitvar stimelogo studentt template1
61
- template2 test test2 testdata thinout timcount timcovar tinfo transxy trms
62
- tstepcount vardes vardup varmul varquot2test varrms vertwind write_e5ml
63
- writegrid writerandom yearcount]
64
-
65
- @@outputOperatorsPattern = /(diff|info|output|griddes|zaxisdes|show|ncode|ndate|nlevel|nmon|nvar|nyear|ntime|npar|gradsdes|pardes)/
66
-
67
- private
68
- def Cdo.getOperators(force=false)
69
- # Do NOT compute anything, if it is not required
70
- return State[:operators] unless (State[:operators].empty? or force)
71
- cmd = @@CDO + ' 2>&1'
72
- help = IO.popen(cmd).readlines.map {|l| l.chomp.lstrip}
73
- if 5 >= help.size
74
- warn "Operators could not get listed by running the CDO binary (#{@@CDO})"
75
- pp help if Cdo.debug
76
- exit
77
- end
78
- # in version 1.5.6 the output of '-h' has changed
79
- State[:operators] = case
80
- when Cdo.version < "1.5.6"
81
- (help[help.index("Operators:")+1].split + @@undocumentedOperators).uniq
82
- else
83
- help[(help.index("Operators:")+1)..help.index(help.find {|v| v =~ /CDO version/}) - 2].join(' ').split
84
- end
41
+ private # {{{
42
+
43
+ # split arguments into hash-like args and the rest
44
+ def Cdo.parseArgs(args)
45
+ operatorArgs = args.reject {|a| a.class == Hash}
46
+ opts = operatorArgs.empty? ? '' : ',' + operatorArgs.join(',')
47
+ io = args.find {|a| a.class == Hash}
48
+ io = {} if io.nil?
49
+ args.delete_if {|a| a.class == Hash}
50
+ # join input streams together if possible
51
+ io[:input] = io[:input].join(' ') if io[:input].respond_to?(:join)
52
+
53
+ return [io,opts]
85
54
  end
86
55
 
87
- def Cdo.hasError(cmd,retvals)
88
- if (State[:debug])
89
- puts("RETURNCODE: #{retvals[:returncode]}")
90
- end
91
- if ( 0 != retvals[:returncode] )
92
- puts("Error in calling:")
93
- puts(">>> "+cmd+"<<<")
94
- puts(retvals[:stderr])
95
- return true
56
+ # collect the complete list of possible operators
57
+ def getOperators(path2cdo)
58
+ if version <= '1.7.0' then
59
+ cmd = path2cdo + ' 2>&1'
60
+ help = IO.popen(cmd).readlines.map {|l| l.chomp.lstrip}
61
+ if 5 >= help.size
62
+ warn "Operators could not get listed by running the CDO binary (#{path2cdo})"
63
+ pp help if @debug
64
+ exit
65
+ end
66
+
67
+ @operators = help[(help.index("Operators:")+1)..help.index(help.find {|v| v =~ /CDO version/}) - 2].join(' ').split
96
68
  else
97
- return false
69
+ cmd = "#{path2cdo} --operators"
70
+
71
+ @operators = IO.popen(cmd).readlines.map {|l| l.split(' ').first }
98
72
  end
99
73
  end
100
74
 
101
- def Cdo.env=(envHash)
102
- State[:env] = envHash
75
+ # get supported IO filetypes form the binary
76
+ def getFiletypes
77
+ _, _, stderr, _ = Open3.popen3(@cdo + " -V")
78
+ supported = stderr.readlines.map(&:chomp)
79
+
80
+ supported.grep(/(Filetypes)/)[0].split(':')[1].split.map(&:downcase)
103
81
  end
104
- def Cdo.env; State[:env]; end
105
82
 
106
- def Cdo.call(cmd)
107
- if (State[:debug])
83
+
84
+ # Execute the given cdo call and return all outputs
85
+ def _call(cmd)
86
+ if (@debug)
108
87
  puts '# DEBUG ====================================================================='
109
- pp Cdo.env unless Cdo.env.empty?
88
+ pp @env unless @env.empty?
110
89
  puts 'CMD: '
111
90
  puts cmd
112
91
  puts '# DEBUG ====================================================================='
113
92
  end
114
- stdin, stdout, stderr, wait_thr = Open3.popen3(Cdo.env,cmd)
115
93
 
94
+ @logger.info(cmd+"\n") if @logging
95
+
96
+ stdin, stdout, stderr, wait_thr = Open3.popen3(@env,cmd)
116
97
  {
117
98
  :stdout => stdout.read,
118
99
  :stderr => stderr.read,
@@ -120,269 +101,211 @@ module Cdo
120
101
  }
121
102
  end
122
103
 
123
- def Cdo.run(cmd,ofile='',options='',returnCdf=false,force=nil,returnArray=nil,returnMaArray=nil)
124
- cmd = "#{@@CDO} -O #{options} #{cmd} "
104
+ # Error handling for the given command
105
+ def _hasError(cmd,retvals)
106
+ if @debug
107
+ puts("RETURNCODE: #{retvals[:returncode]}")
108
+ end
109
+ if ( 0 != retvals[:returncode] )
110
+ puts("Error in calling:")
111
+ puts(">>> "+cmd+"<<<")
112
+ puts(retvals[:stderr])
113
+ @logger.error("FAILIURE in execution of:"+cmd+"| msg:"+retvals[:stderr]) if @logging
114
+ return true
115
+ else
116
+ return false
117
+ end
118
+ end
119
+
120
+ # command execution wrapper, which handles the possible return types
121
+ def _run(cmd,ofile='',options=nil,returnCdf=false,force=nil,returnArray=nil,returnMaArray=nil)
122
+ options = options.to_s
123
+
124
+ options << '-f nc' if options.empty? and ( \
125
+ ( returnCdf ) or \
126
+ ( not returnArray.nil? ) or \
127
+ ( not returnMaArray.nil?) \
128
+ )
129
+ cmd = "#{@cdo} -O #{options} #{cmd} "
130
+
125
131
  case ofile
126
132
  when $stdout
127
- retvals = Cdo.call(cmd)
128
- State[:logger].info(cmd+"\n") if State[:log]
129
- unless hasError(cmd,retvals)
133
+ retvals = _call(cmd)
134
+ unless _hasError(cmd,retvals)
130
135
  return retvals[:stdout].split($/).map {|l| l.chomp.strip}
131
136
  else
132
- raise ArgumentError,"CDO did NOT run successfully!"
137
+ if @returnNilOnError then
138
+ return nil
139
+ else
140
+ raise ArgumentError,"CDO did NOT run successfully!"
141
+ end
133
142
  end
134
143
  else
135
- force = State[:forceOutput] if force.nil?
144
+ force = @forceOutput if force.nil?
136
145
  if force or not File.exists?(ofile.to_s)
137
146
  ofile = MyTempfile.path if ofile.nil?
138
147
  cmd << "#{ofile}"
139
- retvals = call(cmd)
140
- State[:logger].info(cmd+"\n") if State[:log]
141
- if hasError(cmd,retvals)
142
- raise ArgumentError,"CDO did NOT run successfully!"
148
+ retvals = _call(cmd)
149
+ if _hasError(cmd,retvals)
150
+ if @returnNilOnError then
151
+ return nil
152
+ else
153
+ raise ArgumentError,"CDO did NOT run successfully!"
154
+ end
143
155
  end
144
156
  else
145
- warn "Use existing file '#{ofile}'" if Cdo.debug
157
+ warn "Use existing file '#{ofile}'" if @debug
146
158
  end
147
159
  end
160
+
148
161
  if not returnArray.nil?
149
- Cdo.readArray(ofile,returnArray)
162
+ readArray(ofile,returnArray)
150
163
  elsif not returnMaArray.nil?
151
- Cdo.readMaArray(ofile,returnMaArray)
152
- elsif returnCdf or State[:returnCdf]
153
- Cdo.readCdf(ofile)
164
+ readMaArray(ofile,returnMaArray)
165
+ elsif returnCdf or @returnCdf
166
+ readCdf(ofile)
154
167
  else
155
- return ofile
168
+ ofile
156
169
  end
157
170
  end
158
171
 
159
- def Cdo.parseArgs(args)
160
- # splitinto hash-like args and the rest
161
- operatorArgs = args.reject {|a| a.class == Hash}
162
- opts = operatorArgs.empty? ? '' : ',' + operatorArgs.join(',')
163
- io = args.find {|a| a.class == Hash}
164
- io = {} if io.nil?
165
- args.delete_if {|a| a.class == Hash}
166
- # join input streams together if possible
167
- io[:input] = io[:input].join(' ') if io[:input].respond_to?(:join)
168
- return [io,opts]
169
- end
172
+ # Implementation of operator calls using ruby's meta programming skills
173
+ #
174
+ # args is expected to look like
175
+ # [opt1,...,optN,:input => iStream,:output => oStream, :options => ' ']
176
+ # where iStream could be another CDO call (timmax(selname(Temp,U,V,ifile.nc))
177
+ def method_missing(sym, *args, &block)
178
+ puts "Operator #{sym.to_s} is called" if @debug
170
179
 
171
- def Cdo.method_missing(sym, *args, &block)
172
- ## args is expected to look like [opt1,...,optN,:input => iStream,:output => oStream] where
173
- # iStream could be another CDO call (timmax(selname(Temp,U,V,ifile.nc))
174
- puts "Operator #{sym.to_s} is called" if State[:debug]
175
- if getOperators.include?(sym.to_s)
180
+ if @operators.include?(sym.to_s)
176
181
  io, opts = Cdo.parseArgs(args)
177
- if @@outputOperatorsPattern.match(sym)
178
- run(" -#{sym.to_s}#{opts} #{io[:input]} ",$stdout)
182
+ if OutputOperatorsPattern.match(sym.to_s)
183
+ _run(" -#{sym.to_s}#{opts} #{io[:input]} ",$stdout)
179
184
  else
180
- run(" -#{sym.to_s}#{opts} #{io[:input]} ",io[:output],io[:options],io[:returnCdf],io[:force],io[:returnArray],io[:returnMaArray])
185
+ _run(" -#{sym.to_s}#{opts} #{io[:input]} ",io[:output],io[:options],io[:returnCdf],io[:force],io[:returnArray],io[:returnMaArray])
181
186
  end
182
187
  else
188
+ return false if @returnFalseOnError
183
189
  raise ArgumentError,"Operator #{sym.to_s} not found"
184
190
  end
185
191
  end
186
192
 
187
- def Cdo.loadCdf
193
+ # load the netcdf bindings
194
+ def loadCdf
188
195
  begin
189
196
  require "numru/netcdf_miss"
190
- include NumRu
191
197
  rescue LoadError
192
198
  warn "Could not load ruby's netcdf bindings. Please install it."
193
199
  raise
194
200
  end
195
201
  end
196
202
 
197
- def Cdo.getSupportedLibs(force=false)
198
- return unless (State[:libs].nil? or force)
199
- _, _, stderr, _ = Open3.popen3(@@CDO + " -V")
200
- supported = stderr.readlines.map(&:chomp)
201
- with = supported.grep(/(with|Features)/)[0].split(':')[1].split.map(&:downcase)
202
- libs = supported.grep(/library version/).map {|l|
203
- l.strip.split(':').map {|l|
204
- l.split.first.downcase
205
- }[0,2]
206
- }
207
- State[:libs] = {}
208
- with.flatten.each {|k| State[:libs][k]=true}
209
- libs.each {|lib,version| State[:libs][lib] = version}
210
- end
211
-
212
- public
213
- def Cdo.debug=(value)
214
- State[:debug] = value
215
- end
216
- def Cdo.debug
217
- State[:debug]
218
- end
219
- def Cdo.forceOutput=(value)
220
- State[:forceOutput] = value
221
- end
222
- def Cdo.forceOutput
223
- State[:forceOutput]
224
- end
225
- def Cdo.log=(value)
226
- State[:log] = value
227
- end
203
+ # }}}
228
204
 
229
- def Cdo.log
230
- State[:log]
231
- end
205
+ public # {{{
232
206
 
233
- def Cdo.version
234
- cmd = @@CDO + ' 2>&1'
235
- help = IO.popen(cmd).readlines.map {|l| l.chomp.lstrip}
236
- regexp = %r{CDO version (\d.*), Copyright}
237
- line = help.find {|v| v =~ regexp}
238
- version = regexp.match(line)[1]
207
+ # show Cdo's built-in help for given operator
208
+ def help(operator=nil)
209
+ if operator.nil?
210
+ puts _call([@cdo,'-h'].join(' '))[:stderr]
211
+ else
212
+ operator = operator.to_s
213
+ puts _call([@cdo,'-h',operator].join(' ')).values_at(:stdout,:stderr)
214
+ end
239
215
  end
240
216
 
241
- def Cdo.setReturnCdf(value=true)
242
- if value
243
- Cdo.loadCdf
217
+ # collect logging messages
218
+ def collectLogs
219
+ if @logger.instance_variable_get(:'@logdev').filename.nil?
220
+ @logFile.rewind
221
+ return @logFile.read
222
+ else
223
+ return File.open(@logFile).readlines
244
224
  end
245
- State[:returnCdf] = value
246
225
  end
247
-
248
- def Cdo.unsetReturnCdf
249
- setReturnCdf(false)
226
+ # print the loggin messaged
227
+ def showLog
228
+ puts collectLogs
250
229
  end
251
230
 
252
- def Cdo.returnCdf
253
- State[:returnCdf]
254
- end
231
+ # check if cdo backend is working
232
+ def check
233
+ return false unless system("#@cdo -h 1>/dev/null 2>&1")
255
234
 
256
- def Cdo.hasCdo?(bin=@@CDO)
257
- return true if File.exists?(@@CDO) and File.executable?(@@CDO)
258
- ENV['PATH'].split(File::PATH_SEPARATOR).each {|path|
259
- return true if File.exists?([path,bin].join(File::SEPARATOR))
260
- }
261
- end
235
+ retval = _call("#{@cdo} -V")
236
+ pp retval if @debug
262
237
 
263
- # test if @@CDO can be used
264
- def Cdo.checkCdo
265
- unless hasCdo?(@@CDO)
266
- warn "Testing application #@@CDO is not available!"
267
- exit 1
268
- else
269
- puts "Using CDO: #@@CDO"
270
- puts IO.popen(@@CDO + " -V").readlines
271
- end
272
238
  return true
273
239
  end
274
240
 
275
- def Cdo.setCdo(cdo)
276
- puts "Will use #{cdo} instead of #@@CDO" if Cdo.debug
277
- @@CDO = cdo
278
- Cdo.getOperators(true)
279
- Cdo.getSupportedLibs(true)
280
- end
281
-
282
- def Cdo.getCdo
283
- @@CDO
241
+ def version
242
+ IO.popen("#{@cdo} -V 2>&1").readlines.first.split(/version/i).last.strip.split(' ').first
284
243
  end
285
244
 
286
- def Cdo.operators
287
- Cdo.getOperators if State[:operators].empty?
288
- State[:operators]
245
+ # return cdf handle to given file readonly
246
+ def readCdf(iFile)
247
+ loadCdf
248
+ NumRu::NetCDF.open(iFile)
289
249
  end
290
250
 
291
- def Cdo.libs
292
- getSupportedLibs
293
- State[:libs]
251
+ # return cdf handle opened in append more
252
+ def openCdf(iFile)
253
+ loadCdf
254
+ NumRu::NetCDF.open(iFile,'r+')
294
255
  end
295
256
 
296
- def Cdo.hasLib?(lib)
297
- return Cdo.libs.has_key?(lib)
298
- return false
257
+ # return narray for given variable name
258
+ def readArray(iFile,varname)
259
+ filehandle = readCdf(iFile)
260
+ if filehandle.var_names.include?(varname)
261
+ # return the data array
262
+ filehandle.var(varname).get
263
+ else
264
+ raise ArgumentError, "Cannot find variable '#{varname}'"
265
+ end
299
266
  end
300
267
 
301
- def Cdo.libsVersion(lib)
302
- unless Cdo.hasLib?(lib)
303
- raise ArgumentError, "Cdo does NOT have support for '#{lib}'"
268
+ # return a masked array for given variable name
269
+ def readMaArray(iFile,varname)
270
+ filehandle = readCdf(iFile)
271
+ if filehandle.var_names.include?(varname)
272
+ # return the data array
273
+ filehandle.var(varname).get_with_miss
304
274
  else
305
- if State[:libs][lib].kind_of? String
306
- return State[:libs][lib]
307
- else
308
- warn "No version information available about '#{lib}'"
309
- return false
310
- end
275
+ raise ArgumentError,"Cannot find variable '#{varname}'"
311
276
  end
312
277
  end
313
278
 
314
- def Cdo.showlog
315
- @@file.rewind
316
- puts @@file.read
317
- end
279
+ # }}}
280
+
281
+ # Addional operators: {{{
318
282
 
319
- #==================================================================
320
- # Addional operotors:
321
- #------------------------------------------------------------------
322
- def Cdo.boundaryLevels(args)
323
- ilevels = Cdo.showlevel(:input => args[:input])[0].split.map(&:to_f)
283
+ # compute vertical boundary levels from full levels
284
+ def boundaryLevels(args)
285
+ ilevels = self.showlevel(:input => args[:input])[0].split.map(&:to_f)
324
286
  bound_levels = Array.new(ilevels.size+1)
325
287
  bound_levels[0] = 0
326
- (1..ilevels.size).each {|i|
288
+ (1..ilevels.size).each {|i|
327
289
  bound_levels[i] =bound_levels[i-1] + 2*(ilevels[i-1]-bound_levels[i-1])
328
290
  }
329
291
  bound_levels
330
292
  end
331
293
 
332
- def Cdo.thicknessOfLevels(args)
333
- bound_levels = Cdo.boundaryLevels(args)
294
+ # compute level thicknesses from given full levels
295
+ def thicknessOfLevels(args)
296
+ bound_levels = self.boundaryLevels(args)
334
297
  delta_levels = []
335
- bound_levels.each_with_index {|v,i|
298
+ bound_levels.each_with_index {|v,i|
336
299
  next if 0 == i
337
300
  delta_levels << v - bound_levels[i-1]
338
301
  }
339
302
  delta_levels
340
303
  end
341
304
 
342
- def Cdo.readCdf(iFile)
343
- Cdo.loadCdf unless State[:returnCdf]
344
- NetCDF.open(iFile)
345
- end
346
-
347
- def Cdo.openCdf(iFile)
348
- Cdo.loadCdf unless State[:returnCdf]
349
- NetCDF.open(iFile,'r+')
350
- end
351
-
352
- def Cdo.readArray(iFile,varname)
353
- filehandle = Cdo.readCdf(iFile)
354
- if filehandle.var_names.include?(varname)
355
- # return the data array
356
- filehandle.var(varname).get
357
- else
358
- raise ArgumentError, "Cannot find variable '#{varname}'"
359
- end
360
- end
305
+ # }}}
361
306
 
362
- def Cdo.readMaArray(iFile,varname)
363
- filehandle = Cdo.readCdf(iFile)
364
- if filehandle.var_names.include?(varname)
365
- # return the data array
366
- filehandle.var(varname).get_with_miss
367
- else
368
- raise ArgumentError,"Cannot find variable '#{varname}'"
369
- end
370
- end
371
-
372
- def Cdo.help(operator=nil)
373
- if operator.nil?
374
- puts Cdo.call([@@CDO,'-h'].join(' '))[:stderr]
375
- else
376
- operator = operator.to_s unless String == operator.class
377
- if Cdo.operators.include?(operator)
378
- puts Cdo.call([@@CDO,'-h',operator].join(' '))[:stdout]
379
- else
380
- puts "Unknown operator #{operator}"
381
- end
382
- end
383
- end
384
307
  end
385
-
308
+ #
386
309
  # Helper module for easy temp file handling
387
310
  module MyTempfile
388
311
  require 'tempfile'
@@ -411,3 +334,5 @@ module MyTempfile
411
334
  @@_tempfiles.each {|f| print(f+" ") if f.kind_of? String}
412
335
  end
413
336
  end
337
+
338
+ #vim:fdm=marker