Jean-Hadrien Chabran

Troll-coding wizard

Build a Chrome Extension With Coffee Script

Ever had an idea for a great Chrome Extension ? Did you know that a chrome extension is just javascript ? And where there’s Javascript, we can write some CoffeeScript !

This post aims to give you an overview of building a chrome extension wrote in CoffeeScript. While being familiar with the latest is mandatory to understand what’s going on there, no previous experience with Google Chrome is needed.

Our chrome extension will be a fully fonctional tab switcher that mimics Command-T feature of Textmate (also known as fuzzy finding).

Code on Github and Extension on Chrome store

Why doing it in CoffeeScript ?

Coffee Script is a thin and elegant syntaxic layer on top of Javascript, allowing you to write cleaner and concise code and still outputting almost readable javascript. Why should we avoid a such nice tool to write a chrome extension ! Plus it’s fun to write, it will remind you Ruby and Python, while still letting you do Javascript wizardry.

For french readers, I gave a talk at a recent Paris.rb event, you can read my slides until we get the video online.

Our goal

Command-T is a battle-tested quick-file-access method that proved to be efficient. It should be useful to have it available in Chrome, especially if you often have more than 20 tabs opened, where they all look like pinned ones. Typing a few letter of the URL is clearly faster than hammering like a monkey the next tab hotkey !

Couldn’t we port that great feature in Chrome ?

Dissecting an extension

Chrome being a popular browser, it is as expected from a modern browser, pretty easy to extend. Google’s starter guide is a good resource and gives you a quick intro.

Skipping implementation details, it’s basically the following :

  • A content script is executed in the context of the current page, having access to the DOM
  • An extension script is executed in what you could call chrome context, meaning it can manipulate chrome objects like tabs, windows
  • The background page include the extension script
  • These two contexts are sandboxed, meaning you can’t collide with the scripts running on the page
  • Communication between them are made through message passing

Get confortable

The absolute minimum is the following structure :

1
2
3
tabswitcher               # Repository root 
      /background.html    # Extension's 'main view'
      /manifest.json      # Extension settings 

Coffee Script need to be compiled in the first place, automating it brings two benefits : it’s comfortable to develop with, a contributor can just check out your sources and run your command to build the whole thing. This lower the entry barrier for contributing to our extension =).

The simplest way to handle compilation easily is to build a Cakefile (a Rakefile or Makefile in CoffeeScript).

We’ll write it to take *.coffee input from /src and output javascript in /build using this command. Our goal is to do the following to build our extension :

1
  $ cake build

But while in development, it’s easier to have our files monitored to reflect changes as we save them. So To watch the src/ folder and reflect any changes made there, there’s the watch command :

1
  $ cake watch

coffee -h tells us these commands are directly available :

1
  $ coffee --output build/ --watch src/

Good. It’s time to bake this into a Cakefile. Below are the interesting parts of it :

1
2
3
4
5
6
7
8
task 'build', 'Build extension code into build/', ->
  if_coffee ->
    ps = spawn("coffee", ["--output", JAVASCRIPTS_PATH,"--compile",COFFEESCRIPTS_PATH])
    ps.stdout.on('data', log)
    ps.stderr.on('data', log)
    ps.on 'exit', (code)->
      if code != 0
        console.log 'failed'

If you’ve alreay wrote any Rakefile, it’s quite similar. If not, we basically declare the command build to be invokable through cake build. We handle if the coffee binary is available or not in the $PATH and finally execute our coffee command as expected.

A small overview

Manipulating the DOM through the standard API bores me to death, so let’s grab Zepto to do the big work for us. We could have used JQuery but we don’t need all the browser compatibility stuff, so Zepto with its minimal features set is a perfect match. Let’s store it in /libs.

Our final structure is the following :

1
2
3
4
5
6
7
tabswitcher               # Repository root 
      /build              # Generated Javascripts end there
      /libs               # Dependencies
      /src                # Our code
      /background.html    # Extension's 'main view'
      /manifest.json      # Extension settings 
      /Cakefile           # Starts build task

Ok, we’re now ready to spill some coffee into Chrome :

1
  $ cake watch

The extension itself

