Autonomous Machine

Posts from 2009

Carmen now supports states for nine countries

Thanks to Andriy Tyurnikov and Tobias Schmidt for recently adding support for the Ukraine and Germany, respectively. And to the others that have fixed bugs or brought issues to my attention.

Carmen is a collection of country and state names and abbreviations, along with replacements for the state_select and country_select plugins that leverage this data.

  • December 16, 2009
  • Article
  • Code, Rails, Ruby

Apply jQuery event handlers once with $.fn.safe()

Today I was in this situation:

  • I had a page that could mutate in a lot of different ways (AJAX, user interaction, etc)
  • I needed a way to execute a block of code against only the new DOM elements on the page (to avoid duplicating the event handlers on the already existing elements)
  • I didn't want to use livequery (delegation would have created its own set of problems)

This is what I came up with:

$('ul.choices a').safe('choices').click(function(event) {
  // code to only execute once for any DOM element
};

$.fn.safe works similarly to jQuery's filter. It returns a jQuery object containing only DOM elements that haven't already been returned for the specified key ('choices' in the above example). So it's safe to execute the code above multiple times without repercussions. Obviously, looping through a large set of elements can be costly, but for smaller sets this simple solution works great.

Here's what's going on behind the scenes:

var $$ = function(param) {
    var node = jQuery(param)[0];
    var id = jQuery.data(node);
    jQuery.cache[id] = jQuery.cache[id] || {};
    jQuery.cache[id].node = node;
    return jQuery.cache[id];
};

$.fn.safe = function(name) {
  return this.filter(function(i, element) {
    $$(this).unsafe = $$(this).unsafe || [];
    if ($.inArray(name, $$(this).unsafe) == -1) {
      $$(this).unsafe.push(name);
      return true;
    } else {
      return false;
    }
  });
};

I'm using the $$ helper I first saw on Yahuda Katz's blog- you should too. It makes working with jQuery's data cache much easier.

See Also

LowPro for Prototype will only apply a given behavior to a DOM element once, but I haven't used it since moving to jQuery for all new projects.

  • November 20, 2009
  • Article
  • Code, JavaScript, jQuery

Convert HTML to Haml within Textmate

I have been integrating a lot of well built HTML templates into Rails applications this week. I've found Haml to be the best templating language, and I like to convert preexisting templates to use it as I add them to a project.

The Haml gem installs a command line executable that performs this conversion, html2haml. The program takes whatever is passed as standard input, performs the conversion, and writes the result to standard out. For a while I was using command this in combination with the pbcopy/pbpaste commands (I work on a Mac) to apply this to the content of the clipboard:

pbpaste | html2haml | pbcopy

This worked, but the extra copying and pasting was annoying. So I made a TextMate command to eliminate the tedium:

Screenshot of the TextMate bundle editor

The actual command is this (it's a bit hard to see above):

echo $TM_SELECTED_TEXT | html2haml

You could add a key binding as well, but so far I haven't bothered to.

  • November 13, 2009
  • Article
  • Code, Mac, Rails, Ruby

Add an ActiveRecord scope for easier eager loading

In the optimization phase of a project, one of the first things I do is look for pages that could benefit from the use of ActiveRecord's eager loading. Since named_scopes were introduced in Rails 2.1, most of my model loading has involved a chain of scopes, and it is always frustrating to have to change

@blogs = Blog.published.active

to

@blogs = Blog.published.active.scoped(:include => [:slugs, :topic => :slugs])

It's a small difference, but one I've never really liked. It seems like an obvious choice would be to add a named_scope to all subclasses of ActiveRecord::Base and have that handle the :include parameter. I finally wrote the 11 lines of code required to do this today:

module ActiveRecord
  module EagerLoadScope
    def self.included(base)
      base.named_scope :eager_load, lambda { |*inclusion|
        {:include => inclusion}
      }
    end
  end
end

ActiveRecord::Base.class_eval { include ActiveRecord::EagerLoadScope }

And now that line looks like this:

@blogs = Blog.published.active.eager_load(:slugs, :topic => :slugs)

Again, it's a small improvement, but one I consider worthwhile.

  • October 28, 2009
  • Article
  • Code, Rails, Ruby

Hiding parts of a view while in development

Most of my recent projects have involved working with a front end developer, who would provide HTML/CSS templates with dummy content to be integrated into a Rails backend. During the process of wiring up the views, I often wish to hide a section of a template or partial until the models and controllers are setup to support them.

I've started using this little view helper to hide code in a view, while providing a console message to remind me to go back and address the issue when the time is right:

def hide(&block)
  if Rails.env == 'development'
    match, file, line = caller.first.match(/([^:]+):(\d+).+/).to_a
    Rails.logger.info "** Hiding content on line #{line} of #{file}"
  end
end

It's simple to use, especially in HAML:

- hide do
  = some_view_code_that_will_not_execute

The helper will print something like this to your console:

** Hiding content on line 14 of /Users/jimb/Projects/demo/app/views/objects/show.html.haml
  • September 11, 2009
  • Article
  • CSS, Rails, Ruby

Dollar Spec is a tiny JavaScript spec library

Dollar spec is a tiny JavaScript spec framework that gets out of the way and lets you get things done. It supports nested describes and before/after blocks, but that is about it. The standard set of expectations is small right now, but they are easy to add.

Why another JavaScript spec framework?

I wanted a small, pure-JS framework that supported a syntax I didn't have to think too much to use. I wanted to only add as much syntax as was neccessary, and leave the rest to JavaScript. I wanted to run JavaScript tests from the console. I wanted a really extensible way to add new expectations. And I wanted minimal namespace usage. DollarSpec creates a single global entity, $spec.

Also, it seemed like a good way to stretch my brain. I've been doing a lot of JavaScript development recently, and it was fun to apply some of the new techniques I've picked up.

OK Let's See What You've Got

Here's a sample spec for an awesome addition function:

$spec.describe('my awesome addition function', function(spec) {

    var add = function() {
       var total = 0;
       for (var i = 0, l = arguments.length; i < l; i++) {
           total += arguments[i];
       }
       return total;
    };
    
    spec.before(function() {
        // code in this block is run before each spec
        // and yes, describe blocks can be nested
    });
    
    spec.it('adds two numbers', function(expect) {
        expect(add(2,2)).to.equal(4);
    });

    spec.it('adds three numbers', function(expect) {
        expect(add(1,1,2)).to.equal(4);
    });

    // to show a failing spec
    spec.it('adds two numbers', function(expect) {
        expect(add(1,1,2)).to.equal(5);
    });

    // to show a pending spec
    spec.it('casts and adds strings');
});

$spec.run();

If you run this inside Firefox, you will get some messages in the Firebug console:

Filter results

These message can be adjusted to your liking:

// To disable console output (defaults to true)
$spec.opts.console = false;

// Print a line to the console for each test (defaults to false)
$spec.opts.verbose = true;

If you don't want to use the console reporting, the results of all specs are collected and returned by $spec.stats(). This makes it easy to do whatever you wish with the results.

Supported expectations

Keep in mind that any expectation can be negated using not(). For example:

expect(3).to.not().equal(8);

expect(actual).to.be(object)

Compares the expected and actual values using ===.

expect(actual).to.beA(Object)

Verifies that actual is an instance of Object.

expect(actual).to.equal(expected)

Compares actual and expected using ==.

expect(affectorFunction).to.change(affectedFunction)

Compares the value returned from calling affectedFunction before and after calling affectorFunction.

expect(affectorFunction).to.change(affectedFunction).by(amount)

Compares the difference between the value returned from calling affectedFunction before and after calling affectorFunction to amount.

expect(callbackFunction).to.raiseError([verifyFunction])

Verifies that callbackFunction raises an exception when called.

Optionally will call verifyFunction and pass in a raised exception for further inspection. verifyFunction must return true or false.

Custom expectations

DollarSpec's expectations are built from two pieces- verbs and matchers.

Verbs

A verb is a function that can be used in an expectation to define something about the parameters of what is expected. The most basic verbs simply define which matcher to use. Verbs must always return this in order to allow chaining to work. Here is the verb definition for equal:

$spec.verb('equal', function(expected) {
    this.set('matcher', 'equal');
    this.set('expected', expected);
    return this;
});

This verb will cause the expectation to use the equal matcher, and stores the value of expected so it can be examined in the matcher later.

Matchers

Matchers are functions that determine if an expectation passes or fails. They can also define messages to be displayed to the user when a test fails. Here's the beA matcher:

$spec.matcher('beA', function(result) {
  result.failure = "Expected an instance of " + this.klass.toString() + ", but it was " + typeof(this.actual);
  result.negatedFailure = "Expected instance of a class other than " + this.klass.toString() + ", but it was one";

  return this.actual instanceof(this.klass);
});

result.negatedFailure is the message to be displayed when the tests passes, but was used in a negated expectation using not().

You can see how the built-in verbs and matchers are implemented in the Github repository.

Dogfood for Dinner

Dollar Spec has a suite of specs, written using Dollar Spec and powered by diligence, a tiny remote JavaScript test runner library. It requires Node.js, which is awesome. Go check it out if you haven't yet.

To run the specs:

cd spec
node suite.js

Open a browser and go to localhost:5678. You should see something like the following:

Dollar Spec Results

Diligence is still under development, but its working well so far on a few JavaScript projects I'm working on. I'll post more about it as it matures.

More information

There is a bit more info in the README on Github. Feedback is always welcome!

Alternatives

You should probably take a look at ScrewUnit if you're looking for a more complete/mature BDD framework.

Now go write some specs.

  • September 04, 2009
  • Article
  • Code, JavaScript