development

In an earlier post I presented a number of rake tasks that were designed to ease git development. They helped mostly with managing branches, pulling from a remote branch (git-svn or plain ol git), and with pushing to remote branches. After using them for a while and getting others to use them, I discovered a problem

They actually make things harder. Consider the user who doesn’t know git that well, is coming from Subversion, and wants something to help ease the transition. A set of rake tasks to make git feel a bit more like Subversion sounds like a good thing, right? Well, yes and no. Yes in that they should allow you to use the more familiar concepts, but no in that, as written, they didn’t educate you about what they were actually doing under the hood. So you never learned about git-stash, the difference between git-fetch and git-pull, and more of that sort of thing. This is bad because you will become dependent on the tools, which is a problem because

Things go wrong. When a merge conflict happens in a rebase and most of the output is hidden from you, you cant fix it because (a) you have very little information about the problem and (b) you don’t even know what a rebase is! Granted, good tools can and do help with this sort of thing by providing help when things go wrong, but my tasks just didn’t do it enough. After a while I got to thinking about the tasks as suitable for someone who didn’t know git, and that was my mistake, because

These tools are meant for people who don’t need them. Only once I realized this did I start over, drop the sake-git project, stop using the thor-git project, and went back to pure git. After using that for a while, I noticed some of the same patterns that lead me to write the original tasks in the first place, so I decided to write (based on Rein Hs hack and ship) scripts for people who know what they’re doing, but want to do it faster. They’re written in bash, they’re fast, they tell you whats going on, they don’t hide anything, and they’re a gist with an installer, but nothing else.

Enjoy.

programming

Update: I’m ditching Sake for Thor. These tasks have been ported to thor and are available on github.

I’ve started using Git as my SCM of choice for Subversion projects over the last several months and have found that, while I don’t want to use Subversion anymore, there are some things it makes easier than git. For example, let’s say you’re working on something and you want to pull in the changes from other people on your team. With svn its simply:

$ svn up

With git things are different, since it only merges changesets and not locally changed files. This was a pain before git-stash came along, since I’d have to back out a change, update, and then reapply it. Even with git-stash things are a bit more painful. Here’s the equivalent to the above for a git-svn project:

$ git stash
$ git svn rebase
$ git stash apply

Oh, and that’s only if you’re on the master branch. If you’re on another one (and you should be), then heres what it looks like if you want to keep master up to date too:

$ git stash
$ git checkout master
$ git svn rebase
$ git checkout mybranch
$ git rebase master
$ git stash apply

Whew! Note that this mostly applies to git-svn projects. For regular git projects a git-pull will do nicely.

I got sick of this, and I noticed that the Rubinius project uses a Rakefile to handle a fair number of the git commands, including updating and pushing. Here’s a Sake script that gives you two tasks: git:update and git:push which automatically check whether the project is a git-svn project and do the right thing. Install it like so:

$ sake -i http://pastie.caboo.se/147964.txt

And now were back to a one-liner:

$ sake git:update

Update: I just added git:open and git:close which you should think of as opening and closing issues. They just create and delete branches and can be used like this:

$ sake git:open
* Name your branch: ofx
* Switching to master
Switched to branch "master"
Switched to a new branch "ofx"
$ sake git:close
* Switching to master
* Deleting branch ofx

And don’t worry, git:close is safe and won’t destroy your work if you haven’t merged it yet:

$ sake git:close
* Switching to master
* Deleting branch ofx
* Branch ofx isn't a strict subset of master, quitting

Update: I gave this its own repo on github, so go forth, and git.

apple macbook

I upgraded to Leopard over the weekend after borrowing a friends copy, since I just couldn’t wait for mine to arrive (thanks Coda!), and its been overall a Good Thing. Leopard has brought us new versions of just about every app in OS X. The most notable ones, for me, are Mail, iCal, Terminal, Spaces, and Stacks.

Mail.app