Our extension is quite simple in its behavior :

  • listen for keyboard events if ctrl-\ was pressed instead of command-t because we can’t bind to command in Chrome and it’s not portable edit : cmd sends meta under osx
  • if pressed, insert some html in the page containing our UI
  • display opened tabs
  • wait for user input
  • on enter in the input, go to that tab

So in these steps, those two are calls to chrome api :

  • list all opened tabs, we’ll name it getTabs
  • go to a tag, as switchTab

Our content script that run in the current page, it will send these two messages to the background script, which is the only one that can make these calls.

We end with the following process :

The red arrows are message passed from the content script to the background page ( message passing ). It’s similar to firing custom events with JQuery and listening for them, but with a particular API.

Implementation

The content script is src/content.coffee and background script lives in src/background.coffee

First things first : a tab. It’s simpler than what you may have expected

1
2
3
4
5
6
  tab = 
    id : 43
    windowId : 4
    url: "http://google.com"
    title: "Google"
    ...

We don’t need to handle them directly as the Chrome API will do the job for us, but it’s a starting point.

Let’s examine the content script, which is where all the work happens.

An Application class encapsulates the main logic. It setups the UI, binds the callbacks and pass messages to the background page.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class Application
  constructor: ->
    # Inject our html into the view
    @injectView()

    # Install a listener for our input 
    @element().find('input').keyup (event)=>
      @onInput(event)

    # Spawn a view that handle results display
    @tabListView = new TabListView @element().find('ul')

  element: ->
    # Return our base div
    @element_ ||= $('#tabswitcher-overlay')

  onInput: (event)->
    # When something is entered is the input, filter tabs !
    candidates = fuzzy(@tabs(), event.target.value)

    # Update tabs that match
    @tabListView.update candidates

    # If enter
    if event.keyCode == 13
      # Go to that tab
      @switchTab candidates[0].tab if candidates?

  hide: ->
    # ...
  show: ->
    # ...

  switchTab: (tab)->
    # We're switching tab, hide the UI before leaving
    @hide()

    # Send message to the background script
    chrome.extension.sendRequest(message:"switchTab", target:tab)

  hotKeyListener: (event)->
    # Listen for ctrl-\
    if event.keyCode
      if event.ctrlKey && event.keyCode == 220 # Ctrl + \
        # Send message to background script, ask for list of tabs
        chrome.extension.sendRequest {message: "getTabs"},
          (response)=>
            @tabs_ = response.tabs
            @show()

      else if event.keyCode == 27 # ESC
        @hide()

  injectView: ->
    # Inject our UI in the DOM
    $('body').append ...

app = new Application()

# Attach our handler
window.addEventListener("keyup", (e)->
  app.hotKeyListener(e)
,false)

After defining Application we just instanciate it and bind our listener, to grab keyboard events. For the sake of readability, I’ve skipped the fuzzy filter implementation, which is kind of naive but do the job as expected. Bold stuff as you can see in the screenshot in the beginning of the post is handled in another class named TabView.

Let’s now see the script running on the background page that respond to calls made from the content script :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Install the message listener
chrome.extension.onRequest.addListener (request, sender, sendResponse)->
  # Select the right response given the message
  switch request.message
    # Grab all tabs
    when "getTabs"
      chrome.windows.getCurrent (window)->
        chrome.tabs.getAllInWindow window.id, (tabs)->
          # We've collected all tabs, let's send them back
          sendResponse(tabs:tabs)
      break
    when "switchTab"
      chrome.tabs.update(request.target.id, selected:true)
      sendResponse({})
      break
    else
      sendResponse({})

Pretty straight-forward, we just take incoming message and handle them. Only the message ‘getTabs’ sends back a response : an array of tabs returned by Chrome.

What now ?

Well, beside some crappy HTML to render tabs, there’s nothing left. The complete code of this extension is available on GitHub where you can explore it, fork it as you want !

Remember that you need to enable developer’s mode in chrome extensions to install it directly from the sources.

You can also install the released version. It doesn’t have all original features, you can’t select the second result for example, but it’s usable.

Coffee Script is available everywhere you can use javascript, with some tooling to kick in compilation ! Set up two tasks, adjust .gitignore and there it works.

Chrome extensions are way simpler to write than I thought ! Next, understanding how Chrome handles security and isolation through sandboxing and still sharing DOM access is pretty impressive.

Once you grasped the big picture, it’s finally just like building any web app interactive UI !

