Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ruby/lib/cdo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Hash
alias :include? :has_key?
end

# Copyright 2011-2022 Ralf Mueller, [email protected]
# Copyright 2011-2023 Ralf Mueller, [email protected]
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
Expand Down
54 changes: 54 additions & 0 deletions ruby/lib/cdoNG.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
load 'info.rb'
class CdoNG
attr_reader :options, :cdo, :forceOutput
attr_accessor :returnFalseOnError, :returnNilOnError, :debug, :logging, :logFile, :tempdir

def initialize(executable: 'cdo',
returnFalseOnError: false,
returnNilOnError: false,
forceOutput: true,
env: {},
debug: false,
tempdir: Dir.tmpdir,
logging: false,
logFile: StringIO.new)

@executable = executable
@returnFalseOnError = returnFalseOnError
@returnNilOnError = returnNilOnError
@forceOutput = forceOutput
@env = env
@debug = debug
@tempdir = tempdir
@logging = logging
@logFile = logFile

@commands = []
@operators = CdoInfo.operators(@executable)

end
def run(output: nil,
returnArray: false,
returnMaArray: false,
force: true,
env: {},
debug: false)
return true
end
def method_missing(sym, *args, **kwargs)
operatorName = sym.to_s
puts "Operator #{operatorName} is called" if @debug

# exit eary on missing operator
unless @operators.include?(operatorName)
return false if @returnFalseOnError
raise ArgumentError,"Operator #{operatorName} not found"
end

# check of kwargs, raise error on missing or unknown
# output is invalid
# options might be valid (special operatiors need options attached)
#
# append operator incl parameters
end
end
122 changes: 122 additions & 0 deletions ruby/lib/info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
require 'semverse'
require 'json'
require 'open3'

# Support module that should encapsulate basic operations with the binary
module CdoInfo
# hardcoded fallback list of output operators - from 1.8.0 there is an
# options for this: --operators_no_output
# this list works for cdo-1.6.4
NoOutputOperators = %w[cdiread cmor codetab conv_cmor_table diff diffc diffn
diffp diffv dump_cmor_table dumpmap filedes gmtcells gmtxyz gradsdes griddes
griddes2 gridverify info infoc infon infop infos infov map ncode ndate
ngridpoints ngrids nlevel nmon npar ntime nvar nyear output outputarr
outputbounds outputboundscpt outputcenter outputcenter2 outputcentercpt
outputext outputf outputfld outputint outputkey outputsrv outputtab outputtri
outputts outputvector outputvrml outputxyz pardes partab partab2 seinfo
seinfoc seinfon seinfop showattribute showatts showattsglob showattsvar
showcode showdate showformat showgrid showlevel showltype showmon showname
showparam showstdname showtime showtimestamp showunit showvar showyear sinfo
sinfoc sinfon sinfop sinfov spartab specinfo tinfo vardes vct vct2 verifygrid
vlist xinfon zaxisdes]
TwoOutputOperators = %w[trend samplegridicon mrotuv eoftime
eofspatial eof3dtime eof3dspatial eof3d eof complextorect complextopol]
MoreOutputOperators = %w[distgrid eofcoeff eofcoeff3d intyear scatter splitcode
splitday splitgrid splithour splitlevel splitmon splitname splitparam splitrec
splitseas splitsel splittabnum splitvar splityear splityearmon splitzaxis]

"return CDI versio as string"
def CdoInfo.version(executable)
info = IO.popen(executable+' -V 2>&1').readlines.first
info.split(' ').grep(%r{\d+\.\d+.*})[0].to_s
end

"return semantiv version of CDO"
def CdoInfo.semversion(executable)
Semverse::Version.new(CdoInfo.version(executable))
end

" get supported filetypes of the binary and other configuration data"
def CdoInfo.config(executable)
config = {}
config.default(false)

if Semverse::Version.new('1.9.3') < CdoInfo.semversion(executable) then
config.merge!(JSON.parse(IO.popen(executable + " --config all").read.chomp))
config.each {|k,v| config[k] = ('yes' == v) ? true : false}
else
warn "Cannot check configuration of the binary!"
warn "Please check manually with '#{executable} -V'"
end
config
end

"Check if the --operators is present"
def CdoInfo.hasOperatorsOption(executable)
log, status = Open3.capture2e("#{executable} --operators")
return (0 == status)
end

" get an infentory for the operators provided by the executable "
" this depends on the availability of the --operators option "
def CdoInfo.operators(executable) #{{{
operators = {}

unless CdoInfo.hasOperatorsOption(executable) then
warn "Cannot create database of operators!"
exit(1)
end

version = CdoInfo.semversion(executable)

# little side note: the option --operators_no_output works in 1.8.0 and
# 1.8.2, but not in 1.9.0, from 1.9.1 it works again
case
when (version < Semverse::Version.new('1.8.0') or Semverse::Version.new('1.9.0') == version) then
cmd = "#{executable} --operators"
_operators = IO.popen(cmd).readlines.map {|l| l.split(' ').first }

