Forward references?

L

Lloyd Zusman

Is there a way to define forward references to functions? Due to my own
personal eccentricities, I like to have the main body of a program
appear at the top of its source file, and the functions it uses at the
end.

By "define forward reference", I mean something analogous to the
following C construct:

static char* foo(); /* forward reference */
static char* bar(); /* forward reference */

main () {
printf("%s\n", foo());
printf("%s\n", bar());
exit(0);
}

char* foo() {
return ("foo");
}

char* bar() {
return ("bar");
}

If I could make use of forward references in ruby, the above program
could look something like this:

# somehow, define a forward reference to function foo()

# somehow, define a forward reference to function bar()

# program starts here

x = foo()
y = bar()
puts x
puts y
exit(0)

# real function definitions ...

def foo
return "foo"
end

def bar
return "bar"
end

Am I totally out of luck, or is there some kind of ruby trick I can
perform which will give me this capability?

Thanks in advance.
 
M

Mark Sparshatt

Lloyd said:
Is there a way to define forward references to functions? Due to my own
personal eccentricities, I like to have the main body of a program
appear at the top of its source file, and the functions it uses at the
end.

By "define forward reference", I mean something analogous to the
following C construct:

static char* foo(); /* forward reference */
static char* bar(); /* forward reference */

main () {
printf("%s\n", foo());
printf("%s\n", bar());
exit(0);
}

char* foo() {
return ("foo");
}

char* bar() {
return ("bar");
}

If I could make use of forward references in ruby, the above program
could look something like this:

# somehow, define a forward reference to function foo()

# somehow, define a forward reference to function bar()

# program starts here

x = foo()
y = bar()
puts x
puts y
exit(0)

# real function definitions ...

def foo
return "foo"
end

def bar
return "bar"
end
Since Ruby doesn't check whether the functions exist until the code that
calls them is actually run, one way to do this would be

def main
puts foo
puts bar
exit(0)
end

def foo
return "foo"
end

def bar
return "bar"
end

main
 
M

Michael Neumann

Lloyd said:
Is there a way to define forward references to functions? Due to my own
personal eccentricities, I like to have the main body of a program
appear at the top of its source file, and the functions it uses at the
end.

By "define forward reference", I mean something analogous to the
following C construct:

static char* foo(); /* forward reference */
static char* bar(); /* forward reference */

main () {
printf("%s\n", foo());
printf("%s\n", bar());
exit(0);
}

char* foo() {
return ("foo");
}

char* bar() {
return ("bar");
}

If I could make use of forward references in ruby, the above program
could look something like this:

# somehow, define a forward reference to function foo()

# somehow, define a forward reference to function bar()

# program starts here
x = foo()
y = bar()
puts x
puts y
exit(0)

Wrap this code into a END block:

END {
x = foo
y = bar
...
}

Regards,

Michael
 
L

Lloyd Zusman

Michael Neumann said:
Lloyd said:
Is there a way to define forward references to functions? Due to my own
personal eccentricities, I like to have the main body of a program
appear at the top of its source file, and the functions it uses at the
end.

[ ... ]

Wrap this code into a END block:

END {
x = foo
y = bar
...
}

Aha! And this got me to think of a similar way to do it that is even
more C-like:

def main
puts foo
puts bar
exit(0) # or it could be this: return (0)
end

def foo
return "foo"
end

def bar
return "bar"
end

END { exit(main) }
# ... or without the END block as long as the last line of
# the file is exit(main)


Thank you very much!
 
R

Robert Klemme

Lloyd Zusman said:
Is there a way to define forward references to functions? Due to my own
personal eccentricities, I like to have the main body of a program
appear at the top of its source file, and the functions it uses at the
end.

By "define forward reference", I mean something analogous to the
following C construct:

static char* foo(); /* forward reference */
static char* bar(); /* forward reference */

main () {
printf("%s\n", foo());
printf("%s\n", bar());
exit(0);
}

char* foo() {
return ("foo");
}

char* bar() {
return ("bar");
}

If I could make use of forward references in ruby, the above program
could look something like this:

# somehow, define a forward reference to function foo()

# somehow, define a forward reference to function bar()

# program starts here

x = foo()
y = bar()
puts x
puts y
exit(0)

# real function definitions ...

def foo
return "foo"
end

def bar
return "bar"
end

Am I totally out of luck, or is there some kind of ruby trick I can
perform which will give me this capability?

Just define them duplicately, the first time with an empty body:

# "prototypes"
def foo;end
def bar(name)end

# main stuff
x = foo()
y = bar()
puts x
puts y
exit 0

# "functions"
def foo
# foodoo
end

def bar(name)
# bardo
end

Regards

robert
 
L

Lloyd Zusman

Robert Klemme said:
Lloyd Zusman said:
Is there a way to define forward references to functions? Due to my own
personal eccentricities, I like to have the main body of a program
appear at the top of its source file, and the functions it uses at the
end.

[ ... ]

Just define them duplicately, the first time with an empty body:

# "prototypes"
def foo;end
def bar(name)end

