March 2005 Entries

Online Gamer Stabbed - A Case for a Virtual Police Force and Courts?

Did you read about the Chinese online gamer who stabbed a competitor after the competitor sold his borrowed virtual sword?

One phrase in the article caught my attention:

But other experts are calling for caution. "The 'assets' of one player could mean nothing to others as they are by nature just data created by game providers," a lawyer for a Shanghai-based Internet game company was quoted as saying.

I don't buy that argument as I told my friend Walter who wrote about this from a legal perspective. The argument that the "assets" of one player means nothing to others just because its virtual actually applies to all property, physical or otherwise.

If society in general decides that virtual assets are somehow inherently different than physical assets, then it behooves the gaming companies to create a microcosm of the real world within the virtual space. Think about it for a second. If I'm paying $20 to $40 a month (not to mention my time) to play some online game, I want to make sure that if someone swindles me of property that has real-world market value, I have some means for recompense.

Can you picture it? Using a portion of the online gaming fees, these gaming companies might neet do hire virtual police force where characters can resolve violations of the "law". Likewise you might start a character to be a virtual lawyer to handle arbitration between characters. Heck, I'd probably create a character and provide consulting work within the virtual market

Look, your dragon slaying operations is suffering from bottlenecks at the weapons manafacturing plant. I've got some magic potions here that will integrate your Dwarven procurement system with an EERP (Elven ERP) system providing efficiencies in your supply chain. I can also build you a portal.

The problem with this of course is that a game world is not meant to be like the real world. For example most MMORPGs involve a lot of killing. Some even allow killing other characters. If my character were to kill your character and then take your stuff, should you be able to sue me for the real-world material value of your character's possesions? That would certainly put a damper on the game.

I don't think many game companies anticipated that virtual assets would command such real world market value and did not prepare for thes scenarios. It seems to me that they ought to require waivers from players indemnifying all other players and the company from any loss involving virtual assets. Either that, or put an in-game arbitration system in place and have players sign waivers stating they agree to resolve disputes via the in-game system first.

Now I've Seen It All. Harmonica Beat Boxer

This guy isn't half bad.

They're Hiring

I will neither confirm nor deny that this is where I used to work before going independent. I'll only say that they'll be doing some interesting work with .NET and it's worth checking out.

We are looking for Software Engineers. If you have a minimum of 3-5 software development experience - including at least 2 years of C#, ASP.NET and SQL Server and if you are interested in a position in Los Angeles (right near Miracle Mile) please post a comment.
[Via wagnerblog.com]

Rant: Beware of Misleading Credit Card Offers

Forgive me, but I really need to rant about shitty customer service. I've been burned bad by misinformation from ignorant and/or misleading reps. This time however, it involves our credit card company, Bank of America.

We received one of those offers where we can take a $10,000 loan for 6 months with no APR. Since we needed a short term loan we thought, "Hey, we'll take that, use part of it, put the rest in the bank, earn interest on it, then pay it back in 6 months."

Does a six month interest free loan of $10K sound too good to be true? We thought so. So we called customer service and double checked. We ran through various scenarios including asking,

So if we take this loan, and say make $1000 in purchases using our credit card, at the end of the month, if we only pay $1000 towards our credit card bill, we won't have to pay interest, right?

The reply?

Yep
So we don't have to pay the entire balance each month which would include this $10K loan, right?
That's correct.

It's a good thing we double checked today. It turns out in the fine print, "which we read multiple times", there was this opaque clause that now makes sense since I know what it means:

By making a balance transfer and/or using these checks, you understand that future payments will be applied to Promotional balances prior to any existing or new regular balances on the account.

So simply put, if we take the loan, and make $1000 in purchases, and then pay $1000 towards our bill, we'll now have $9000 as an interest free loan and a $1000 balance generating tons of interest.

Now you might say that's what you get for not reading the small print. Perhaps. But it bothers me that the amount of effort they put forth into making the message clear that this is a "0% Promotional APR for 6 months!!!" dwarfs the effort to make the big catch very clear.

It was obvious to me that they want consumers to take the loan and then not be able to pay it off at the end of 6 months because they want interest paying customers. But I figured we'd be safe if we paid it off in 6 months. Not so. What infuriates me is that we called customer service and they egregiously misinformed us (which is a pattern with some other financial institutions I've dealt with).

In any case, we've figured out an easy way to take advantage of this offer and get a $10,000 loan free for 6 months. Simply take the loan with no balance on the card and stop using that credit card for 6 months till you pay it back. Use another card. I think it'd be great if thousands, nay millions, of people did just that and sent a big "Fuck You" to B of A. But that's just me being vindictive. Not to mention I'd have to get a Slashdot sized crowd to read this. ;)

Ok, rant over. Thank you for your patronage.

Putting a Crimp in the RSS Fire Hose

fire hose In my post entitled Drinking From an RSS Fire Hose I dealt with some of the issues surrounding the flood of incoming RSS entries within an RSS aggregator raised by Dare's post "Nightcrawler Thoughts: Thums Up, Thumbs Down and Attention.xml".

The Keep It Simple Stupid Solution
Reading through some of the comments on both posts, I realize that for a great majority of users, a very simple system will satisfy their needs. One user mentioned that it'd be nice to be able to have items with specific keywords automatically marked as read. This is great if you're tired of hearing about, say, Paris Hilton. Add the keyword "Paris Hilton" and no longer will you have to endure her name in your aggregator.

A Short Story
I started to get a buttload of comment spam on this blog recently. I thought about using CAPTCHA, Bayesian Spam Filtering, etc... But in the end, I simply added a trigger modified from this one that simply blocks posts with a certain number of link. This resulted in a dramatic decrease in the number of posts about online poker and has been working quite well for me. At some point, I'll probably need to employ more sophisticated tactics, but for the time being, this simple rule works.

Extensibily Model
Personally, I think the initial solution isn't a filter at all, but the extensibility model prototyped by Torsten.

Rules Engine
On top of this, I'd probably build a simple rules engine plug-in similar to Outlook's. For example, you might create a keyword rule associated with one of the following actions: Mark as Read, Flag For Review, Give Priority, etc... As my short story above illustrates (see, there was a point to it), a simple rules engine approach will often give you the 80% of the 80/20 rule.

The Goal
The goal with this approach is to get something to the users quickly that will elicit feedback on what the pimped out "baysesian/collaborative/neural networked/throw dart at dartboard" filter should do. A collateral benefit is that users will inevitably create their own plug-ins (we hope) and we have the option to take the best ideas and integrate them as a first class feature.

