J
James B. Byrne
I have written my first live ruby script that actually performs useful
work as an exercise in syntax discovery. It is very much a straight
line utility that could just as easily be written in bash. What I would
like is for someone to look at the code reproduced below and guide me
through the steps to convert this into something approximating the ruby
OOP idiom. I come from a 4GL universe working on mid-range and high end
iron and OS rather than unix/linux and OOP. So, I have a major
challenge getting my head out of first gear.
Looking at this I wondered what can be placed into separate classes. It
seems to me that the check for environment variables and defaluts and
then creating the resulting directory strings and regexp objects is
either one functional group or two, depending if one considers the
search for environmental settings totally separate from the directory
validation process or not. I tend to see two classes; GetDirs and
CheckDirs. Another class would likely be MoveFiles.
module mv2csv
class GetDirs(envlist[],arglist[]=nil)
def initalize
...
end
def envlist.each do |env|
...
end
end
My difficulty is that I do not see how all this hangs together after the
classes and methods are written. Does the script end up as a ruby
module? Then what do I do? Do I have a "main" procedure below the class
definitions in the module? I would really appreciate it if someone
could invest the time to walk me through this because once I see how to
map what I presently do to the ruby way I expect that many things I am
unsure about will suddenly become clear. I am particularly interested
in how to redo this so that Test::Unit can be used to drive it for
testing.
Regards,
Jim
The working code:
#!/usr/bin/env ruby
#
#--
# Put RDOC comments between ++ and -- comment tags.
#--
#++
# hll-qpxfr.rb - Byrne, James B. - JBB8 -
# version: 1.0 2006 Feb 15 - Initial draught.
#
# A ruby script to transfer csv data files delivered via FTP to
# an anonymous world-readable site to a secure data directory
# and append .csv extension to each.
#
# The files originate on an HP3000 and are ascii csv files
# sans header line. Their names take the form QP?????#
#--
#
#### Standard code layout follows.
#
## require <library> # loads library first time encountered.
require 'find'
require 'fileutils'
include FileUtils:ryRun
require 'syslog'
## GLOBAL CONSTANTS
#
# Run unit tests and traces?
VERBOSE_ON = TRUE
# If the appropriate environmental variables are not set then
# find files matching this pattern:
DFLT_FILE_RE = "^QPCCCMR[[:digit:]]$" # alternate "^QPCCCMR[1-3]$"
# using these options:
DFLT_FILE_RE_OPT = ""
# and add this extension to them:
DFLT_FILE_EXT = ".csv"
# while searching this directory:
DFLT_DIR_IN = "./ruby/data/source"
# and move the result to here.
DFLT_DIR_OUT = "./ruby/data/target"
# Munge output filenames to lowercase? uppercase? Ignore? (niu:
2006-02-10)
#DFLT_FILE_OUT_CASE = "IGNORE"
#DFLT_FILE_OUT_CASE = "DNCASE" # down-shift case
#DFLT_FILE_OUT_CASE = "UPCASE" # up-shift case
#
## module <Modulename>
#
## class <Classname>
#
## include <library> # add library code to class defintion
#
## def <methodname>
#
## load <library> # loads library each time encountered.
#
## main line code after here
#
begin
#
# Set up SYSLOG
log = Syslog.open($0)
log.info("Searching for updated files from HP3000 in ftp drop.")
# ENV check for: HLL_QP_DIR_IN, HLL_QP_DIR_OUT, HLL_QP_REGEX,
# HLL_QP_EXT and overide DEFAULT values if found.
# Regardless, verify that a valid directory is provided.
f_dir_in = ENV["HLL_QP_DIR_IN"] ? ENV["HLL_QP_DIR_IN"].to_s :
DFLT_DIR_IN
if !FileTest.directory?(f_dir_in) then
$stderr.print "Invalid source directory " + f_dir_in + " specified.\n"
log.err(("Invalid source directory " + f_dir_in + " specified"))
if ENV["HLL_QP_DIR_IN"] then
$stderr.print "Check setting of ENV: HLL_QP_DIR_IN\n"
log.err(("Check setting of ENV: HLL_QP_DIR_IN"))
end
Process.exit
else
log.info(("Source directory is: " + f_dir_in))
if !File.readable?(f_dir_in) then
$stderr.print f_dir_in + " is NOT readable. Process aborting.\n"
log.err((f_dir_in + " is NOT readable. Process aborting"))
Process.exit
end
end
f_dir_out = ENV["HLL_QP_DIR_OUT"] ? ENV["HLL_QP_DIR_OUT"].to_s :
DFLT_DIR_OUT
if !FileTest.directory?(f_dir_out) then
$stderr.print "Invalid target directory " + f_dir_out + "
specified.\n"
log.err(("Invalid target directory " + f_dir_out + " specified"))
if ENV["HLL_QP_DIR_OUT"] then
$stderr.print "Check setting of ENV: HLL_QP_DIR_OUT\n"
log.err(("Check setting of ENV: HLL_QP_DIR_OUT"))
end
Process.exit
else
log.info(("Target directory is: " + f_dir_out))
if !File.writable?(f_dir_out) then
$stderr.print f_dir_out + " is NOT writable. Process aborting.\n"
log.err((f_dir_out + " is NOT writable. Process aborting"))
Process.exit
end
end
# set the file extension value if present. May be nil.
f_nam_ext = ENV["HLL_QP_EXT"] ? ENV["HLL_QP_EXT"].to_s : DFLT_FILE_EXT
# create a valid RE for search. Note that setting options in
# Regexp.new(RE[,opt]) requires a bit pattern expressed as an integer or
the
# equivilent library CONSTANTS (REGEXP::INSENSITIVE, etc.) not, as might
# be expected, a character string. Further, if any non-nil value is
# passed as an option then the /i switch is turned on even if the option
# value is invalid. (So much for the principle of least surprise!)
f_nam_re_s = ENV["HLL_QP_REGEX"] ? ENV["HLL_QP_REGEX"].to_s :
DFLT_FILE_RE
f_nam_re = Regexp.new(f_nam_re_s)
# check that any files in the source directory are read-write
# enabled for current process gid or uid.
Find.find(f_dir_in) do |filename|
if VERBOSE_ON then
puts filename + " " + File.basename(filename) + " " +
f_nam_re.to_s
end
if FileTest.file?(filename) and
File.basename(filename) =~ f_nam_re
then
if VERBOSE_ON then
puts filename
puts FileTest.readable?(filename) ? "\treadable" \
: "\tnot readable"
puts FileTest.writable?(filename) ? "\twritable" \
: "\tnot writable"
puts filename + " moves to " + \
(File.join(f_dir_out, (File.basename(filename) +
f_nam_ext)))
else
# Move oldfile to same name in target dir with extension
appended.
# If we ever enable case munging then this is where it will
happen.
FileUtils.move((filename), \
(File.join(f_dir_out, (File.basename(filename) +
f_nam_ext))))
log.info(("Moving " + filename + " to " + \
(File.join(f_dir_out, (File.basename(filename) +
f_nam_ext)))))
end
next
else
# do not recurse directories.
if FileTest.directory?(filename)
if VERBOSE_ON then puts filename + " is a directory." end
if File.basename(filename)!= File.basename(f_dir_in)
if VERBOSE_ON then
puts File.basename(filename) + " is not a regular file."
end
Find.prune
else
next
end
end
end
end
#
## catch problems below here
rescue
if VERBOSE_ON then
puts "rescue block entered: " + $!.to_s
puts
end
#
## put finalizer code below here
ensure
if VERBOSE_ON then
puts "ensure block entered: " + $!.to_s
puts
end
##-- put test code below here
if VERBOSE_ON
#
## Display working data set.
puts "\t Input directory: " + f_dir_in.to_s
puts "\tTarget directory: " + f_dir_out.to_s
puts "\tSet extension to: " + f_nam_ext.to_s
puts "\nThe passed regexp is: \n\t" + f_nam_re_s
puts "The regexp used is: "
p f_nam_re
puts "$= is: " + $=.to_s
# Set up a set of strings for possible file names
qpa =
["qpcccmr1","QPCCCMR1","QPCCMMR1","qpCCCMR1","QPCCCMRC","QPCCCMR1.csv"]
qpa += ["QPCCCMR9","XXQPCCCMR9","QPCCCMR9yyy","XyZQPCCCMR3AbC"]
# Test to see which names match
qpa.each do |a|
case a
when f_nam_re then puts a + " matches"
else puts a + " does not match"
end
end
puts "Ruby run of " + __FILE__.to_s + " as " + $0.to_s + \
" ends at line: " + __LINE__.to_s
#end VERBOSE_ON block
end
log.info(($0 + " finished"))
#end main
end
work as an exercise in syntax discovery. It is very much a straight
line utility that could just as easily be written in bash. What I would
like is for someone to look at the code reproduced below and guide me
through the steps to convert this into something approximating the ruby
OOP idiom. I come from a 4GL universe working on mid-range and high end
iron and OS rather than unix/linux and OOP. So, I have a major
challenge getting my head out of first gear.
Looking at this I wondered what can be placed into separate classes. It
seems to me that the check for environment variables and defaluts and
then creating the resulting directory strings and regexp objects is
either one functional group or two, depending if one considers the
search for environmental settings totally separate from the directory
validation process or not. I tend to see two classes; GetDirs and
CheckDirs. Another class would likely be MoveFiles.
module mv2csv
class GetDirs(envlist[],arglist[]=nil)
def initalize
...
end
def envlist.each do |env|
...
end
end
My difficulty is that I do not see how all this hangs together after the
classes and methods are written. Does the script end up as a ruby
module? Then what do I do? Do I have a "main" procedure below the class
definitions in the module? I would really appreciate it if someone
could invest the time to walk me through this because once I see how to
map what I presently do to the ruby way I expect that many things I am
unsure about will suddenly become clear. I am particularly interested
in how to redo this so that Test::Unit can be used to drive it for
testing.
Regards,
Jim
The working code:
#!/usr/bin/env ruby
#
#--
# Put RDOC comments between ++ and -- comment tags.
#--
#++
# hll-qpxfr.rb - Byrne, James B. - JBB8 -
# version: 1.0 2006 Feb 15 - Initial draught.
#
# A ruby script to transfer csv data files delivered via FTP to
# an anonymous world-readable site to a secure data directory
# and append .csv extension to each.
#
# The files originate on an HP3000 and are ascii csv files
# sans header line. Their names take the form QP?????#
#--
#
#### Standard code layout follows.
#
## require <library> # loads library first time encountered.
require 'find'
require 'fileutils'
include FileUtils:ryRun
require 'syslog'
## GLOBAL CONSTANTS
#
# Run unit tests and traces?
VERBOSE_ON = TRUE
# If the appropriate environmental variables are not set then
# find files matching this pattern:
DFLT_FILE_RE = "^QPCCCMR[[:digit:]]$" # alternate "^QPCCCMR[1-3]$"
# using these options:
DFLT_FILE_RE_OPT = ""
# and add this extension to them:
DFLT_FILE_EXT = ".csv"
# while searching this directory:
DFLT_DIR_IN = "./ruby/data/source"
# and move the result to here.
DFLT_DIR_OUT = "./ruby/data/target"
# Munge output filenames to lowercase? uppercase? Ignore? (niu:
2006-02-10)
#DFLT_FILE_OUT_CASE = "IGNORE"
#DFLT_FILE_OUT_CASE = "DNCASE" # down-shift case
#DFLT_FILE_OUT_CASE = "UPCASE" # up-shift case
#
## module <Modulename>
#
## class <Classname>
#
## include <library> # add library code to class defintion
#
## def <methodname>
#
## load <library> # loads library each time encountered.
#
## main line code after here
#
begin
#
# Set up SYSLOG
log = Syslog.open($0)
log.info("Searching for updated files from HP3000 in ftp drop.")
# ENV check for: HLL_QP_DIR_IN, HLL_QP_DIR_OUT, HLL_QP_REGEX,
# HLL_QP_EXT and overide DEFAULT values if found.
# Regardless, verify that a valid directory is provided.
f_dir_in = ENV["HLL_QP_DIR_IN"] ? ENV["HLL_QP_DIR_IN"].to_s :
DFLT_DIR_IN
if !FileTest.directory?(f_dir_in) then
$stderr.print "Invalid source directory " + f_dir_in + " specified.\n"
log.err(("Invalid source directory " + f_dir_in + " specified"))
if ENV["HLL_QP_DIR_IN"] then
$stderr.print "Check setting of ENV: HLL_QP_DIR_IN\n"
log.err(("Check setting of ENV: HLL_QP_DIR_IN"))
end
Process.exit
else
log.info(("Source directory is: " + f_dir_in))
if !File.readable?(f_dir_in) then
$stderr.print f_dir_in + " is NOT readable. Process aborting.\n"
log.err((f_dir_in + " is NOT readable. Process aborting"))
Process.exit
end
end
f_dir_out = ENV["HLL_QP_DIR_OUT"] ? ENV["HLL_QP_DIR_OUT"].to_s :
DFLT_DIR_OUT
if !FileTest.directory?(f_dir_out) then
$stderr.print "Invalid target directory " + f_dir_out + "
specified.\n"
log.err(("Invalid target directory " + f_dir_out + " specified"))
if ENV["HLL_QP_DIR_OUT"] then
$stderr.print "Check setting of ENV: HLL_QP_DIR_OUT\n"
log.err(("Check setting of ENV: HLL_QP_DIR_OUT"))
end
Process.exit
else
log.info(("Target directory is: " + f_dir_out))
if !File.writable?(f_dir_out) then
$stderr.print f_dir_out + " is NOT writable. Process aborting.\n"
log.err((f_dir_out + " is NOT writable. Process aborting"))
Process.exit
end
end
# set the file extension value if present. May be nil.
f_nam_ext = ENV["HLL_QP_EXT"] ? ENV["HLL_QP_EXT"].to_s : DFLT_FILE_EXT
# create a valid RE for search. Note that setting options in
# Regexp.new(RE[,opt]) requires a bit pattern expressed as an integer or
the
# equivilent library CONSTANTS (REGEXP::INSENSITIVE, etc.) not, as might
# be expected, a character string. Further, if any non-nil value is
# passed as an option then the /i switch is turned on even if the option
# value is invalid. (So much for the principle of least surprise!)
f_nam_re_s = ENV["HLL_QP_REGEX"] ? ENV["HLL_QP_REGEX"].to_s :
DFLT_FILE_RE
f_nam_re = Regexp.new(f_nam_re_s)
# check that any files in the source directory are read-write
# enabled for current process gid or uid.
Find.find(f_dir_in) do |filename|
if VERBOSE_ON then
puts filename + " " + File.basename(filename) + " " +
f_nam_re.to_s
end
if FileTest.file?(filename) and
File.basename(filename) =~ f_nam_re
then
if VERBOSE_ON then
puts filename
puts FileTest.readable?(filename) ? "\treadable" \
: "\tnot readable"
puts FileTest.writable?(filename) ? "\twritable" \
: "\tnot writable"
puts filename + " moves to " + \
(File.join(f_dir_out, (File.basename(filename) +
f_nam_ext)))
else
# Move oldfile to same name in target dir with extension
appended.
# If we ever enable case munging then this is where it will
happen.
FileUtils.move((filename), \
(File.join(f_dir_out, (File.basename(filename) +
f_nam_ext))))
log.info(("Moving " + filename + " to " + \
(File.join(f_dir_out, (File.basename(filename) +
f_nam_ext)))))
end
next
else
# do not recurse directories.
if FileTest.directory?(filename)
if VERBOSE_ON then puts filename + " is a directory." end
if File.basename(filename)!= File.basename(f_dir_in)
if VERBOSE_ON then
puts File.basename(filename) + " is not a regular file."
end
Find.prune
else
next
end
end
end
end
#
## catch problems below here
rescue
if VERBOSE_ON then
puts "rescue block entered: " + $!.to_s
puts
end
#
## put finalizer code below here
ensure
if VERBOSE_ON then
puts "ensure block entered: " + $!.to_s
puts
end
##-- put test code below here
if VERBOSE_ON
#
## Display working data set.
puts "\t Input directory: " + f_dir_in.to_s
puts "\tTarget directory: " + f_dir_out.to_s
puts "\tSet extension to: " + f_nam_ext.to_s
puts "\nThe passed regexp is: \n\t" + f_nam_re_s
puts "The regexp used is: "
p f_nam_re
puts "$= is: " + $=.to_s
# Set up a set of strings for possible file names
qpa =
["qpcccmr1","QPCCCMR1","QPCCMMR1","qpCCCMR1","QPCCCMRC","QPCCCMR1.csv"]
qpa += ["QPCCCMR9","XXQPCCCMR9","QPCCCMR9yyy","XyZQPCCCMR3AbC"]
# Test to see which names match
qpa.each do |a|
case a
when f_nam_re then puts a + " matches"
else puts a + " does not match"
end
end
puts "Ruby run of " + __FILE__.to_s + " as " + $0.to_s + \
" ends at line: " + __LINE__.to_s
#end VERBOSE_ON block
end
log.info(($0 + " finished"))
#end main
end