# main stuff
x = foo()
y = bar()
puts x
puts y
exit 0

# "functions"
def foo
# foodoo
end

def bar(name)
# bardo
end

Thank you very much, but this doesn't appear to work. Here's what I
tried:

def foo;puts "forward ref of foo";end
def bar(arg);puts "forward ref of bar(#{arg})";end

foo()
bar('abc')
exit(0);

def foo
puts "foo"
end

def bar(arg)
puts "bar: #{arg}"
end

When I ran it, it produced the following output:

forward ref of foo
forward ref of bar(abc)

In other words, the second function definitions did not override the
first ones.
 
L

Lloyd Zusman

Sam McCall said:
In ruby, function definitions are code that gets executed, rather than
declarations that get found in the source file and put into a table.
So basically, the definitions have to come before the code that uses
them (but not before the definition of the methods that use them) in the
program flow. Therefore, if you want the main code at the top of the
file, put it in an END block or a method as already mentioned, or in a
separate file etc.
Defining stubs at the top won't work, because your main code runs before
the methods are redefined.
Sam

Thank you very much, and thanks to all the rest of you for your helpful
replies. Based on all this, I have settled on the following approach,
which I have mentioned earlier in this thread:

def main
# my main routine
#
# ... etc. ...
#
return (exitcode) ### or this: exit(exitcode)
end

def function1
# ... etc. ...
end

def function2
# ... etc. ...
end

# other functions ...

# final line ...
exit(main)
 
R

Robert Klemme

Lloyd Zusman said:
Thank you very much, and thanks to all the rest of you for your helpful
replies. Based on all this, I have settled on the following approach,
which I have mentioned earlier in this thread:

def main
# my main routine
#
# ... etc. ...
#
return (exitcode) ### or this: exit(exitcode)
end

def function1
# ... etc. ...
end

def function2
# ... etc. ...
end

# other functions ...

# final line ...
exit(main)

Sorry, but I get the feeling that you are trying to make Ruby similar to C
too much. IMHO the result is worse than straight ruby code. Let Ruby be
Ruby and C be C. :)

Kind regards

robert
 
L

Lloyd Zusman

Robert Klemme said:
Lloyd Zusman said:
[ ... ]

def main
# my main routine
#
# ... etc. ...
#
return (exitcode) ### or this: exit(exitcode)
end

def function1
# ... etc. ...
end

def function2
# ... etc. ...
end

# other functions ...

# final line ...
exit(main)

Sorry, but I get the feeling that you are trying to make Ruby similar to C
too much. IMHO the result is worse than straight ruby code. Let Ruby be
Ruby and C be C. :)

I just want to have my program's main body at the top of the program
file, and the subsidiary functions at the end. The only thing C-like
about this is the fact that I chose the word "main" for the routine that
houses the main body of code.

This construct is not necessary. I could just as easily name the main
routine as "foobar", and it would look less C-like without losing the
fact that the main body of the code comes first in the program file.

Never fear ... the code that I write tends to look ruby-ish and not
C-like. :)
 
R

Robert Klemme

Lloyd Zusman said:
Robert Klemme said:
Lloyd Zusman said:
[ ... ]

def main
# my main routine
#
# ... etc. ...
#
return (exitcode) ### or this: exit(exitcode)
end

def function1
# ... etc. ...
end

def function2
# ... etc. ...
end

# other functions ...

# final line ...
exit(main)

Sorry, but I get the feeling that you are trying to make Ruby similar to C
too much. IMHO the result is worse than straight ruby code. Let Ruby be
Ruby and C be C. :)

I just want to have my program's main body at the top of the program
file, and the subsidiary functions at the end. The only thing C-like
about this is the fact that I chose the word "main" for the routine that
houses the main body of code.

This construct is not necessary. I could just as easily name the main
routine as "foobar", and it would look less C-like without losing the
fact that the main body of the code comes first in the program file.

Here's another idea: put your code into two files and require (or load) the
helper code.

Or put your helper code after __END__ and use eval to compile it:

eval DATA.read

p foo()
p bar()

__END__

def foo
"foo"
end

def bar
"bar"
end

Never fear ... the code that I write tends to look ruby-ish and not
C-like. :)

Dare you! :)

Kind regards

robert
 
L

Lloyd Zusman

--=-=-=

Robert Klemme said:
Lloyd Zusman said:
[ ... ]

I just want to have my program's main body at the top of the program
file, and the subsidiary functions at the end. The only thing C-like
about this is the fact that I chose the word "main" for the routine that
houses the main body of code.

This construct is not necessary. I could just as easily name the main
routine as "foobar", and it would look less C-like without losing the
fact that the main body of the code comes first in the program file.

Here's another idea: put your code into two files and require (or load) the
helper code.

I know that I can do that. But often I just want one file. I believe
that the best use of `require' or `load' is to include code from shared
libraries. Putting simple subsidiary routines in one or more separate
files often complicates installation and maintenance.

Or put your helper code after __END__ and use eval to compile it:

[ ... etc. ... ]