Mail got an iTunes-like sidebar and a little Mail Activity status area (yes, there are other things like Notes and Todos, but I haven’t bothered to use them yet). The update coincided with GMail’s addition of IMAP, a long-awaited feature especially for us iPhone users. My previous solution of forwarding all my accounts to a fastmail.fm account is pretty much defunct. Setting up GMail’s IMAP on two accounts with a combined message count of 17,904 was not a fun experience. Now that Ive set it up and tweaked a few things it seems to be humming along quite nicely, Inbox Zero style. Heres what I recommend you do if you’re going to set up IMAP with Gmail:

  1. Log into gmail.com
  2. Archive everything (except stuff you haven’t dealt with yet)
  3. Delete all your labels (maybe, see below for an explanation)
  4. Enable IMAP under Settings
  5. Add the GMail account to Mail.app (Ill call it Personal)
  6. On the sidebar in Mail select Personal → [Gmail] → Sent Mail, then choose the Mailbox → Use This Mailbox For → Sent
  7. Do the same for Trash, Junk, and Drafts
  8. Install Mail Act-On (might require some Terminal tweaking)
  9. Create an archive rule called Act-On: y | Archive and say that if its in Personal to move the message to Personal → [Gmail] → All Mail
  10. Repeat #9 for each other account you have

Now you’ll have the ability to get to that holy grail of email, Inbox Zero, with more ease than before. Under this setup you’ll have an inbox, but it’ll be empty most of the time. The email you’ve dealt with will be archived and will be visible in Mail under Personal → [Gmail] → All Mail and under All Mail on gmail.com. Oh, and about step #3.. when Gmail first came out and told us search, don’t sort and gave us labels I thought I understood what to do, but what I ended up doing was pretty much sorting email into labels, going totally against how Gmail is set up. What I discovered is that labels should only be for emails that you still have an active interest in, otherwise you’re just sorting and filing. In GTD-speak thats only use labels for emails that still have an action associated with them. By the way, this applies locally to Mail.app as well. Get rid of your folders. You only need Smart Mailboxes and search. Try it, you’ll like it.

iCal

The only thing that looks the same (almost) in this version of iCal is the events view. Everything else has been tweaked. The mini-calendar is more legible and larger. The list of calendars has, like Mail, been iTunes-ified. The biggest change is the dropping of the edit drawer in favor of a popup. My only complaint with it is that I don’t understand why they went with a drop shadow under the text boxes inside rather than a fuzzy blue border like in Safari.

Terminal

So Terminal has real preferences now, and tabs. The former is nice, but I’ve already set things up so I doubt it’ll have much effect. The latter has been mitigated by the fact that I use screen. Oh well, they’re welcome additions anyway. Thanks Apple!

Spaces

This is probably the best new feature in Leopard as far as Im concerned. Don’t get me wrong, I’m excited about Time Machine, but mostly because it’ll encourage other people to back up more, making the Mac ecosystem a better place to be. Most of the other things in Leopard are not really big-ticket items, but are nevertheless cool to have. I’m still noticing little things, like the fact that ⌘⇧4 (grab selection) now has coordinates and dimension numbers nice touch!

Anyway, Spaces is exactly what Virtue Desktops and Desktop Manager should have been. The animation is exactly right (windows slide off the screen in the direction of motion, the pager comes onto the screen with the arrow inside the old space pointing to the new one), windows that live on all spaces are supported and predictably stay put when switching spaces. Dragging apps around when viewing all spaces is dead simple, and Exposé even works in that view. All-in-all, Im very impressed with their implementation of virtual desktops.

Stacks

Ive only got the default stacks in my dock right now (Downloads and Documents), but I’m digging this feature already enough to make me leave the Dock visible and put it on the left side. So now the Dock does have a use for me: My Active Stuff. I removed all apps from it, so only running ones show up. I can easily click on the stacks in the dock, pull out the thing I want to mess with, and either open it or drag it to an application (usually opening folders in TextMate).

Ruby

