BashPodder Praise and Roaming Feed Profile

As I’ve been doing some travelling, I’ve had to set up podcast clients on my laptop. Two issues: * It’s a hassle to copy podcast client settings from one PC to another. What I like about a service like bloglines is that you’re free to move between PCs. That’s not so easy with Podcast clients which sit on your PC rather than a web server. * It’s a hassle to install a new podcast client and add all the feeds. I’d like to just have a list of feeds, and tick those I’m interested in. Instead, there’s a whole lot of mousing involved for every single feed. That’s using ipodder and BlogMatrix sparks, haven’t set up .net framework to test the others.

So I decided instead to boot into linux and “set up” bashpodder. Bashpodder config is so simple: just edit a single text file with all the feeds and run it. Bashpodder is a tiny podcast client written by linc, 44 lines of bash elegance. There’s still some issues with certain feeds and torrents, though the contributions on the bashpodder site apparently fix a lot of that.

Also, I wanted a way to permanently track my podcast feeds, so I can have something like the roaming profile that I have on bloglines. So the result was a little mod that wgets the config file from a webserver, and only looks at lines beginning with “http”. I’ll send it to Linc (Mr. BashPodder), but here are the main change if you want it.

The main changes were:

  • Download the config file if a URL argument is specified
  • Read only lines beginning with “#”
  • Also “touch” podcast.log as I encountered an error running it first time (unmodified), if the log doesn’t work.

Here’s the full script after those few changes.

!/bin/bash

By Linc 10/1/2004

Find the latest script at http://linc.homeunix.org:8080/scripts/bashpodder

Last revision 12/14/2004 - Many Contributers!

If you use this and have made improvements or have comments

drop me an email at linc dot fessenden at gmail dot com

I'd appreciate it!

Ensure podcast.log exists

touch podcast.log

Grab fresh bp.conf from a URL if specified

if [ ! -z $1 ] ; then if [ -e bp.conf ] ; then mv bp.conf bp.conf.old fi wget -O bp.conf $1 fi

Make script crontab friendly:

cd $(dirname $0)

datadir is the directory you want podcasts saved to:

datadir=$(date +%Y-%m-%d)

Check for and create datadir if necessary:

if test ! -d $datadir ; then mkdir $datadir fi

Delete any temp file:

rm -f temp.log

Read the bp.conf file and wget any url not already in the podcast.log file:

while read feed ; do if [ ${feed:0:4} == 'http' ] ; then file=$(wget -q $feed -O - | tr 'r' 'n' | tr ' " | sed -n 's/.url="([^"])".*/1/p') echo -e "---nFrom $feed, downloading:n$file" for url in $file ; do echo "Downloading $url" echo $url >> temp.log if ! grep "$url" podcast.log > /dev/null ; then wget --progress=dot -q -P $datadir "$url" fi done fi done < bp.conf

Move dynamically created log file to permanent log file:

cat podcast.log >> temp.log sort temp.log | uniq > podcast.log rm temp.log

Create an m3u playlist:

ls $datadir | grep -v m3u > $datadir/podcast.m3u

Refactoring from super.setUp()

Cedric rightly points out that JUnit’s “Call super” is a serious antipattern. This happens a lot in my experience too – project-wide test classes that where any special setUp() must call super.setUp(). JUnit has served its purpose admirably, but I think there are now more effective test frameworks (I’d like to check out JBehave, TestNG, and Artima SuiteRunner), but widespread familiarity with the JUnit dictates that JUnit is often the de facto choice.

Encouraging or forcing people to call super is completely evil. It brings out the worst of OO inheritance. What can go wrong? I’ll use JUnit to illustrate, though the points relate to any OO architecture, not just JUnit or testing. These are the main problems:

  • The subclass author might forget to call super.setUp(), or might not be aware its necessary.

  • super.setUp() might one day change behaviour, so the assumptions of subclasses are no longer valid. The subclass may be using a variable that is no longer initialised, for instance.

  • Now that we’re using this “sometimes call super” pattern, subclass authors have to tread delicately … any non-final, non-private, method in the superclass is now in play … it’s a candidate for calling super from the subclass. How about tearDown(). Do I have to call super.tearDown(). Only way to find out is to inspect the code, rely on documentation, or ask someone. Hopefully, it won’t change. How about some of those protected utility methods that I’m allowed to override for greater granularity? Do I have to call super on them too?

The best antidote is usually a template method. That’s the thing to do in the case of JUnit:

public class SpaceGameTest extends TestCase { // UNCOMPILED!

public <b>final</b> void setUp() {

    createTeams(); // Example of project-wide setup
    <b>afterSpaceGameSetUp();</b>
}

protected abstract afterSpaceGameSetUp();

}

--

public class BattleTest extends TestCase { ... public void afterSpaceGameSetUp() { score = 0; }

}

Great, the subclass author is now forced to override afterSpaceGameSetUp() and write setUp(). Immediate notification from your friend, AKA the compiler, if you forgot. And the method tells you exactly when it will occur in the test lifecycle.

BTW I’m not sure I agree with Cedric’s idea on multiple calling setUpAnything()? What order would it call them in? Use reflection to call superclasses first, then subclasses, and so on down to the bottom? It would be a confusing violation of OO (even beyond that reasonably performed by JUnit for testing purposes). Or maybe alphabetical order, so you’d end up with a convention of setUp01Blah() … but that would defeat the purpose of separating out base classes from subclasses. I’m fairly happy with the template class solution as it keeps the test framework model simple.

Also, if, for some reason, you insist on requiring super.setUp() (“industry standard” is one such reason, though I don’t buy it), then it’s worth easing the pain as follows: * Declare anything “final” that you don’t expect to be overridden. Makes it more obvious when you do need to override things. * I strive to avoid comments where possible, but that’s because I favour neat code and clean architecture over extensive documentation. Since the “call super” design is error-prone, this is one time where comments really are needed, so explain what needs to be done. * Basic psychology suggests people will inevitably forget to call super. If the consequence is subtle, people will be frustrated as they spend time trying to work out what’s gone wrong. Make the consequence slap in the face with an assertion: in the base class, write a “final” test method that checks the assumptions set up by the base setUp() (create a flag variable if necessary, to test that base setUp() has been called).