I had a few reasons to look at my subscription list this morning - I needed to create a "human readable" reading list to share with people who aren't necessarily familiar with RSS/Atom. Yes, I could have just taken the *gag* OPML list from BottomFeeder and applied XSLT, but I know Smalltalk way, way better than I know XSLT. And besides, I was going to have to remove the feeds that come from scripts or ssh gateways. So, I opened a workspace in Bf and started hacking out a quick script. I ended up having to match against three things to exclude things from the list, so I created a "function" (i.e., a block) to do that:
"A block to define match strings"
matchBlock := [:matcher :string | ((matcher match: string) or: [matcher match: string])].
That provides the matching code. Next, gathering the appropriate links, filtering out things unique to my setup - the ssh based feeds that use my Linux box, the file urls that use scripts, and the PubSub feeds that I've set up:
"grab the content"
dict := Dictionary new.
feeds := RSSFeedManager default getAllMyFeeds.
feeds do: [:each |
| title |
title := (each title copyWithout: Character tab) trimBlanks.
((each url isNil) | (matchBlock value: '*victoria*' value: each link) | (matchBlock value: '*pubsub*' value: each link) | (matchBlock value: '*file://*' value: each link))
ifFalse: [dict at: title put: (Array with: each url with: each link)]].
That just runs through the internal data, and sets up a simple dictionary - the key is the title of the feed, and the value is an array - html page and xml feed link. With that in hand, I had a shortened list, and I was able to dump out the data in tabular form:
"now write the content, as HTML and as plain text"
out := WriteStream on: (String new: 10000).
out2 := WriteStream on: (String new: 10000).
out nextPutAll: '<html><body>'; cr.
out nextPutAll: '<table width="50%" border="1" cellpadding="3"><tr>'; cr.
out nextPutAll: '<td>Title</td><td>HTML Page</td><td>XML Page</td></tr>'; cr.
matcher1 := '*victoria*'.
matcher2 := '*file://*'.
matcher3 := '*pubsub*'.
dict keysAndValuesDo: [:key :value | | first last |
first := value first isNil ifTrue: [''] ifFalse: [value first].
last := value last isNil ifTrue: [''] ifFalse: [value last].
((matchBlock value: matcher1 value: last) | (matchBlock value: matcher2 value: last) | (matchBlock value: matcher3 value: last))
ifFalse: [out nextPutAll: '<tr>'; cr.
out nextPutAll: '<td>', key, '</td>'.
out nextPutAll: '<td>', first, '</td>'.
out nextPutAll: '<td>', last, '</td>'.
out nextPutAll: '</tr>'; cr.
out2 nextPutAll: first; cr.
out2 nextPutAll: last; cr]].
out nextPutAll: '</table>'; cr.
out nextPutAll: '</body></html>'; cr.
That dumps two things - an HTML file, and a flat text file (but to internal streams). One more snippet and we have it to the file system:
"dump to external files"
file := 'bf-html-list.html' asFilename writeStream.
file nextPutAll: out contents.
file close.
file2 := 'bf-list.txt' asFilename writeStream.
file2 nextPutAll: out2 contents.
file2 close.
And that's it. It's pretty nice to be able to do all that without exporting the data and running it in the development environment - because I have all the tools I need in my runtime environment.