Archive for July, 2007
7/26 Is there honor left in bike racing? (0)
I just began watching Stage 17 of this year’s Tour de France, recorded from this morning, and was greeted with a second punch in the face from the collective cycling elite. Only two days after Alexandre Vinokourov failed a drug test, the Danish team Rabobank has released their star climber and current race leader Michael Rasmussen. He has not failed a drug test but his sneaking around Italy and avoiding team-mandated off-season drug tests has cast a shadow on his integrity.
It may seem odd to dismiss this rider before he officially failed a test but I agree with former professional cyclist and commentator Paul Sherwen that drastic measures are required. I wanted badly to believe that last year’s shakedown among the peloton’s elite riders was the last straw but I’ve been betrayed once again. This year has only proven that no tolerance for doping is not enough. Professional cycling cannot afford to tolerate even suspicion anymore.
What is next for this once-great sport? Every accomplishment is now doubted. Every duel is now unequal. Every fan watching now wonders whether what they’re seeing is possible. This is not sustainable. In the name of everyone like me who wishes he could ride like the pros, fix this. Vive le tour.
7/22 Develop and deploy on the same box with Capistrano (1)
You’re crazy if you’re not deploying Rails apps with Capistrano. This tool makes it dead-simple to push your latest Subversion to your production server, run database migrations and restart your server all in one simple command. Plus with extra tasks like cold_deploy, getting a new box added to the pool is a snap. One problem, though — this tool was designed with real environments in mind, and my one-box setup for development and production doesn’t quite fit in. If you’re in the same boat, fear not, I have a solution.
The root of this problem is the assumption that your development and production server environments are identical. But when deploying to localhost, using the same mongrel_cluster.yml is just not an option, since you need to use separate ports for your development and production versions.
We need to user two different mongrel_cluster.yml files, one for production and one for development. We can leave the development version out of Subversion and create the production version over again on each deploy. But how do we deal with actually restarting the server?
It turns out Capistrano creates a directory structure that supports this directly, since the *.pid files in the log/ directory are symlinked into place and persist between deployments. So it seems like we can just create the config file and restart the server, right?
Wrong. deploy.rb’s run command doesn’t quite work like your average, everyday shell script. To help it out, I created a shell script that handles the server restart. The shell script allows me to change directories into the application directory and set the PATH variable so mongrel_rails can be found.
Putting it all together, here is config/deploy.rb:
set :application, 'halvesies'
set :repository, 'protocol://repository/url'
role :web, 'localhost'
role :app, 'localhost'
role :db, 'localhost'
set :deploy_to, '/path/to/production/app'
task :restart, :roles => [:web, :app, :db] do
run “#{deploy_to}/current/config/restart.sh”
end
And here is config/restart.sh:
#!/bin/sh cd /path/to/production/app/current export PATH="$PATH:/var/lib/gems/1.8/bin" mongrel_rails cluster::configure -e production -p 11001 -a 127.0.0.1 -N 3 mongrel_rails cluster::restart cd -
And after all of this, you still deploy your site by just running cap deploy.
7/17 Cross platform XPCOM (a howto) (7)
The most important part of Mozilla’s XPCOM technology is its promise of a cross-platform interface between Javascript and native code. Yet few examples exist showing how to build a codebase for both Windows and Mac OS X. Building on Mark Finkle’s Windows how-to, here is a completion showing the parallel track for Mac OS X and how to setup your development environment for a code-once, compile-twice, use anywhere XPCOM build.
Our entire app is going to reside within the app_repo directory. Really you can place it wherever you like, but remember to change paths appropriately. You know the drill. Confusingly, the root of our app will be the app subdirectory. So far we have:
- app_repo
- app
XULRunner
Since the goal here is an XPCOM object for use in XULRunner, we’ll start there. Installing XULRunner is quite painless. We have a version for each platform, which are kept quite separate.
XULRunner for Windows is simply a ZIP file, which you should extract into the root of your app directory. Copy the xulrunner-stub.exe program from app/xulrunner down into app and rename it however you like. This stub just looks for a XULRunner app in the current directory and runs it. Handy.
XULRunner for Mac is slightly more complicated because of strict requirements for GUI apps running in OS X. First, go download XULRunner and install the package. It will create itself deep within /Library/Frameworks (quite separate from the Windows version). In app_repo, create a new directory called MacApp.app or something else ending in .app. Within this directory create one called Contents (capitalization is important), and within Contents create Frameworks and MacOS. Now create three symbolic links to complete the Mac directory structure:
ln -s /Library/Frameworks/XUL.framework MacApp.app/Contents/Frameworks/XUL.framework ln -s ../../app MacApp.app/Contents/Resources ln -s /Library/Frameworks/XUL.framework/Versions/Current/xulrunner MacApp.app/Contents/MacOS/xulrunner
Now create MacApp.app/Contents/Info.plist and dump this in, making sure to change things in ALL CAPS. I am almost certain this is not optimal as it repeats itself a lot. But it is functional. Can someone who knows demystify this?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>xulrunner</string>
<key>CFBundleGetInfoString</key>
<string>3.0</string>
<key>CFBundleIdentifier</key>
<string>YOUR_APP_ID</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>YOUR_APP_VERSION</string>
<key>CFBundleName</key>
<string>YOUR_APP_NAME</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>YOUR_APP_VERSION</string>
<key>CFBundleSignature</key>
<string>YOUR_APP_ID</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>YOUR_APP_NAME</string>
<key>CFBundleURLSchemes</key>
<array>
<string>chrome</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>YOUR_APP_VERSION</string>
</dict>
</plist>
As a review, here’s how our tree looks now:
- app_repo
- app
- xulrunner
- MacApp.app
- Contents
- Frameworks
- XUL.framework -> /Library/Frameworks/XUL.framework
- Info.plist
- MacOS
- xulrunner -> /Library/Frameworks/XUL.framework/Versions/Current/xulrunner
Resources -> ../../app
- xulrunner -> /Library/Frameworks/XUL.framework/Versions/Current/xulrunner
- Frameworks
- Contents
- app
The rest of your XULRunner app
Building the XUL and Javascript part of a XULRunner app is a bit outside of the scope of this already very long post. There are plenty of good references on this though, starting with XUL Planet. Go forth and learn.
The Gecko SDK
You’ll need to grab the Gecko SDK to build XPCOM objects. I took these and placed them in app at gecko-sdk.win and gecko-sdk.mac. I haven’t started building PPC Mac versions, but the SDK for those will be under gecko-sdk.ppc.
- Windows and PPC Mac: http://developer.mozilla.org/en/docs/Gecko_SDK
- Intel Mac: http://www.oxymoronical.com/view/1114
At last, some C++
Now that your development environment is setup, I’ll let you write some C++ code. I created another directory within app called xpcom, in which I placed my project, called foo. Here are some stub files that will get you off the ground. Be sure to create your own UUID.
foo.idl
#include "nsISupports.idl"
[scriptable, uuid(0e0d0b74-2c06-11dc-8314-0800200c9a66)]
interface IFoo : nsISupports {
attribute AString name;
long add(in long a, in long b);
};
foo.cpp (just to make Windows happy)
#include "windows.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
return TRUE;
}
foo_impl.h
#ifndef FOO_IMPL_H
#define FOO_IMPL_H
#include "foo.h"
#include "nsStringAPI.h"
#define FOO_CONTRACTID "@richarddcrowley.org/foo;1"
#define FOO_CLASSNAME "Foo"
#define FOO_CID { 0x0e0d0b74, 0x2c06, 0x11dc, { 0x83, 0x14, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
class CFoo : public IFoo {
public:
NS_DECL_ISUPPORTS
NS_DECL_IFOO
CFoo();
private:
~CFoo();
protected:
nsString mName;
};
#endif
foo_impl.cpp
#include "foo_impl.h"
NS_IMPL_ISUPPORTS1(CFoo, IFoo)
CFoo::CFoo() {
}
CFoo::~CFoo() {
}
NS_IMETHODIMP CFoo::GetName(nsAString & aName) {
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP CFoo::SetName(const nsAString & aName) {
mName.Assign(aName);
return NS_OK;
}
NS_IMETHODIMP CFoo::Add(PRInt32 a, PRInt32 b, PRInt32 *_retval) {
*_retval = a + b;
return NS_OK;
}
foo_module.cpp
#include "nsIGenericFactory.h"
#include "foo_impl.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(CFoo)
static nsModuleComponentInfo components[] = {
{
FOO_CLASSNAME,
FOO_CID,
FOO_CONTRACTID,
CFooConstructor,
}
};
NS_IMPL_NSGETMODULE(”FooModule”, components)
Building for Windows
Because the length here is getting out of hand, I will defer to Mark Finkle for the ins-and-outs of the Visual Studio build. The short version is you setup a few includes and libraries, set some preprocessor directives, and go. Mark Finkle’s Weblog » Mozilla Platform - XPCOM in C++
I used Visual Studio 2005 on Windows XP and did exactly as Mark prescribed. It worked on the first try. I wanted to gloat a little bit, but I seriously hope you have as much luck.
Building for Mac
Building on the Mac is easy since a Mac is Unix-y. Use this Makefile:
GECKO_SDK := ../../gecko-sdk.mac XULRUNNER := /Library/Frameworks/XUL.framework/Versions/Current DEFINE := -DXP_UNIX -DXP_MACOSX all: xpt dylib xpt: $(GECKO_SDK)/bin/xpidl -m header -I$(GECKO_SDK)/idl foo.idl $(GECKO_SDK)/bin/xpidl -m typelib -I$(GECKO_SDK)/idl foo.idl impl: g++ -w -c -o foo_impl.o -I $(GECKO_SDK)/include $(DEFINE) foo_impl.cpp module: g++ -w -c -o foo_module.o -I $(GECKO_SDK)/include $(DEFINE) foo_module.cpp dylib: impl module g++ -dynamiclib -o foo.dylib foo_impl.o foo_module.o -L$(GECKO_SDK)/lib -L$(XULRUNNER) -Wl,-executable_path,$(XULRUNNER) -lxpcomglue_s -lxpcom -lnspr4 clean: rm *.o rm foo.h rm foo.xpt rm foo.dylib
Having the XPT file (which is the same for all platforms) as well as a DLL and a DYLIB, we can dump them into app/components, increment BuildID in application.ini and run our application.
The interface between C++ and Javascript
Now that the application has access to the native code, let’s write some Javascript to call it. The code here will create the object, alerting us of any error, and then use the add function we defined in foo_impl.cpp.
try {
var xpcom = Components.classes['@richarddcrowley.org/foo;1'].createInstance();
foo = xpcom.QueryInterface(Components.interfaces.IFoo);
} catch (err) alert(err);
alert(foo.add(3, 4));
That wasn’t so bad, was it?
This has been a bit of a whirlwind tour, I know. But that’s how it goes when you’re playing with relatively new technologies. It will be sweet, though, the first time you get 7 from 3 + 4.
7/12 CVS oops fix (0)
Committing a new project to CVS is quite a chore. I have been slowing getting it together for the past couple of days, screwing up early and often. If you ever find yourself with a directory tree full of CVS directories and you would rather not, try this:
<?php
function scrub($path) {
$dir = opendir($path);
while (false !== $d = readdir($dir)) {
if ('.' == $d{0}) continue;
if ('CVS' == $d) `rm -rf $path/$d`;
else if (is_dir("$path/$d")) scrub("$path/$d");
}
}
scrub('.');
?>
I’ve discovered other nasty quirks, too. Like you can’t actually remove a directory, but you can suppress empty ones on cvs update using the -P option. Adding binary files and trusting CVS to play nicely is a terrible idea. Instead you need to think for it and do cvs add -k b file to let it know you’re adding a binary file.
Or, if you have decision-making power, you could just switch to Subversion.
7/9 Comcast DVR != TiVo (1)
Disclaimer: I am not planning on buying a TiVo Series 3 box.
I am now officially a discouraged Comcast HD DVR customer. This service and the hardware that provides it is absolutely terrible. I will cover the various deficiencies in its user interface in a moment, but today while watching the 3-hour recording of Stage 2 of the 2007 Tour de France (which was quite a stage, let me tell you) the sound would intermittently and inexplicably cut out. The silence would, as far as my patience let me go, continue indefinitely, but was always fixed by hitting the quick-back button (or certainly just by interrupting the recording in any way). How can playing back a recording be so difficult for a box Motorola built for this exact purpose?
So far this is the only functional issue I have had with the machine but that is not to say that it is without other flaws. The rest of this post will be an attempt to justify the $800 price tag of a TiVo Series 3. I of course have already spoiled the conclusion, but bear with me.
Having been spoiled for the past two years with a TiVo Series 2, I know that folders are a very convenient way to organize recorded programs. Motorola missed this memo and instead provide a flat list of recordings, ordered by date, channel or title. Combine this with the fact that the interface only fills the lower half of the screen and you get a very trying experience.
Not that keeping a long list of recordings is terribly easy. I have not yet decided if there are software bugs or if there are hidden limitations on the Season Pass equivalent but in either case, it is very difficult to setup repeated recordings of your favorite shows. When reordering series to record, no warning is given when conflicts (even with the dual-tuner) will alter the upcoming recording schedule. When setting up a series, it is not clear whether all channels are searched for upcoming titles.
And of course this uncertainty has other consequences. Whenever I browse through the program guide, I expect some indication of which programs are scheduled to be recorded. Sometimes there is. Other times there is not. And even two levels deep in menus I am sometimes unsure whether a program is set to record.
All of this, though, and I cannot justify $800. A damn shame.
7/7 Google-burned (0)
So I’m setting up a server for a project I’m starting (more on that as it develops), following my own instructions (http://old.richarddcrowley.org/blog/view/123). It turns out they’re not quite right anymore and I can’t figure it out.
I’m getting constant 403 errors even though RewriteLog seems to indicate that my rewrites are happening correctly. The solution is of course to google for the solution, but in most of my searches, my old site is the top result. Uh oh.
But I figured it out thanks to Server Watch forum users. In a nutshell, Ubuntu now forces you to explicitly enable more bits of the Apache proxying system by hand. Thus:
a2enmod proxy a2enmod proxy_balancer a2enmod proxy_http a2enmod proxy_ftp a2enmod proxy_connect
7/5 It’s been two weeks? (1)
Time flies at Flickr. I am sure this is not due to my frequent absence for things like getting parking passes, meeting the Comcast guy, and meeting the (very late) movers. It’s because the place is just awesome. Never have I encountered a smarter group of people — it is downright intimidating at times. But intimidation never lasts because of how welcoming they have been and how frequently “real work” is interrupted by a finger-rocket war.
Though I just got my MacBook Pro today and still don’t have access to my work email, I’ve been quite busy, learning the ins and outs of XULRunner and Apollo. Apollo is actually made up entirely of new and exciting contradictions that prevent any real web-like development of desktop apps. XULRunner, not to be outdone, provides the developer an exercise in divining best-practices from partial examples, partial documentation, and partial Firefox builds. (Tomorrow will be fun.)
Back when Apollo still seemed promising (yesterday) I was going to write an entire entry about how to work around a major shortcoming in the platform, but since it seems likely I won’t be doing much Apollo work, I thought I’d share this little tidbit here. You cannot embed a Flash movie in an HTML Apollo app as you would in a normal webpage, meaning that hybrid HTML/Flash apps aren’t possible. The workaround, if you really need Flash, is to build the entire app using Flex. I know, it’ll feel like developing Javascript in 1998 when there weren’t good libraries like jQuery, what with all the click=”foo();” attributes scattered about. It’s OK, though, you’ll make it just like I did. More to come on XULRunner as I learn.
7/1 Do it right (0)
My new apartment is awesome in almost every conceivable way but one: the new paint job was not done right. There is one cabinet that I cannot open, there are two I cannot close and there is one door that does not close correctly. The culprit in all cases is the paint. The permanently closed cabinet is locked with a fresh coat of paint along the bottom edge. The cabinets and door that won’t close all have too many layers of paint to fit into their respective openings. What gives?
Were I a painter, I would certainly want the reputation of doing a spectacular job. To that end I would sand the edges of doors and cabinets to insure they would open and close without binding and I would remove all hinges and hardware rather than painting carelessly over them. Doors would be reinstalled once they were dry, complete with unpainted hardware and a perfect fit.
Were I a landlord, I would seek out painters with that kind of reputation. It would be well worth the money to know that my doors would close correctly, that all my cabinets would open and that none of these will have to be replaced prematurely because they were broken while being pryed open (for the record I have not broken the cabinet door (yet)).
But I am only a tenant. Do I fix these things myself? Do I barter my services for reduced rent? Or do I just deal with this minor annoyance? The opportunity cost is probably not worth the small improvement this would make to my life, except for the bathroom door.