Despite the best laid plans of mice and men, Ruby is still broken under Leopard. Not as broken as Tiger, mind you, but broken is broken. So Im sticking with my Tiger setup of using MacPorts for everything. Oh well.

3rd-Party Apps

  • Spanning Sync went bonkers and now wants to delete all my iCal events.
  • 1Password refused to work at first, but has been updated for Leopard and now works just as before
  • Pyro and Safari 3 are feuding, and as a result you cant upload files in Pyro

So all in all Im quite pleased with the upgrade, and I recommend you check out the Ars Technica review for more. It has already changed the way I work for the better, though how much of that is coincident Im not sure yet.

female accountant

I’ve been away for the last 10 days or so on a well-deserved vacation. Here are a few updates that all have to do with money:

  1. The Fed lowered the Prime Rate, and ING Direct lowered their interests rates the next day
  2. Helio finally gave me the $272 they owed me (which after 9 months they should owe me $10 interest, but whatever)
  3. I now work for wesabe.com, a personal finance website
  4. I’m switching to EverBank from INGs Orange Savings and Wells Fargos checking

I’m really excited about the new job, in part because I think its a cool company with a good product, but also because I’ll still get to do Rails work with some pretty cool people, like Coda Hale and Andre Arko. Attendio was a good learning experience for me, and I wish them luck in the future, but I think it was time for me to move on and I think I made the right decision.

Number 1 doesn’t bother me so much because of number 4, as EverBank has yet to lower their rates (which stand at 5.01% APY vs. INGs 4.20% APY for Money Market and 3.65% APY vs. WellsFargos 0.00% APY on checking). EverBank also lets you send electronic (or paper) checks, though you cant write them yourself you do it online and you get an ATM card which you can use at BofA, Wells Fargo, and more and they will reimburse you up to $6/mo in fees from other banks, which I estimate to be about two withdraws per month more than I ever make.

The Helio thing came four days after my last conversation with them (9/17 to 9/21), indicating that perhaps the only way to get your refund back is to be a persistent and bitchy thorn in their side. Im glad that chapter in my life is closed.

smartphone

What is this?

Recently I switched from AT&T to Cingular as my cell phone service provider. Seeing as they are the same company, it was really just an internal migration – or at least it should have been. What happened instead was about 47 days of confusion, followed by the inevitable battle to set things right. This page and the blog will chronicle my progress in that battle. And just as it was gearing up, the battle is over – see below.

