Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Forward references?

Reply
Thread Tools

Forward references?

 
 
Lloyd Zusman
Guest
Posts: n/a
 
      07-31-2004
--=-=-=

"Robert Klemme" <> writes:

> "Lloyd Zusman" <> schrieb im Newsbeitrag
> news:...
>>
>> [ ... ]
>>
>> 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.


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

>
> 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=C 7=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

God bless you.

--=-=-=--


 
Reply With Quote
 
 
 
 
Mark Hubbart
Guest
Posts: n/a
 
      07-31-2004

On Jul 30, 2004, at 7:31 AM, Lloyd Zusman wrote:

> 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



 
Reply With Quote
 
 
 
 
Robert Klemme
Guest
Posts: n/a
 
      08-01-2004

"Lloyd Zusman" <> schrieb im Newsbeitrag
news:...

> > 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.


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.

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

> >
> > 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.

<snip/>
> 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

 
Reply With Quote
 
Lloyd Zusman
Guest
Posts: n/a
 
      08-01-2004
"Robert Klemme" <> writes:

> "Lloyd Zusman" <> schrieb im Newsbeitrag
> news:...
>>
>> [ ... ]
>>
>> [ ... ] 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.



--
Lloyd Zusman

God bless you.



 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      08-01-2004

"Lloyd Zusman" <> schrieb im Newsbeitrag
news:...

<snip/>

> > [ ... ] 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)

> > - 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)


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.

> > - 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.


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

 
Reply With Quote
 
Lloyd Zusman
Guest
Posts: n/a
 
      08-01-2004
"Robert Klemme" <> writes:

> "Lloyd Zusman" <> schrieb im Newsbeitrag
> news:...
>>
>> [ ... ]
>>
>> 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


>> 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.


??? 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.


--
Lloyd Zusman

God bless you.



 
Reply With Quote
 
Lloyd Zusman
Guest
Posts: n/a
 
      08-01-2004
Mark Hubbart <> writes:

> [ ... ]
>
> 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.


--
Lloyd Zusman

God bless you.



 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      08-02-2004

"Lloyd Zusman" <> schrieb im Newsbeitrag
news:...
> "Robert Klemme" <> writes:
>
> > "Lloyd Zusman" <> schrieb im Newsbeitrag
> > news:...
> >>
> >> [ ... ]
> >>
> >> 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.

> >> 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.

>
> ??? 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.

> > 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.


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

> > 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.


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

 
Reply With Quote
 
Lloyd Zusman
Guest
Posts: n/a
 
      08-02-2004
"Robert Klemme" <> writes:

> "Lloyd Zusman" <> schrieb im Newsbeitrag
> news:...
>>
>> [ ... ]
>>
>> 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.


--
Lloyd Zusman

God bless you.



 
Reply With Quote
 
Robert Klemme
Guest
Posts: n/a
 
      08-02-2004

"Lloyd Zusman" <> schrieb im Newsbeitrag
news:...
> "Robert Klemme" <> writes:
>
> > "Lloyd Zusman" <> schrieb im Newsbeitrag
> > news:...
> >>
> >> [ ... ]
> >>
> >> 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

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Thunderbird 0.7 / 0.8 Forward Mark Dalgua Firefox 1 10-22-2004 06:56 AM
How to forward ports... =?Utf-8?B?Y29tZWR5XzE3?= Wireless Networking 1 08-03-2004 11:45 PM
Thunderbird 0.5 doesn't forward attachments + little issue with news carreg3|j'aime pas le spam| Firefox 5 04-04-2004 12:59 PM
Re-forward declaration of types which were already forward declared qazmlp C++ 1 02-15-2004 07:00 PM
Hot to change the forward header...? Marius Firefox 0 08-27-2003 06:42 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57