Friday, March 5

Microsoft Roadshow Returns to Tech Valley

Microsoft Northeast Roadshow

The Microsoft Northeast Roadshow is back in town, this time Friday, March 12th at the Troy Hilton Garden inn. Developer Evangelist Jim O'Neil has a blog post with further details.

Coffee: I prefer to buy 'local,' but ...

I prefer to buy local/do business locally when possible, but almost invariably I order my coffee online from remote (to varying degrees) vendors.

Case in point: I would love to buy my coffee from Uncommon Grounds, an Albany/Saratoga coffee house, roaster, bagel shop venture with two locations. I believe they're about 18 years old. They've recently become fairly social media savvy; I don't know specifically how long they've been online in general, but they seem to have a decent enough online coffee & t-shirt store.

Uncommon Grounds seems to have a strong following in the local community. Having been to the Saratoga location in the distant past, and perhaps once to the Albany location in more recent times, I personally don't have any particular attachment to their physical locations. I don't eat out (or get out, for that matter) often, and when I most urgently need to restock my bean inventory, (during code crunch time) I need it to be as quick and painless as possible, while interrupting my workflow as little as possible as well. This typically leads to online ordering.

When I order coffee, first and foremost I want fresh-as-possible, quality beans of varying roasts that arrive as quickly as possible. Close behind that, I prefer organic, fairtrade, preferably shadetree grown beans. After that it's all about price and customer service.

I just placed my second order with Porto Rico Importing Co. out of NYC, (three hours away) tipped to me by Ed Costello. (thanks again Ed!) I placed an order in February on a Sunday, and it arrived Tuesday morning. The beans were, while maybe not the absolute best I've ever had, pretty darn good. They've been in business some 103 years, their website isn't anything flashy, and as far as I can tell, they're not particularly active when it comes to social media. Their store has much more diverse offerings than coffee and t-shirts however, including filters, syrups and machines.

While I prefer to do business locally, I, currently an independent consultant with irregular income, have a hard time justifying a 44% premium on coffee, a staple that I generally consume 2lbs. of each month. I have to wonder, is it a simple matter of profit margin? Or is it a differential driven by sales volume, or diversity of inventory? 44% is a steep premium for buying local.

Monday, March 1

All coming together ...

As I approach the start date for a new fulltime role and work to wrap up outstanding consulting obligations, it's been a crazy busy few weeks with not enough sleep, food or fresh air! However, it all seems to be coming together.

A quick snapshot of my morning, the culmination of days of grinding it out:

It's all coming together

  • We got StorageByMail's revamped, Rackspace Cloud-converted site launched.

  • We've been prototyping a creative multi-product stealth-mode SaaS offering for a NYC-area startup. I've been acting in both consulting CTO and lead dev roles, and have had the first chance in a while to work with Java and Tomcat applications commercially.

  • My work continues on a near-beta Twitter business services app; that app is now growing to include LinkedIn and Facebook as well.

  • I continue work on a still-stealth multi-social platform biz app I'm partnered with a great team out of NYC on. (Public beta by mid-March!)

As part of these efforts, in the last three weeks I have:
  • Started using ADO.NET Entities and LINQ to Entities instead of LINQ-to-SQL. Entities seem gosh darn piggish when it comes to RAM consumption -- even when managing repository lifetime in what I understand to be best-practice fashion. I don't recall that issue with LINQ-to-SQL. Generating the model/updating the model from the database is painful when it comes to some relationships that seem to require manual removal every regeneration. I hope Microsoft gets it right in 4.0.

  • Got over my fear of lambda expressions, at least fairly simple ones. I still need to fully grok compiled functions. Baby steps.

  • Realized yet again how much MS ASP.NET AJAX stinks. Obscure and painful to work with.

  • Written a .NET consumer for the Twitter Streaming API. I haven't come across any other full implementations in .NET, though my basic incremental HTTP consumer is modeled after others' examples. I will probably publish this to Google Code soon.

  • Started working with Google Buzz.

  • Written a .NET PubSubHubbub subscriber client and callback handler. This has been published to Google Code.

  • Worked with the new Buzzzy API on top of Google Buzz. Useful API for my needs, but the 250 requests/hour limit is kneecapping.

  • Created my own Google Buzz firehose by crawling 4.2M Google Profile IDs (crawl in-progress now, finally got a nice multi-threaded crawler purring) and subscribing to push notification for all of their Buzz feeds through pubsubhubbub.appspot.com. Unfortunately 4.2M is still not the complete set of profiles, but it's a great start.