Summary of Events

  • 31 Dec 2004 Initial activation of the new account at the store in Bakersfield on Stockdale. I was told that my account would be switched over within a few days and that Id receive one last bill, prorated, from AT&T.
  • 2-3 Jan 2005 At another store in Bakersfield (on Oak?), I went in to ask why my phone was unable to receive calls when using my Cingular SIM card. I told them that my AT&T SIM card still worked fine, and that I had been using that. They said that Cingular had been having problems with people who switched over from AT&T around the time I did, and that it might be another week at most.
  • 7-9 Jan 2005 At the Westwood store, I asked again why my phone was unable to receive calls using my Cingular SIM card. I explained that Id been using my AT&T one. They told me that it should be working soon.
  • 14 Jan 2005 Received a Cingular bill for $30.91, the expected prorated amount. I noticed that the number of calls I made was shorter than I expected, but I thought this was just a lapse of memory.
  • 22 Jan 2005 The only helpful Cingular employee I’ve met works at the Santa Monica store on Wilshire (south side, on a corner). I explained the situation again to him, and he tried calling customer care, which, as always, was either unavailable or had a 5 min+ wait time. He told me that, by using my AT&T SIM card I might be double-billed. Uh oh.
  • 1,3,8 or 10 Feb 2005 I got an email saying that I had an AT&T bill ready to be viewed online. Id opted for the paperless bills, and automatic payment. I tried logging in to see my account. I got a page that said, The wireless number you entered does not match an active enrolled account. Huh? I just got a bill from these people! I called Cingular to ask about the SIM card and the bill, and the guy told me that I should be getting one last bill from AT&T, that it was normal, but that he couldn’t tell me what my account balance was, and that Id have to call AT&T for that. Huh? Doesn’t merger and welcome to the Cingular family imply a little more continuity than that?
  • 15 Feb 2005 My checking account shows a deduction of $101.65 from AT&T (I later find out this is because I used 404 anytime minutes when my account with AT&T was only set up for 300).
  • 16 Feb 2005 I received a Cingular bill for $50.34 (08 Jan 2005 – 07 Feb 2005) that showed no usage. None. Zip, Zero, Zilch. Uh oh.
  • 17 Feb 2005 I called Cingular Customer Care and asked about my large AT&T bill and my suspiciously blank Cingular bill. By now I had an idea of what was going on, but I wanted to hear what they had to say. I first told them that my Cingular SIM card wasnt working in my new phone, and that my AT&T bill was huge.
    • The Cingular SIM They wanted to try to call me on with my Cingular SIM, so I switched it out and they called me and it worked! Lo and behold it worked! It wasn’t working about 10 days ago when I last checked, but it was working now. Okay, great.
    • The AT&T login Now can you help me with my billing problem? Actually, it was AT&T that charged you sir, not Cingular. Um aren’t you the same company now? Apparently they are the same company only in a legal sense. I was transferred to Mohammed at AT&T, a nice enough guy who helped me reenable my AT&T online login. He asked me what email address I would like my new password to be sent to and I told him. I never received the email.
    • The AT&T bill I called 866.293.4634, the AT&T support number, and got Brittany, a cheerful and sympathetic but ultimately powerless girl who transferred me to Karon, a more stuffy but still helpful woman. She tried calling the Cingular Customer Care for me while I was on hold. In discussing my situation with them, she eventually concluded that they were not at fault, and that they could offer me a $24 credit. Well, thats nice of her, but Im out more than $140, so can I talk to a manager? A senior rep. was put on named Natasha, who was the least helpful but the most illuminating of the three. She told me that AT&T didn’t do anything wrong since I was using the AT&T SIM card, and that I should take it up with Cingular. I appealed to her sense of reason when I reminded her that they are now the same company, but the way she responded baffled me (this is a paraphrase): AT&T and Cingular have merged, yes, but the backend is still very much separate. AT&T accounts are not Cingular accounts – in fact, AT&T does not have access to your Cingular account and Cingular does not have access to your old AT&T account. AT&T policies are different from Cingular policies, and I am actually not very familiar with the Cingular policies. I asked her why it was that my port, as they call it, took more than 45 days to complete and she said that sometimes that just happens, and that shed actually seen them take longer. I asked her if I was just expected to deal with the fact that I have a half-working cell phone for all that time. She basically said yes, and suggested I take recourse with Cingular since it was their SIM card that was faulty. I asked her what time the port finally finished and she answered, Oh, just a couple of minutes ago. Seems they weren’t going to do anything about it if I never complained.
  • 20 Feb 2005 I called them again and asked to speak with the supervisor and made it clear that I would not hang up until Id gotten to someone who could do something about it. Two levels up, three people talked amongst themselves and finally decided to:
    • Refund the amount on the latest AT&T bill above the base amount (effectively no activity, but still charging for the account).
    • Refund any activity from 28 Jan to 17 Feb, effectively making the account cancelled on 28 Jan.

Interpretation

I contacted AT&T and Cingular numerous times, each time explaining my situation as clearly as I could, telling them that I had switched from AT&T to Cingular and that Id like to know why my SIM card isn’t working properly. In only one case did anyone hint that it might not be a good idea that I use the AT&T SIM card, and what he said was double-bill, which I interpreted as that the minutes I used would go under both my AT&T and Cingular accounts. It turned out that I was not being double-billed in that sense, but that both of my accounts were very much alive and kicking. No one told me this might happen, and the reps (particularly Natasha) that I talked to said nearly explicitly that it was my fault for not knowing this. While it isn’t an unreasonable assumption to think that my accounts would be separate, neither is the assumption that they would be continuous! Are there really so many cases where the customer actually wants their cell phone services to overlap by 45 days? I doubt it.

