Older blog entries for johnnyb (starting at number 177)

24 Aug 2007 (updated 24 Aug 2007 at 12:10 UTC) »

Been working on some stuff for Evidence-based health search. It's still under development.

Announcing: ActiveShipping for Ruby and/or Rails

I just got the initial version of ActiveShipping ready to go. ActiveShipping is a new shipping API for ruby based on projects that my company is developing internally.

Google Code Page:

http://code.google.com/p/activeshipping/

ActiveShipping Documentation:

http://dev.newmedio.com/activeshippingdocs/

How to Launch a Website

I have been frustrated at the several website launches (heavy on the technology - not just static stuff) that my company has performed. I think this is primarily because customers don't understand technology or project management. So here are several launch tips and why I think they are important:

1) Do not wait until a site is perfect to launch.

This is important, because a site will never be perfect. But what's great is that a website can always be changed. If you wait until a site is perfect to launch it, then it will be forever delayed, the developers will be frustrated, the customer will be frustrated (why haven't you launched my site? Because you're being unreasonably picky!).

People often get the false impression that writing code is like writing a book. The editorial process should be fairly predictable and at the end everything should work. But this is a false analogy. If you were to liken a software launch to a book launch, and demanded the same standard of perfection, you have to remember that if your code is off just a little then the site doesn't work. That's like saying that a book must not only not contain any typographical errors, but that it must also not contain any factual or logical errors. In that case, you would need to vet each book by several practitioners, philosophers, and the like, and not release the book until the full content of the book was agreed by all that it is logically sound. As you can see, this could take a while, and there is no predictable way to tell when this process will be finished.

So what you _have_ to do, to finish at all, is decide that at a certain point below perfection, it is reasonable to launch. Most importantly, your real bugs will be found by your customers after you launch anyway. So trying to clean up something that you're going to have to rewrite later is just a waste of time.

Not only that - as you spend your time "perfecting", the client will spend their time thinking -- and coming up with new ways that the site is lacking, thereby postponing the launch almost indefinitely.

So the long and short of it is, LAUNCH. Even if small problems remain, launching is the only way to move forward. Without a strong ethic of launching, the site will sit forever in purgatory.

2) Launch only when people will be around to watch it break. That means that you should NEVER launch on Friday, and NEVER launch in the evening.

The problem is that #1 and #2 are in constant conflict. Clients usually have to be "ready" to launch. They don't get "ready" to launch until the evening. I have countless experiences with this. If they are "ready" to launch in the evening and you wait a day, then in the morning they will no longer be "ready" to launch. What's worst is if they are ready to launch thursday evening, because then, if you delay it, you wind up on friday, which you should NEVER EVER launch on. Then, if you delay until Monday, not only have you lost pretty much a whole week, but the client has 4 DAYS to think about new ways in which they don't like the website! This will take at least until Thursday to implement, at which point you are in the same boat.

How to Solve It?

I'm really not sure. The only thing I can think of is to stress to the client (a) the importance of launching, (b) that just because you launch doesn't mean that it can't change or be fixed, and (c) that sometimes you just need to get something out the door for real-world feedback, and that endless speculation does not help to improve a site, it only delays its launch. Also, I should add (d) that you should launch early in the day because you the client should expect some initial issues. I've never done a large-scale web technology launch that didn't need some first-day post-launch fixes.

Having said that, I must admit that I will launch a site in the evening and on a friday, for the simple fact that if I don't I know that the project will never really end. A project _can_ be successful if it has starting bumps. A project can _never_ be successful if it is never launched.

Everyone should check out Hanson's new website. Yes, that Hanson. They've grown up a lot since the old days, and their current music is fantastic. You can listen to their whole album from their website - the music player is on the top right of the page. Check out "Great Divide" especially.

Converting a Base64-encoded SHA1 hash to a hex (hexadecimal) encoded SHA1 hash

We are getting user accounts from a new company. Thankfully, they do SHA1 encoding on their passwords like we do. However, depending on which subsystem they are in, some of them are encoded in BASE64 and some are encoded as HEX. Ruby, as far as I'm aware, only handles hex digests for SHA1. Therefore, I had to figure out a way to convert these two formats. I'm sure someone else has already figured this out and there's an easy tool for this. But, if not, here's my ruby code:


sha1_hash_base64 = "nSOeWicjE1xBYo9b7fB3e9XLYhQ="
sha1_hash_hex = 
   sha1_hash_base64.
   unpack("m")[0].
   unpack("aaaaaaaaaaaaaaaaaaaa").
   map {|x| sprintf("%x", x[0])}.
   join

The output is 9d239e5a2723135c41628f5bedf0777bd5cb6214 which I believe to be correct (I don't have my data in front of me, so I can't be sure). At minimum, it is the right length :)

So now, I can use this value to compare against the output of Digest::SHA1.hexdigest.

Hideous annoyances with IVR (voice and touch-tone interactive) systems:

  • If an IVR doesn't have a specific option to speak to a human, the IVR should be deactivated permanently, because the owners are too stupid to use one.
  • Unless there is a REALLY good reason, IVRs should make it _easy_ to do everything with the dial pad. Are you really doing secure banking over the phone if you have to speak your password or even your account number or your SSN? It is just easier for the entire interaction if you just push numbers.
  • Don't sound like you are being conversational. IVR systems ARE NOT conversational, so pretending to be so is just stupid and annoying. "Let me see" is a colloquialism of a human speaking. If you are not a human, you shouldn't be speaking this way. I hate using the phone anyway, and if you make me listen to idiocy while I'm on the phone I'm going to hate your company for all eternity.