Writing Readable Model Specs

Writing Rails specs with RSpec and FactoryGirl is easy to do when you got a basic understanding of testing principles but you may have noticed how these specs tends to get cluttered over time. Even to the point you don’t get what’s going on at all and call your co-worker who wrote them and ask him to handle your task!

The following points are basic principles to keep in mind while writing specs to avoid being stuck with an unreadable spec.

We deal with four models : User, Cart, Order and Item. Their relationships are obviously as simplified as possible to keep ourselves focused on their tests.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class User
  has_one :cart
  has_many :orders
end

class Cart
  belongs_to :user
  has_many :items
end

class Order
  has_many :items
  belongs_to :user
end

class Item
  belongs_to :cart
  belongs_to :order
  belongs_to :product
end

Don’t Repeat Yourself

As usual, the DRY principle. Consider the following code :

user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
describe User do
  before :each do
    @user = Factory.create :user
  end

  it "should order the cart with one item" do
    @cart = Factory.create :cart, :user => @user
    @item = Factory.create :item, :cart => @cart

    @user.order! @cart
    @cart.should be_ordered
  end

  it "should discard the cart" do
    @cart = Factory.create :cart, :user => @user
    @item = Factory.create :item, :cart => @cart

    @user.discard_cart
    @cart.items.should be_empty
  end
end

Quite clean by itself, we create a user for each test, as expected for a spec about the user model. Yet you can easily notice we’re building other models in our two tests.

We can factorize these factories instanciation to stay DRY :

user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
describe User do
  before :each do
    @user    = Factory.create :user
    @cart    = Factory.create :cart, :user => @user
    @item    = Factory.create :item, :cart => @cart
  end

  it "should order the cart with one item" do
    @user.order! @cart
    @cart.should be_ordered
  end

  it "should discard the cart" do
    @user.discard_cart
    @cart.items.should be_empty
  end
end

Now we got two tests and this example rise the following principle :

Test code should be almost a direct translation of its name

Any context initialization, should be done in a before block to avoid polluting the test code itself.

Enhance readability

As we avoid to pollute code to enhance readability, we can also emphasize on what’s important. It allows the reader to grasp with ease what’s going on.

user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
describe User do
  before :each do
    @user    = Factory.create :user
    @cart    = Factory.create :cart, :user => @user
    @item    = Factory.create :item

    @cart.items << @item # focus on adding an item
  end

  # ...
end

The main point of this before block is to craft a cart with an item within. As factories are cool, it doesn’t mean we have to use their features all the time.

Using the << operator on line 7, on the items association emphasize on adding our item to the cart instead of diluting it through the factories. This line of is the most important considering we’re testing how a user interacts with items.

So while writing your test code, be sure to avoid embedding your intentions in the basic plumbing.

One expectation per test please

To pursue in our readability quest, you may have noticed that the example used in the previous points was really simple. But what makes theses so simple ? Those two tests got only one expectation at a time.

Consider the following code :

user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe User do
  before :each do
    @user = Factory.create :user
    @cart = Factory.create :cart, :user => @user
    @item = Factory.create :item, :cart => @cart

    @order = @user.order! @cart
  end

  it "should finalize the order" do
    @order.finalize!
    @user.should have(1).finalized_orders
    @order.should be_finalized
  end
end

We’ve got two should call there. Even if it’s just slightly more complicated than before, you can separate concerns. We wrote describe User meaning we talk about user here. We do not want to mix expectations about orders and users.

Accordingly expectation on line 12 , even if being really similar to line 11 has nothing to do here. So we can rewrite this test in two separated tests :

order_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe Order do
  before :each do
    @cart  = Factory.create :cart
    @user  = @cart.user
    @cart.items << Factory.create_list :item, 3

    @order = @user.order! @cart
  end

  it "should finalize the order" do
    @order.finalize
    @order.should be_finalized
  end
end
user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe User do
  before :each do
    @user = Factory.create :user
    @cart = Factory.create :cart, :user => @user
    @item = Factory.create :item, :cart => @cart

    @order = @user.order! @cart
  end

  it "should finalize the order" do
    @order.finalize!
    @user.should have(1).finalized_orders
  end
end

