Smalltalk Error handling with Transcript - error-handling

Suppose I have the following method:
MyClass>>addCategory: aCategory toEvent: anEvent
| cat |
cat := anEvent addCategory: aCategory
...
Now, the method #addCategory can either return some other object (e.g. something of class Foo) or throw an error (using Error signal: 'message').
In case of an error, I would like to print the message of the error on the Transcript.
In case of an object, I would like to print some message on the Transcript (e.g. Transcript show: 'Category added!') and return the object.
I have been looking at aBlock ifError: aBlock, something like this:
MyClass>>addCategory: aCategory toEvent: anEvent
| cat |
cat := [anEvent addCategory: aCategory] ifError: [ :err | Transcript show: err. ]
...
But I can't quite figure out how to handle the variable cat afterwards, in order to get the behavior I want.

Here is another way to do the same. The idea is to enclose your code as if would not fail and wrap it with an on: Error
MyClass>>addCategory: aCategory toEvent: anEvent
| cat |
[
cat := anEvent addCategory: aCategory.
Transcript show: 'Category added!']
on: Error
do: [:err | Transcript show: err messageText].
^cat
Notice that cat will not get assigned in case of Error and hence the method will answer with nil. Notice also that there is no need to ^nil from within the error-handler block.
Remember that the idea of on:do: is to allow you to write naïve code and then handle possible error conditions without inlining them into the error-free section of your code.
[
<my naive
and clean
lines of code>] on: Error do: [:err | oops!]
Your solution is ok but inlines error handling code inside the main code, making it a little bit harder for the reader to get the main idea of the relevant code.

One possible way to solve this is the following:
MyClass>>addCategory: aCategory toEvent: anEvent
| cat |
cat := [ anEvent addCategory: aCategory] on: Error do [ :err | Transcript show: err messageText. ^nil. ]
Transcript show: 'Category added!'.
^cat
This solution will print the error to the Transcript and return nil from the method. In case there is no error, the code will continue, print the message on the Transcript and return the object.
This might not be the best solution, but it is one possible way of doing it. As long as you don't mind that nil is returned in case of an error.

Related

How to use text file as input to feed in the interactive input of smalltalk and redirect output to a file

I am struggling to find out is there a way to feed input
to the interactive command of gst a.st b.st ... -
and redirect the output. Normally, the interactive buffer will
have st> ... and when you type a command it will output something by calling
the default/override displayString method to the interactive output. How to get the input
and feed the output using linux command or maybe a tiny smalltalk test script to do that.
Thank you.
Here's a contrived demonstration program. It reads in strings from standard input until EOF, sorts them, then prints them out:
input := stdin nextLine.
c := OrderedCollection new.
[ input ~= nil ] whileTrue: [
c add: input.
input := stdin nextLine.
].
c sort do: [ :each | each printNl ]
You can run it interactively (pressed Ctrl-D after entering hhh):
$ gst sortprog.st
tttt
aaa
vvvv
hhh
'aaa'
'hhh'
'tttt'
'vvvv'
Or I can create a text file test.in with the following contents:
tttt
aaa
vvvv
hhh
Then run:
$ gst sortprog.st < test.in > test.out
And then check the contents of the output file:
$ cat test.out
'aaa'
'hhh'
'tttt'
'vvvv'
If your program has prompts, they will appear in the output file, of course. Anything going to stdout will go to that file.

How to write txt file in smalltalk

I try with this code:
f := 'testfile.txt' asFileReference.
f2 := f writeStream.
f2 nextPutAll: 'hello world'.
f2 close.
f content.
But I get this exception:
**FileDoesNotExistException**
'testfile.txt' asFileReference
writeStreamDo: [ :stream | stream << 'Hello, World!' ].
This should work. But this is another way to express what you did before so I suspect some writing permission is wrong or something around that.
Just to add to Estaban's response, one surprising behaviour of Pharo is that writeStreamDo overwrites the existing file, so if the existing file is longer than the new data, you end up with the new data and the tail end of the old data. Fortunately, there is a simple solution: you can simply include truncate. So a slightly "safer" version is:
'testfile.txt' asFileReference
writeStreamDo: [ :stream | stream truncate. stream << 'Hello, World!' ].

smalltalk inspect - output to transcript or file

