Monday, June 04, 2007

Revenge Cat


U CROSSED PATH, originally uploaded by Kitty de Medici.

I don't usually post stuff like this but .... HA HA HA HA!

Wednesday, May 30, 2007

Mocking and stubbing with Mocha

Here is a nice idiom for unit testing in Rails with the Mocha library. Mocha is fantastic tool for testing, and its is the one we use at my job for mocking with expectations. There are two methods I wasn't aware of, mock and stub. They're very easy to use and are especially handy for situations where you might instantiate another ActiveRecord object temporarily.

For instance, let's say I'm testing messages, and I want to verify that a given call properly delegates to a subordinate object of another type:
 def test_read_by
@message.message_recipients.expects(:find_by_recipient_id).
with(@profile.id).
returns(MessageRecipient.new(:status_read => true))
assert @message.read_by?(@profile)
end

This is pretty straightforward, but seeing that other class name in there bothers me. I've had trouble with over-specific tests before. Here's a version using the stub method:
 def test_read_by
@message.message_recipients.expects(:find_by_recipient_id).
with(@profile.id).
returns(stub(:is_read? => true))
assert @message.read_by?(@profile)
end

This is similar, but there are a couple of important differences. First, I no longer have to hardcode the class name of the subordinate object, which may change later. More importantly, I can mock the friendlier external interface of MessageRecipient without having to know anything else about the object or its contents. Other than that it's exactly the same as the previous test.

Now let's try it with mock:
 def test_read_by
@message.message_recipients.expects(:find_by_recipient_id).
with(@profile.id).
returns(mock(:is_read? => true))
assert @message.read_by?(@profile)
end

Once again, very little difference. The call to stub is replaced by a call to mock with the same arguments. However if you run this test you'll see an extra assertion being checked. This is because mock will verify that every mocked method was called, as though you'd used expects. This means that in addition to the benefits conferred by using a stub object, you're now verifying that the object was used as intended.

Tuesday, May 08, 2007

Remote Control Fun with iTunes and Ruby

Lately I've been using the Ruby programming language, at work and at home, and I often find myself in an interactive Ruby command line. A well-written interactive interpreter is an extremely useful thing in the Ruby language, especially when you include facilities like persistent command history and method name completion. When I found out about RubyOSA, which allows you to script the Apple user interface in Ruby, my first thought was "Oooh, I could even use that interactively!" Not that I would control iTunes on my own machine using Ruby, that would be silly. I mean, I've got Quicksilver for that. However I'm keenly interested in controlling iTunes on the iBook we use as a client to our music server.

It didn't take long to install RubyOSA on my MacBook and find out how to set up remote scripting on the iBook. I soon set to manipulating my music player remotely in irb:
$ irb -r rubygems -r rbosa
> itunes = OSA.app('iTunes', :machine=>'casper.local')
At this point I get an authentication window, and entered the name and password of my user on the iBook. This works, but it would be nice to authenticate automatically. I've tried passing :username and :password to OSA.app, but haven't had much luck with it. If you get stuck with repeated authentication errors, try opening up Keychain Access and removing the assocated entries before you try again. Also, even though I'm not the user controlling this running instance of iTunes, all of these commands work anyway. I guess it's because my user is an administrator.
> itunes.sources
=> #<OSA::ObjectSpecifierList:0x1104888 desired_class=OSA::ITunes::Source>
> itunes.sources.collect{|x|x.name}
=> ["Library", "Radio"]
Just inspecting the sources isn't going to do it, I need to collect the names to get a meaningful list. Now I can see the sources available in this remote instance of iTunes. Unfortunately, the music server isn't available until I go to the iBook and access the music server manually. Once that's done:
> itunes.sources.collect{|x|x.name}
=> ["Library", "Radio", "Music Server"]
Now the music server is listed! Let's play with it!
> music_server = itunes.sources[2].playlists.first
> music_server.search('bill cosby').collect{|x|x.name}
=> ["200 MPH Car", "Dogs", "My Brother Russell", "My Father", "Snakes and Alligators", "Kindergarten", "Personal Hygiene", "Shop", "Baby", "Driving In San Francisco", "-75 Car", "The Toothache", "Hofstra", "Tonsils", "The Playground", "Lumps", "Go Carts", "Chicken Heart", "Shop", "Special Class", "Niagara Falls"]
> music_server.search('bill cosby').first.play
Aha! We can get the playlist from the music server, and search it to find tracks. After that we can just get the first track and call the method play to switch to that track in iTunes. After that track is over, the next song in the global playlist plays, not the next one in the search results. This is a bit disappointing, I wonder if we can do better. Now is probably a good time to examine the documentation:
> quit
$ rdoc-osa --name iTunes
$ open doc/index.html
Looking through the API documentation gives me a little better idea what I can do with this facility. One approach to a temporary playlist might be:
$ irb -r rubygems -r rbosa
> t = OSA.app('iTunes', :machine=>'casper.local')
> music_server = t.sources[2].playlists.first
> music_server.search('bill cosby').sort {|a,b| a.duration<=>b.duration}.each {|x| x.play;sleep x.duration}
Technically, this will play the search results sequentially sorted by duration, although it seems pretty heavy-handed. Also, it doesn't return right away. There are doubtless better ways to do this, but I'm not much of a Ruby (or AppleScript) programmer yet.

