For making backups, it is a common problem to copy a large tar file from one machine to another in the same network. Normally I would use commands like:
SourceServer> ncat -l --send-only 1701 < backup.tgz
DestinClient> ncat SourceServer 1701 > backup.tgz
Then compare the checksums:
DestinClient> cksum backup.tgz
This is taken from the NMap group. Recently a problem came up where the client did not have ncat, I thought to use Pharo instead so the client code is below (lifted from the SocketStream class comment):
| file socketStream result blocksize writeStream |
blocksize := 1024.
file := File openForWriteFileNamed: 'backup.tgz'.
[
socketStream := SocketStream openConnectionToHostNamed: '127.0.0.1' port: 1701.
[ [ socketStream binary.
[ socketStream atEnd ] whileFalse: [
result := socketStream next: blocksize.
file nextPutAll: result ] ]
ensure: [ socketStream close ] ]
on: ConnectionTimedOut
do: [:ex | Transcript show: ex asString; cr. ex resume]
] ensure: [ file close ]
The address '127.0.0.1'
needs to be changed to the IP where the ncat listener is already running once local testing is done.
With the Pharo project progressing as it has, it is necessary to load Pier on Pharo 9. This should also help to Retrieve HTTPS from Pharo.
A good introduction to baselines is on Github.
Pier work started with a separate baseline for core, add-ons, to-do, google, etc. This matched the setup on the previous code repository - Smalltalkhub. Problems would occur when loading the larger projects, the package dependencies are:
PierAddons -requires-> PierCore -requires-> Pillar
When everything cleanly loads, things work well. Recently Pillar loads with with dirty package (internal code is updated), so after loading PierCore, Pillar is attempted to reload when loading PierAddons. This causes the load time to drag on for an hour.
Another recent problem is that the tests would not load due to missing the core class PRObjectTest
from the Pillar-Tests-Core
package.
To bypass these problems, it was easier to create a large Pier baseline, which has labels for each of the possible parts - addons, todo, etc.
One way to test faster is create the BaselineOfPier
class in the image and load it without writing to the repository.
Suppose one has a wiki page for a project, named Smallwiki
. Sub-pages can be added and link back to the main page with the text *..*
. If the project changes name to Pier
, then a single page can have the title changed and all of the sub-pages have the correct name.
An example of a parent link for this page is *..*
which goes to Updates, and *../..*
goes to John Borden.
A problem occurs when reading in a wiki during an upgrade. Each page is read as an independent entity with no reference to the other pages. A link to a parent like *..*
is changed to *.*
. This is the expected behavior when editing the root page.
One option is to link to the parent/grandparents/etc to be fully qualified before exporting:
(PRKernel instanceNamed: 'pier') root enumerator all; do: [ :page | page outgoingReferences do: [ :link | (link isInternal and: [ link target ~~ page and: [ page parents includes: link target ] ]) ifTrue: [ link reference: link target absolutePath ] ] ].
If one is not importing into the latest Pier code, this can convert back to relative links with:
(PRKernel instanceNamed: 'pier') root enumerator all; do: [ :page | page outgoingReferences do: [ :link | (link isInternal and: [ (link reference first = $/) and: [ page parents includes: link target ]]) ifTrue: [ link reference: (page relativePathTo: link target) ] ] ].
Recently I removed an old task from a PierToDo. I noticed it raised a walked-back from PRAdminRemoveCommand>>#doValidate
. The problem was the message self pierAdminAnnouncer
returned nil. I was able to reproduce the problem in a clean Pier image using a simple component:
- Create a component and choose the
counter
option - Increment the counter (it needs some sort of data)
- Click remove and confirm. Error is the same PRAdmin remove problem
Removing the page that contains the component does not raise the error.
Another problem is that http://localhost:8080/piersetup does not open due to missing the TWBSTabWidget
class.
A straight-forward way to resolve this is to remove Pier-Admin from the Pier baselines. It is part of the add-Ons baseline, and the extentions are in part of the core baseline.
I recently started playing a game against my wife called anagrams. Since she is rather good at it, my first attempt was to use command line and the MacOS dictionary:
0 Johns-MBP$ grep '^[pcilas][pcilas][pcilas][pcilas][pcilas][pcilas][pcilas]$' /usr/share/dict/words | grep -v ll
aplasia
asialia
classic
...
This wasn't very fast since it took too long to type the pattern, and also to guess what double-letter options should be avoided.
Next, created some Smalltalk code for possible matches:
| pattern matches stream |
pattern := 'ufmets' asBag.
matches := OrderedCollection new.
stream := '/usr/share/dict/words' asFileReference readStream.
[ stream atEnd ] whileFalse: [ | word boolean |
word := stream nextLine.
( word size <= pattern size and: [ (word allSatisfy: [ :c | pattern includes: c ]) and: [ boolean := true.
word asBag doWithOccurrences: [ :letter :number | (pattern occurrencesOf: letter) < number ifTrue: [ boolean := false ] ].
boolean ] ] ) ifTrue: [ matches add: word ] ].
stream close.
matches sorted: [ :a :b | a size > b size ]
This takes care of the double-letter problem, and won my first game. Unfortunately it requires a change in the pattern (the text ufmets
above).
To make this faster, the next step is to allow the pattern as a command-line argument, this booklet explains the idea. Creating the class:
CommandLineHandler subclass: #WordCommandLineHandler
instanceVariableNames: ''
classVariableNames: ''
package: 'Custom-CommandLine'
On the class side, created:
commandName
^ 'word'
Instance side has:
activate
self activateHelp
ifTrue: [ ^ self ].
self arguments size = 1
ifFalse: [ self printHelp.
^ self exitFailure: 'Missing Arguments' ].
self
printAnagrams;
quit
and:
printAnagrams
| pattern matches stream |
pattern := self arguments first asBag.
matches := OrderedCollection new.
stream := '/usr/share/dict/words' asFileReference readStream.
[ stream atEnd ]
whileFalse: [ | word boolean |
word := stream nextLine.
(word size <= pattern size
and: [ (word allSatisfy: [ :c | pattern includes: c ])
and: [ boolean := true.
word asBag
doWithOccurrences: [ :letter :number |
(pattern occurrencesOf: letter) < number
ifTrue: [ boolean := false ] ].
boolean ] ])
ifTrue: [ matches add: word ] ].
stream close.
(matches sorted: [ :a :b | a size > b size ])
do: [ :str |
str size > 2
ifTrue: [ self stdout
print: str;
lf ] ]
After staving the image, this can be ran as:
0 Johns-MBP$ ./pharo ./anagram.image word pcilas
'spical'
'spica'
'scalp'
'salic'
...
As I was writing this, 3Blue1Brown released a wordle video that is related.