Archive for August, 2008

’tis the season…

…for voting! (Well, for trying to convince people to vote, anyway.)

DSC_0111

New Hardware

I just go a slick new work machine: a Lenovo ThinkPad X300. It’s got loads of RAM (4GB), a 60GB solid-state disk in place of a hard drive, and it all weighs in at just under 3 lbs. It’s the best laptop I’ve ever used, and if it’s anything like the other ThinkPads I’ve owned, it should be with me for a while.

The nicest surprise has actually been just how well Ubuntu worked out of the box. Wireless, video, webcam, and both pointing devices worked right off the bat. One well-documented config tweak later, and I had suspend/resume (and fast, too — just about as good as my MBP). Audio took a bit more work, mainly because Ubuntu distributed an older ALSA snapshot. Once I re-compiled the audio drivers from the latest ALSA release and moved the Ubuntu versions aside, I had loud, clear sound.

I do mean loud, too — the built-in speakers on this thing is significatly louder than my MacBook Pro, or any other X-series ThinkPad I’ve tried (X24, X41, X60). Since music + video are a big part of what I do with a laptop, that’s actually really nice to have.

Overall, this is a machine I can highly recommend. The 1.2 GHz processor and integrated graphics chip should feel poky compared to my year-old but faster-on-paper Mac, but the solid-state disk helps quite a bit, and certain key applications (Firefox in particular) just feel faster under Linux than they do on OS X.

Thank you, Steven Frank

For those who don’t already know, Steven Frank (a.k.a. stevenf on many sites) is a developer at Panic, Inc., a multiple-award-winning Mac software shop here in Portland. He (and his firm) are unapologetic Mac boosters, and have based their entire business around producing applications which appeal in large part to the aesthetic and functional preferences of the most die-hard Apple fans.

I’m unlikely to work as a proprietary desktop software developer any time soon, given that I haven’t tried to write a desktop GUI application in about five years, but I do appreciate the work that Panic puts into polishing their user experience, and have a great deal of respect for them as a development team. And so, it was extremely refreshing to me to see many of the fears and frustrations I’ve expressed about the iPhone/App Store platform lock-in as an outside being echoed by someone who is working within that ecosystem.

One realization I had early on in my IT career was that almost any two firms could easily become competitors. Given the rate at which a competent team can develop most any class of application on the planet, and the generality of the platforms on which our code runs, it is entirely conceivable that your platform vendor of choice today (or your client buying your tools or OS) could become a direct competitor tomorrow. For that very reason, trusting the future of your business to the continuing benevolence of a vendor is a very risky decision.

Every developer working on applications for the iPhone/iTouch platform is in exactly that precarious position right now. Those innovative applications that drive sales of the platform could easily be incorporated into the next release of the OS, since Apple has more than enough resources to re-implement any 3rd-party app without breaking a sweat. (There is certainly precedent for this: just ask the developers of Watson what it’s like to one-up Apple in the system utility space.) Even worse, truly disruptive applications that offer capabilities beyond Apple’s comfort zone (or that of one or more of their mobile network providers) could find themselves summarily dropped from the App Store, effectively strangling any business the developer hoped to build around the product.

This is, to me at least, an unacceptable bargain, and I’m surprised that so many other developers seem to have no problem with the arrangement. (Have I mentioned how much I can’t wait for Android?)

Mixins without monkeypatching

Anyone who’s done much Ruby metaprogramming (or even just skimmed the source code to Rails) should be intimately familiar with the following idiom:

klass = Foo::Bar
# ...
klass.extend(Some::Module)

This is a programmatic mixin — it adds the methods defined in Some::Module to the class Foo::Bar for the duration of the current Ruby process. It also does so for all instances of Foo::Bar, whether defined in some scope known to the caller of extend or not.

If may not look like it, but this is yet another case of monkeypatching. The only reason you don’t see it being denounced up and down the ‘tubes like, say, overriding method_missing on NilClass is that it’s usually confined to an application or framework-specific class, like ActiveRecord::Base.

Now, I’m all for the judicious use of powerful language constructs like open classes, but they can be a problem for large-scale projects, or those where collaboration between team members is less-than-perfect. The global and persistent scope of a class-level #extend call like the above can cause unexpected side-effects, too.

As an alternative, I humbly propose the use of instance-scoped extensions. In cases where not all instances of a class may need the additional functionality provided by the mixin, try calling #extend on just that instance. It keeps your namespace clean, doesn’t introduce as many potential pitfalls for code in other scopes, and is reversible: just nil your current reference to the object, and re-create it without the mixin.

Here’s an example, cribbed from some refactoring I’m doing of a web service implementation:

def change_password
  token = AuthToken.find(params[:token])
  active_user = Account.find(token.identity)
  password = params[:password]

  raise ArgumentError, "passwords did not match" unless password = params[:confirm_password]

  if token.has_privilege?(:password_reset)
    active_user.extend(PrincipalManagement)
    active_user.change_password(password)
  end

  render :x ml => Account.to_xml