[Listening to: So What'cha Want - Beastie Boys - Check Your Head (3:37)]

Visual Studio.NET New Project Template Folders

I'm sure you're constantly asking yourself this, because I certainly wake up every morning in a cold sweat wondering. When you add a new project in Visual Studio.NET 2003 (friends call her VS.NET), you get the following dialog

Add New Dialog
The beginnings of another bug ridden coding section...

Now looking at that familiar dialog underneath the "Project Types:" section, you probably noticed the usual suspects are there: "Visual C# Projects", "Visual Basic Projects", etc... But did you also notice there's a few other folders there such as "Visual C# Projects for DotNetNuke 3"?

Now for the million dollar question: How do you add your own folder there?

Reading through what I could find online, I understand how to use your .vsdir and .vsz files to create a new template, but I couldn't find anything that described how to create your own project type grouping.

So I did a little digging through the registry and found the following registry location:

HKEY_LOCAL_MACHINE\
    SOFTWARE\
        Microsoft\
            VisualStudio\
                7.1\
                    NewProjectTemplates\
                        TemplateDirs\

Just to make sure that adding keys to this location in the registry was sufficient, I brashly took Regedit (without even backing up my registry, an incredibly stupid thing to do), and created a new sub key, using SQL Query Analyzer and the newid() function to generate a new GUID for me. Under that key I added a sub key named "/1". Under that key I set the following three values as seen in this screen shot.

Registry Settings
Must this egotistical idiot use his last name in everything?

And here you can see the registry keys structure. The one I added is at bottom.

Registry Keys
Ahh, "Haack" is nowhere to be seen. Forturnately it's not a proper GUID.

So the next step is to create a VS.NET Setup and Deployment project to package my templates and add this registry setting automatically. Hope you can sleep peacefully now.

A Stable Application Entry Point - Rethinking "Main()".

Jason Clark enlightens us on creating a stable application entry point at the wintellect blog. I highly recommend reading the whole discussion, but one of the key takeaways is...

A Stable Entry Method

Fortunately it doesn’t take too much effort to make your entry method stable. Here are some guidelines:

  1. Avoid type-loads of any kind in Main. Main should only call methods implemented in the same type definition.
  2. Do not implement a static constructor in the type that contains Main. (Avoiding static fields entirely is the safest way to go).
  3. Derive the type that contains Main from Object. This means that you need to hoist Main out of the type definition for your main form if you use Visual Studio .Net wizards to create your project.
  4. Keep the type that contains Main focused on getting your application started. Don’t think of it as the “Application” type that holds your application-level state.
[Via Wintellog]

RSS Bandit Available on Download.com

Be sure to review it!

VS.NET Web Development Is a Memory Hog. 2GB or Not 2GB (i.e. 1GB)?

I've been playing with DotNetNuke at home and after an hour or two, VS.NET 2003 is typically using up around 150 to 200 MB of RAM as well as 200 MB of virtual memory. Web development on VS.NET is a big memory hog when the project size gets large enough.

Since I'm running on a paltry 512 MB of RAM (since when did 512 MB of RAM become paltry? I spent $99 to upgrade my Amiga from 512 KB of RAM to 1MB, but I digress) I thought it's about time to upgrade my system.

Looking at prices today, I found a nice deal on a 2GB Kingston kit (2x1GB) for $278.98. Not a bad price, eh? I'm wondering if 2GB is just plain overkill. Then again, if I run some virtual pc instances, I might want that extra memory.

Compare this to a Kingston 1GB kit (2x512MB) for $94.48. 1GB is a lot cheaper. I should mention that I have a 800 MHZ FSB so I'm looking at PC3200 DDR 400 memory cards.

Oh, and this post is my new personal record for number of acronyms in a post.

[Listening to: Sapphirecut - Free your mind - Danny Tenaglia - Back To Basics (CD2) (5:45)]

TestNG - It's like NUnit, But in Java

Good ideas get coopted. That's what they do. For example, JUnit was coopted by the .NET community and ported into NUnit. However in NUnit 2.0, the .NET community innovated with the usage of attributes.

Now Java has caught up with TestNG (thanks to Kyle for pointing me to this). With the brand spanking new version of Java out there (JDK 5.0 aka Tiger), Java now has Annotations. This is pretty much equivalent to Attributes in .NET. TestNG takes advantage of Annotations and builds a unit testing framework around them that looks quite familiar if you regularly use NUnit 2.*.

And I'm certain both these platforms have borrowed ideas from even older platforms.

ReSharper Return to Intended Price

ReSharper is no longer going to be the super bargain it currently is as they are raising the price. Even at $149, it's a worthwhile product. But if you see yourself doing development with VS.NET 2003 for a long while (and don't kid yourself, you will be), I highly recommend giving this product a twirl.

Well, the time has come for ReSharper to return to its originally intended price of $149 USD. The current $99 USD price was planned to be an introductory offer for the first month of its release, but we were having so much fun spreading ReSharper's angelic features to the .NET world, we just kept it that way for the last 9 months! Womb jokes aside, we'll be offering free upgrades from ReSharper 1.x to our new baby, ReSharper 2.0... is that a deal or what? And seeing that we don't like to be spoil-sports, we are giving a month long warning before the new price goes into effect on April 5th, so take advantage of the $99 offer now, while supplies last - and don't forget to tell your friends.

Get it here: http://www.jetbrains.com/resharper/buy/

-The JetBrains ReSharper Team

[Via JetBrains News]

Hiking the Paseo Miramar Trail

Today Akumi and I hiked the Paseo Miramar trail. To get there, simply take the PCH North till you get to Sunset. Take a right onto Sunset, and go up a little ways till you see Paseo Miramar on the left. Take it till it dead ends and look for street parking.

The trail itself almost instantly has gorgeous views of the Pacific and the coast line of Santa Monica. Unfortunately, today was a bit hazy. I was hoping for the clarity that most assuredly would have been the result had we hiked just after the rains.

The hike itself is around 5 miles (round trip) and at the end you're greeted with a nice overlook.

Paseo Miramar Hike

On the way back, we met one of the native inhabitants of the Santa Monica Mountains.

Snake

Chivas USA vs Osasuna

Went to watch a friendly between Chivas USA and Osasuna of La Liga (Spanish premier league) at the Home Depot Center Saturday night. Thanks to a soccer buddy Ed (who happens to work for Fox Sports World), Akumi and I were sitting in some pretty nice seats.
Near the end of the rather unexciting game, we were invited by Ed's friend to see the luxury suite. And when he said "THE" luxury suite, he wasn't kidding. It was the stadium owner's double sized suite right on the 50. Very nice.

Tried of Typing C# Properties?

Saw this post on Consult Utah's Weblog...

Is anyone else getting tired of writting:

...
private bool _autoAccept;
...
public bool AutoAccept 
{ 
  get {return _autoAccept;} 
  set {_autoAccept = value;} 
} 

I've pretty much decided that it is just preferable in these cases to just make the member variable public.  I'm am being bad?  ;-) 