If I'm going to bother to interact with an IVR, then I want it to be:

  • Serious. I'm making a serious call. If I wanted to talk to someone about a non-serious subject, I'd call my 4-year-old.
  • VERY CLEAR. Most IVR calls I make are about money or service. I want the communication to be clear and exact. If an IVR says "Ummm, okay", it is being personable. If they want the pretext of being personable, then GIVE ME A FREAKIN PERSON! Likewise for dialing versus speaking. Dialing is unambiguous. If I dial the wrong thing, it's my fault. But speaking is a different story. Speaking can be very ambiguous. So while I am not a big fan of any IVR, I _prefer_ those that can be dialed to those that you have to speak to. Both sides of the communication must be clear and exact. Don't BS around.
  • Optional. Computers are stupid, and computer systems are quirky. I don't know all of the quirks of your system, but I can bet that an operator does. So if there is some question, it should ALWAYS be an option to go to a real human being.
Simple Tabbed Pane/Panel using Rails

app/helpers/application.rb:


def tabbed_panel(base_id, &block)
	panels = []


concat("<div class='tabbed_panel' id='" + base_id + "'>\n", block.binding); concat("<ul class='tabselector' id='" + base_id + "_tabs'>\n", block.binding);

yield panels panels.each do |panel| concat("<li class='pane_tab tab_unselected' id='" + panel[2] + "_tab'>" + link_to_function(panel[0], "select_panel('" + base_id + "', '" + panel[2] + "')") + "</li>", block.binding) end

concat("</ul>\n", block.binding); concat("<ul class='tabpanes' id='" + base_id + "_panels'>\n", block.binding); panels.each do |panel| concat("<li class='panel_panel panel_unselected' id='" + panel[2] + "_panel'>" + panel[1] + "</li>\n", block.binding) end concat("</ul>\n", block.binding); concat("</div>\n", block.binding); end

public/javascripts/panel.js:


function select_panel(baseid, panel_id_base) {
	var tabs = $(baseid + "_tabs").childNodes;
	var tab_id = panel_id_base + "_tab";
	var panel_id = panel_id_base + "_panel";
	$A(tabs).each( 
		function(the_tab) {
			if(the_tab.nodeName == "LI") {
				the_tab.removeClassName("tab_unselected");
				the_tab.removeClassName("tab_selected");
				if(the_tab.id == tab_id) {
					the_tab.addClassName("tab_selected");
				} else {
					the_tab.addClassName("tab_unselected");
				}
			}
		}
	);
	var panels = $(baseid + "_panels").childNodes;
	$A(panels).each(
		function(the_panel) {
			if(the_panel.nodeName == "LI") {
				the_panel.removeClassName("panel_unselected");
				the_panel.removeClassName("panel_selected");
				if(the_panel.id == panel_id) {
					the_panel.addClassName("panel_selected");
				} else {
					the_panel.addClassName("panel_unselected");
				}
			}
		}
	);
}

public/stylesheets/panel.css:


/* Clearly you need better CSS, but this is a decent start */
.panel_selected { display: block; }
.panel_unselected { display: none; }
.tab_selected { background-color: gray; }
.tab_unselected { background-color: white; }

Then, to use it in your rails app, just put this in the view:


<% 
#3-panels to choose from in the following example:
tabbed_panel 'all_props' do |panels| 
	panels << ["Form Properties", render(:partial =>
"basic_info"), "fprops"]
	panels << ["Toolbox", render(:partial =>
"toolbox"), "tbox"]
	panels << ["Element Properties", render(:partial =>
"properties"), "eprops"]
end 
%>
PLEASE STOP USING THE HOLLY HACK FOR HASLAYOUT

If you need to get an IE element to get HasLayout, the method is very simply, and you don't need to hide it from any other browser. Add the following CSS property:


zoom: 1;

THAT'S IT. YOU DON'T NEED ANY OTHER HACKING WIZARDRY. IT doesn't need to be hidden from anyone. It actually does what it says it does. There's nothing even hacky about it. It just works, and it has the nice bonus of adding hasLayout on IE.

Getting VNC Working on Vista (fuzzy fonts and black screens)

There are several potential problems I've heard of getting VNC to run on Vista. There's firewalling which may prevent connections. But the more interesting one is the "black screen" problem, which, thankfully, is easy enough to fix. I'm running TightVNC, but it probably is the same for RealVNC and UltraVNC. The fix is this -- turn OFF the VNC service from the control panel/services menu. Services in Vista DO NOT have access to the console by default (there's probably some way to get it to work, but I'm not familiar with it). Once the service is off, launch the VNC server directly from the desktop. Now people can connect and they don't just get a black screen.

Also, if your fonts are coming through really, really fuzzy, that means you need to turn off JPEG compression. My guess is that previous VNC versions were able to detect direct font writing and send them as discreet font updates, but that Vista's rendering enginer requires that they get transmitted as images. Anyway, just turn off JPEG compression and it will work fine.

Programming the Playstation 3, part 3

Part 3 of my PS3-on-Linux programming series just got posted. This one focuses on communication between the PPU and the SPU. You can see it here:

http://www-128.ibm.com/developerworks/power/library/pa-linuxps3-3/

Part #2 and Part #3 were originally one article, so it probably makes the most sense if they are read together.

Part #2 is here: http://www-128.ibm.com/developerworks/power/library/pa-linuxps3-2/

Part #1 is here: http://www-128.ibm.com/developerworks/power/library/pa-linuxps3-1/

There is one more assembly-language article to go, and then the series will move up to C/C++.

168 older entries...

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!