What gives with this blame transfer? AT&T blames Cingular because it was their SIM card that was not working and Cingular blames AT&T because it is their bill that I’m having a problem with. They’re both missing the point. BOTH of their representatives large and small told me nothing about what continued usage of my AT&T SIM card would do, and you know why? I’m guessing they didn’t know. They were probably not trained to handle a situation like this, so even if it crossed their mind, they didn’t want to tell the customer something that might upset him, especially when its something that they don’t know for sure. Only one guy was brave enough to do it, and I thank him for it. The point remains that this single company is telling me that it is not to blame because I should have guessed the information I wanted to know – or, more accurately, I should have guessed the right questions to ask.

What Should Happen

I want all the minutes used on my AT&T SIM card from 31 Dec 2004 and later to be retroactively transferred to my Cingular account, and to retroactively close my AT&T account as it should have been closed. At least, this will have the effect I want: using 404 of my 450 anytime minutes from my Cingular plan during the last billing cycle and whatever minutes were used from 7 Feb to 17 Feb (the time during the current billing cycle that my AT&T SIM was still active). They can do whatever they want to their databases to get the desired effect, so long as the money and the minutes come out the same. I don’t care how they get it done, just get it done.

If they do not want to do this, I would like to suggest that they let me cancel my Cingular account without a termination fee. This way we both get hurt somewhat and we both walk away: they no longer get my business for the remainder of the 26 or so months Im obligated (and likely for life), and I am forced to pay them for their screw up. If they will not take my first or second suggestion, then I would like to take them to small claims court, but unfortunately I hardly see this as a worthwhile use of my time and money. So what will likely happen instead is that I will use whatever resources I deem expendable enough to ensure that no one I know uses AT&T or Cingular for any of their needs ever again. Ive got one person who’s prepared to boycott them: my girlfriend. What about you?

What Did Happen

We compromised. They appeared to be sick of dealing with me and I was pretty sick of dealing with them. The offered to clear all activity from the last billing cycle, making my AT&T bill $50 and my Cingular bill $50. Not great, but I’m $50 ahead at that point. Also, they offered to completely wipe the account and usage from 28 Jan to 17 Feb, which means I come out about $20 ahead. All in all, roughly $70 back out of an estimated $140. This doesn’t make me happy, but it satisfies me enough not to boycott them.

ruby-on-rails

Figuring out whats wrong in Ruby can be a pain. That dynamic typing that you find so nice while writing code can sometimes work against you when reading it and troubleshooting it. What most of us end up resorting to is basically one step up from how most JS debugging happens: puts.

While this is annoying, it doesnt have to be this way. Ruby comes with a debugger, but it is slow on non-trivial applications. The best alternative is a gem called Ruby Debug, which is fast and has a bunch of goodies. I come from the GUI-debugging world of VS.NET and IDEA, so getting into Ruby Debug was a little bit of a challenge for me at first. If you come from the gdb world you should feel right at home.

Watch the Ruby Debug Basics screencast

from the screencast: fib.rb

  1. def fibn
  2. @fib_cache ||= 1, 1
  3. @fib_cachen ||= fibn-1 + fibn-2
  4. end
  5. puts fib20

Next screencast will cover debugging a Rails application.

ruby on rails

Ruby is a great language. Really. How many other languages let you extend other classes? Modify their behavior? Overload operators? With great power comes great opportunity, and in this post Ill cover one of my recent extensions to Ruby that was recently accepted into Rails core.

Date and Time

Managing dates and times is not exactly fun in any language. There are time zones, leap years, two meridians, twenty-four hours, 365 days (sometimes), and a mess of inconsistently long months. Those of us who use Ruby frequently are familiar with how to get objects corresponding to the current time and the current day:

  1. >> Time.now
  2. => Thu Jan 25 20:12:50 -0800 2007
  3. >> Date.today
  4. => #<Date: 4908251/2,0,2299161T>

