Dr Nic

Validate and Save your Ruby in TextMate – with secret Rubinus superpowers

In some TextMate bundles, if you save a file it will also validate the file and show any syntax errors in a tooltip. This is awesome. (e.g. JavaScript and CoffeeScript)

So I added the same thing to my Ruby.tmbundle. Install this, save a dodgy Ruby file and you’ll now see something like:

Validate and Save - No Rubinius

Rubinius superpowers

Do you think the following syntax error tooltip is more useful?

Validate and Save - Rubinius installed

Yes it lovely, and the new Ruby.tmbundle will automatically do this if it can find rbx in your TextMate’s $PATH. Yeah yeah.

If you have Homebrew installed:

brew install rubinius

Then in TextMate, add your homebrew bin folder to the $PATH.

  • Go to TextMate’s Preferences (Cmd+,)
  • Go to “Advanced”, then “Shell Variables”
  • Edit the PATH variable, and add “:/path/to/homebrew/bin”

For example, if you have homebrew installed in ~/.homebrew then you might add :/Users/drnic/.homebrew/bin

. My complete $PATH in TextMate is:

/usr/bin:/bin:/usr/sbin:/sbin:/opt/local/bin:/usr/local/bin/:/Users/drnic/.homebrew/bin

Save a dodgy Ruby file and see the beautifully helpful syntax message.

Install Ruby.tmbundle

To install via Git:

mkdir -p ~/Library/Application\ Support/TextMate/Bundles
cd ~/Library/Application\ Support/TextMate/Bundles
git clone git://github.com/drnic/ruby-tmbundle.git "Ruby.tmbundle"
osascript -e 'tell app "TextMate" to reload bundles'

Using CoffeeScript in Rails and even on Heroku

I’m pretty excited about CoffeeScript as a clean-syntax replacement for pure JavaScript.

What is CoffeeScript?

Imagine all the syntactical delights of Ruby and Haml for your JavaScript. You write in a nice language, but get normal JavaScript at runtime. All whilst having full access to 3rd-party JavaScript libraries (jQuery, PrototypeJS), debugging support (it becomes pure, readable JavaScript), existing support from test suites (it’s normal JavaScript) and growing support from various text editors (TextMate, Vim, Emacs).

What simple delights?

No trailing semi-colons. No { some_code() } function/closure brackets. String interpolation. Multi-line strings. Explicit class syntax. Array slicing. An existential ? operator.

Scroll down the home page for awesome example after example.

These aren’t library extensions. This is clean, purposeful syntax.

You can play with the joyful syntax of CoffeeScript on the website. After reading the basic examples on the CoffeeScript home page, press “TRY COFFEESCRIPT” in the header menu.

As you play with the syntax, the equivalent JavaScript is printed on the right hand side (see image above).

How nice is that syntax? Very.

Installing CoffeeScript

  1. Install NodeJS
  2. Install CoffeeScript

For NodeJS (get latest release URL; using 0.1.31 as 0.1.32 doesn’t unpack for me):

cd /usr/local/src
wget http://nodejs.org/dist/node-v0.1.31.tar.gz
tar xfv node-v0.1.31.tar.gz
cd node-v0.1.31
./configure
make
sudo make install

For CoffeeScript (get latest release URL):

cd /usr/local/src
wget http://github.com/jashkenas/coffee-script/tarball/0.5.5
tar xfv jashkenas-coffee-script-bcf7b3f.tar.gz
cd jashkenas-coffee-script-bcf7b3f
sudo bin/cake install

Now test that everything is in place:

$ coffee --version
CoffeeScript version 0.5.5
$ coffee -e "sys: require 'sys'; sys.puts 'hello world\n'"
hello world

Phew!

Note, in the command-line/on the server, you are using the NodeJS JavaScript environment. It supports the CommonJS API for loading modules (normal JavaScript: var sys = require('sys')).

Um, but how do I use it in my web app?

Your application source code will have *.coffee files containing your sexy, short CoffeeScript. But at runtime, the browser needs the generated JavaScript.

I’ve been using the Jonas Nicklas’ bistro_car gem:

gem install bistro_car
mkdir -p app/scripts

In your Rails config/environment.rb file, add:

config.gem 'bistro_car'

And in your layouts, such as app/views/layouts/application.html.erb add to the <head> or the bottom:

<%= coffee_script_bundle %>