Wednesday, January 10, 2007

At the Speed of Google

Well I've pretty much given Google the keys to the kingdom. At last inventory, they've got all of my search queries, email and blog postings. Once they perfect a bookmark service a la Delicious they will be the gateway for every damn thing I do on the network. They keep producing services that I find extraordinarily useful, then add features to those services at exactly the rate that I need them.

Now, just today apparently, an important piece of the puzzle has fallen into place. I can finally host my Blogger postings at my own domain, which I had registered not long ago specifically for use with Google Apps. Integration with Blogger was the final bit I had been waiting for, and I didn't have to wait long!

I've been mucking around with the new Blogger interface and ended up replacing my template with something more flexible, if a little generic. The posts seem to have made it through the upgrade wringer without being altered, which is more than I can say for other early-adopters of the new Blogger. I think this bodes well for another year of self-referential literary wanking!

Wednesday, November 08, 2006

Practically Web Hacking in Perl6

Audrey Tang recently announced that the Pugs project has been moved to new facilities, and the subsequent reissuing of my commit bit spurred me to fix a long-standing issue with the modules CGI and HTTP::Server::Simple (think of Ruby's webrick). Just a couple of small fixes allowed me to finally use these two modules together to create simple a standalone web application in Perl6:

use v6;
use HTTP::Server::Simple;
use CGI;

class WebApp is HTTP::Server::Simple {
method handler {
my $cgi = CGI.new;
my $name = $cgi.param('name');
$.remote.say("<html><head><title>Web App</title></head><body>");
if $name {
$.remote.say("<p>Hello {$name}.</p>");
} else {
$.remote.say('<form method="get">Name: <input name="name" /></form>');
}
$.remote.say("</body></html>");
}
}

WebApp.new( port => 8080 ).run;


The above now works in Pugs, as of r14679 or so. The fixes required to make this work were trivial, but the result is enormously satisfying. I can tell this thing will require further investigation. More tests are certianly needed to ensure that these modules continue to interact properly. First thing's first, however. I think I'll enjoy some nice Perl6 web hacking before bed tonight.

Saturday, September 30, 2006

Four more years!

Boy oh boy, do I miss Bill Clinton. C'mon, who's up for a constitutional amendment allowing him to run again?

Seriously, the guy wasn't perfect, but he was smart, reasonable and likable. Plus he shares my fondness for plump young women. Remember when he refused to sign that bullshit budget and the resulting showdown actually shut down the federal government? Talk about leadership. Can you imagine George W. Bush doing anything like that?

I sure don't think friggin' John Kerry would have it in him. Probably Howard Dean was our best chance at outdoing the Clinton administration, but someone sure fucked that up. I have to admit that I'm about ready to start considering Hillary. I dunno. It's like: What choice have I got? I'm not going to vote Republican, and I'm too pragmatic for a protest vote. I know I'll toe the line like a good little Democrat because that's just what I did for (grr!) Kerry.

I'm a fairly young man, but I remember (barely!) when the words liberal and conservative both represented positive values that I wished to balance in my life and my government. I also remember when Newt Gingrich, Pat Robertson, and Rush Limbaugh started to twist the term "liberal" into a catch-all pejorative. Of course, the term conservative is now poisoned as well. I've always been mindful of the many true conservatives of this country who must be incensed at seeing the word associated with lying (watch how his eyes tick to the left with each one!), secrecy, outrageous spending and crass Constitutional blasphemy (Note: Bush lost the case mentioned in that piece).

Tuesday, September 12, 2006

Side Projects

I've been hacking a lot lately. Since the MacBook is in the shop due to random shutdowns, I decided to perform a long-anticipated (and dirt cheap!) upgrade to Cyclops, my aging workstation. I pretty much ended up replacing the whole machine, reusing only peripherals, power supply and hard drive. Cyclops is now a dual-core Athlon 64, which means faster compile times for indulging my favorite pastime: software research. I've been building the development versions of parrot, pugs and perl5, just to run the tests. I even tried using the v6-alpha compiler, but didn't have as much success. However, pugs is getting quite capable. I've built the version with perl5 support, so it's possible to load and use perl5 modules in perl6 code. I'm going to play around with this and try implementing a usable network server in perl6.

Using Ubuntu on a fast, modern system is a pleasure. It's cemented my resolve to switch to Ubuntu on the MacBook eventually, although I'm trying to give Mac OS X a chance. For the most part, Ubuntu makes it easy to do the stuff that's most important to me, but Mac OS X offers compelling features that I can't get from Ubuntu. Luckily I now have decent systems on which to run both, or will when the MacBook is fixed.

Just for fun I also created a templating module in perl5 (hasn't everyone?). It was fun to implement, I did some clever stuff with hierarchical data structures that I think fills a tiny niche for inline formatting of nested data. Hopefully I'll get to use it more often. It's assured that I'll be the only one.