Plain simple, just formulate expectations about your current subject while writing test and ignore the rest. Why ? Because if you don’t you’re leaving the coast of unit tests to head around integration testing land.

Slice your specs with different contexts

When it comes to models, there’s a lot to handle. Business logic, mass-assignements, validation sanity.( Remember fat models for skinny controllers eh ? It’s for a reason ! )

While you can argue if you should test validations and assignments or not, which is out of the topic here, we still have to test for a wide range of business logic cases.

All of these case can easily be sliced by concerns, for example a user can be edited and can order items through a cart. An easy way to name contexts is to use the ing form of the verb describing the action :

user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
describe User do
  context "editing informations" do
    # ...
  end

  context "ordering items" do
    # ...
  end

  context "canceling cart" do
    # ...
  end
end

Writing “while editing …” or “when editing …” is a matter of taste, while I personally tend to prefer a concise description.

And if we add validations and assignments ? (helpers are provided by should-matchers)

user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
describe User do
  describe "validations" do
    it { should validate_presence_of(:email) }
    it { should validate_presence_of(:name) }
  end

  describe "assignments" do
    it { should allow_mass_assignment_of(:email) }
    it { should allow_mass_assignment_of(:name) }

    it { should_not allow_mass_assignment_of(:administrator) }
  end

  context "editing informations" do
    # ...
  end

  context "ordering items" do
    # ...
  end

  context "canceling cart" do
    # ...
  end
end

That makes a readable skeleton for our tests.

The point of writing specs is to keep them enjoyable and litteraly act as documentation for everyone. Those four advices are just basics but at least ensure you’re heading in the right direction with your specs.

Notes on Migrating to Lion

I bought Lion yesterday ! People have already tested it against tools I use for work : Homebrew, RVM, MacVim so installing it on a friday night shouldn’t be too painful. Plus I got backups everywhere (local server + dropbox + github + tarsnap).

What went well

  • Downloading it, I got a stable and fast ADSL connection ( thanks Free.fr )
  • Installing it right after the download
  • Installing Xcode and Homebrew.

FAIL : Uninstalling MacFuse

  • Uninstalling MacFuse after the install. I forgot to remove it before intalling Lion, since it’s quite unmaintained, you can guess it’s going be boring to remove it correctly.
  • The uninstall script will fail with various errors.
  • Removing it manually, but wait that’s my fault ! I inspected the uninstall shell script and decided to do some quick shell script surgery.
    • Looks like I messed up there. Even if I manually unplugged the Kext before starting it, I got a weird freeze where no more application could be launched, they were bouncing endlessly.
    • Forced reboot.
    • Lion detects a broken os and decided to re-install itself.

FAIL : Admin privileges

  • Second install, Lion did not set any admin rights to my main user. This means :
    • I can’t touch anything in system preferences.
    • I can’t sudo.
  • Fixed it by booting in single user mode and adding myself manually to admin group :
    • Reboot and hold cmd+s
    • Remount your partition to have it writable
      • /sbin/fsck -fy
      • /sbin/mount -uw /
    • Add myself to admin group
      • dseeditgroup -o edit -a myusername -t user admin
    • Reboot

FAIL : SSH encoding issues

  • SSH’ing into any box messed up completely my encoding settings.
  • Comment SendEnv LANG LC_ * in /etc/ssh_config to get back to the pre-Lion behavior.
  • Lion does not set its locale to en_us.utf8 by default, appending it to your ~/.profile with export LC_ALL=en_US.UTF-8 corrects this issue if you want to adapt to the new default.

FAIL : Rvm and Ruby Entreprise edition ( Ree )

  • Segfault !
1
2
3
4
ld: warning: directory not found for option '-L/opt/local/lib'
./ext/purelib.rb:2: [BUG] Segmentation fault
ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin11.0.0],
MBARI 0x6770, Ruby Enterprise Edition 2011.03

fulls logs

  • Fixed by using gcc instead of llvm
1
2
3
rvm remove ree
export CC=/usr/bin/gcc-4.2
rvm install --force ree

StackOverflow thread

FAIL : Rvm and Postgresql with Homebrew

  • Fail to exec the Postgresql recipe :
1
Error: undefined method `strip' for #<KegOnlyReason:0x10ac404b8>
  • I updated and relaunched brew install postgresql which ran smoothly thanks to this fix

FAIL : Fullscreen :

  • Can’t use cmd+` to alternate windows while in fullscreen, this isn’t vital but it’s quite annoying.

