1. Unicode dominating the Web?

    May 5th, 2008

    I wish, but more likely this is a measure of the reported encoding of Web pages based on their HTTP headers.  And that would not be surprising.  Let’s be real, it’s just a header that we all copy over and over just because it seems like the right thing to do.  And most modern frameworks put it there for you, even when you’re serving ASCII text from a latin1 encoded database table. Still, A for effort.

     

  2. Distributed Twitter Client in 20 lines of code

    May 5th, 2008

    There you go.

    require 'rubygems'
    require 'xmpp4r'
    require 'xmpp4r/roster'
    
    puts 'Connecting ...'
    client = Jabber::Client.new(ARGV[0]).connect(’talk.google.com’)
    client.auth(ARGV[1])
    puts ‘Receiving’
    roster = Jabber::Roster::Helper.new(client)
    roster.add_presence_callback do |roster_item, old_presence, new_presence|
      if new_presence
        from = roster_item.iname || “#{new_presence.from.node}@#{new_presence.from.domain}”
        if new_presence.status
          puts “#{from}: #{new_presence.status}”
        end
      end
    end
    client.send(Jabber::Presence.new)
    Thread.stop
    client.close

    Run from the command line:

    $ ruby distwit.rb <jabber_id> <password>
    Connecting …
    Receiving
    Andre Lewis: Away
    Matthieu Riou: Enjoying a JavaOne couch
    Matthieu Riou: Entertaining Assaf
    Alexis Midon: hacking in a couch @community-one 

    Now just wait for your friends to status away using their IM client.

    And don’t forget, please yo-yo the rrm.

  3. Git forking for fun and profit

    April 30th, 2008

    Originally written and posted to the Buildr developers mailing list. In the past few weeks all changes were pushed using git-svn and a synchronized Git repository hosted on Github. It rocks!

    Apache built a great infrastructure around SVN, lots of sweat and tears went into making it happen, and at first I felt like we’re circumventing all of that. But the longer I thought about it, the more I realized that Git is just more social than SVN, and that’s exactly what Apache is about.

    So there you go. Feel free to adapt to whatever project you’re working on.

    On The Morality of Forking

    One thing I love about open source is that it gives you the right to fork. Don’t like how the project is managed? Want to take it in a different direction? Tired of seeing a broken trunk and re-fixing the same typos? Copy it over and start a new project. The Apache project started life that way.

    In open source culture, forking is often used as a four-letter word. One fork means two different code bases. What happens next depends on the tools you use, but typically keeping these two forks synchronized, sharing changes and bug fixes, can be a pretty daunting if not impossible task. Even branching in the same code repository is a tricky maneuver — when was the last time you did an SVN branch only to fix a one-line bug?

    There’s a high bar to forking, so people don’t do it lightly. We generally prefer not to, reserving forking to dead projects and irreconcilable artistic differences. Being forked is a stigma you don’t want on your project’s resume.

    At least it used to be that way. Back in the days of ugly source control systems, forking would lead to all sorts of nasty side effects. So I want to correct that impression and explore a different kind of forking — one that’s fun, healthy and a way to build a better community around the forked project.

    Forking Alone

    The way SVN works, and I assume you’re familiar with SVN, you check the code out of a central repository and into your working copy. You make changes locally, and when you’re done, you commit those back to the central repository (or hand someone a patch to commit on your behalf).

    The working directory is an offline copy of “the real thing”, a local cache for easy editing. Officially we’re all working on the same code base, whatever edits you do in the privacy of your own computer is your business (until you commit).

    Distributed version control works in a different way. I’m going to talk exclusively about Git for reasons that will become obvious later, but the same goes for whatever distributed version control you decide to use.

    When you source control is distributed, there’s really nothing else to do all day but fork. To get anything meaningful to happen you start by cloning the central repository to your hard disk. What you end up with is a full blown copy, history and all. Clone, branch, or whatever else you call it, it really is a fork.

    Now that you’re working with your very own fork, you can branch, commit, rollback, merge and do all sorts of interesting things on your own repository. You can also fetch the latest changes from the central repository, and push the work you’ve done back to the central repository, or send a patch that someone else can pull in.

    Forking in private is most of what you do, but not all.

    Forking In Public

    Open source is great, open source with open development is teh awesome. Open development is done in public. You don’t go hiding in a cave only to emerge a few months later with a big code drop. Do the work where everybody else gets to check it out, participate and hopefully contribute.

    We don’t look too kingly on anti-social behavior. On the other hand, tools like Git are great for cave digging and dwelling. Why would I think forking is such a good idea?

    To begin with, social problems are not solved with technology. The point of a source control system is to make development easier, not annoy people into socializing. That should come from a fun, creative and supportive community.

    Git is wonderful for committers. Rule #1 of source control: don’t break the trunk! When you break the trunk, everybody else has a bad day. They can’t get any work done.

    But during development we often reach this point when you’ve got something incomplete, perhaps broken, but significant enough that you’ll want to check it in. You want that checkpoint because it allows you to move forward and experiment with different ideas. Worse case, you can always roll back. The ability to take these checkpoints and make local commits and branching without breaking trunk is quite powerful. Use it wisely.

    Git is also wonderful for those of you who are not committers (yet). You can get to be a committer by racking up karma points. You get more points form major contributions. Major contributions require a lot of work, you’ll want to source control it, you’ll want to involve other developers so they can help make it happen. With SVN you can’t do that until you get your committer status.  Catch-22.

    So fork in public. You do that by setting up a public repository, synchronizing it with the central repository, and pushing your changes to your public repository. Other developers can then clone your repository, check the code you’re working on, use it and test it, send you patches and even push changes to your repository, working together away from the trunk.

    When you’re done working on a big enough change you can merge all these changes into a set of patches and send them over for inclusion in the central repository. That way you can contribute as little or as much as you like without waiting for SVN access, even better, you can share these features with others while waiting for them to be included upstream.

    Forking is Fun

    So let’s review some of the things you get out of using Git.

    You can branch, commit, go back in history and do all sorts of useful things offline. Offline means you can do them on a plane or a train, which some people think is really cool. Even if you’re always connected, you’ll love how everything happens so damn fast. It’s like strapping a T3 line straight into the ethernet port.

    You don’t have to bother anyone else. You can branch as often as you need to, which is damn useful when you’re working on two things at the same time. (Yes, SVN had branches since forever.  Not the same thing.) Actually, I recommend branching any time you’re working on something: branch, change, commit, merge.  Did I mention, fast?

    You don’t have to hold on commits until everything works. You can write test cases and commit, get some code working against these tests and commit, get more code working and commit again. When you’re finally ready with that changset is when you push it into the central repository, by which point you won’t be breaking any trunk. Frequent commits are great if you like to experiment with new ideas, or share work in progress.

    If you’re doing something big, you can fork the central repository and get other people working against that fork, helping to make it happen. You don’t need to be a committer to start off on a major contribution, you don’t have to wait for a patch inclusion before others can start using your code. Best, you don’t need to bother with SVN branches, which ironically are harder to synchronize with trunk than using Git.

    Come to think of it, just giving all that power to contribute to developers who are not yet committers is a killer feature, and why I’m writing this piece to begin with.

    I realize all this fun stuff might be hard to imagine, might not even sound plausible, if you’re used to the SVN way of doing things, but once you let go of centralized source control, everything in the universe will start making sense.

    Parenting and Custody

    I started by saying open source provides the right to fork. Each open source license expresses that right in a different way, the one we use is the Apache Software License. That works as long as we provide all the code under the terms of the ASL, which we can do since every contribution included an agreement to let Apache distribute it under the ASL.

    To avoid all sorts of nasty custody fights, which we really don’t have time to deal with, we’re trying to get software done, we have to make sure all the code coming in is accompanied by the Contributor License Agreement. We have two ways of doing that.

    Some code comes directly from committers, all of which signed the CLA, easy. Other code comes from patches, which go through JIRA. JIRA gives you the option to CLA the patch before uploading it, telling committers they can go right ahead and add it to the code base. That way we have a commit trail showing who contributed what.

    So you understand why the official source repository has to be hosted by Apache, and while we’re waiting for Git to happen, right now we’re stuck with SVN. No Git for us? Turns out, it’s not such a big problem.

    For starters, you can always use git-svn to clone the SVN repository and then use Git instead of SVN. You get all the awesomeness of Git and an easy way to keep it consistent with trunk. I’ll explain how to do this when we’ll talk about the mechanics of forking.

    If you find someone you trust who’s already managing a Git clone that’s synchronized with SVN, you can clone their Git repository. I use Victor’s Git repository.

    If you’re working on something big, you’ll probably want to fork in public, creating a remote repository that others can tap into. Lots of options to choose from, the one I use, because it’s wonderful and I don’t want to host one myself, is Github. If you want to clone someone else’s repository to create your own public repository, you’ll love the “fork” button. Can’t miss it.

    Now, all of this introduces an interesting problem. Say you decide to work on something big enough that you need a public repository. You also need other people to help you, by contributing their changes and fixes. You want to bring all that code into the central repository so this cool feature shows up in all future releases, but the code is now a mishmash of contributions from different people.  How would you get something like that approved?

    Here are three things you can do to help us approve these contributions:

    1.  If in doubt, ask. Mailing list is the best place. We’ll revise these guidelines as we learn what works best.

    2.  Keep an ongoing commit trail. If you accept a patch from someone else, commit it and include an attribution in the commit message (see here for guidance). When you use git format-patch, it creates one patch for each commit, we apply these individually, preserving the commit trail. Pushing does the same (but double check).

    3.  Ask contributors to sign the CLA. It’s quick, it’s easy and you don’t have to be a committer. Check the list of committers and non-committers who already signed the CLA.

    The Mechanics of Forking

    So let’s discuss the few ways in which you can fork, starting with git-svn:

    $ git svn clone http://svn.apache.org/repos/asf/incubator/buildr/trunk buildr -r <revision>

    Apache maintains one huge repository shared by all projects, and while Git will only clone the history for a given project, it will need some time to process through an endless stream of revisions. How long? Really long. Most likely you don’t need all that history going back to the very first day, so just clone from a recent revision, it will only take a couple of minutes.

    Since all projects share the same repository, svn info will show you two revision numbers. The first, the actual SVN revision number, is not the one you want — cloning it will fetch nothing. The second, the “Last Changed Rev” is the one you want to clone from.

    Check that it worked:

    $ cd buildr
    $ ls
    .....
    $ git svn info
    Path: .
    URL: http://svn.apache.org/repos/asf/incubator/buildr/trunk
    .....

    You’ll want to set your name/e-mail so they show up in all commits, which you can do on each Git repository, or once using the –global option:

    $ git config --global user.name Assaf
    $ git config --global user.email assaf@apache.org

    To pull updates from SVN and fix (rebase) all your local commits against the most recent SVN update:

    $ git svn rebase

    Best way to work on a new feature is to start with a new branch:

    $ git checkout -b teh-awesome
    $ git branch
      master
    * teh-awesome

    Do some work, commit as often as necessary and when you’re done, rebase these commits against the latest changes from SVN, and generate some patches:

    $ git svn rebase
    $ git format-patch origin

    You’ll get one patch file per commit. Depending on what you did to get here, that could be a boatload of patches, so you might want to roll together (squash) some commits, or even change their order (that way, we’ll think you wrote test cases ahead of the code!) Check the documentation, git rebase -i master is your friend. 

    Cloning someone else’s repository is just as easy, for example:

    $ git clone git://github.com/vic/buildr.git
    $ cd buildr

    This time around you’re working against a Git remote repository, so you grab updates using git fetch/pull and rebase accordingly. Everything else involving branching and patching works the same way.

    You can also work with both at the same time. A local repository that clones a remote repository, the one you’re using to share your work with others, and also synchronizes with SVN trunk. (Bet you didn’t know, but your local repository can synchronize with several remote repositories)

    You’ll want to start with git svn clone and then add the remote repository using git remote add. Or just use the buildr-git script, courtesy of Victor, which sets up everything to work just right, and adds useful commands like git apache-fetch, git apache-pull and git synchronize.

    There’s a few command line options you can set to use this script with any other Apache project.

    So go, have fun, and Git away!

     

  4. Rounded Corners - 202 (SaaS + open disclosure)

    April 27th, 2008

    Open SaaS.   AllMyData is an online backup/storage solution that, at $5/month for unmetered storage sounds like a pretty good deal.  That’s not what I want to talk about.  Check out Tahoe, their open source secure distributed filesystem.  You can grab the sources (lots of Python code, no surprise) and run your own storage grid, or share the backup load with friends, P2P.  The part that interests me: you can rent the storage from AllMyData, but know exactly how they’re handling your data, security, availability and all.

    This is an interesting marriage of SaaS of open source.  Provide the service, and source code as a form of open disclosure.  Hoping we’ll see more open disclosure SaaS in the future.

    Pockets of resistance.  As you know Microsoft is at odds with its customers regarding the future of XP.  Come August you will no longer be able to purchase XP, except for this work around.  If you purchase a computer running Vista Business/Premium, you can request a pre-installed downgrade to XP.  Customers that want a working machine get XP installed at the factory, while Microsoft gets another “sale” of the pricey Vista Business/Premium edition, proving once and for all that there’s no demand for XP.

    Never mind the smell, this is one clever business tactic of using customer’s best interest against them.  Except for this nagging little quote: ”there are some pockets”.  I’ve heard that once before, usually when a dictatorship runs at odds with its populace and has to fight those damn nagging pockets of resistance.

    The thing about numbers.  Part of the problem is “that software piracy had grown in April, impacting revenue.”  I’m sure Microsoft has more accurate numbers to prove than my little, unscientific, baseless research.  A quick torrent research finds 3093 results for Windows XP, and only 2071 results for Windows Vista.  And of those Vista search results, second from the top is actually a torrent of Windows XP SP3.  You can blame it on piracy, but when piracy is lukewarm, who do you blame it on then?

    The cure for your PHP blues. I’m quoting out of context, but it’s more fun this way.  Sun, once again, promises the JDK will be open sourced.  This time, for real.  After all, you can’t keep people waiting forever:

    We’re trying to get Java into places it’s never been before …  Linux developers, absent of an open-source Java, have been building applications with languages like C, C++, and PHP  … What we can do is create Java programs and then run them on Linux.

    Truly cool helmet. This one is for bike riders.  When I last went shopping for a motorcycle helmet, all the good (and expensive) helmets came equipped with an ambient-sensitive setting that automatically switches between two modes: foggy and steamy.  Now comes this marvel from Givi.  A replaceable chinbar and vents for warm-day comfort, probably as close as you can get to an AC.  And, not a first but still damn cool, a drop-down tinted visor for that jet fighter look and cure to the common sun blindness.  No bulky D-ring either.  WANT!

    Picture, the ultimate iPhone unboxing.

  5. How many payments in a 4-payment plan?

    April 19th, 2008

    My car insurance is handled by Geico, which means every six months I pay the premium, and a few weeks later get a check for the total sum of 400 US cents. First time that happened, I thought my premium was adjusted down and Geico was just returning the balance (it happens).  Then the second time, and the third time, and eventually it clicked.

    At some point in the past, owned to a few points on my license and a crazy premium to match my driving habits, I enrolled in Geico’s 6 payment plan.  When you’re on a multi-payment plan, they charge an extra $4 on each individual payment.

    Years ago my premium landed back on earth, and I stopped using the payment plan.  (There’s another story there involving Progressive, and why I won’t use them ever again)  I guess I’m still billed for the $4 payment plan luxury, but since I pay it all in one go, they refund me for the surcharge.  Kind of annoying having to cash that check every six months (can’t they just use it as forward balance?)

    Now for today’s little surprise.  I made a change on my policy, bumping up the premium by $89.97.  Everything went smoothly, and like clock work Geico e-mailed the new policy documents, along with an updated bill for the remaining balance.  That comes to a total of $89.97, if I decide to pay once under the one-payment plan.

    But what if I decide to pay once under the two-payment plan?  That would be one payment of $93.37.  How about one payment under the four-payment plan?  Also $93.37.  At least the six-payment plan offers the possibility for splitting the bill in two payments.

    I sleep better at night knowing that some of my premium goes towards mailing checks to compensate customers for unnecessary charges.

     

     

  6. Rounded Corners - 201 (I can has Shawarma?)

    April 16th, 2008

    Speed is in the eye of the beholder. Rethinking the Progress Bar.  If you can’t be fast, at least you can appear snappier with this simple mathematical slight of hand.

    Worse than I thought. Looks like Google/Firefox released the lock on Rob Yates.  That was fast.  I did some research today, looking for a new (to me) car, and found out AutoWorld is also on probation.  Blocked.

    Putting the squeeze on malware is a good idea, I like it.  But this one is going form 0-60 in no seconds. Is anyone on the Firefox team reading this?  Please start small, give site owners time to adjust, and us users something to access in the meanwhile.  I don’t like it when Firefox goes dark.

    Connecting with the blogger generation. FeedBurner is running an ad campaign for a new blog that’s a teaser for an upcoming TV show (or movie, can’t tell).  AdSense link: check.  Blog with GMail address: check.  Twitter and Facebook: check.  YouTube video: not on the site, but a Google cache copy of the sight includes a couple.  Brough to you by A&E.

    I can has Shawarma? Last night’s food coma, brought to you by Wally’s Cafe in Emeryville. It’s a tiny diner, tucked behind a pub,  run by the owner, and open late in an otherwise sleepy neighborhood.  You just know it has to be good and cheap.  Wrong.  It was amazing.  Best Mediterranean food I had in years, not to mention Lebanese, which is my favorite style.  Did I mention how amazing it was?

    Picture, from the inspiring tumblog Insomnio. (Via ffffound!)

  7. Rounded Corners - 202 (Ruby roundup)

    April 15th, 2008

    Go with the flow. If you need to check out what’s happening in Ruby land, check out RubyFlow, “a community driven Ruby links site”. (Via Ruby Inside)

    Documented. Unfortunately I’m back to RDBMS land, but I’m really curious about StrokeDB.  The short version:

    StrokeDB is an embeddable distributed document database written in Ruby. It is schema-free, it scales infinitely, it even tracks revisions and perfectly integrates with Ruby applications.

    Obviously this one will be compared to CouchDB, which now defines the category.  Don’t.  It has some interesting ideas that are easy to miss if you’re looking at why “this one is not like the other”.  I hope to read more about its version control, the API has elegance to it and can easily resolve references, and there’s promise of a JavaScript port.  I’m not sold on the embeddable aspect, I’d rather use a database that’s independent of the runtime language (once Derby, twice shy).  But the API is compelling and native code will deal with performance issues.

    Responsible monkeying. Sustainable development in Ruby: Introduction, Part I (inherit early, inherit often) and Part II (inject as little as necessary).  Eh what?  Just because you can monkey patch every piece of code doesn’t mean you should.  Remember, you’d have to maintain it someday!  Avdi Grimm explains how to do useful stuff with the mininum of collateral damage on future generations.  I buy it.  More sustainable development.  Please.

    Ruby.exe. Peter Cooper asks, Is Windows a First Class Platform for Ruby? From what I’ve seen, the Windows experience leaves much to be desired, though some of it is just the fact that Windows is, well, Windows.  How many of you are using Windows to develop/deploy Ruby apps?

    Untouchable. Google, not particularly fond of Ruby.  Evidence no.2: Using Ruby with the Google Data APIs.  (Evidence no.1 is lack of a client library)  It’s the opposite of anything Ruby stands for: not simple, not elegant, and nowhere near DRY.  By comparison, the Java documentation looks easier.  But I’m sure there are no language sentiments at play here, just insufficient time to dig through RubyForge and find several pre-existing Google Data APIs.

    Picture: LOLcats toy with Schrodinger.  LOLcats win.

  8. Rounded Corners - 201 (Twinkle, twinkle little twit)

    April 14th, 2008

    Twinkle me Twitter. If you’re on Twitter and got a broken-in iPhone, check out Twinkle.  You’ll want to install this one. Compared with MobileTwitter and m.twitter.com, it’s the better client of the three.  It does pictures (so does MobileTwitter).

    Here’s the kick: besides being the best client, it also knows where you’re twitting from, and can grab statuses from anyone in a 1-50 miles radius.  Now, that’s mobile.  And counting the traffic report that showed up when I first played with it, relevant.

    Git cheat. Git link roundups seems to be all the rage nowdays.  Here’s a couple that will come in handy.  The Git cheatsheet (download, print, stick to cubicle wall). And if you’re using cheat (aren’t you?):

    cheat git
    cheat gitsvn

    This will be big. I finally took the time to look over the Passenger architecture (aka mod_rails). For large scale deployment I still prefer reverse proxy, but for shared hosting, this one is a killer. Finally, Rails has a solution for those who can’t afford a dedicated host.

    This will take a bit longer. Ditz is a Ruby-based distributed bug tracking system.  The basic premise is keeping all issues logged in the same repository as the source code.  That means you can work on issues the same way you work on code, locally, and along different branches.  There’s a others out there, Ditz seems to be the only one making forward progress.

    In the cloud. So we have Software as a Service.  But not everything can ride on pre-existing code, which is why we also have Platform as a Service.  I was just happy to stop there, but no, we also need to thin of the Do-Evil crowed.  Yes, Crimeware as a Service.  With that in mind, I already started planning my Halloween costume: Assaf as a Service.  I’m sketchy on the details, but I do know it will involve clouds of some sorts.

  9. Google/Firefox, please go easy on the trigger

    April 13th, 2008

    Anyone seen this before?

    It popped up when I followed a link from a comment left on this blog. Firefox would not let me see the site, not even view source.  I have to say, it’s a great way to protect users from potential phishing, scamming and malware.

    Not this nosy blogger, though. Something didn’t sit right, so I used wget and peeked at the source.  Seems benign to me.  The post references my talk about read consistency databases, CouchDB and Google AppEngine.  Doesn’t look like the dumb stuff you find on spam/scam blogs (and I get a lot of these trackbacks).

    Firefox suggest the site is marked as malware by Google.  Indeed, a search on the author name returned 95 links to that blog, all of which are marked with:

    This site may harm your computer.

    A LinkedIn search finds the author is a Senior Programmer at IBM.  So I opened the blog in Safari and started reading.  JSON this, Atom that, OpenID, Java, comments and good technical discussion all around.  Actually a good blog all around, nothing malicious.

    I don’t know what tripped the Google Homeland Security system, but I see no evidence of foul play, just a false positive.

    I contacted the author, let’s see what comes out of this.

    Update: Turns out this is a known problem:

    Currently, many sites that are the subject of Google’s warnings have been the victims of a malicious hacking attack, in which code linking directly to badware through exploits was inserted onto an otherwise innocent, but poorly secured, website. In other cases, a website with no intention to distribute badware hosts content (such as ads or hit counters) provided by a third party, and can inadvertently distribute badware through that content. If you are confused about why your site has a Google warning, then there are strong odds that your site has experienced one of the above situations.

    At the very least they do attempt to made contact:

    Google makes a good faith effort to contact the owners and administrators of sites with Google search warnings. Google sends emails to potential site owner addresses such as webmaster@domain.com Google also notifies site owners with Webmaster Tools accounts.

    Update 2: This is getting better. AutoWorld is now blocked for the very same reason (I’m researching my next car).

  10. Git it is

    April 11th, 2008

    I went looking for the Rails repository yesterday, and aimlessly browsing around, discovered that Victor maintains a fork of Buildr on github. And so I caved in. I blame it on peer pressure, cool kids, but mostly on tools that I need and just can’t find anywhere else.  I finally sudo port install git +svn, fetched and switched from hg to git.

    I still much prefer Mercurial.  I develop, so really what I need is an rsynch that can handle branching and merging, not a lot to ask for.  I’m not, and never will be, in the business of Source Control Provisioning, Management & Administration, so I appreciate anything that aims for small and simple.  Git … well, there are 138 command line tools to choose from, some of which are multi-tools in their own right, so you tell me.

    So why Git?  A couple of reasons.  First, Apache is all SVN all the time.  I tried hg + svn, I kept at it for a few weeks, but eventually through some branching and merging lost the .hg repository.  That was a failed experiment.  Git-svn looks like a clear winner, doing almost all that I expect it to do, aside from renaming all svn tasks to something else (did I mention usability is a non-feature?)

    Then there’s Github.  Wonderful.  In so many ways.  How can you not fall in love with a social network that has syntax highlighting?  And though I couldn’t care less for yet another social anything, this one is damn useful and productive.  You won’t find anything like that outside of Gitland.

    It’s only between first git svn fetch and dcommit that I discovered Github just had its official launch.  Congratulations.  It’s the thing I always wanted, just didn’t know how much.

    So 24 hours after switching, here’s what my bash history reads like:

    ~/work/buildr $ history|awk '{print $2}'|sort|uniq -c|sort -rn|head
    232 spec
    50 rake
    32 sudo
    29 git
    19 svn
    19 cd
    18 gem
    17 ls
    9 irb
    8 vim

    Here’s Bill de hÓra who started it, and Chris Adams who identified it as the meme du geek.  Make of it what you will.