Now you’re good to go. Add your CoffeeScript files in app/scripts/*.coffee and they will be automatically available as JavaScript.

WARNING: Check your version of CoffeeScript

Check that this hasn’t happened:

$ coffee --version
CoffeeScript version 0.3.2
$ which coffee
/usr/bin/coffee

Arrgh, we should be using /usr/local/bin/coffee. bistro_car currently installs the old rubygem-based version of coffee-script; and you might be unlucky to have your $PATH find the wrong one first.

Either delete it (sudo rm /usr/bin/coffee and restart your shell) or make sure /usr/local/bin is earlier in your $PATH than /usr/bin, where RubyGems installed the old, unnecessary version of coffee command.

Let’s drink the CoffeeScript

Create a file app/scripts/application.coffee with contents:

powers: [1,2,3,4].map (i) -> i * i
alert powers

Load up a view in a browser and see [1,4,9,16]. You win! Throw in some jQuery/PrototypeJS/whatever. Beautiful.

View the source of the page, navigate to public/javascripts/bundle/default.js and you’ll see the generated source:

(function(){
  var powers;
  powers = [1, 2, 3, 4].map(function(i) {
    return i * i;
  });
  alert(powers);
})();

The problem: Heroku doesn’t have CoffeeScript installed

Heroku is a great place to host apps. Though it doesn’t have CoffeeScript installed so it cannot dynamically convert the *.coffee files into JavaScript.

If you want to use Heroku I guess we need to perform the conversion locally and deploy it.

But. In development and integration testing I want bistro_car’s dynamically generated default.js. In production, I need a cached version.

In application.html.haml I use (I can’t keep pretending I use erb):

- if Rails.env.production?
  = javascript_include_tag "coffeescripts"
- else
  = coffee_script_bundle

Now we’re just left with the hassle of automatically generating public/javascripts/coffeescripts.js.

First, a rake task. Second, a git pre-commit hook.

Create lib/tasks/bistro_car.rake:

desc "Generate the cached bundle/default.js file from app/scripts/*.coffee files"
task :bistro_car => :environment do
  path = "public/javascripts/coffeescripts.js"
  puts "Building *.coffee -> #{path}"
  File.open(path, "w") { |file| file << BistroCar::Bundle.new('default').to_javascript }
end

file "public/javascripts/coffeescripts.js" => Dir[File.join(Rails.root, 'app/scripts/*.coffee')] do |t|
  Rake::Task["bistro_car"].invoke
end

Now you can create coffeescripts.js and add it to the repo with:

rake public/javascripts/coffeescripts.js
git add public/javascripts/coffeescripts.js
git commit -m "Initial bundled coffeescripts file"

Now create .git/hooks/pre-commit:

#!/bin/sh

exec rake public/javascripts/coffeescripts.js

And make it executable (and git commit will invoke it automatically):

chmod +x .git/hooks/pre-commit

Phew.

Now, whenever you change a *.coffee script and you are about to commit it, the cached-production-only coffeescripts.js is automatically updated and included in the same commit.

Seems like a clean hack.

Summary

Why not make a library to do this? Well I’m hoping there is a better, cleaner way. Perhaps bistro_car can include a rails generator to package these bits and pieces itself, if my approach happens to be the best way.

Nonetheless, let history record that CoffeeScript is very cool though in the world of Heroku living with it is non-trivial at the moment.

Hacking someone’s gem with github and gemcutter

gemcutter

Ever used a rubygem, found a bug, and just wanted to quickly bust out the big guns and fix it quickly?

The gem command doesn’t come packed with a way to find the original source repository for a gem. At best, most gems at least come bundled with the complete source, tests and documentation. Some gems don’t. Fair enough, since having access to the complete source via the gem still doesn’t allow you to fix a bug and share it with the world.

For that you access to the repo, a quick way to fork it, and a post-github way to share a gem version from yours truly.

The github gem and gemcutter are the modern day tools of master hackermanship.

Instant forking fun

Let’s say you find a bug in a gem, say rails, and you want to go to town on its source.

You know the gem is called rails but you’ve no idea what the github repo is called. Never fear.

$ gem sources -a http://gemcutter.org
$ sudo gem install github
$ gh clone --search rails
Select a repository to clone:
1.  rails/rails                         # Ruby on Rails
2.  technoweenie/restful-authentication # Generates common user ...
3.  justinfrench/formtastic             # A Rails form builder plugin ...
?

Press 1 and you’ll get a clone of rails/rails.

Alternately, if you want a fork or you know the exact user/repo already:

$ gh clone rails/rails

Now, fork your own version:

$ cd rails
$ gh fork

You now have your own fork. The origin remote also now points to your fork rather than the rails/rails repository:

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:drnic/rails.git
  Push  URL: git@github.com:drnic/rails.git

So, make your changes, push them. Send a pull request or github issue or lighthouse ticket or what have you.

Want to get to the github project home page for your fork?

$ gh home

Instant gem sharing

Let’s say you patched the rails gem itself but you want to share your changes via your own gem.

In the olden days, github did this for you. Now you use gemcutter, and a little manual effort to do your own renaming.

First, install the gems locally, use them, and make sure all is good.

For rails, you install the edge gems (3.0.pre) with:

$ rake install

You can’t see ‘rake install’ in the rake -T list (hence my patch), but I think the following expression displays all tasks regardless if they have a description or not:

$ rake -P | grep "^r"

Rails is composed of several gems, unlike most projects that are distributed as a single gem. Here we want to share our commit within a new drnic-rails gem, but not touch the others.

Edit the railties/rails.gemspec file from:

Gem::Specification.new do |s|
  s.platform = Gem::Platform::RUBY
  s.name = 'rails'
  s.version = '3.0.pre'
...

and give your personal gem a new name:

Gem::Specification.new do |s|
  s.platform = Gem::Platform::RUBY
  s.name = 'drnic-rails'
  s.version = '3.0.pre'

To build and distribute the new gem:

$ gem build railties/rails.gemspec
$ sudo gem install gemcutter
$ gem push drnic-rails-3.0.pre.gem
  Pushing gem to Gemcutter...
  Successfully registered gem: drnic-rails (3.0.pre)

Follow any first-time gemcutter instructions and SUCCESS! Now I have my own drnic-rails gem.

Summary

To find, clone, and fork any rubygem that is hosted on github:

$ sudo gem install drnic-github
$ gh clone --search rails
$ gh fork

To personalise the gem and share it on gemcutter:

> edit the project.gemspec to have a unique name, e.g. yourname-project
$ gem build project.gemspec
$ sudo gem install gemcutter
$ gem push yourname-project-1.0.0.gem

I think this makes it much easier, faster and more fun to hack other people’s stuff.

First look at rails 3.0.pre

This article is out of date in some aspects. See comments, and perhaps this summary of reading materials about Rails 3.

3.pre

Today I had my first look at rails 3.0.pre and below are the sequence of steps I had to take to create a rails 3.0.pre application, and get it’s generators to work.

Why was I looking at the top-secret, yet open-source Rails 3.0? Their generators are being migrated over to Thor and I wanted to see them in action. I was thinking I might migrate newgem to use Thor too.

Here’s a quick poke around of getting started and interesting things I found. Any hiccups and workarounds are meant as a guide to pre-pre-3.0 users/developers and not as a criticism of rails-core. Rails 3.0 is looking shiny and awesome.

NOTE: Since this is a “how to install and use” rails 3.0 edge, which is still in heavy development, this set of instructions might break. Let’s hope not.

Getting Started

As of today, you cannot install 3.0.pre from rubygems [1]. So, let’s install them from source. Which is handy, you might like to patch something.

$ cd ~/gems
$ git clone git://github.com/rails/rails.git
use_ruby_191 *

[*] If you are on OS X Snow Leopard I think you can ignore this. Otherwise, since you don’t have the 3.0.pre gems installed, you’re about to hit bump #1. Ruby 1.8.6 doesn’t have Symbol#to_proc but it’s required to create a rails app. This means you’ll need to be able to switch to another version of ruby temporarily if you’re on ruby 1.8.6 [2].

cd ~/Sites
ruby ~/gems/rails/railties/bin/rails

Oooh, look at all the new options! Some new ones are:

-F, [--freeze]             # Freeze Rails in vendor/rails from the gems
-O, [--skip-activerecord]  # Skip ActiveRecord files
-T, [--skip-testunit]      # Skip TestUnit files
-J, [--skip-prototype]     # Skip Prototype files

The -D, --with-dispatchers flags have been removed. --freeze isn’t new, but -F is.

So, to create an app, I dutifully used:

ruby ~/gems/rails/railties/bin/rails edgerailsapp -F

BAM! Fail. The -F option to freeze/vendor rails fails without the gems installed. So don’t use it.

ruby ~/gems/rails/railties/bin/rails edgerailsapp
ln -s ~/gems/rails vendor/rails

If you’re on Windows without the symlink command ln, then copy the downloaded rails source into vendor/rails.

Fetch Rails’ dependencies

Rails 3.0 source uses the new bundler project to describe its own dependencies. From Nick Quaranto’s article on bundler, get the latest:

cd ~/gems
git clone git://github.com/wycats/bundler
cd bundler
sudo rake install

Now, back in your app, you need to install some rails dependencies here too. It’s a good chance to see how you’ll bundle gem dependencies in the future.

$ cd ~/Sites/edgerailsapp

Change the Gemfile in your project to the following:

gem "rack",          "1.0.1"
gem "rack-mount",    :git => "git://github.com/rails/rack-mount.git"
gem "rack-test",     "~> 0.5.0"
gem "erubis",        "~> 2.6.0"
gem "arel",          :git => "git://github.com/rails/arel.git"
gem "sqlite3-ruby"
gem "rails", "3.0.pre", :git => "git://github.com/rails/rails.git"

Welcome to the future of gem dependencies for rails apps. Ultimately you won’t need to manually add these lines yourself. When rails is distributed as gems it will automatically install these for you, I assume/hope/guess. But for today, you seem to need them.

Now locally (within your app) install these gems:

$ gem bundle

If you get “can’t convert Pathname into String” then revert to ruby 1.8.X and reinstall bundler into your 1.8 gem cache.

Phew. Ooh, my god. Phew. Only now will script/generate work.

$ script/generate

For me, this outputs:

Please select a generator.
Builtin: app, controller, generator, helper, integration_test, mailer, metal, migration, model, model_subclass, observer, performance_test, plugin, resource, scaffold, scaffold_controller, session_migration, stylesheets.
Others: app_layout:app_layout, check_migration_version:check_migration_version, home_route:home_route.

The “Builtin” generators are the latest and greatest in Thor technology. Rails 3.0 no longer uses its own generator but is built upon Thor.

For example, our old favourite model generator works thusly:

$ script/generate model Post title:string --no-fixture
    invoke  active_record
    create    db/migrate/20091103030824_create_posts.rb
    create    app/models/post.rb
    invoke    test_unit
    create      test/unit/post_test.rb

Interestingly, --no-fixture isn’t mentioned in the usage information for script/generate model. It mentions the --fixture flag, but I had to guess that --no-fixture was also supported.

Hmm, I want to use rspec. So, let’s destroy these files:

$ script/destroy model Post title:string
      invoke  active_record
.../vendor/gems/dirs/rails/railties/lib/rails/generators/active_record.rb:14:in
  `next_migration_number': uninitialized constant ActiveRecord::Base (NameError)

Oh well.

What if I wanted to run rspec and cucumber generators, for example, against an edge rails app?

Rails 2 generators

The “Others” generators are my own local generators from ~/.rails/generators. Amusingly, instead of app_layout it is called app_layout:app_layout. Not surprisingly at all, if I try to run the rails 2 generator it fails:

$ script/generate app_layout:app_layout
[WARNING] Could not load generator at "/Users/drnic/.rails/generators/app_layout/app_layout_generator.rb". Error: uninitialized constant Rails::Generator
Could not find generator app_layout:app_layout.

Poop.

Note, I have rspec, rspec-rails and cucumber gems installed locally but I cannot see their rails generators above. Rails 3 doesn’t look for generators in the same way and old Rails 2 generators don’t work anymore.

That’s the news: every rails 2 generator is broken.

When I start to migrate some of mine I’ll post about it. In the meantime, José Valim has written some introduction thoughts on using Thor as a generator.

You can also probably learn about how to write rails 3.0 generators by looking at the source code for the new generators like rails, model (see main and test_unit), and scaffold/resource.

Finally, José Valim has a sample Rails 3 app with some vendored generators in it.

These are the things I’m researching now.

Summary

This article is long, mostly because rails 3.0.pre hasn’t been released as a set of RubyGems. If it had, then all the dependencies would be installed automatically.

It also introduces gem/plugin writers to the first upgrade issue: your current generators are neither discovered nor work by a rails 3.0 app. We’re all clever cookies, so here’s hoping we can figure out the upgrade path and that it’s simple enough to not be the topic of Dan Brown’s next book.

Footnotes

[1] Two portions of rails 3.0.pre are available as pre-release gems: activesupport (which is now very modularised and only loads up the parts that you require) and activemodel (which is shiny and new and hence completely safe for rails-core to release).

[2] There are two popular ways to have easy, non-intrusive access to alternate versions of ruby: rvm and ruby_switcher.sh.

TDD for Greasemonkey scripts; and introducing Ninja Search JS

“this article shows how I used test-driven development tools and processes on a Greasemonkey script.” Though it also includes free ninjas.

1. Long drop downs hate humans

When I do online banking I need to select from a large list of other people’s bank accounts to which I might like to transfer money too. It is the massive drop down list that I must scroll through that I wish to raise issue with today. The problem of having to give other people money is probably a different discussion.

And take those time-zone selector drop down lists, for example, the massively long list rendered by Rails’ time_zone_select helper. Granted, I am thankful for you letting me choose my timezone in your web app. Though for those of us not living in the USA we must hunt for our closest city in the list. Dozens of locations, ordered by time zone and not the name of the city (see adjacent image). Unfortunately you can’t easily type a few letters of your current city to find it. Rather, you have to scroll. And if you live in the GMT+1000 time zone group (Eastern Australia), you have to scroll all the way to the bottom.

5. Choose from a small list

So I got to thinking I’d like a Greasemonkey (for Firefox) or GreaseKit (for Safari) script that automatically converted all ridiculously long HTML drop down lists into a sexy, autocompletion text field. You could then type in “bris” and be presented with “(GMT+1000) Brisbane”, or given the less amusing banking scenario then I could type “ATO” and get the bank account details for the Australian Tax Office.

I mean, how hard could it be?

This article is two things: an introduction to Ninja Search JS which gives a friendly ninja for every drop down field to solve the above problem. Mostly, the rest of this article shows how I used test-driven development tools and processes on a Greasemonkey script.

Introducing Ninja Search JS

Ninja Search JS banner

Click the banner to learn about and install the awesome Ninja Search JS. It includes free ninjas.

Currently it is a script for Greasemonkey (FireFox) or GreaseKit (Safari). It could be dynamically installed as necessary via a bookmarklet. I just haven’t done that yet. It could also be a FireFox extension so it didn’t have to fetch remote CSS and JS assets each time.

Ninja Search JS uses liquidmetal and jquery-flexselect projects created by Ryan McGeary.

Most importantly of all, I think, is that I wrote it all using TDD. That is, tests first. I don’t think this is an erroneous statement given the relatively ridiculous, and unimportant nature of Ninja Search JS itself.

TDD for Greasemonkey scripts

I love the simple idea of Greasemonkey scripts: run a script on a subset of all websites you visit. You can’t easily do this on desktop apps, which is why web apps are so awesome – its just HTML inside your browser, and with Greasemoney or browser extensions you can hook into that HTML, add your own DOM, remove DOM, add events etc.

But what stops me writing more of them is that once you cobble together a script, you push it out into the wild and then bug reports start coming back. Or feature requests, preferably. I’d now have a code base without any test coverage, so each new change is likely to break something else. Its also difficult to isolate bugs across different browsers, or in different environments (running Ninja Search JS in a page that used prototypejs originally failed), without a test suite.

And the best way to get yourself a test suite is to write it before you write the code itself. I believe this to be true because I know it sucks writing tests after I’ve writing the code.

I mostly focused on unit testing this script rather than integration testing. With integration testing I’d need to install the script into Greasemonkey, then display some HTML, then run the tests. I’ve no idea how’d I’d do that.

testing running

But I do know how to unit test JavaScript, and if I can get good coverage of the core libraries, then I should be able to slap the Greasemonkey specific code on top and do manual QA testing after that. The Greasemonkey specific code shouldn’t ever change much (it just loads up CSS and more JS code dynamically) so I feel ok about this approach.

For this project I used Screw.Unit for the first time (via a modified version of the blue-ridge rails plugin) and it was pretty sweet. Especially being able to run single tests or groups of tests in isolation.

Project structure

summary of project structure

All the JavaScript source – including dependent libraries such as jquery and jquery-flexselect – was put into the public folder. This is because I needed to be able to load the files into the browser without using file:// protocol (which was failing for me). So, I moved the entire project into my Sites folder, and added the project as a Passenger web app. I’m ahead of myself, but there is a reason I went with public for the JavaScript + assets folder.

In vendor/plugins, The blue-ridge rails plugin is a composite of several JavaScript libraries, including the test framework Screw.Unit, and a headless rake task to run all the tests without browser windows popping up everywhere. In my code base blue-ridge is slightly modified since my project doesn’t look like a rails app.

Our tests go in spec. In a Rails app using blue-ridge, they’d go in spec/javascripts, but since JavaScript is all we have in this project I’ve flattened the spec folder structure.

The website folder houses the github pages website (a git submodule to the gh-pages branch) and also the greasemonkey script and its runtime JavaScript, CSS, and ninja image assets.

A simple first test

For the Ninja Search JS I wanted to add the little ninja icon next to every <select> element on every page I ever visited. When the icon is clicked, it would convert the corresponding <select> element into a text field with fantastical autocompletion support.

For Screw.Unit, the first thing we need is a spec/ninja_search_spec.js file for the tests, and an HTML fixture file that will be loaded into the browser. The HTML file’s name must match to the corresponding test name, so it must be spec/fixtures/ninja_search.html.

For our first test we want the cute ninja icon to appear next to <select> drop downs.

require("spec_helper.js");
require("../public/ninja_search.js"); // relative to spec folder

Screw.Unit(function(){
  describe("inline activation button", function(){
    it("should display NinjaSearch image button", function(){
      var button = $('a.ninja_search_activation');
      expect(button.size()).to(be_gte, 1);
    });
  });
});

The Blue Ridge textmate bundle makes it really easy to create the describe (des) and it (it) blocks, and ex expands into a useful expects(...).to(matcher, ...) snippet.

The two ellipses are values that are compared by a matcher. Matchers are available via global names such as equals, be_gte (greater than or equal) etc. See the matchers.js file for the default available matchers.

The HTML fixture file is important in that it includes the sample HTML upon which the tests are executed.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title>Ninja Search | JavaScript Testing Results</title>
  <link rel="stylesheet" href="screw.css" type="text/css" charset="utf-8" />
  <script src="../../vendor/plugins/blue-ridge/lib/blue-ridge.js"></script>
</head>

<body>
  <div>
    <label for="person_user_time_zone_id">Main drop down for tests</label>
    <select name="person[user][time_zone_id]" id="person_user_time_zone_id" style="display: inline;">
      <option value="Hawaii">(GMT-10:00) Hawaii</option>
      <option value="Alaska">(GMT-09:00) Alaska</option>
      <option value="Pacific Time (US & Canada)">(GMT-08:00) Pacific Time (US & Canada)</option>
      <option value="Arizona">(GMT-07:00) Arizona</option>
      <option value="Mountain Time (US & Canada)">(GMT-07:00) Mountain Time (US & Canada)</option>
      <option value="Central Time (US & Canada)">(GMT-06:00) Central Time (US & Canada)</option>
      <option value="Eastern Time (US & Canada)">(GMT-05:00) Eastern Time (US & Canada)</option>
    </select>
  </div>
</body>
</html>

In its header it loads the blue-ridge JavaScript library, which in turn loads Screw.Unit and ultimately our spec.js test file (based on corresponding file name), so ninja_search.html will cause a file spec/ninja_search_spec.js to be loaded.

To run our first test just load up the spec/fixtures/ninja_search.html file into your browser.

Your first test will fail. But that’s ok, that’s the point of TDD. Red, green, refactor.

Simple passing code

So now we need some code to make the test pass.

Create a file public/ninja_search.js and something like the following should work:

(function($){
  $(function() {
    $('select').each(function(index) {
      var id = $(this).attr('id');

      // create the Ninja Search button, with rel attribute referencing corresponding >select id="...">
      $('> class="ninja_search_activation" rel="' + id + '">ninja search>/a>')
      .insertAfter($(this));
    });
  });
})(jQuery);

Reload your test fixtures HTML file and the test should pass.

Now rinse and repeat. The final suite of tests and fixture files for Ninja Search JS are on github.

Building a Greasemonkey script

Typically Greasemonkey scripts are all-inclusive affairs. One JavaScript file, named my_script.user.js, typically does the trick.

I decided I wanted a thin Greasemonkey script that would dynamically load my ninja-search.js, and any stylesheets and dependent libraries. This would allow people to install the thin Greasemonkey script once, and I can deploy new versions of the actual code base over time without them having to re-install anything.

Ultimately in production, the stylesheets, images, and JavaScript code would be hosted on the intertubes somewhere. Though during development that would be long-winded and painful to push the code to a remote host just to run tests.

So I have three Greasemonkey scripts:

  • public/ninja_search.dev.user.js – loads each dependent library and asset from the local file system
  • public/ninja_search.local.user.js – loads compressed library and asset from the local file system
  • public/ninja_search.user.js – loads compressed library and assets from remote server

Let’s ignore the optimisation of compressing dependent JavaScript libraries for the moment and just look at the dev.user.js and user.js files.

The two scripts differ in the target host from which they load assets and libraries. ninja_search.dev.user.js loads them from the local machine and ninja_search.user.js loads them from a remote server.

For example ninja_search.dev.user.js loads local dependencies like this:

require("http://ninja-search-js.local/jquery.js");
require("http://ninja-search-js.local/ninja_search.js");

And ninja_search.user.js loads remote dependencies like this:

require("http://drnic.github.com/ninja-search-js/dist/jquery.js");
require("http://drnic.github.com/ninja-search-js/dist/ninja_search.js");

In the final version of ninja_search.user.js we load a simple, conpressed library containing jquery, our code, and other dependencies, called ninja_search_complete.js.

Using Passenger to server local libraries

The problem with loading local JavaScript libraries using the file:// protocol, inferred earlier, is that it doesn’t work. So if I can’t load libraries using file:// then I must use the http:// protocol. That means I must route the requests through Apache/Ningx.

Fortunately there is a very simple solution: use Phusion Passenger which serves a “web app’s” public folder automatically. That’s why all the javascript, CSS and image assets have been placed in a folder public instead of src or lib or javascript.

On my OS X machine, I moved the repository folder into my Sites folder and wired up the folder as a Passenger web app using PassengerPane. It took 2 minutes and now I had http://ninja-search.local as a valid base URL to serve my JavaScript libraries to my Greasemonkey script.

Testing the Greasemonkey scripts

I can only have one of the three Greasemonkey scripts installed at a time, so I install the ninja-search.dev.user.js file to check that everything is basically working inside a browser on interesting, foreign sites (outside of the unit test HTML pages).

Once I’ve deployed the JavaScript files and assets to the remote server I can then install the ninja-search.user.js file (so can you) and double check that I haven’t screwed anything up.

Deploying via GitHub Pages

The normal, community place to upload and share Greasemonkey scripts is userscripts.org. This is great for one file scripts, though if your script includes CSS and image assets, let alone additional JavaScript libraries, then I don’t think its as helpful, which is a pity.

So I decided to deploy the ninja-search-js files into the project’s own GitHub Pages site.

After creating the GitHub Pages site using Pages Generator, I then pulled down the gh-pages branch, and then linked (via submodules) that branch into my master branch as website folder.

Something like:


git checkout origin/gh-pages -b gh-pages
git checkout master
git submodule add -b gh-pages git@github.com:drnic/ninja-search-js.git website

Now I can access the gh-pages branch from my master branch (where the code is).

Then to deploy our Greasemonkey script we just copy over all the public files into website/dist, and then commit and push the changes to the gh-pages branch.


mkdir -p website/dist
cp -R public/* website/dist/
cd website
git commit -a "latest script release"
git push origin gh-pages
cd ..

Then you wait very patiently for GitHub to deploy your latest website, which now contains your Greasemonkey script (dist/ninja-search.user.js) and all the libraries (our actual code), stylesheets and images.

Summary

Greasemonkey scripts might seem like small little chunks of code. But all code starts small and grows. At some stage you’ll wish you had some test coverage. And later you’ll hate yourself for ever having release the bloody thing in the first place.

I wrote all this up to summarise how I’d done TDD for the Ninja Search JS project, which is slightly different from how I added test cases to _why’s the octocat’s pajamas greasemonkey script when I first started hacking with unit testing Greasemonkey scripts. The next one will probably be slightly different again.

I feel good about the current project structure, I liked Screw.Unit and blue-ridge, and I’m amused by my use of GitHub Pages to deploy the application itself.

If anyone has any ideas on how this could be improved, or done radically differently, I’d love to hear them!

Polish your Rails project with Mocra

I want to help you, your business, your boss and your project reach delightful levels of wickedly awesomeness. I’m so proud of the small team of ace Rails developers here at Mocra and what I know we can do for you.

Send an email to rails@mocra.com about your current/future projects. Dare us to be more awesome!

While you wait for a reply perhaps you’d like to learn more about How we do it at Mocra?