Archive for November, 2007

Better Threading in Mail.app

I really like the new Mail.app in Leopard. With Gmail moving to IMAP, I was finally able to get virtually all of the functionality that I’m used to in Gmail on my desktop Mail application, which is really cool.

Well… except for one little thing. Mail.app has threads, but it does not thread Sent Mail in with Received mail. In Gmail, on the other hand, inbound email gets threaded with outbound email, so I get a nice breakdown of the actual discussions I’ve been having. Devoid of outbound context, the threads are frequently confusing or meaningless.

The workaround I came up with is to create a new Smart Folder that includes Inbound email and Outbound email (Inbox and Sent Items). The Smart Mailbox, in threaded mode, correctly handles putting all of the email together. The little caveat is that outbound email that was not a reply to something appears in this view too (it does not appear in Gmail), but I think I actually prefer that mode of operating.

Textmate Search in a Folder (with grep) Part Deux

I posted my initial “Search in a Folder” solution a couple of days ago, and it turned out it had a few bugs (like the backgrounded ruby script never closing, and thus spawning a new ruby process for each time you loaded up the window. There were other user-experience annoyances, too. The good news is that that’s all in the past now.

The new version of the Wycats bundle has the following changes:

  • I am now using a utility window, so the search window always remains in front
  • I am now tracking open windows, so if you have an open window, another one doesn’t open
  • I track all available exit conditions and correctly exit the event loop
  • Other bugfixes and tweaks

Download version 1.0.1 today!

Proper Search in Directory

In August, I refactored a TextMate bundle to support Search within a folder via Grep. It was nice, but was fairly ugly, and non-configurable. Today, I’m releasing a (very alpha quality) version of a dialog that packages up grep within a directory into a nice GUI package (it looks a lot like Textmate’s default Search Within a Project dialog).

With this bundle, you can search within the entire project or any selected directory and get back the data in the bottom of the search pane, just like Textmate’s default search box. The bundle is activated by hitting Apple-Shift-F, but you can change it to whatever combination you like.

A couple of caveats

  • I don’t current block new windows from being created if one already exists. As a result, close your windows once you’re done with them.
  • Double-clicking a line in the bottom section of the dialog brings up the file with the caret on the line, as you expect. However, it hides the search window. Hit Apple-~ to get back to the search window. Hitting Apple-Shift-F will create a new window, so don’t do that.

Download the bundle today!

JSpec: BDD for JS

A couple of weeks ago I wrote a nice little piece of JS that implemented BDD-style testing in JS. It was originally 70 or so lines of code, and supported code like:

jspec.describe("Some stuff", function() {
  it("should rock", function() {
    7.should("==", 7);
  });
});

It was (and still is) a very simple piece of code, that only dumps the output to the Firebug console. Because should() was a simple method, I was able to delegate out to a pluggable jspec.matchers object, which is where I specified the “match” definition, and allowed the customization of a “failure message.” The original jspec had just two matchers (”==” and “include”), and allowed the creation of new ones by extending the jspec.matchers object (all JS, no magic).

When I tried to actually use it to test real-world code, though, some issues came up. First, functions were being dumped in full into the output, which was sucky. Secondly, the default describe syntax was sucking a bit for certain cases. And finally, I wanted you to be able to plug in any logger you wanted (for say, pretty HTML output). I added those new features, and am now releasing this quick and dirty code for people to look at.

Some examples

(which are included in the downloadable file)

jspec.describe("JSpec", function() {
  it("should support ==", function() {
    (1).should("==", 1);
    var arr = [];
    arr.should(”==”, arr);
    var obj = new Object;
    obj.should(”==”, obj);
    document.should(”==”, document);
  });

  it(”should support include”, function() {
    [1,2,3,4,5].should(”include”, 3);
    [1,2,3,4,5].should_not(”include”, 3);
    document.getElementsByTagName(”div”).should(”include”, document.getElementById(”hello”))
  });

  it(”should support exists”, function() {
    document.should(”exist”);
  });

  jspec.matchers["have_tag_name"] = {
    describe: function(target, not) {
      return jspec.compress_lines(this) + ” should ” + (not ? “not ” : “”) + “have ” + target + ” as its tag name.”
    },
    matches: function(target) {
      return (this.tagName && this.tagName == target) ? true : false;
    },
    failure_message: function(target, not) {
      return “Expected ” + this.toString() + (not ? ” not ” : ” “) + “to have ” + target + ” as its tag name,” +
        ” but was ” + this.tagName;
    }
  };

  it(”should support custom matchers”, function() {
    document.getElementById(”wrapper”).should(”have_tag_name”, “DIV”);
    document.getElementById(”wrapper”).should_not(”have_tag_name”, “SPAN”);
    document.getElementById(”wrapper”).should(”have_tag_name”, “SPAN”);
  });
});

And the Output

In Firebug

JSpec
  - should support ==
    - 1 should equal 1 (PASS)
    - [] should equal [] (PASS)
    - [object Object] should equal [object Object] (PASS)
    - [object HTMLDocument] should equal [object HTMLDocument] (PASS)
  - should support include
    - 1,2,3,4,5 should include 3 (PASS)
    - 1,2,3,4,5 should not include 3 (FAIL)
      Expected [[1,2,3,4,5]] not to include 3
    - [object HTMLCollection] should include [object HTMLDivElement] (PASS)
  - should support exists
    - [object HTMLDocument] should exist. (PASS)
  - should support custom matchers
    - [object HTMLDivElement] should have DIV as its tag name. (PASS)
    - [object HTMLDivElement] should not have SPAN as its tag name. (PASS)
    - [object HTMLDivElement] should have SPAN as its tag name. (FAIL)
      Expected [object HTMLDivElement] to have SPAN as its tag name, but was DIV

Some caveats

  • Despite some work put in, I can’t figure out how to get jspec to recover gracefully from errors and continue on. This will work in a future release
  • I temporarily override the Object prototype (evil, I know) for the duration of the test. You will still be able to load in libraries that expect Object to work correctly, but you will not be able to test functions (such as prototype’s Object.extend) without accounting for the fact that Object.prototype has been extended.

Happy hunting (Download here)

Update

There is now a git repo for the project at git.caboo.se/jspec.git

Merb 0.4 — The One That Rocks!

So I’ve been talking quite a bit about Merb on my blog, and for those of you who could care less, I apologize. The Merb 0.4 pre-release will be coming out tomorrow, and I wanted to preview some of the really need features that have been added since 0.3.7.

  • Generators: Thanks to DrNic’s rubigen, we have full-fledged generators now. But it gets better. Our generators use the ORM and Testing framework you’re using to generate specific files that are appropriate. So if you do script/generate model Foo and are running DataMapper with RSpec, you’ll get new model files for DataMapper, and new spec files for RSpec (same with ActiveRecord, and Test::Unit, of course).
  • Parameterized Actions: If you specify parameters in your action methods, incoming query parameters will automatically get assigned as appropriate. Some examples:
    class Foos < Merb::Controller
      def index(id, search_string = "%")
        @foo = Foo.find_with_search(id, search_string)
      end
    end

    Going to /foos/index/12 will call the index method with the parameters “12″ and “%” (the default provided). Going to /foos/index will throw a BadBehavior error (status code 400) because id is a required parameter, but it was not passed in. Going to /foos/index/5?search_string=hello will call the index method with parameters “5″ and “hello”. The bottom line is that you get to use your actions like real methods. Of course, the params hash is still available and is provided untouched.

  • Great Exception Handling: When an exception is raised inside a Merb::Controller, it is automatically delegated to the Exception Controller, where you can easily specify special behavior or custom templates. Since Exceptions are now just actions inside a controller (a NotFound error renders Exception#not_found), you can take advantage of layouts, helpers, filters, and assigned variables in your error screens.
  • Awesome Developer Mode stack traces: The developer mode exceptions all render a stack trace, in which each line is both clickable (and will pull up the affected file in Textmate) and expandable (you can get a quick overview of the surrounding lines of code without having to open up your editor)
  • Great content-type negotiation: Instead of laboriously doing a respond_to for every action, for each content-type, you can use strong declarative syntax and sensible defaults to slim down your controllers:
    class Users < Merb::Controller
      provides :xml, :json
      def index
        @users = User.all
        render @users
      end
    
      def show(id)
        @user = User[id]
        render @user
      end
    end

    The example shown above uses DataMapper, but the idea is simple. You declare, in your controller, which content-types will be supported. Then the browser sends the requested content-type (either via the content-type header or by specifying a file-type extension such as .json).

    When the action is called, Merb first checks if the content-type requested is available (and throws an error if it is not). When render @users is called, Merb first looks for index.json.erb (if, for instance, JSON is requested). If a template is not found, Merb calls @users.to_json. So in this example, you would simply provide an index.html.erb, and let Merb call @users.to_xml and @users.to_json for the other content-types.

    No more messing around with respond_to; the framework handles all the content-type negotiation for you!

While you’re at it, feel free to take a look at my Why Merb? document, which goes into more depth about the architecture of Merb, and why you should use it!

That’s a fair roundup of some of the coolest new features in Merb 0.4, and there’s more to come on both my blog and others in the days to come. Enjoy!