cdo 1.2.7 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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