Haven't yet had time to catch my breath or catch up on sleep ... but there's light at the end of the tunnel! More launches/betas to come! And more aggravation, learning and ... "opportunities" along the way I'm sure :)

Sunday, February 28

.NET PubSubHubbub Subscriber Client

Finally had a chance to clean up the simple .NET PubSubHubbub client and callback handler I wrote last weekend. Currently implements just a Subscriber Client and an ASHX handler for processing subscription verification and push notification receipt.

I don't believe pubsubhubbub.appspot.com supports localhost callbacks, so you'll need to have a publicly exposed IP address or domain name to host the callback handler. I use DynDNS myself. (I could be wrong, I'm a dilettante when it comes to this PubSubHubbub stuff.)

NPubSubHubbub - find it here.

Saturday, February 27

Unpaid product endorsement: Sorel "Blizzard" Boots

In the midst of the wet, heavy snow followed by rain, freezing rain and sleet upstate New York has enjoyed this week, I've been appreciating the heck out of my winter boots. I have had these boots since I delivered papers and cleared driveways as a kid, some 16-17 winters now.

The boots in question are Sorel "Blizzard" Boots, rated to -35 or -40. I believe I got them at some sort of outlet or discount store in the Lake George "Million Dollar Half Mile," and they are one of the earliest and also most worthwhile, lasting clothing/gear purchases I ever made. I think I paid $45 or $55, maybe a little more. You can find an example here:

Sorel Blizzard Boots (sold out)

Sierra Trading Post - Sorel Blizzard Boots (sold out)

I'm not sure if mine were made in China or not, as it looks like the most recent generations were. I can't find the Blizzards on the Sorel website, so sadly, it's possible they don't even make them anymore.

These boots have been great for everything from newspaper delivery to dog walking to snowblowing to walking through downtown slop after parking six blocks from the office to being urinated on by cats. The original laces, after a cleaning, are still going strong. The waterproof bottoms are still 100% intact and keeping my feet dry. The nylon uppers are still in like-new condition as well. The liners are in great shape as well -- no real signs of wear on anything, 16 or 17 years later. These are a great utility muck boot for winter and early spring. I wish more products made these days had this kind of value and staying power.

Parting word: I hope Sorel still makes the Blizzard -- I don't see another men's winter boot in their lineup that fits the same niche at the same price point.

Monday, February 22

My First Google Buzz App: Who gives a frak?

Subhead: My impression of Google Buzz/the Google Buzz "API."



As most are probably aware, Google Buzz recently launched, while with much less fanfare than the anti-climactic and rather disappointingly immature, but retaining potential, Google Wave, still plenty of hype, noise and clamor.

OK, Google Buzz itself is decent. Nothing super special, but it has potential. Aggregation can make my life easier in various projects. I'm not wowed by it, but, without having had anything in the way of expectations, I'm not disappointed, either.

Now, on the API side ... WHAT API!? There's really not a whole lot of programming to do against Google Buzz yet. Users can associate various accounts for aggregation from other well-known social media services, and Google Buzz provides some XFN markup for non-standard content or services to enable custom associations:

<link rel="me" type="text/html" href="http://www.google.com/profiles/your.username"/>

As far as I can tell, though I'll grant you my weekend was spent on the output side of Buzz and not so much the input, the only way to get content into Google Buzz right now is by an associated service or feed -- which can include your own RSS or ATOM feed, but you can't directly post an update to Google Buzz via API.

On the consumer side, you can pull a user's Buzz updates as ATOM using:

http://buzz.googleapis.com/feeds/{33-digit profile ID}/public/posted "ugly URL"

(You are supposed to be able to use your username, i.e. andrew.badera, instead of that 33-digit profile ID but apparently those so-called "friendly URLs" aren't pushing updates as properly or completely to the hub as the ugly URL-based subscriptions.)

You can subscribe to push notifications of a user's feed via pubsubhubbub.appspot.com. I didn't spend a ton of time digging, but in what time I did spend, I didn't find a .NET PubSubHubbub client subscriber or push notification callback handler floating around the internets. I've written a C# subscriber utility and an ASP.NET .ASHX handler for subscription verification and push callback receipt and handling which I'll be posting later today after hosting it somewhere like Google Code.

So far, Google Buzz and its API seem like Just Another Aggregator. Yes, the PubSubHubbub protocol is pretty freakin' cool, and, at least for now, seems highly responsive when it comes to subscription requests and push deliveries. I'm not yet sure however what's going to entice mainstream social network users to make the leap to Buzz. I'm compelled for business purposes, not personal, and social networking apps don't make the leap without personal engagement and enthusiasm.

Sunday, February 21

Update: Custom URL Shortener

This is an update to a previous piece on shortening a URL/encoding an ID.

TSQL CHARINDEX starting at position zero returns an unexpected value of ' '. TSQL updated to do some explicit swaps around position 0, as well as to better randomize the encoding characterset.

ShortenID:


CREATE PROCEDURE [dbo].[ShortenID]
@idToShorten numeric(18,0),
@debug bit = 0,
@shortenedID varchar(20) OUT
AS

BEGIN
SET @shortenedID = 'NULL ID'

IF @idToShorten < 1
RETURN

DECLARE @i int = 0
DECLARE @ShortChars varchar(100) = 'TrCn8A2faBpb95ceDYdFhGjK6kmL3NxORPqSUtV7XvyH4zuQMsgJ'
DECLARE @charBase int = LEN(@ShortChars)
DECLARE @currentCharacter char
DECLARE @currPos int

DECLARE @shortened varchar(20) = ''

WHILE @idToShorten > 0
BEGIN
IF @debug = 1
BEGIN
PRINT CONVERT(varchar(20), @i) + ': @idToShorten: ' + CONVERT(varchar(20), @idToShorten)
END

SET @currPos = (@idToShorten % @charBase)
IF @debug = 1
BEGIN
PRINT '@currPos: ' + CONVERT(varchar(20), @currPos)
END

SET @currentCharacter = SUBSTRING(@ShortChars, @currPos, 1)
IF @debug = 1
BEGIN
PRINT '@currentCharacter: ' + CONVERT(varchar(20), @currentCharacter)
END

IF @currentCharacter = ' '
BEGIN
SET @currentCharacter = 'Z'
END

SELECT @shortened = @currentCharacter + @shortened
IF @debug = 1
BEGIN
PRINT '@shortened: ' + CONVERT(varchar(20), @shortened)
END

SET @idToShorten = FLOOR(@idToShorten/@charBase)
SET @i = @i + 1
END

SELECT @shortenedID = @shortened
END


ExplodeID

CREATE PROCEDURE [dbo].[ExplodeShortID]
@shortenedID varchar(20),
@debug bit = 0,
@idExploded numeric(18,0) OUT
AS

BEGIN
SET @idExploded = -1

IF LTRIM(RTRIM(@shortenedID)) = ''
RETURN

DECLARE @ShortChars varchar(100) = 'TrCn8A2faBpb95ceDYdFhGjK6kmL3NxORPqSUtV7XvyH4zuQMsgJ'
DECLARE @charBase int = LEN(@ShortChars)
DECLARE @currentCharacter char
DECLARE @currPos int = 0

DECLARE @exploded numeric (18, 0) = 0

DECLARE @lenShortenedID int = LEN(@shortenedID)
DECLARE @i int = @lenShortenedID
DECLARE @pow int = 0;
DECLARE @currPosSource int = 0

WHILE (@i > 0)
BEGIN
IF @debug = 1
BEGIN
PRINT '@i: ' + CONVERT(varchar(20), @i)
END

SET @currPosSource = (-1 * ( @i - LEN(@shortenedID) ))
IF @debug = 1
BEGIN
PRINT '@currPosSource: ' + CONVERT(varchar(20), @currPosSource)
END

SET @currentCharacter = SUBSTRING( @shortenedID, @currPosSource + 1, 1 )
IF @debug = 1
BEGIN
PRINT '@currentCharacter: ' + CONVERT(varchar(20), @currentCharacter)
END

IF @currentCharacter = 'Z'
BEGIN
SET @currentCharacter = ' '
END

SET @currPos = CHARINDEX(@currentCharacter, @ShortChars COLLATE Latin1_General_CS_AS)
IF @debug = 1
BEGIN
PRINT '@currPos: ' + CONVERT(varchar(20), @currPos)
END

SET @pow = POWER(@charBase, @i-1)
IF @debug = 1
BEGIN
PRINT '@pow: ' + CONVERT(varchar(20), @pow)
END

SET @exploded = @exploded + (@currPos * @pow)
IF @debug = 1
BEGIN
PRINT '@exploded: ' + CONVERT(varchar(20), @exploded)
END

SET @i = @i - 1
END

SET @idExploded = @exploded
END