_operators.each {|op| operators[op] = 1 }
operators.each {|op,_|
oCounter = 0 if NoOutputOperators.include?(op)
oCounter = 2 if TwoOutputOperators.include?(op)
oCounter = -1 if MoreOutputOperators.include?(op)
operators[op] = {:in => 1, :out => oCounter}
}


when version < Semverse::Version.new('1.9.3') then
cmd = "#{executable} --operators"
_operators = IO.popen(cmd).readlines.map {|l| l.split(' ').first }
cmd = "#{executable} --operators_no_output"
_operatorsNoOutput = IO.popen(cmd).readlines.map {|l| l.split(' ').first }

# build up operator inventory
_operators.each {|op| operators[op] = 1 }
_operatorsNoOutput.each {|op| operators[op] = 0}
operators.each {|op,_|
oCounter = 0 if _operatorsNoOutput.include?(op)
oCounter = 2 if TwoOutputOperators.include?(op)
oCounter = -1 if MoreOutputOperators.include?(op)
operators[op] = {:in => 1, :out => oCounter}
}

else
cmd = "#{executable} --operators"
operators = {}
IO.popen(cmd).readlines.map {|line|
lineContent = line.chomp.split(' ')
name = lineContent[0]
iCounter, oCounter = lineContent[-1][1..-1].split('|')
operators[name] = {:in => iCounter.to_i , :out => oCounter.to_i}
}
end
return operators
end
# check if the CDO binary is present and works
def CdoInfo.works?(executable)
status = system("#{executable} -V >/dev/null 2>&1")
fullpath = File.exist?(executable) and File.executable?(executable)
return (status or fullpath)
end
end
1 change: 1 addition & 0 deletions ruby/test/test_cdo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def test_hasCdo
assert(@cdo.hasCdo) if File.exist?(@cdo.cdo)
end
def test_getOperators
pp @cdo.operators
%w[seq random stdatm info showlevel sinfo remap geopotheight mask topo thicknessOfLevels].each {|op|
if ["thicknessOfLevels"].include?(op)
assert(@cdo.respond_to?(op),"Operator '#{op}' not found")
Expand Down
57 changes: 57 additions & 0 deletions ruby/test/test_cdo_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
$:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'info'
require 'minitest/autorun'
require 'semverse'
#===============================================================================
def rm(files); files.each {|f| FileUtils.rm(f) if File.exist?(f)};end
#===============================================================================
EXECUTABLE = ENV.has_key?('CDO') ? ENV['CDO'] : 'cdo'
#===============================================================================

class TestCdoInfo < Minitest::Test
def test_version
version = CdoInfo.version(EXECUTABLE)
assert_equal('2.1.1',version)
version = CdoInfo.semversion(EXECUTABLE)
assert_equal(Semverse::Version.new('2.1.1'),version)
end
def test_config
config = CdoInfo.config(EXECUTABLE)
# if Semverse::Version.new('2.1.1') == CdoInfo.semversion(@executable) then
expectedConfig = {"has-cgribex"=>true, "has-cmor"=>false, "has-ext"=>true,
"has-grb"=>true, "has-grb1"=>true, "has-grb2"=>true,
"has-hdf5"=>true, "has-ieg"=>true, "has-magics"=>true,
"has-nc"=>true, "has-nc2"=>true, "has-nc4"=>true,
"has-nc4c"=>true, "has-nc5"=>true, "has-nczarr"=>true,
"has-openmp"=>true, "has-proj"=>true, "has-srv"=>true,
"has-threads"=>true, "has-wordexp"=>true, "has-hirlam_extensions"=>false}
# else if false then
# end
assert_equal(config,expectedConfig)
end
def test_operators
operators = CdoInfo.operators(EXECUTABLE)

# check for specific operators properties
# maps abritrary inputs and 0 output
assert_equal(-1,operators["map"][:in])
assert_equal(0,operators["map"][:out])

assert_equal(-1,operators["select"][:in])
assert_equal(1,operators["select"][:out])

assert_equal(1,operators["fldmean"][:in])
assert_equal(1,operators["fldmean"][:out])


assert_equal(1,operators["zaxisdes"][:in])
assert_equal(0,operators["zaxisdes"][:out])
end

def test_works
assert(CdoInfo.works?('cdo'))
assert(!CdoInfo.works?('cda_____o'))
assert(!CdoInfo.works?('ls'))
assert(CdoInfo.works?(EXECUTABLE))
end
end
35 changes: 35 additions & 0 deletions ruby/test/test_cdo_ng.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
$:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'cdoNG'

require 'minitest/autorun'

Cdo = CdoNG

#===============================================================================
def rm(files); files.each {|f| FileUtils.rm(f) if File.exist?(f)};end


class TestCdo < Minitest::Test

DEFAULT_CDO_PATH = 'cdo'

@@show = ENV.has_key?('SHOW')
@@maintainermode = ENV.has_key?('MAINTAINERMODE')
@@debug = ENV.has_key?('DEBUG')

parallelize_me! unless @@debug

def setup
@cdo = Cdo.new
end

def test_cdo
assert_equal(true,@cdo.run)
end

def test_operator_missing
assert_raises ArgumentError do
@cdo.noexistent(:input => '-for,d')
end
end
end