That can work, but if I ever want to put real data after __END__ and
read it via the DATA handle, I'd be out of luck.

Dare you! :)

OK. Attached is a ruby program that I recently wrote. It is a
specialized "tail -f". In addition to standard "tail -f" capabilities,
it also can simultaneously tail multiple files, and in addition, it will
detect if a new file has replaced the one that is being tailed, in which
case it starts tailing the newly created file automatically.

The second feature is useful when I'm tailing log files that get
recycled. For example, if syslog always writes log data to a file
called, say, "foobar.log", and if once a day the following commands are
run ...

/bin/rm -f foobar.log.old
/bin/mv foobar.log foobar.log.old
kill -HUP $pid # where $pid is the process ID for syslogd

... then after these commands are invoked, syslog will start writing
log data to a new, empty "foobar.log" file. If I had done this ...

tail -f foobar.log

... then I would keep looking at the no-longer-changing "foobar.log.old"
file after the log files are recycled. But if I invoke my new command
(which I call "rtail") as follows ...

rtail foobar.log

... then once the logs are recycled, the data that is being added to
"foobar.log" will continue to appear in real time.

Here's the code ...


--=-=-=
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=rtail
Content-Transfer-Encoding: quoted-printable
Content-Description: Ruby program 'rtail'

#!/usr/bin/ruby

# Do a 'tail -f' simultaneously multiple files, interspersing their
# output.
#
# See the 'usage' routine, below, for a description of the command
# line options and arguments.

require 'sync'
require 'getoptlong'