My answer? I think he should be publicly flogged, smeared with tar and feather, and paraded around town in a pink tutu.

Ok, that might be a bit extreme. I don't really believe that.

I think using a public field is fine in some cases. If the interface is unlikely to change. If you're not writing library code. etc... If you can easily refactor it later if necessary. I'm not a complete and total purist about this.

Now before you OO purists get on my case, I'd like to point out that I always use full properties because I HATE FxCop warnings in my code. ;) Also, I have a nice little code expansion template in Resharper. I simply type

prop<TAB>

and I get a full expansion of the property, allowing me to fill in the type and private member name. You can see a description here.

Resharper also has an ALT+INSERT shortcut for generating code snippets, but I find my little expansion to be faster for me.

Although I'm not an OO purist nazi, I do think that the encapsulation benefits of properties are worth the trouble, especially when they aren't much trouble.

Drinking From an RSS Fire Hose

Firehose So now that you’ve subscribed to your 4000 feeds, how do you keep on top of the flood of incoming items? Dare talks about this, “the attention problem”, that faces power users of RSS (and ATOM) aggregators such as RSS Bandit.

Ideally a user should be able to tell a client, “Here are the sites I’m interested in, here are the topics I’m interested in, and now only show me stuff I’d find interesting or important”. This is the next frontier of features for RSS/ATOM aggregators and an area I plan to invest a significant amount of time in for the next version of RSS Bandit.

One way to think of it is that there’s a cacophony of content out there. You want an automated system to filter out the noise and allow through the music.

There are several difficulties inherent in any automated system designed to filter content based on your tastes and preferences. Often times, you don’t really know what you like till you see it. So how does the automated filter know if you’re going to like something or not? Well, you can train it by rating items. You can perhaps incorporate ratings of others. You can build rules about which content you like and dislike.

All of these methods run into the problem that your likes and dislikes tend to evolve and change over time as a product of your life’s experience and automated filters tend to narrow the items they allow through. If you set up hard rules for filtering data, you need to make sure to change them over time. A constant task of tweaking. If you’re using a system that requires you to train it based on an initial set of sample data, you have to make sure the training set is not to small or narrow. Otherwise the filter will only bring in items that meet some narrow facet of your personality. Collaborative filters are particularly prone to this problem. Think of how drab a lot of music on your mega-radio stations are today, a result of a gigantic collaborative filter. I doubt I could train a human to filter items for myself, much less an automated system.

This is not to say that filters can’t do a half-way decent job of being a personal editor. They can. The point here is that a really good filter has to allow a bit of noise through. My favorite radio station right now is KCRW, especially the show Metropolis. I don’t necessarily like everything Jason Bently spins, but I’m constantly being introduced to music that I’ve never heard of that I end up really enjoying. I believe that’s a result of a filtering system (human DJ) that allows some bit of noise through in order to expose listeners to new items.

Some noise is essential to a good content filtration system.

In any case, the effort to add filtration to RSS Bandit is of particular interest to me. Having studied Bayes theorem and read up a small amount on autonomous agent systems, I’m really excited about the potential for intelligent filtering in RSS Bandit. In particular, one method of filtering I like is creating de-facto editors via assignment. For example, like Dare, I read everything by Don Box. So I might assign a rule that always includes items by Don. But I might also go a step further and think that anything Don links to will probably be of interest to me, so I might state that everything he links to should be subscribed to automatically (perhaps based on my filtering rules based on how much I trust Don). Effectively, Don becomes an editor for my aggregator (without even knowing so). He becomes a content DJ.

Twiggy Update: She Likes That I'm Working From Home

A while ago I introduced you to Twiggy, our skittish Italian Greyhound. Well she's no longer the skittish type. Skittish was so five minutes ago she tells us. Now she's acting more like her full sized Greyhound cousins by being a total couch potato.
Especially now that I work from home and she's free from her exercise pen during the day. Her main agenda item for the day is making sure to find a nice sunbeam to lay in as evidenced in this photo taken today.
Twiggy Finds Her Beam
She works very hard to stay in the sunbeam as evidenced by this photo taken two days ago.
Twiggy struggles for the beam
I'm trying to get her to pull her own weight around the house. She eats, sleeps, pees, and poos but doesn't give anything back. So I tried to get her to write some code for me while I went to get a drink, figuring I could give the client a discount for work she does, but this is how I found her when I got back. Lazy dog.
Twiggy Sleeps On The Job

Customizing Keyboard Settings In RSS Bandit Part 1

Keyboard You might not know this, but RSS Bandit supports customizing keyboard shortcuts via an XML configuration file. The reason you might not know this is because it is an undocumented feature. Since I implemented adding customizability to keyboard shortcuts, I thought I might as well document how it works as of version 1.3.02.26.

First, I'm going to delve a bit into how Keyboard shortcuts are implemented in RSS Bandit before I highlight a tool I wrote for modifying the settings without having to dink around with the XML. As you read this discussion, imagine that I've placed a

using System.Windows.Forms;

at the top of this article. (Or for you VB.NET lovers out there, an Imports System.Windows.Forms).

Methods of Handling Keyboard Events
There are two methods for handling keyboards shortcuts in RSS Bandit. The first is by simply setting a Shortcut property of a MenuItem instance to a proper Shortcut enum value. Perhaps a very simple example will make it clear.

MenuItem item = new MenuItem();

item.Shortcut = Shortcut.AltF4;

In the above example, the key combination of Alt and F4 is mapped to a menu item. When the user presses the key combination of ALT + F4, that is equivalent to the user clicking on that menu item.

The second method is by implementing the PreFilterMessage method of the IMessageFilter interface on the main form.

This method allows us to intercept Windows messages (specifically keystrokes) before they are dispatched to a control or the main form. Here's a snippet of the implementation of the PreFilterMessage method:

public virtual bool PreFilterMessage(ref Message m) {

    bool processed = false;

    const int WM_KEYDOWN = 0x100;

    const int WM_SYSKEYDOWN = 0x104;

 

    try {

        if (m.Msg == WM_KEYDOWN || m.Msg == WM_SYSKEYDOWN) {

           

            Keys msgKey = ((Keys)(int)m.WParam & Keys.KeyCode); ... SNIP ...

At the end of this snippet, you'll notice there's a variable named msgKey of type Keys. This is a bitmask of the pressed keys that we'll use to determine which shortcut is being invoked. Make note of it as we'll mention it again later.

Associating Settings to a Command
Ok, so now that we have a rudimentary understanding of how the code can handle a keyboard event, let's look at how we configure the settings. As you might guess, we have two ways to configure a keyboard shortcut based on whether it falls under method 1 or method 2. In both cases, a keyboard shortcut is associated with a command name. For example, the "cmdCloseExit" command closes and exits RSS Bandit. Since there's a menu item associated with this command, we simply associate a Shortcut enum value to it.

For the command "GiveFocusToUrlTextBox", however, we need to use the PreFilterMessage approach. So we specify a comma separated list of Keys enum values. In this case, we have two different key combinations mapped to that command - F11 or Alt + D.

App Data Path
These settings are configured in a file named ShortcutSettings.xml. In a default installation of RSS Bandit, that file is compiled into the executable as a resource. However, you can override the default settings by placing a file named "ShortcutSettings.xml" (in the correct format) in the User Application Data folder for RSS Bandit. On my system the path is

C:\Documents and Settings\Phil\Application Data\RssBandit\

This file is a bit fragile, so be careful if you modify it by hand. It requires that every shortcut command have a definition. Below is an example that shows the structure of the shortcut settings file. You'll notice that under the root <shortcut> node, there are two main nodes: <menu> and <keyboardCombinations>

<?xml version="1.0" encoding="utf-8" ?> 
<shortcuts>
    <menu>
        <shortcut display="true">
            <command>cmdNewFeed</command>
            <shortcutEnumValue>F4</shortcutEnumValue>
        </shortcut>
        ... more shortcuts ...
    </menu>
    <keyboardCombinations>
        <shortcut>
            <command>GiveFocusToUrlTextBox</command>
            <keyCombination>F11</keyCombination>
            <keyCombination>Alt,D</keyCombination>
        </shortcut>    
        ... more shortcuts ...
    </keyboardCombinations>
</shortcuts>

Now we get to the reason for that whole discussion of the two types of shortcuts. In order to configure a shortcut correctly, you need to know which type it is, which is easily done by looking at the existing ShortcutSettings.xml file.

Configuring a Menu Shortcut
Configuring a "menu" shortcut is very easy. Just specify a valid Shortcut enumeration value in the <shortcutEnumValue> node.

Configuring a Keyboard Combination Shortcut
Configuring a shortcut that's invoked via PreMessageFilter's a little more complex. First of all, it's possible to have more than one key combination map to a single command. Hence the multiple <keyCombination> elements. For each <keyCombination> you can specify a comma separated list of valid Keys enumeration values.

One thing to note with the keyboard combination shortcuts is that it is possible (in some cases) to have the same key combination mapped to two different commands. This is because some commands are dependent on which control has focus. Unfortunately, the dependency of a shortcut on a control is not clearly mapped via the configuration file. That is definitely something worth looking into for a future release. The potential drawback to adding a controlname to the settings schema is the performance penalty of using reflection to determine if a control has focus. The potential benefit is that it may enable the code to be cleaner in the PreFilterMessage method.

Invoking a Command
So now you're ready to press a key on your keyboard, what happens next? Well in the case of a menu shortcut, that's handled by the operating system. For the nitty gritty, check out this blog post. The menu items are assigned their shortcut value via the ShortcutHandler class I wrote. This class reads in the configuration file and a menu item is mapped to its Shortcut enum value by simply calling

public Shortcut GetShortcut(string command)

In the case of a keyboard combination shortcut (i.e. PreMessageFilter), things are a little more tricky. We have a big chain of if else statements that run through the commands and checks each command to see if it was invoked and if the control associated with that command has focus (see snippet below).

if (this.listFeedItems.Focused && _shortcutHandler.IsCommandInvoked("CollapseListViewItem", m.WParam))

The IsCommandInvoked method first extracts a Keys enum bitmask from the m.WParam value passed in, which represents the keys that the user has pressed and is equivalent to the msgKey variable described earlier (I told you I'd get back to it). Afterwards, it iterates through each <keyCombination> value associated to the command being checked and combines the comma separated values into a Keys enumeration bitmask. This bitmask is compared to the extracted bitmask. As soon as a match is found, it returns true, otherwise it returns false. In this manner, we can determine which command is being invoked by a key combination.

Ok, So How Do I Configure Keyboard Settings Without Mucking Around With XML?
Well now that I've given you this background, which probably contains more than you'll ever want to know about how keyboard shortcuts are implemented in RSS Bandit, I must defer to part 2 of this series where I describe a simple utility I wrote for setting up shortcuts.

Customizing Keyboard Settings In RSS Bandit Part 2

In part 1, I bored you with excrutiating detail about how we keyboard shortcuts are implemented in RSS Bandit. In this second more exciting part, I'll outline how to use a tool I wrote for editing shortcut settings. You can consider it sort of an unofficial RSS Bandit Power Toy. When we really nail down a nice UI for editing shortcuts, we'll possibly add this as a configuration dialog within RSS Bandit. But for now, this is an experimental utility, use at your own risk.

To get started with the utility, download a debug version here.

First Time Usage
Figure 1 shows a screenshot of the editor as it looks when you first run it (assuming you don't already have a ShortcutSettings.xml file in your application data path). When you launch the editor, it automatically checks the default application data folder for a file named ShortcutSettings.xml. In this case, none was found so everything is blank.

Shortcut Editor - First Time
Figure 1: Shortcuts Editor, no file specified.

Create a ShortcutSettings File
The next step is quite simple, click on the Create button to specify a location to save a copy of the default settings file. The Save File dialog opens in the application data directory (see figure 2). This is where you'll want to save the settings file unless you've modified your RSSBandit.exe.config file and specified a different location for RSS Bandit's application data by adding an appSetting entry for "AppDataFolder" (generally not recommended).

Shortcut Editor Save Settings Dialog
Figure 2: Shortcut Editor Save Settings Dialog.

Modify Settings
Once you click the "Save" button, you'll see that the "Menu Shortcuts" and "Filter Shortcuts" are populated. Setting a menu shortcut is as simple as selecting the command you want to change in the "Command" droppdown and selecting a new value in the "Shortcut" dropdown and then clicking the "Set" button.

Setting Filter Shortcuts (PreFilterMessage method) works a bit differently. Next to the "Command" dropdown you'll see a list of "Keys". Each entry in this list corresponds to a <keyCombination> entry in the config file (as discussed in part 1). To add a new entry, click on the "Add" button which brings up a Shortcut Entry Form as in figure 3.

Shortcut Editor Entry Form
Figure 3: Shortcut Editor Entry Form.

This little form implements IMessageFilter itself, so when you click in the text area, it interprets your keystrokes and displays the keys you've pressed. You can specify a key combination by pressing the individual keys one at a time. For example, if you wanted a command to be invoked by pressing ALT+F4+F5 all at the same time, you'd type ALT followed by F4 followed by F5.

When you are satisfied with your key combination, click "OK". This will result in a new key combination being mapped to the command as shown in the closeup in Figure 4.

Shortcut Editor Detail With New Filter Entry
Figure 4: Shortcut Editor Detail With New Filter Entry.

Saving Your Settings
Finally, don't forget to click the "Save" button to save your new settings. This will bring up a dialog (like the Create button did) allowing you to specify where to save the file. Feel free to save multiple versions of these settings files and share them with friends and family.

If for some odd reason, a change you make causes RSS Bandit to crash a horrible flaming death, merely delete the ShortcutSettings file or Create a new one and start from scratch.

Conclusion
If you're a keyboard control maniac, I hope this temporarily satisfies your thirst for being able to configure keyboard settings within RSS Bandit. As this is an ongoing project, there will be improvements and your suggestions will be taken into consideration.

What's the Demand For a Decent Open Source Web Based Aggregator?

Forgive me for not doing a thorough search for prior art, but I noticed this post by Oleg Tkachenko in which he's in need of a decent server-side aggregation code base.

The reason it caught my attention is that "server-side" is my specialty. That's where I've spent the great majority of my time in my various day jobs. But doing server-side work is like living underground. Every once in a while, you need to get cleaned up, shave, and poke your head aboveground and let humanity see your face. That was partly my motivation in haacking on RSS Bandit. I wanted to get more exposure to working on a "client-side" product. (The other part is that it was such a kick-ass product, I had to get involved.)

Taking the core RSS Bandit "NewsComponents.dll" and slapping it on a server for simple server-side aggregation probably wouldn't take much work at all. What's would be more interesting (and a lot more work) is taking it a step further and building a full-featured web-based aggregator.

Using so called "Ajax" techniques (which is really just a repackaging of existing technologies, including Microsoft's proprietary XMLHttpRequest object and should be called POXSCRIPT for Plain Old Xml and Scripting), I can envision a web-based RSS Bandit that looks and functions quite similarly to the rich client. We could call it "RSS Bandit Web Access" as a nod to "Outlook Web Access".

Now, the need for such an undertaking is dampened by the fact that RSS Bandit has a very nice synchronization mechanism so that using it from multiple computers isn't a huge pain. Necessity being the mother of invention, this feature makes it so that I personally don't see a web-based version as a huge necessity.

The other dampener is that I do have to pay my bills via billable work now. This would be the type of project I'd love to dive into and work on full time. The temptation would probably be too great and I'd neglect so many responsibilities to do so, becoming that underground mole I mentioned earlier. ;)

Anyways, this is just food for thought. How popular would such a package be? How many people have a web-server to install it on?

[Listening to: Intec Jimpy / Talkin (Terrantella Vs Redanka Remix) - Sasha - Global Underground Ibiza CD 1 (7:03)]

Going Independent Status Update

In case you were wondering, I thought I'd tell y'all how it's going. First of all, I've somehow picked up a southern accent as evidenced by my use of "y'all".

Health Insurance: I thought about limiting our insurance to taking vitamins and being VERY careful, but apparently that isn't perceived as being very responsible nor wise. So with that option being, how shall I put it, stupid, Akumi and I researched the various options out there and settled on two individual PPO plans with Blue Shield. Getting two individual plans is cheaper than the cheapest family plan which was overkill for us. Since we don't have kids and are both relatively young and relatively healthy, this is a good option for us.

Errors & Ommisions Insurance: I've read that this is important for independent contractors, but as I never make mistakes, I couldn't imagine that it would apply to me. Even so, I am looking into it. In the meantime, I had my lawyer buddy Walter give me some wording for my contract to cap my liability at the amount of the contract. That was an idea that Jeanine, my former boss at Solien gave me when I had lunch with her recently. Right now, none of my contracts require E&O coverage. Do you need E&O coverage?

The Contract: Immediately after quitting my job, the start date for the bulk of the development work got pushed back to June. This kinda came out of left field, but that's the risk I took by going independent. As it stands, it is actually working out nicely as I'm getting a much needed break. Tomorrow we're getting started on some billable architecture work. We'll definitely have some work to do, just nothing full time till June I think. In the meanwhile, I'm pursuing a couple leads on some short term work to fill in the time in between in case I need to do so.

RSS Bandit 1.3.0.26 Released!

RSS Bandit Logo After much hard work by Dare and Torsten (and a little bit of hard work by yours truly), the Wolverine edition of RSS Bandit has been released to the public.

If you're still using a 1.2.* version, I think you'll really like the extra polish and speed of this latest version.

Some highlights to note:

  • Newspaper Styles - Compatible with FeedDemon.
  • Column Chooser - Specify the columns to display in the list view.
  • Item Deletion - A hotly demanded feature.
  • and more...
For the full release notes, visit here.

Also be sure to check out the updated docs for more details on these new features.

Run, don't walk, and download it here!

Good Korean Food in Los Angeles

Korean Food I've been told that for the best Korean food in the world, you need look no further than Los Angeles. The rationale is that you have a vibrant Korean community in Koreatown (affectionately known as K-Town) with access to better quality meat and vegetables than exists in the homeland.

I'm not ready to start an international food fight by saying whether or not I agree with that remark, but I will tell you about some of my favorite places for Korean food in Los Angeles. This is the short list.

Soot Bull Jeep - Korean Bar-B-Q
3136 W. Eighth St., (213) 387-3865.
Be careful as your clothes and hair will smell like smoke leaving this place. Instead of your usual gas grills, they use coals to cook the meat at your table.

Beverly Soon Tofu ("BehVuhLee Soon Dubu" in Korean)
ph: 213.380.11132
717 West Olympic Boulevard #108
Los Angeles, California 90006-2642
Northwest Corner of Olympic and Vermont
This place has the BEST soon dubu. HIGHLY RECOMMENDED. The interior is small, but decorated such that it looks like you're between two straw thatched houses in rural Korea. The Bi-Bim-Bap is very excellent as well.

Mandarin House
3074 W. Eighth St., (213) 386-8976.
This is in a strip mall just west of Vermont.
Don't let the name fool you, this place has the best Cha Jang Mein around. To refresh your memory, that's the thick noodles with the dark brownish black sauce made from black beans, onions, and meat.

Chosun Galbi
3330 W. Olympic Blvd., (323) 734-3330.
Just west of Western and Olympic.
This is a more conservative choice for Korean Bar-B-Q than Soot Bull Jeep, as you won't stink. It's more authentic than Woo Lae Oak.

Hodori
1001 S. Vermont Ave., No. 102, (213) 383-3554.
24 hour Korean restaurant for those late night Korean cravings.

BCD Tofu House (SoonDubu)
3575 Wilshire BlvdLos Angeles, CA 90010
(There's actually a couple of locations in L.A.
(213) 382-6677
This place is nowhere near as good as Beverly Soon Tofu, but it's open 24 hours so it works as a fine backup for those late night munchies. In the late hours (after 2 AM), expect hungry bar-hoppers and club-goers. Watch out for projectile vomit. I've had my own bad experience there which involved a bowl of Mandu Soon Dubu and a few hard drinks prior.

[Listening to: Saints & Sinners - Pushin Too Hard - Darren Emerson - Global Underground 015 - Uruguay Disc 1 (7:25)]

Google Is Giving Back

Looks like Google is responding to some critics who say Google takes all and doesn't give back to the open source community. They've just launched Google Code, Google's place for Open Source software.

From their FAQ:

What is code.google.com?
Code.google.com is our site for external developers interested in Google-related development. Its where well publish free source code and lists of our API services.

Who are the people behind code.google.com?
A lot of people worked together to both prepare source code for release and prepare code.google.com for launch and ongoing maintenance. We really care about free and open source software (F/OSS) at Google, and this site is one aspect of that affection.

Via Google Blog - Live.

Someone Is Using My Credit Card

Crook Apparently someone went on an online shopping spree with our credit card number. Fortunately the Bank of America protection kicked in before they could do too much damage and we will not be charged for the fraudelent activity. It's mind bogglingly easy for someone to fraudelently use your credit card. Think about that the next time you hand your credit card to the waiter at the local dive.

In any case, there are a couple of options at your disposal. My friend Walter writes about placing a fraud alert on your credit file as well as the California Credit Freeze option. I highly recommend taking a look at this whether or not you've been a victim of credit card fraud.

Walt also writes about Internet Fraud in this post.

An Abstract Boilerplate HttpHandler

My last project involved writing a lot of HttpHandlers to respond to HTTP requests originating from a cell phone. To simplify my life, I created an abstract base handler that handled a lot of the repetitive tasks in writing an HTTP handler.

So today, I read Scott Hanselman’s post about the boilerplate HttpHandler he uses. He says one day he'll get more organized and make an abstract base class to handle this kind of boilerplate stuff.

I've got your back Scott.

I went ahead and took my base class and quickly (about 10 minutes) incorporated some of the things he has in his boilerplate and voila! An abstract base class! Enjoy.

/// <summary>
/// An abstract base Http Handler for all your
/// <see cref="IHttpHandler"/> needs.
/// </summary>
/// <remarks>
/// <p>
/// For the most part, classes that inherit from this
/// class do not need to override <see cref="ProcessRequest"/>.
/// Instead implement the abstract methods and
/// properties and put the main business logic
/// in the <see cref="HandleRequest"/>.
/// </p>
/// <p>
/// HandleRequest should respond with a StatusCode of
/// 200 if everything goes well, otherwise use one of
/// the various "Respond" methods to generate an appropriate
/// response code.  Or use the HttpStatusCode enumeration
/// if none of these apply.
/// </p>
/// </remarks>
public abstract class BaseHttpHandler : IHttpHandler
{
    /// <summary>
    /// Creates a new <see cref="BaseHttpHandler"/> instance.
    /// </summary>
    public BaseHttpHandler() {}
 
    /// <summary>
    /// Processs the incoming HTTP request.
    /// </summary>
    /// <param name="context">Context.</param>
    public void ProcessRequest(HttpContext context)
    {
        SetResponseCachePolicy(context.Response.Cache);
 
        if(!ValidateParameters(context))
        {
            RespondInternalError(context);
            return;
        }
 
        if(RequiresAuthentication
            && !context.User.Identity.IsAuthenticated)
        {
            RespondForbidden(context);
            return;
        }
 
        context.Response.ContentType = ContentMimeType;
 
        HandleRequest(context);
    }
 
    /// <summary>
    /// Indicates whether or not this handler can be
    /// reused between successive requests.
    /// </summary>
    /// <remarks>
    /// Return true if this handler does not maintain
    /// any state (generally a good practice).  Otherwise
    /// returns false.
    /// </remarks>
    public bool IsReusable
    {
        get
        {
            return true;
        }
    }
 
    /// <summary>
    /// Handles the request.  This is where you put your
    /// business logic.
    /// </summary>
    /// <remarks>
    /// <p>This method should result in a call to one 
    /// (or more) of the following methods:</p>
    /// <p><code>context.Response.BinaryWrite();</code></p>
    /// <p><code>context.Response.Write();</code></p>
    /// <p><code>context.Response.WriteFile();</code></p>
    /// <p>
    /// <code>
    /// someStream.Save(context.Response.OutputStream);
    /// </code>
    /// </p>
    /// <p>etc...</p>
    /// <p>
    /// If you want a download box to show up with a 
    /// pre-populated filename, add this call here 
    /// (supplying a real filename).
    /// </p>
    /// <p>
    /// </p>
    /// <code>Response.AddHeader("Content-Disposition"
    /// , "attachment; filename=\"" + Filename + "\"");</code>
    /// </p>
    /// </remarks>
    /// <param name="context">Context.</param>
    public abstract void HandleRequest(HttpContext context);
 
    /// <summary>
    /// Validates the parameters.  Inheriting classes must
    /// implement this and return true if the parameters are
    /// valid, otherwise false.
    /// </summary>
    /// <param name="context">Context.</param>
    /// <returns><c>true</c> if the parameters are valid,
    /// otherwise <c>false</c></returns>
    public abstract bool ValidateParameters(HttpContext context);
 
    /// <summary>
    /// Gets a value indicating whether this handler
    /// requires users to be authenticated.
    /// </summary>
    /// <value>
    ///    <c>true</c> if authentication is required
    ///    otherwise, <c>false</c>.
    /// </value>
    public abstract bool RequiresAuthentication {get;}
 
    /// <summary>
    /// Gets the content MIME type.
    /// </summary>
    /// <value></value>
    public abstract string ContentMimeType {get;}
 
    /// <summary>
    /// Sets the cache policy.  Unless a handler overrides
    /// this method, handlers will not allow a respons to be
    /// cached.
    /// </summary>
    /// <param name="cache">Cache.</param>
    public virtual void SetResponseCachePolicy
        (HttpCachePolicy cache)
    {
        cache.SetCacheability(HttpCacheability.NoCache);
        cache.SetNoStore();
        cache.SetExpires(DateTime.MinValue);
    }
 
    /// <summary>
    /// Helper method used to Respond to the request
    /// that the file was not found.
    /// </summary>
    /// <param name="context">Context.</param>
    protected void RespondFileNotFound(HttpContext context)
    {
        context.Response.StatusCode 
            = (int)HttpStatusCode.NotFound;
        context.Response.End();
    }
 
    /// <summary>
    /// Helper method used to Respond to the request
    /// that an error occurred in processing the request.
    /// </summary>
    /// <param name="context">Context.</param>
    protected void RespondInternalError(HttpContext context)
    {
        // It's really too bad that StatusCode property
        // is not of type HttpStatusCode.
        context.Response.StatusCode =
            (int)HttpStatusCode.InternalServerError;
        context.Response.End();
    }
 
    /// <summary>
    /// Helper method used to Respond to the request
    /// that the request in attempting to access a resource
    /// that the user does not have access to.
    /// </summary>
    /// <param name="context">Context.</param>
    protected void RespondForbidden(HttpContext context)
    {
        context.Response.StatusCode 
            = (int)HttpStatusCode.Forbidden;
        context.Response.End();
    }
}

You can also download the file from http://tools.veloc-it.com/

MMORPGs Come Of Age - WOW Reviewed on NPR

World of Warcraft On my drive home today (being my last day at work. I'm now fully independent.), I was listening to the show "All Things Considered" on NPR. Much to my surprise there was a reviewer named Robert Holt who was describing his experience with World of Warcraft, a massively multiplayer online role playing game.

If you're not in the know (and I assume most of my readers are), an MMORPG is basically Dungeons & Dragons, but online in full color 3-D with surround sound. Holt describes an adventure in which he and four other real people are deep within the bowels of a thieves den, looking to nab the leader.

What struck me about this review is hearing it on NPR. As a kid, Dungeons & Dragons (or D&D for short) was a guilty pleasure. Guilty because I knew how stigmatizing it would be for people in some of the circles I ran with to find out I played. This was during the years where I was a bit of a closet geek.

It didn't help when you heard from church types that playing the game would cause you to worship Satan, drink goats blood, and give you a "Go Straight To Hell, Do Not Pass Go" card.

Strangely enough, I've never killed anyone as a human sacrifice as a result of dabbling in the game. In fact, I felt that I learned good planning skills, improved my vocabulary, and learned how to tell a story as a result.

So now that RPGs have gone mainstream, I guess I can dust off my old D&D books, my oddly shaped dice, my back issues of Dungeon magazine, and display such items proudly. Until my wife gets tired of the clutter it would cause and kindly asks me to place them elsewhere. :)

Writing Games for the T-Mobile Sidekick

Tiny Charms No, this post will make no mention of Paris Hilton. Promise. (Fully realizing that if I had mentioned her, it would attract an undesirable crowd via Google).

I don't personally have a T-Mobile, but a former coworker of mine recently had a game he developed published on the T-Mobile stack. The royalties he receives from just one game is an eye opener. He can't retire (nor quit his day job) just yet, but it sure is a nice source of extra income. His entire lineup so far can be seen at his site.

It's almost enough to get me more involved in programming for mobile devices. It's like the early days of programming on my Commodore. On many phones, you have roughly the same constraints. I wouldn't be surprised to see a Commodor 64 emulator on a cell phone.

Apart from my limited contribution to RSS Bandit, I tend to be a server-side developer. My last project was to expose my company's platform to mobile devices over HTTP. I didn't spend much time in the client end of it at all.

Blogging about Blogging about Blogging about ...

In the 1998 movie, The Big Hit, the protagonists kidnap the daughter of an extremely wealthy Japanese businessman. When they call to deliver the ransom notice, they turn to Gump who employs a brand name Trace Buster to prevent police from tracing the call.

Unbeknownst to Gump, the father has a Trace-Buster-Buster at his disposal. This in turn triggers Gump to use his Trace-Buster-Buster-Buster in an ever escalating battle to evade detection.

I just love that scene.

But I'm not here to talk about movies. I've recently noticed a post or two critical of the practice of blogging about blogging i.e. Metablogging.

I couldn't help but to think of the Trace-Buster-Buster-Buster... The irony of dismissing blogging about blogging is that in doing so, you're blogging about blogging about blogging. And in highlighting this, I'm blogging about blogging about blogging about ... well you get the picture. I've become a Meta-Meta-Meta-Meta blogger.

Not that I'm taking sides here. If you find blogging about blogging boring, so be it. It is quite easy to avoid it. But I doubt it will simply die off. Unless of course blogging itself dies off.

I recently walked through the hallowed shelves of the local Borders book store in Santa Monica with my wife and noticed an entire shelf of books on writing. The thought that we might see the end of writing about writing never occured to me.

What is Your Blogger Handle?

TRS 80 My first couple computers (TRS 80 and Commodore 128) led very solitairy lives. Though they had the constant company of a human companion, they lacked the familiar company of their own kind. I wonder if they longed for a form of communication that didn’t require a fur-less primate jabbing his fingers at a keyboard.

Commodore 128 To foster computer to computer community, I purchased my first Modem for my third computer, an Amiga 500. Not content to slap on just any modem, I shelled out the big bucks (in 1 dollar bills from tips bagging groceries) for a 1200 baud modem. That’s a full four times faster than the laggard 300 baud modem my loser friends were using at the time.

Amiga 500 It didn’t take long before I was a member of every bulletin board on the island (Guam). Then again, it doesn’t take long to join two or three boards. Surprisingly, Guam actually had more BBS systems running out of people’s homes than you’d think. I was a co-sysop of one board for a short while.

When signing up for a BBS (Bulletin Board System) membership, it was essential to choose a good handle. Now you might mistakenly associate a handle and a login as the same thing. But you’d be way off base. Way off base. A handle was much more significant than a measly login. It was your identity in the interconnected ASCII based world of the BBS. A well chosen handle was a reflection of your personality, or lack thereof.

Maverick Think Top Gun. He didn’t go by the moniker “TomC1962” he was freakin’ Maverick! How lame would a trucker feel if she was known as Ellie32. No way man. For those long lonely rides on the freeway, a trucker needs a handle with character and panache. Something that generates r-e-s-p-e-c-t. When you get on the CB, you’re talking to “Storm Chaser” and “Rusty Nail”, not some lame ass “JohnD” or “JaneD” (no disrespect if that really is your handle).

F-16 So back in those days I went by the handle “Dragon”, assuming it was available. I felt it was a good reflection of my east-meets-west heritage and love for LOTR. Of course every geek boy with a modem was after that handle, so I started going by “Falcon”, in reference to my favorite fighter jet, the F-16. I couldn’t be content with the handle “Dragon_Int64.MaxValue”

Nowadays, this concept of the “blogosphere” evokes some of the same feelings that the early days of the BBS craze (if you could call it that) did. Part of that feeling is rooted in the use of handles by some bloggers I know. For example, you might say Dare’s handle is “Carnage4Life”. Eric Porter dons a cape and parse tree and becomes the Human Compiler. Robert Scoble sprinkles his link juice (rel="please do follow") across the blogosphere because he is... (cue dramatic music) The Scobleizer.

And my handle? Well it should be obvious that it is “Haacked”. It’s basically a play on my last name, “Haack” (pronounced “hack”). So if I post as Haacked in your blog’s comments, it’s not to hide my identity. It’s merely a nod to a bygone era in online history. Just a small way to add a little flavor to an otherwise boring world of bits and bytes. It’s not my goal to hide the fact that my real name is ... *transmission ended*

Blogging With a Beer And In Pajamas

I saw this post on Scoble's Link Blog...

I anxiously await the day when a blogger is no longer charaterized in publications like the New York Times as a "guy sitting in his pajamas drinking beer."

...and I put my beer aside and told myself I really should change out of my PJs.

Quick and Dirty Guide to Configuring Log4Net For Web Applications

UPDATE: I wrote a post with notes on getting this to work with ASP.NET 2.0.

Looking around, I noticed a lot of people struggling with getting Log4Net to work with their web applications (ASP.NET 1.1). I’m not going to spend a lot of time digging into Log4Net here, as you can do a Google search for that. But I will give you a quick and dirty guide to quickly getting it set up for a website. Bar of soap not included.

Using a Separate Config File

Although you can put your Log4Net configuration settings within the web.config file, I prefer to use a separate configuration file. Log4Net is a bit of an elitist. It won’t dare put a FileSystemWatcher on web.config nor App.config. However, if you tell it to use its own config file, it will gladly monitor that log file and update its settings on the fly when the file changes.

Specifying the Log4Net Config File

If you use a separate config file, a quick and easy (and dirty) way to have your application find it is to place the config file in the webroot and add the following attribute to your AssemblyInfo.cs file.

[assembly: log4net.Config.XmlConfigurator( 
ConfigFile="Log4Net.config",Watch=true )]

Declaring the Logger

At the top of each class that I plan to use logging in, I declare a logger like so:

private static readonly ILog Log = LogManager.GetLogger( 
MethodBase.GetCurrentMethod().DeclaringType);

The reason I place a logger in each class is to scope it to that class. If you read the log4Net docs, you’ll see what I mean by this.

Using the Logger

Once you’ve declared the logger, you can call one its logging methods. Each method is named for the logging level. For example:

Log.Debug("This is a DEBUG level message.  
Typically your most VERBOSE level.");

Now whether that message shows up in your logs depends on how you’ve configured your appenders and the logging level you’ve set. Don’t understand what that means? Read the Log4Net introduction.

Sample Web Solution

In order to make all this discussion very concrete, I’ve gone ahead and did all your homework for you by creating a simple ASP.NET 1.1 web solution (Log4NetSampleSolution.zip ) using Visual Studio.NET 2003. After unzipping this solution, you should be able to build it and then view the default.aspx web page. This page will log a few very interesting messages of varying levels to three appenders.

Of special note is the use of the RollingFileAppender as seen in this snippet.

<appender name="RollingLogFileAppender"    
        type="log4net.Appender.RollingFileAppender">

    <file value="..\Logs\\CurrentLog" />
    <appendToFile value="true" />
    <datePattern value="yyyyMMdd" />

    <rollingStyle value="Date" />
    <filter type="log4net.Filter.LevelRangeFilter">
        <acceptOnMatch value="true" />

        <levelMin value="INFO" />
        <levelMax value="FATAL" />
    </filter>

    <layout type="log4net.Layout.PatternLayout">
        <conversionPattern 
        value="%-5p %d %5rms %-22.22c{1} %-18.18M - %m%n" />
    </layout>

</appender>

Note that the file value (with backslashes escaped) points to ..\Logs\CurrentLog. This specifies that Log4Net will log to a file in a directory named Logs parallel to the webroot. You need to give the ASPNET user write permission to this directory, which is why it is generally a good idea to leave it out of the webroot. Not to mention the potential for an IIS misconfiguration that allows the average Joe to snoop through your logs.

[Listening to: Envy / Faith - Deep Dish - Global Underground 021 - Moscow CD2 (4:51)]

It's Easier To Find Your Blog Than You Think

My friend Walter points out some common sense advice about how not to get fired for the content of your blog. Of course this issue has been beaten to a pulp in the larger blogosphere, but one thing he mentioned caught my attention.

Of course most employers will probably never find any particular employees blog, and if they did they probably would not be offended.

You'd be surprised how easy it is for an employer to find your blog without even meaning to. Unless you restrict your content to something quite unrelated to any of your company's interest, your content is almost certainly going to have posts that are related to your field. That's when Google kicks in. The large majority of my referrals come from Google searches. Often from people searching for information on a particular technical problem I just happened to write about.

That's why the solution is to hide your identity and lie lie lie!

Building a Better Extensibility Model For RSS Bandit

Currently, the only plug-in model supported by RSS Bandit is the IBlogExtension interface. This is a very limited interface that allows developers to write a plug-in that adds a menu option to allow the user to manipulate a single feed item.

The ability to interact with the application from such a plug-in is very limited as the interface doesn't define an interface to the application other than a handle. (For info on how to write an IBlogExtension plug-in, see this article.)

So last night I was thinking about an email that a user sent me. He wants to know how to add an intelligent module for filtering and classifying RSS feeds to RSS Bandit. This is for his thesis project.

Since that might not be a necessary feature for RSS Bandit at this point, I suggested that he implement it in a private build. RSS Bandit is open source and he can easily obtain the source code. But it occurred to me that there are hundreds of feature requests like this that have the potential to be great solutions in the future, but would be best implemented as a plug-in in the near term.

So as I sat there thinking about it, Torsten goes ahead and implements a prototype for it. Mind you, I hadn't talked to Torsten nor Dare about this yet, but I'm sure it's something we've all been thinking about. Especially as the number of feature requests continues to accumulate.

So what areas of extensibility might we want to support? Well there's the callback on feed item download that Torsten implemented. That alone is quite useful. I can imagine building a plug-in that would score items based on how likely I'd want to see it or not. Thus it could sort items based on importance in a special feed. This would help with the increasingly large number of feeds I'm subscribed to.

Dare mentioned (and he'll write more on this) the idea to support new data sources and formats. For example, it'd be interesting to build NNTP support as a plug-in.

I'd also like to build the ability to provide an interface for plug-in data storage. For example, suppose you build a plug-in that keeps statistics of your RSS feeds. You might want that stored with your settings so that when you synchronize RSS Bandit, your plug-in settings are synchronized as well.

]

Thinking far out there, perhaps adding the ability to support IE toolbars might be an option as I think some users actually use RSS Bandit as their primary browser.

The important thing is to design a nice interface to expose the RSS Bandit application to the plug-ins. In tandem we need to prioritize these extensibility options by looking at current feature requests and thinking about how many of them could be implemented as plug-ins.

The Freshest BreakDancing Ever!

A coworker sent me this video of some of the most amazing breakdancing I've seen. And no, this isn't that pop locker dude. Check out the way he does push-ups near the end. Amazing.