Smalltalk inspect is a powerful tool. Is there any (easy) way to get the information from inspect and show it in Transcript window or write into a file instead of showing it in new window?
I need it because I want to create a kind of debbuger for a program that runs as unix process (not a 'window' program) and logs information into a log file.
Thanks for help!
If you're asking whether something is built in, then I don't think so (although it would help if you tagged the question with which Smalltalk you are using).
Although it would be pretty easy to walk over the inst vars and roll your own (although maybe not for immediate objects), the "easiest" way might be to look at the inspector code and see how it operates. For example, in Pharo 4.0 one could (very basically) leverage the inspector code like so:
i := EyeInspector inspector: 1.
Transcript show: i objectClass; cr.
i elements do: [ :e | Transcript show: e; cr ].
which would print:
SmallInteger
'self'->1
'hex'->1
'octal'->1
'binary'->1
'character'->Character home
In Pharo, you can also get all of the Transcript output going to the console with:
NonInteractiveTranscript stdout install
If you are about debugging, you can have debugger interactions dump things into files (of course, you'll not be able to step in there but it can be useful for headless systems):
NonInteractiveUIManager compile: 'openDebuggerOn: process context: context label: title contents: contentsStringOrNil fullView: bool
| out |
out := VTermOutputDriver stdout.
out
<< ''NonInteractive Debugger: '';
<< title;
cr.
contentsStringOrNil ifNotNil: [ out << contentsStringOrNil; cr ].
(context stackOfSize: 20) do: [:s | out << s printString; cr ].
out << ''------------------------------''; cr; cr.
^ self nonInteractiveWarning: ''Opening Debugger''' classified: #'ui-requests'.
This and Sean's answer should go a long way.
You can get back to normal with the Transcript with:
ThreadSafeTranscript install.
Pharo 3.0 here.

How to increase Transcript buffer size?

I am working with Pharo 3 and I use the Transcript to record operations.
However the size of the current buffer is short for my needs. How to increase it? There is characterLimit but this is a method constant and therefore not easy to set up without changing a core package.
I do not want to use NonInteractiveTranscript because I want to stay in the image.
No, there is no other way to change the buffer length of the Transcript then to modify #characterLimit (usually of ThreadSafeTranscript). However, try inspecting ThreadSafeTranscript allInstances and you'll see that the underlying stream is much longer (50000 something is the write limit there). So, whatever you're printing to Transcript is not actually lost but just not visible.
That being said, using Transcript for extensive output is generally not a good idea because:
output is cut off (as you've already seen)
Transcript is really slow when called repeatedly:
try
1 to: 10000 do: [ :i | Transcript show: i ]
vs.
Transcript show: (String streamContents: [ :stream |
1 to: 10000 do: [ :i | stream nextPutAll: i asString ] ])
you can't use the output somewhere else (e.g. to write to file or pass along to a method)
In my opinion Transcript is ok for occasional quick debugging but shouldn't be used for anything application related.

How do I sleep for a few seconds in Smalltalk Pharo, and be able to interrupt this?

I'm debugging some keyboard event code and I want to loop with a sleep (to give me a chance to create the keyboard event), however when I do this Pharo won't let me quit with Command-. so debugging is difficult. I had to wait 500 seconds to fix something in the code below...
100 timesRepeat: [
Transcript show: 'Type an a... '.
(Delay forSeconds: 5) wait.
(Sensor keyPressed: $a) ifTrue: [ Transcript show: 'you pressed a' ].
]
So how can I make Command-. work, or is there something more suitable than (Delay forSeconds: 5) wait.?
Works fine in Squeak on Mac OS X (using peekKeyboardEvent, it does not have keyPressed:). So it's not your code's fault, interrupting this should work fine.
I am not entirely shure this works in Pharo, but in Squeak you can just fork your code in a new process, so it does not block the UI:
[
100 timesRepeat: [
Transcript show: 'Type an a... '.
(Delay forSeconds: 5) wait.
(Sensor keyPressed: $a) ifTrue: [ Transcript show: 'you pressed a' ].
].
] fork.
I just started with Pharo and it seems what you're really running into is still an issue among beginners (myself included). Looking at your code it seems you want to the Transcript to update every 5 seconds. Here is how to do it (comments included to make certain nuances clear).
| process | "If you're running outside a playground, you should declare the variable, otherwise you should not declare it because it needs to bind to the playground itself"
process := [
100 timesRepeat: [
Transcript show: 'Type an a... '; cr. "I like a newline, hence the cr"
(Delay forSeconds: 5) wait.
"In Pharo 10, the following doesn't work, still need to figure out how to do this"
"(Sensor keyPressed: $a) ifTrue: [ Transcript show: 'you pressed a' ]."
]
] fork.
process terminate. "You can run this to terminate the process inside the playground"
process suspend. "Also possible"
process resume.