$program =3D $0.sub(/^.*\//, '')

$stdout.extend(Sync_m)
$stdout.sync =3D 1

$stderr.sync =3D 1

$waitTime =3D 0.25

# Default values for flags that are set via the command line.
$tailf =3D true
$fnamePrefix =3D false

$defLineLen =3D 80
$defLines =3D 80
$lineLen =3D (ENV['COLUMNS'] =3D=3D nil ? $defLineLen : ENV['COLUMNS'])=
to_i
if $lineLen < 1 then
$lineLen =3D $defLineLen
end
$lines =3D (ENV['LINES'] =3D=3D nil ? $defLines : ENV['LINES']).to_i

$opts =3D GetoptLong.new(
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
[ "--lines", "-l", GetoptLong::REQUIRED_ARGUMENT ],
[ "--exit", "-x", GetoptLong::NO_ARGUMENT ],
[ "--name", "-n", GetoptLong::NO_ARGUMENT ]
)

# My list of threads.
$fileThreads =3D []

# Main routine
def rtail

begin

# Parse and evaluate command-line options
$opts.each do

|opt, arg|

case opt
when "--help"
usage
# notreached
when "--lines"
begin
$lines =3D arg.to_i + 0
rescue
usage
# notreached
end
when "--exit"
$tailf =3D false
when "--name"
$fnamePrefix =3D true
end
end
rescue
usage
# notreached
end

if ARGV.length < 1 then
usage
# notreached
end

# Calculate the size of a screen so we can choose a reasonable
# number of lines to tail.
if $lines < 1 then
$lines =3D $defLines
end

# One more full line than the maximum that the screen can hold.
$backwards =3D $lineLen * ($lines + 1)

# Signal handler.
[ 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGTERM' ].each {
|sig|
trap(sig) {
Thread.critical =3D true
Thread.list.each {
|t|
unless t =3D=3D Thread.main then
t.kill
end
}
$stderr.puts("\r\n!!! aborted")
Thread.critical =3D false
exit(-1)
}
}

# Start a thread to tail each file whose name appears on
# the command line. The threads for any file that cannot
# be opened for reading will die and will be reaped in
# the main loop, below.
ARGV.each {
|arg|
Thread.critical =3D true
$fileThreads << Thread.new(arg, $tailf, &$fileReadProc)
Thread.critical =3D false
}

# Main loop: reap dead threads and exit once there are no more
# threads that are alive.
loop {
Thread.critical =3D true
tcount =3D $fileThreads.length
Thread.critical =3D false
if tcount < 1 then
break
else
# Don't eat up too much of my CPU time
waitFor($waitTime)
end
}

# Bye-bye
exit(0)
end

# Add my own 'textfile?' method to the IO class.
class IO

private
# List of items that I want to treat as being normal text
# characters. The first line adds a lot of European characters
# that are not normally considered to be text characters in
# the traditional routines that distinguish between text and
# binary files. This is used within the 'textfile?' method.
@@textpats =3D [ "^=E1=E9=ED=F3=FA=E0=E8=EC=F2=F9=E4=EB=EF=F6=FC=F8=E7=F1=
=C1=C9=CD=D3=DA=C0=C8=CC=D2=D9=C4=CB=CF=D6=DC=D8=C7=D1=A1=BF",
"^ -~",
"^\b\f\t\r\n" ]

public
# This is my own, special-purpose test for text-ness. I don't want
# to treat certain European characters as binary. If the
# 'restorePosition' argument is true, make sure that the the position
# pointer within the IO handle gets repositioned back to its initial
# value after this test is performed.
def textfile?(testsize, restorePosition =3D false)
if restorePosition then
pos =3D self.pos
else
pos =3D nil
end
begin
block =3D self.read(testsize)
rescue
return false
end
len =3D block.length
if len < 1 then
return true # I want to treat a zero-length file as a text file.
end
result =3D (block.count(*@@textpats) < (len / 3.0) and=20
block.count("\x00") < 1)
unless pos.nil?
begin
self.seek(pos, IO::SEEK_SET)
rescue
return false
end
end
return result
end
end


# Do a timed 'wait'.
def waitFor(duration)
startTime =3D Time.now.to_f
select(nil, nil, nil, duration)
Thread.pass
# We could be back here long before 'duration' has passed.
# The loop below makes sure that we wait at least as long
# as this specified interval.
while (elapsed =3D (Time.now.to_f - startTime)) < duration
select(nil, nil, nil, 0.001)
Thread.pass
end
# Return the actual amount of time that elapsed. This is
# guaranteed to be >=3D 'duration'.
return elapsed
end

# We make sure that $stdout is syncrhonized so that lines of
# data coming from different threads don't garble each other.
def syncwrite(text)
begin
$stdout.synchronize(Sync::EX) {
$stdout.write(text)
}
rescue
# Fall back to normal, non-sync writing
$stdout.write(text)
end
end

# Decide whether to output a block as is, or with a prefix
# at the beginning of each line. In the "as is" case, just
# send the whole block to `syncwrite'; otherwise, split into
# lines and prepend the prefix before outputting. In other
# words, we only incur the cost of splitting the block when
# we absolutely have to.
def output(item)
prefix, block =3D item
if prefix.nil? or prefix.length < 1 then
syncwrite(block)
else
block.split(/\r*\n/).each {
|line|
syncwrite(prefix + line + "\n")
}
end
end

# Remove myself from the thread list and kill myself.
def abortMyself
t =3D Thread.current
Thread.critical =3D true
$fileThreads.delete(t)
Thread.critical =3D false
t.kill
end

# The main thread proc for tailing a given file
$fileReadProc =3D Proc.new do
|item, follow|

# Open the file, make sure it's a text file, read the last bit
# at the end, and output it. Kill the containing thread if any
# of this fails.
begin
f =3D File.open(item, 'r')
rescue
output([nil, "!!! unable to open: #{item}\n"])
abortMyself()
end
# Get some info about the open file
begin
f.sync =3D true
bytesize =3D f.stat.size
blocksize =3D f.stat.blksize
inode =3D f.stat.ino
rescue
f.close
output([nil, "!!! unable to stat: #{item}\n"])
abortMyself()
end
# Blocksize will be nil or zero if the device being opened
# is not a disk file. Bytesize will also be nil in this case.
if blocksize.nil? or blocksize < 1 or bytesize.nil? then
f.close
output([nil, "!!! invalid device: #{item}\n"])
abortMyself()
end
# Test for text-ness using one blocksize unit, or the length
# of the file if that is smaller.
testsize =3D (blocksize < bytesize ? blocksize : bytesize)
unless f.textfile?(testsize) then
f.close
output([nil, "!!! not a text file: #{item}\n"])
abortMyself()
end
if $fnamePrefix then
prefix =3D File.basename(item) + ': '
else
prefix =3D nil
end

# Position to a suitable point near the end of the file,
# and then read and output the data from that point until
# the end.
begin
if bytesize > $backwards then
pos =3D bytesize - $backwards
else
pos =3D 0
end
f.seek(pos, IO::SEEK_SET)
if pos > 0 then
f.gets # discard possible line fragment
end
output([prefix, f.read])
rescue
end

# If we have made it here, we've read the last bit of the file
# and have output it. Now, if we're not in 'follow' mode, we
# just exit.
unless follow then
f.close
abortMyself()
end

# We can only be here if we're in 'follow' mode. In this case,
# we keep looping to test if there is any more data to output.
loop {
# Get the current inode of the file. This is used to test whether
# or not the file has disappeared and whether or not there is a
# new file by the same name. This is not 100-percent conclusive,
# since a new file might accidentally end up with the same inode
# of an older, deleted file.
begin
newinode =3D File.stat(item).ino
rescue
newinode =3D nil
end
begin
if newinode.nil? or newinode !=3D inode then
# If we're here, the file disappeared or was replaced by
# a new file of the same name. We try to reopen the new
# version before continuing with the loop.
begin
f.close
waitFor($waitTime) # Wait a bit before trying to reopen
f =3D File.open(item, 'r')
f.sync =3D true
unless f.textfile?(testsize, true) then
f.close
output([nil, "!!! reopenable, but not a text file: #{item}\n"])
abortMyself()
end
inode =3D newinode
output([nil, "!!! reopened: #{item}\n"])
rescue
output([nil, "!!! disappeared: #{item}\n"])
begin
f.close
rescue
end
abortMyself()
end
elsif f.eof? then
# If we're here, we're at EOF.
f.seek(0, IO::SEEK_CUR)
waitFor($waitTime)
elsif f.pos < f.stat.size then
# If we're here, more data was added to the file since the last
# time we checked. Output this data, relinquish control to
# other threads, and then repeat the loop.
output([prefix, f.read])
Thread.pass
else
# If we're here, the file hasn't changed since last time.
# Wait a bit so as to not eat up too much CPU time.
waitFor($waitTime)
end
rescue
# Can we ever get here?
end
} # end of loop.
end # end of thread proc

# Print a usage message and exit.
def usage
warn <<EOD

usage: #{$program} [ options ] file [ ... ]

options:

--help, -h print this usage message

--lines=3D<n>, -l <n> tail <n> lines of each file (default #{$defLines})

--exit, -x exit after showing initial tail

--name, -n prepend file basename on each line that is output
EOD
exit(1)
end

# Run it
exit(rtail)

__END__

--=-=-=



--
Lloyd Zusman
(e-mail address removed)
God bless you.

--=-=-=--
 
M

Mark Hubbart

Is there a way to define forward references to functions? Due to my
own
personal eccentricities, I like to have the main body of a program
appear at the top of its source file, and the functions it uses at the
end.

By "define forward reference", I mean something analogous to the
following C construct:

static char* foo(); /* forward reference */
static char* bar(); /* forward reference */

main () {
printf("%s\n", foo());
printf("%s\n", bar());
exit(0);
}

char* foo() {
return ("foo");
}

char* bar() {
return ("bar");
}

If I could make use of forward references in ruby, the above program
could look something like this:

# somehow, define a forward reference to function foo()

# somehow, define a forward reference to function bar()

# program starts here

x = foo()
y = bar()
puts x
puts y
exit(0)

# real function definitions ...

def foo
return "foo"
end

def bar
return "bar"
end

Am I totally out of luck, or is there some kind of ruby trick I can
perform which will give me this capability?

One other way is to use blocks:

# code that does this:
def chunk(&block) ($CHUNKS||=[]) << block end
END{ $CHUNKS.reverse_each{|ch| ch[] } }
# end special code :)

chunk do

x = foo()
y = bar()
puts x
puts y
exit(0)

end

chunk do

def foo
return "foo"
end

def bar
return "bar"
end

end

Of course, you'd name it something other than chunk. And there's probly
a one liner for it, too, but I didn't have enough time to clean it up
much.

cheers,
Mark
 
R

Robert Klemme

I know that I can do that. But often I just want one file. I believe
that the best use of `require' or `load' is to include code from shared
libraries. Putting simple subsidiary routines in one or more separate
files often complicates installation and maintenance.

True. It's a tradeoff: it seemed to me that having prototypes was a
paramount requirement of you.
Or put your helper code after __END__ and use eval to compile it:

[ ... etc. ... ]

That can work, but if I ever want to put real data after __END__ and
read it via the DATA handle, I'd be out of luck.

Yes, of course.
OK. Attached is a ruby program that I recently wrote. It is a
specialized "tail -f". In addition to standard "tail -f" capabilities,
it also can simultaneously tail multiple files, and in addition, it will
detect if a new file has replaced the one that is being tailed, in which
case it starts tailing the newly created file automatically.
Here's the code ...

Very nice! I like especially the documentation. Some remarks from cursory
glancing though:

- I would not use Thread.critical since it is error prone. *If* you use
it, you should use this idiom, because it ensures that Thread.critical is
reset properly:

Thread.critical = true
begin
# stuff
ensure
Thread.critical = false
end

But, in your example this is superior:

require 'monitor'
# My list of threads.
$fileThreads = [].extend MonitorMixin
# later
$fileThreads.synchronize do
# do stuff with $fileThreads
end

- You are using exit a bit too much IMHO, especially since you have
"exit(rtail)" but rtail invokes exit, too. Usage of exit reduces
reusability of code and thus I would limit it to the to level of the script,
something like

begin
# do MAIN stuff
exit 0
rescue Exception => e
$stderr.puts e
exit 1
end

And within the rest of the script I'd use exceptions. Patterns like this
are inferior

begin
block = self.read(testsize)
rescue
return false
end
# further code that works with block

Instead put all the code for the clean case into the block or leave the
rescue completely out here and handle the exception on a higher level. That
makes things much easier.


- There is File.split:

# old: $program = $0.sub(/^.*\//, '')
$dir, $program = File.split $0


I hope, that was not too frustrating... :)

Regards

robert
 
L

Lloyd Zusman

Robert Klemme said:
Lloyd Zusman said:
[ ... ]

[ ... ] Putting simple subsidiary routines in one or more separate
files often complicates installation and maintenance.

True. It's a tradeoff: it seemed to me that having prototypes was a
paramount requirement of you.

Well, actually, what's important (or at least desirable) to me is not
prototypes in and of themselves, but rather, just to have the main body
of the code near the top of the file.

[ ... ]

Very nice! [ ... ]

Thank you.

[ ... ] I like especially the documentation. Some remarks from cursory
glancing though:

require 'monitor'
# My list of threads.
$fileThreads = [].extend MonitorMixin
# later
$fileThreads.synchronize do
# do stuff with $fileThreads
end

Why not just use the 'sync' module that is already being utilized in
the program? i.e. ...

$fileThreads = [].extend(Sync_m)

- You are using exit a bit too much IMHO, especially since you have
"exit(rtail)" but rtail invokes exit, too. Usage of exit reduces
reusability of code and thus I would limit it to the to level of the script,
something like

begin
# do MAIN stuff
exit 0
rescue Exception => e
$stderr.puts e
exit 1
end

Thanks. But how about this slight variation? Since I had exit(rtail),
I could just do it this way ...

# at the bottom of the script ...
begin
rtail
result = 0
# or if I want: result = rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
result = 1
end
exit(result)

... and then raise exceptions every other place where I was using 'exit'.

And within the rest of the script I'd use exceptions. Patterns like this
are inferior

begin
block = self.read(testsize)
rescue
return false
end
# further code that works with block

Instead put all the code for the clean case into the block or leave the
rescue completely out here and handle the exception on a higher level. That
makes things much easier.

Yes, that's a good approach in many cases, and I will do so in those
instances. However, in a few places, I have different kinds of 'rescue'
responses for different steps within the flow of control. Look at
$fileReadProc in my program, for example. I want my error message to
distinguish between "unable to open", "unable to stat", "invalid
device", etc., and I want to use my own error message text for each of
these instances. I don't see how to avoid a series of short
begin/rescue/end blocks in this case.

Hmm ... well, I could do this, but I don't like it:

$errorMessage = 'unknown error'
begin
$errorMessage = 'unable to open'
# open the file
$errorMessage = 'invalid device'
# do stuff that fails if the item is not a file
$errorMessage = 'unable to stat'
# do stat-related stuff
$errorMessage = 'message for next likely exception to be raised'
# ... etc. ...
rescue
begin
f.close
rescue
# file may or may not be open when exception occurs
end
output([nil, "!!! #{$errorMessage}: #{item}]")
abortMyself()
end

In the case I have shown here, I prefer the smaller begin/rescue/end
blocks, even though that's more verbose. It will be clearer to future
maintainers.

- There is File.split:

# old: $program = $0.sub(/^.*\//, '')
$dir, $program = File.split $0

Yes. I'm just in the habit of doing this myself, especially when I
don't want the '$dir' part.

I hope, that was not too frustrating... :)

No, not at all. Everything is much appreciated.
 
R

Robert Klemme

[ ... ] I like especially the documentation. Some remarks from cursory
glancing though:

require 'monitor'
# My list of threads.
$fileThreads = [].extend MonitorMixin
# later
$fileThreads.synchronize do
# do stuff with $fileThreads
end

Why not just use the 'sync' module that is already being utilized in
the program? i.e. ...

$fileThreads = [].extend(Sync_m)

Oh, I overlooked that. Is that std Ruby? (Monitor and Mutex are)
Thanks. But how about this slight variation? Since I had exit(rtail),
I could just do it this way ...

# at the bottom of the script ...
begin
rtail
result = 0
# or if I want: result = rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
result = 1
end
exit(result)

I prefer this:

# at the bottom of the script ...
begin
exit rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
exit 1
end

because it's more robust if you change your mind and want to do different
things (like not exiting). It keeps information more local. Maybe this is
a habit I got from Java because compilers can warn you if you put the exit
(or return for methods) into their respective neighborhood and forget one of
them.
.. and then raise exceptions every other place where I was using 'exit'.

Yes, definitely.
And within the rest of the script I'd use exceptions. Patterns like this
are inferior

begin
block = self.read(testsize)
rescue
return false
end
# further code that works with block

Instead put all the code for the clean case into the block or leave the
rescue completely out here and handle the exception on a higher level. That
makes things much easier.

Yes, that's a good approach in many cases, and I will do so in those
instances. However, in a few places, I have different kinds of 'rescue'
responses for different steps within the flow of control. Look at
$fileReadProc in my program, for example. I want my error message to
distinguish between "unable to open", "unable to stat", "invalid
device", etc., and I want to use my own error message text for each of
these instances. I don't see how to avoid a series of short
begin/rescue/end blocks in this case.

Hmm ... well, I could do this, but I don't like it:

$errorMessage = 'unknown error'
begin
$errorMessage = 'unable to open'
# open the file
$errorMessage = 'invalid device'
# do stuff that fails if the item is not a file
$errorMessage = 'unable to stat'
# do stat-related stuff
$errorMessage = 'message for next likely exception to be raised'
# ... etc. ...
rescue
begin
f.close
rescue
# file may or may not be open when exception occurs
end
output([nil, "!!! #{$errorMessage}: #{item}]")
abortMyself()
end

In the case I have shown here, I prefer the smaller begin/rescue/end
blocks, even though that's more verbose. It will be clearer to future
maintainers.

It will be even clearer if you break this code up into multiple method
invocations. Then you'll have methods like:

begin
File.open("foo.txt") do |io|
# IO is open here
# do more stuff
end
rescue Errno::Enoent => e
$stderr.puts "ERROR: ..."
rescue OtherError => e
...
end

Reusing a global (!) for the current error message looks irritating to me.
A global is not really needed here. And having a single rescue clause for
multiple errors doesn't look good to me either.
Yes. I'm just in the habit of doing this myself, especially when I
don't want the '$dir' part.

Then you can do
$prog = File.split($0)[1]

Advantage of File.split is that it's platform independend. Only introduce
platform dependencies that are really necessary - that might make your life
much easier in the future.

Kind regards

robert
 
L

Lloyd Zusman

Robert Klemme said:
Lloyd Zusman said:
[ ... ]

Why not just use the 'sync' module that is already being utilized in
the program? i.e. ...

$fileThreads = [].extend(Sync_m)

Oh, I overlooked that. Is that std Ruby? (Monitor and Mutex are)

Sync_m has been around ever since 1.6.x, and it's excplicitly mentioned
in the Pickaxe book (on page 120, the last line of the "Condition
Variables" section of Chapter 11, "Threads and Processes"). You can see
the code in RUBYINSTALLDIR/lib/sync.rb

I prefer this:

# at the bottom of the script ...
begin
exit rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
exit 1
end

because it's more robust if you change your mind and want to do different
things (like not exiting). It keeps information more local. Maybe this is
a habit I got from Java because compilers can warn you if you put the exit
(or return for methods) into their respective neighborhood and forget one of
them.

??? In your case, the problem you mentioned (forgetting exit or return)
seems _more_ likely, since you explicitly code 'exit' in two places. In
my version, it only appears once. Using your construct ...

# at the bottom of the script ...
begin
exit rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
###exit 1
accidentally_forget_to_exit()
end

If I want to do something other than exiting, then I can do the
following with my construct:

# at the bottom of the script ...
begin
result = rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
result = 1
end
###exit(result)
do_something_other_than_exiting(result)

It will be even clearer if you break this code up into multiple method
invocations. Then you'll have methods like:

begin
File.open("foo.txt") do |io|
# IO is open here
# do more stuff
end
rescue Errno::Enoent => e
$stderr.puts "ERROR: ..."
rescue OtherError => e
...
end

Yes, I often do that. It just seems like overkill in my small app.

Reusing a global (!) for the current error message looks irritating to
me.

Yep. That's why I said that I don't like it. I gave that example to
show how undesirable it is.

Then you can do
$prog = File.split($0)[1]

Advantage of File.split is that it's platform independend. Only introduce
platform dependencies that are really necessary - that might make your life
much easier in the future.

Good point. I have now replaced the $0.sub(...) call with this:

File.basename($0)

I believe that it's better than the File.split version, because it is
giving me exactly what I want: the basename of the file. That makes the
code more self-documenting; and besides, I already use File.basename
elsewhere in the code for the same purpose.
 
L

Lloyd Zusman

Mark Hubbart said:
[ ... ]

One other way is to use blocks:

# code that does this:
def chunk(&block) ($CHUNKS||=[]) << block end
END{ $CHUNKS.reverse_each{|ch| ch[] } }
# end special code :)

chunk do

x = foo()
y = bar()
puts x
puts y
exit(0)

end

chunk do

def foo
return "foo"
end

def bar
return "bar"
end

end

Of course, you'd name it something other than chunk. And there's probly
a one liner for it, too, but I didn't have enough time to clean it up
much.

This is probably a bit hard to maintain due to its not-so-obvious
algorithm. But it's clever!

Thank you very much.
 
R

Robert Klemme

Lloyd Zusman said:
Robert Klemme said:
Lloyd Zusman said:
[ ... ]

Why not just use the 'sync' module that is already being utilized in
the program? i.e. ...

$fileThreads = [].extend(Sync_m)

Oh, I overlooked that. Is that std Ruby? (Monitor and Mutex are)

Sync_m has been around ever since 1.6.x, and it's excplicitly mentioned
in the Pickaxe book (on page 120, the last line of the "Condition
Variables" section of Chapter 11, "Threads and Processes"). You can see
the code in RUBYINSTALLDIR/lib/sync.rb

Oh, once again learned something new. But as far as I can see it's only
mentioned at this single location - no examples no additional
explanations.
??? In your case, the problem you mentioned (forgetting exit or return)
seems _more_ likely, since you explicitly code 'exit' in two places. In
my version, it only appears once. Using your construct ...

# at the bottom of the script ...
begin
exit rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
###exit 1
accidentally_forget_to_exit()
end

Yeah, but you'll soon recognize that the program does not exit. While
it's more difficult ro recognize that it exits in both cases but you
wanted it to do something else in once case IMHO. Maybe it's a matter of
taste or my usage to Eclipse's excellent Java support which gives you
compile error messages and warnings that help a lot. Of course, Ruby is
not compiled... :)
If I want to do something other than exiting, then I can do the
following with my construct:

# at the bottom of the script ...
begin
result = rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
result = 1
end
###exit(result)
do_something_other_than_exiting(result)

Yeah, but that treats both cases (ok and error) the same and you have to
make a distinction in do_something_other_than_exiting(). That's typically
bad; it's better to have separate methods that handle each case
individually because methods then do just *one* thing and not two. Of
course, if you do the same in every case it's reasonable to have a single
method.
Yes, I often do that. It just seems like overkill in my small app.

Personally I prefer that kind of "overkill" over other strategies.
Yep. That's why I said that I don't like it. I gave that example to
show how undesirable it is.

Ah, ok.
Good point. I have now replaced the $0.sub(...) call with this:

File.basename($0)

I believe that it's better than the File.split version, because it is
giving me exactly what I want: the basename of the file. That makes the
code more self-documenting; and besides, I already use File.basename
elsewhere in the code for the same purpose.

Yeah, much better. I didn't think of File#basename.

Kind regards

robert
 
L

Lloyd Zusman

Robert Klemme said:
Lloyd Zusman said:
[ ... ]

Sync_m has been around ever since 1.6.x, and it's excplicitly mentioned
in the Pickaxe book (on page 120, the last line of the "Condition
Variables" section of Chapter 11, "Threads and Processes"). You can see
the code in RUBYINSTALLDIR/lib/sync.rb

Oh, once again learned something new. But as far as I can see it's only
mentioned at this single location - no examples no additional
explanations.

Yes, sync.rb (containing Sync_m and Synchronizer_m) is not mentioned or
documented anywhere else that I can find. I'm not sure where I stumbled
upon it ... I probably saw it used in someone else's code and then
investigated it. Although the mutex and monitor classes also can work
here, I prefer Sync_m in this case because its name reflects the exact
use to which I am putting it: synchronization.

[ ... ]

Yeah, but you'll soon recognize that the program does not exit. While
it's more difficult ro recognize that it exits in both cases but you
wanted it to do something else in once case IMHO. Maybe it's a matter of
taste or my usage to Eclipse's excellent Java support which gives you
compile error messages and warnings that help a lot. Of course, Ruby is
not compiled... :)
If I want to do something other than exiting, then I can do the
following with my construct:

# at the bottom of the script ...
begin
result = rtail
rescue Exception => e
$stderr.puts("!!! #{e}")
result = 1
end
###exit(result)
do_something_other_than_exiting(result)

Yeah, but that treats both cases (ok and error) the same and you have to
make a distinction in do_something_other_than_exiting(). That's typically
bad; it's better to have separate methods that handle each case
individually because methods then do just *one* thing and not two. Of
course, if you do the same in every case it's reasonable to have a single
method.

Yes. Here I always want to exit. If I wanted to do something different
on exit and on error, I would structure this code snippet differently.

[ ... ]

Yes, I often do that. It just seems like overkill in my small app.

Personally I prefer that kind of "overkill" over other strategies.

Adding a number of small methods would decrease maintainability of my
particular program, IMO. That is not true in general, and I indeed do
this on other kinds of projects. However, think that it does apply in
this case.

But there is plenty of room to differ here.


I refactored the code even more, based on our discussions and some ideas
of my own. If you're interested, I can privately email you the latest
version.
 
R

Robert Klemme

Lloyd Zusman said:
Robert Klemme said:
Lloyd Zusman said:
[ ... ]

Sync_m has been around ever since 1.6.x, and it's excplicitly mentioned
in the Pickaxe book (on page 120, the last line of the "Condition
Variables" section of Chapter 11, "Threads and Processes"). You can see
the code in RUBYINSTALLDIR/lib/sync.rb

Oh, once again learned something new. But as far as I can see it's only
mentioned at this single location - no examples no additional
explanations.

Yes, sync.rb (containing Sync_m and Synchronizer_m) is not mentioned or
documented anywhere else that I can find. I'm not sure where I stumbled
upon it ... I probably saw it used in someone else's code and then
investigated it. Although the mutex and monitor classes also can work
here, I prefer Sync_m in this case because its name reflects the exact
use to which I am putting it: synchronization.

Monitor and Mutex are also fixed terms in the MT community. It looks,
like everyone put his term in here. :) As far as I remember the
difference between Monitor and Mutex is that Monitor is reentrant while
Mutex is not. As far as I can see Sync and Sync_m are reentrant, too:

require 'thread'
require 'monitor'
require 'sync'

[Monitor, Sync, Mutex].each do |cl|
x = cl.new
puts "outside"; p x
begin
x.synchronize { puts "first"; p x; x.synchronize { puts "nest"; p
x } }
rescue Exception => e
puts e
end
end
Adding a number of small methods would decrease maintainability of my
particular program, IMO. That is not true in general, and I indeed do
this on other kinds of projects. However, think that it does apply in
this case.

But there is plenty of room to differ here.

Yeah, often these things are at least partly a matter of taste and
individual habit.
:)
I refactored the code even more, based on our discussions and some ideas
of my own. If you're interested, I can privately email you the latest
version.

[x] interested [ ] not interested

Cheers

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,142
Latest member
arinsharma
Top