Finally

As I wasn’t expecting everything to work for the first time, those small issues did not upset me and weren’t hard to fix. I laughed hard at myself for the macfuse failed surgery where I can only blame myself.

Code-editors or How to Lock Yourself

I tried every code editor out there, every IDE I could find. Six or seven years ago, I came to the conclusion that I do prefer simple editors to them, mostly because I prefer a sharp tool than a clumsy thing that tries to solve every problem. Oh sure, stuff like NetBeans or Eclipse perform really well on Java, but I don’t code with this language, so let’s skip directly to code editors.

I spent something like two years with Emacs, it was great since I really enjoy Lisp but I never really liked the way the you input the shortcuts, making you holding Ctrl every couple of seconds to do something. Moreover, the way you have to setup it, installing loads of libraries, byte-compiling everything to have something still going fast was fun, but those shitloads of stuff to install can drive you crazy when something goes wrong with your install.

Vim had been my favorite code-editor for most of the time, maybe like five years. It’s clearly an awesome piece of software and was convinced it would be my daily companion until something better come-out. Great plugins, blazing fast editing, tons of colorschemes (yeah I like changing those two or three times per week, mostly to visually break routine).

And two years ago I got a MacBook, switched from daily C++ development to Rails. Textmate always tickled me, and I finally gave it a try. I loved it. The feeling of having something modern, clean and simple, focusing on just what you need, providing new features like snippets, that cmd -T shortcut to jump to any file, it was just so cool.

But recently, things felt wrong. I’m using it daily for two years now and some details made me realize it’s going nowhere, which is the point in this blog post.

No window splitting at all Scripting it with your favorite language basically means writing a script which will be called by your TM script You can crash it just by opening any log file Colorschemes are so damn cool nobody tries to create new ones ! But those are minor problems, except for the splitting issue which I really miss. The main problem is :

Textmate 1 is abandoned. We’re all waiting for Textmate 2 to came out. But it’s taking ages, and sincerely like its author said, it just another Duke Nukem Forever incarnation. I even doubt it will be released, maybe because if it’s not perfect, its author will be flamed to oblivion by everyone, encouraging him to continue until its perfect. Well, it may not be the case, but currently we have no clue of a potential release date.

To me, Textmate is like a modern vim (ok, it’s not open-source, it sucks, but I can accept that if the tool is really awesome, which is almost the case). No need to look at your keyboard and asking yourself why the fuck it’s ctrl-] to jump on a help subject, it’s just simple. Another cool one : moving around in Vim with jk keys is really nice, but as I still have to use arrows to browse in my cmd history, I can’t get my fingers escaping from those arrows. For sure another shortcut must exist to do that, there are many reasons to excuse this behavior, but it’s still not “great”, it’s just cool.

You’ll probably think by reading this that I’m an unsatisfied programmer who can’t find its editor (which is true) but the point isn’t exactly that. I found my editor of choice. I’m raging against it because :

Even if they won tons of money while selling Textmate, there’s only one developer. Come on, it’s a code editor, it’s serious business. Communication around Textmate 2 is a nice example of worst practices. Its author is trapped in that Babylon tower thingy, if he don’t release it, he’ll be smashed by everyone (which is already the case) Its author have to release something great or everyone will flame him, write awful frustrated blog posts, Scotty will beam him up to pluto, … Learning an editor is an investment, we all know that, and feeling stuck because you can’t help its author to make it great, you can’t do anything at all ’cause its closed-source. It feels like I bet and lost. I’m not blaming its author for its license choices, I’m okay with that, people have to eat, but that implies Macromates have duties too. A big one, update our fucking editor for god’s sake. Writing good stuff then disappearing is the best fucking way to frustrate everyone. Moreover, I’m stucked. Emacs or Vim don’t satisfy me anymore, 20 years old software rocks for stability, but there’s some evolutions we had since I’d like to have in my editor.

Conclusion: there must be a law against selling closed-source editors, I killed thousands of kittens with the troll potential of this blog post.

Addendum

I finally threw away my never ending thirst of perfection. I’m happily sticking to Vim as it is and it does the job really well. Feel free to browse and fork my vimrc.j