end

One reason this is expecially handy for me is that I can re-use the same model core model classes (like Account) in different applications, only some of which may have the privileges necessary to change passwords, delete accounts, etc.

By limiting the mixin to a single model instance within the scope of a single request, I also protect myself from coding errors that might expose dangerous mutators to untrusted callers. Any attempt to call change_password (or a similarly-restricted method) from outside a scope which explicitly included the mixin will raise a MethodNotFound error, without any additional access-control checks on my part.

Omerta

I am currently being bitten by the iPhone NDA, despite not ever having agreed to be bound by it. I’ve had reports from people who are working on iPhone apps that certain websites appear to render incorrectly on their systems. They have sent screenshots to prove it.

However, checking the same site, from the same OS X build and Safari release version, I see only properly-arranged columns. After eliminating basically every other likely cause, I am left with the suspicion that the iPhone SDK has somehow modified or extended WebKit.framework, causing even the system-packaged Safari to render pages differently.

Unfortunately, I can’t really fix it. I don’t have the iPhone SDK, and won’t “sign” Apple’s NDA to get a copy just to troubleshoot CSS issues that will only affect perhaps a few dozen visitors. (The intersection of iPhone developers and grassroots political volunteers seems not-so-huge.) Unfortunately, I also can’t ask anyone I know doing iPhone development to help, since any information they disclosed could potentially violate the NDA to which they have agreed.

This does not make Lennon a happy little coder.

Update: A little bird told me that their system, which also had the iPhone SDK installed, does render the page correctly. So much for that solution. I am still irritated that the NDA pretty much ties my hands when it comes to more fine-grained troubleshooting.

Happy thoughts

I find it hard to be optimistic on Mondays. Sometimes, I just need a reminder that everything is going to be okay.

So, I put the following code snippet at the end of my ~/.bashrc file:

ruby -rdate -e'puts "#{Date.new(2009, 1, 20) - Date.today} days left until Bush leaves office."'

Now I smile every time I open a terminal on my work machine.

Update: Ten minutes of late-afternoon golfing later, I’m left with this little beauty:

ruby -e'T=Time;puts "%s days, %s minutes, %s hours, and %s seconds left."%[86400,3600,60].inject([(T.local(2009,1,20)-T.now).to_i]){|a,v|p=a.pop;a+=[p/v,p%v]}'

SSL is hard. Let’s go shopping!

Rails has a fun (well, okay, kinda crippled and lame but still interesting) piece of infrastructure called ActiveResource. It basically lets you glue domain model objects from multiple Rails apps together via REST, and it actually works pretty well if you’re using vanilla ActiveRecord models and/or don’t mind doing your own XML serialization logic.

Unfortunately, the authors of said module appear to either a) not understand how SSL works, b) not really care or c) both. As implemented currently, the ActiveResource HTTP client will blindly accept any certificate presented to it, no matter who signed it. This is a Big Deal, at least for any apps using ARes across the firewall.

For those who don’t already grok SSL, here’s a shortened version:

Each site that wants to use SSL to protect traffic first generates a private key (which must be kept secret). They use that key to generate a public key encoded in a certificate-signing request (CSR), and then send that CSR to a certificate authority (CA). The CA signs the CSR using their own private key, and returns it as a finished SSL certificate.

At this point, the certificate serves two purposes: it provides a public key for others to use when encrypting traffic to and from the server, and it asserts that the entity bearing the certificate is the one named in its description. It is the responsibility of the CA to make sure that certificates are only issued to the right organization, which basically means that certificate authorities must themselves be very trustworthy indeed.

Clients connecting to a server using SSL ask for a copy of that server’s certificate before sending any sensitive data. Since the certificate is unforgeably signed by a single CA, the client can decide whether to trust the signing CA. If they do, they can then be reasonably safe in the assumption that the entity identified as the bearer of the certificate is who they say they are. (In most cases, this “entity” will be a simple hostname, like ‘mail.google.com’.)

If, on the other hand, the client does not trust the signing authority, there are two reasonable paths: it can allow the user to temporarily accept the certificate, or just reject the connection entirely. The former is a somewhat-common case for interactive applications like web browsers, but the latter usually makes more sense in a backend-systems case, since it’s better to fall back on secure defaults.

Now, the ActiveResource code takes yet another approach, which I consider unsound: it simply ignores the certificate error and continues with the connection as planned. This unfortunately means that anyone at all could claim to be a popular web service provider (think Twitter, or Campfire, or any of a thousand other popular APIs), and ActiveResource clients would simply accept that assertion and pass along user data without even logging an error.

Normally, I wouldn’t disclose a security issue like this on my blog before making every effort to make sure that the project maintainers had a chance to evaluate and patch the issue, but the current ActiveResource implementation actually goes out of its way to squelch any certificate-related errors. Given that, I suspect that this is really more a case of just needing to identify where ActiveResource is an appropriate solution.