Sexy methods

Rails has these really cool extensions to Numeric that allow computing lengths of time in a more human-readable way. For example, if I wanted to figure out how long 12 hours is, I could just call 12.hours and get back the number of seconds in 12 hours, which could then be conveniently added to an instance of Time, like so:

  1. >> t = Time.now
  2. => Thu Jan 25 19:31:17 -0800 2007
  3. >> t + 12.hours
  4. => Fri Jan 26 07:31:17 -0800 2007

Sweet! Whats even cooler though is that, in addition to Numeric with seconds, minutes, hours, days, weeks, fortnights, months, and years, we also get from_now, ago, since, and until:

  1. >> 1.day.from_now
  2. => Fri Jan 26 19:34:20 -0800 2007
  3. >> 2.weeks.ago
  4. => Thu Jan 11 19:34:35 -0800 2007

but kinda dumb.

Nice! These are so easy to read. These were great for simple things, but since they returned numbers, they couldn’t take into account the current date and how many days were in a month, leap year, etc. Notice that this causes problems when the month doesn’t have 30 days in it:

  1. >> Time.now
  2. => Thu Jan 25 21:01:31 -0800 2007
  3. >> 1.month.from_now
  4. => Sun Feb 24 21:01:34 -0800 2007

So its good for approximations, but not much else. Well what now?

A smart method

To compensate for the lack of accuracy in the above sexy methods, Time#advance was added to make it easy to do smart addition to Time instances. Here’s some of the above using advance:

  1. >> t = Time.now
  2. => Thu Jan 25 21:11:32 -0800 2007
  3. >> t.advance:hours => 12
  4. => Thu Jan 25 21:11:32 -0800 2007
  5. >> t.advance:months => 1
  6. => Sun Feb 25 21:11:32 -0800 2007

Excellent! Not quite as sexy, but definitely smart. A method suitable for Serious Business.

Smart n Sexy

I welcomed Time#advance, like everyone else, but I didnt want to give up the sexy methods. Fortunately, Ruby is great for letting objects masquerade as other objects (the whole duck typing thing), and the de-facto way to do this in Rails is by using Builder::BlankSlate, whose instance undefine almost all of their methods, allowing you to easily proxy all or some methods to something else. The two best examples of its use in Rails are AssociationProxy, which is used by Active Record, and JavaScriptGenerator, which is used by RJS.

To solve the problem, all we need to do is make the duration methods on Numeric return a proxy for the number they used to return which acts accordingly around Time and Date objects. This new class is located in ActiveSupport::Duration, and simply accumulates lengths of time for when it is used around a Time or Date, and a number for use around things that expect it to act like a number (i.e. for backward compatibility). Heres the new, smart and sexy, methods on Numeric:

  1. >> t = Time.now
  2. => Thu Jan 25 21:21:44 -0800 2007
  3. >> t + 1.month
  4. => Sun Feb 25 21:21:44 -0800 2007
  5. >> t + 1.week
  6. => Thu Feb 01 21:21:44 -0800 2007
  7. >> 1.year.from_now
  8. => Fri Jan 25 21:22:00 -0800 2008
  9. >> 4.years.from_now
  10. => Tue Jan 25 21:22:12 -0800 2011
  11. >> 3.weeks
  12. => 21 days

Hows that last one for overriding inspect? To try it out all you have to do is freeze edge in a Rails project near you!

$ rake rails:freeze:edge

Update: I should add that this recent change also corrected handling around Date objects. I implied that above, but I should be explicit. Before Duration, adding Date.today and 1.day didnt yield the expected results at all:

  1. >> Date.today + 1.day.to_time
  2. ArgumentError: time out of range

Now it behaves as expected:

  1. >> Date.today + 1.day.to_time
  2. => Sat Jan 27 00:00:00 -0800 2007