Ignoring certificate verification may be fine and dandy for quick development work, and even within a trusted server subnet, but could easily burn you out on the wild world of the ‘net.

I’ve submitted a ticket and patch, so we’ll see what happens.

Something nerdy this way comes

Just a little preview. More to come soon.

Subversion + Jabber + Ruby

I’ve had a few people ask me to post this since I twittered about it a couple of months back. Basically, it’s just a trivial little Jabber bot which notifies a list of interested people whenever there’s a commit to a watched subversion repository. (Of course, admitting amongst Ruby people that you use Subversion in this day and age is kind of like confessing to a love for MS-DOS, but I consider it a personal victory to have the folks at work using any sort of SCM.)

The notifier has two pieces: a persistent process, which connects to your Jabber server and listens for requests over DRb on a loopback socket, and the post-commit hook, which you install in your Subversion repository, and which gets executed by Subversion after each commit goes through. The only configuration needed is to pass the JID and password for the sending Jabber account to the notifier (which is accomplished via command-line args when you start it), and providing a list of JIDs to be notified on commit, which is accomplished via a whitespace-delimited plaintext file in the repository hooks directory.

(For those of you who haven’t checked out Subversion’s “hook” support, I highly recommend checking out the appropriate chapter in the Red Book. It’s a pretty nice little trick to have up your sleeve, and something tells me similar tricks are possible with Git repositories as well…)

Here’s the code, in its entirety. First, the post-commit hook:

#!/usr/bin/ruby
# post-commit hook; install in SVN_ROOT/hooks/
# give it a file called 'svn-notify' (in the same dir) containing one JID per line

require 'drb'

NOTIFY = File.read("#{File.dirname(__FILE__)}/svn-notify").split

REPOS = ARGV.shift
REV = ARGV.shift

DRb.start_service

log_msg = `svnlook --revision "#{REV}" log "#{REPOS}"`.chomp
commit_by = `svnlook --revision "#{REV}" author "#{REPOS}"`.chomp

project = File.split(REPOS).last
person = commit_by.split('@').first

msg = "New commit (r#{REV}) to `#{project}' by `#{person}': #{log_msg}"

bot = DRbObject.new nil, 'druby://localhost:99843'
bot.deliver(NOTIFY, msg)

And last but not least, the entire Jabber notifier:

#!/usr/bin/ruby
# Jabber commit notifier
# Run from wherever you like, after installing the 'jabber-simple' gem.
# Pass it the JID and password for your sending user as command-line args.

require 'drb'
require 'rubygems'
require 'xmpp4r-simple'

JID = ARGV.shift
PWD = ARGV.shift || gets.chomp

if JID.nil? or (PWD.nil? or PWD.empty?)
  STDERR.puts("usage: #{__FILE__} 
 (password can be on stdin, too)")
  exit 1
end

class RemoteBot
  include DRbUndumped

  def initialize(bot)
    @bot = bot
  end

  def deliver(to, msg)
    @bot.deliver(to, msg)
    return true
  end
end

xmpp = Jabber::Simple.new(JID, PWD)

DRb.start_service 'druby://localhost:99843', RemoteBot.new(xmpp)
DRb.thread.join

Voila! Now you can compete with your co-workers to see who can spam the other with more (and more creative!) commit messages.

Commercial Conferences and Open Source

There’s been a vigorous discussion on the PDX.rb mailing list about the future of FOSCON, our little home-grown free-as-in-beer Ruby mini-con that has taken place during OSCON for the last three years. The conference organizers at O’Reilly have decided to move the event to San Jose next year, which breaks the symbiotic relationship that has made FOSCON so much easier to plan and ensure attendance for each year.

There seem to be at least two different ideas gaining some degree of buy-in: first, a volunteer-driven replacement for OSCON itself, covering the full bredth of open source software and hardware topics, and second, a Ruby-specific event, in the vein of the RubyCentral-funded regional events already held elsewhere.

Personally, I’m going to work on a third option. Based on my experience at RubyFringe, I think there’s a place for a small (100-200 attendee), focused conference without any outside sponsorship. I also think that one of the unrecognized reasons RubyFringe was such a success was that it absolutely was not designed by committee. Pete, Meghann, and a handful of other people really took the event forward, without trying to get consensus from a large decision-making group.

This is why I don’t want to get the Seattle Ruby brigade involved at the very beginning, or even involve the idea to Legion of Tech before things have started to gel on their own. I don’t want the need to maintain a PG-13 rating, or to feature everyone’s favorite local project, to get in the way of a kick-ass event.

I think there’s a place for a whole spectrum of conference types, and I love the fact that the community is rallying so quickly to fill the gap left by the departure of RailsConf and OSCON from our fair city. I just don’t really care about the big conferences, so I’m going to let other people fill those shoes while I go play in the woods for a while.