The pitfalls of calling things “id”

Summary: Don’t call your form inputs “id”, they will clash with the Sizzle engine used in jQuery.

Hi! I’m Victor, one of Coupa’s newest Rails developers. I started working here fresh out of college in June 2010, but I finally persuaded them to give me a blog account. Today’s story is about why naming a form input “id” will break several selectors used by jQuery in IE, or cause forms to have weird ID attributes in other browsers.

I was merrily using all the new Unobtrusive Javascript (which is, ironically, a rather obtrusive name) features offered by Rails 3, when I hit a roadblock – they broke in IE. It was throwing a cryptic ‘Object does not support this method or property’ error every time I hit a particular page. Grumbling, I got the torches ready for another ritual sacrifice to the Internet Explorer gods.

For the first time ever, IE8 includes a handy in-browser Javascript debugger. It’s actually not half bad. Using it, I discovered the error was thrown here:

jquery-1.4.3.js
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
	var old = context.id, id = context.id = "__sizzle__";

	try {
		return makeArray( context.querySelectorAll( "#" + id + " " + query ), extra );

Check out line 3904. See anything shady? Keep in mind that context is a DOM element.

Turns out, the DOM object namespace is pretty cluttered. You can normally access an attribute foo on an element with elem.foo. However, should that element be a form with an input named foo, this will actually return the input element. As a result, context.id on a form element won’t always give you the ID attribute. IE barfs immediately on that line since it’s trying to assign a value to a tag. Webkit and Gecko, on the other hand, will run happily, stringifying the input to [object HTMLInputElement], which is decidedly unhelpful as an ID attribute.

The page that was breaking in IE indeed had a form with an input named id. The interim solution, of course, is to rename your input tags. If you’re writing a library, use the getAttribute() DOM function instead of the dot notation shorthand to make sure you’re getting the attribute.

Moral of the story is, watch out when you’re naming things! Avoid possibly-important words like id, name, or class. And realize that once in a blue moon, IE will actually be helpful in debugging. I don’t think I could’ve figured out why my ID attributes were getting clobbered in FF and Chrome, had IE not crashed so opportunely.

Coupa’s product team is hiring!

I’m putting this out there quickly even before it’s posted on our Jobs page. We’ve got 3 openings for key positions on our team. Here’s the lineup and all positions are San Mateo-based and include the goodies of salary, equity, health benefits, sweet Macbook Pro, snacks, nerf guns and more.

QA Lead
Someone that’s got experience or is ready to step up and be a QA Lead at a dynamic startup. Ideally has a great QA track record, including being hands-on in both manual testing and automation. The person will be responsible for defining all of our QA processes, building automation and managing our offshore QA resources. Objective is to ensure the highest quality of code continues to be delivered to our customers!

Rails Developer
We were one of the first (if not the first) enterprise apps companies to choose Ruby on Rails back in early ‘06. We love it and we’re looking for our next rockstar to drive the innovation and simplicity that has made Coupa successful. Ideal candidate loves working with the latest technologies, has a strong CS background, probably contributed to some open source projects, etc. You’ll be challenged for sure, and you’ll be a key contributor to our future.

iPhone Developer
We’ve got some great plans in mobile and looking for an iPhone developer to join us. You have experience developing beautiful mobile apps and ready for your next challenge. And you’ll probably get to do some Android work too. It’s going to be lots of fun!

Contact us via noah @ OR jobs@coupa.com.

Short note on a repeatable starting point for TDD

For testing to be useful, it needs to be repeatable.  Curious, here’s some notes I took when investigating what happens when you run ‘rake spec’.  This also would apply similarly for cucumber.
Rspec:
rspec specs can be run in various ways:
$ rake spec
$  spec spec
$ spec spec/controller/somefile_spec.rb
$  spec spec/controller/somefile_spec.rb:31  # specify a line number
$ autospec    # ( see  http://zentest.rubyforge.org/ZenTest/ for docs or even better, check  the source. )
Only the first command sets up the environment from scratch.  The other methods assume a working environment and if your tests assume a working environment and that assumption is invalid, then boom!

What happens in a rake spec?  Here’s what I see when I run it (edited for brevity and comments added)

$ rake --trace spec | grep Execute                #  ignore the Invoke lines.  we're only interested in what actually gets  run
(in /Users/eddykim/work/coupa_gitrepo)
** Execute environment
** Execute db:abort_if_pending_migrations
** Execute  db:test:prepare
** Execute db:test:purge        #-- drops all the  tables in the db
** Execute db:test:load          #-- creates the  tables, using schema.rb  (does not populate the tables with any data)
**  Execute db:schema:load     #-- is this really necessary for the test  db?
** Execute spec

The following is the bare minimum of things that need to be setup a clean and working database before you start your TDD mojo.

$ rake db:test:purge
$ rake  db:test:load
$ RAILS_ENV=test rake db:seed:load SEEDS='all'     # (This is a custom task in our project)
Notice that I skip running the db:schema:load which seems to be an unnecessary, time sucking task.  At which point you can run spec manually, or autospec if you so desire.
Additionally, the db:seeds:load task has a similar function as running rake db:fixtures:load.
Moral of the story:  Make sure your test database is in a known good starting state before running your tests.  But to keep your testing speed up, you don’t need to re-setup the db on every test run if you are using transactions to keep your test db clean.

HABTM Checkboxes, slightly revised

Many folks, including us, have made use of Ryan Bates’ RailsCast on updating HABTM relationships using checkboxes. We use a slightly revised version that is a little more helper-friendly, in that it doesn’t require any controller code.

Explanation first: You need the controller code in the first place because unchecked checkboxes don’t get submitted as part of your form. As a result, when none of a set of checkboxes are checked, your <habtm_assoc>_ids= method won’t get called, and any existing associations won’t get removed. (This is a bit insidious, and can be missed in testing.) To fix it, you need to make sure you end up with an appropriately empty (but non-nil) Array in your params hash.

So, as an alternative to this in your controller:

params[:product][:category_ids] ||= []

you can add an appropriately named nil hidden field in your view:

<!-- products/_form.rhtml -->
<%= hidden_field_tag "product[category_ids][]", nil %>
<% for category in Category.find(:all) %>
<div>
  <%= check_box_tag "product[category_ids][]", category.id, @product.categories.include?(category) %>
  <%= category.name %>
</div>
<% end %>

The hidden field will ensure that association removal happens as you intend it to, but will otherwise be ignored. Yay! Skinnier controllers, a pattern we can helperize, and working checkboxes, and it’s only a slightly ugly hack.

Wanna Work at Coupa?

Once again, we’re expanding our development team at Coupa.  Even if we want to hire lots of people, we find best success when we bring on 1 or 2 developers at a time.  More hands-on attention which benefits everyone in the long-term.  With that said, we want to find those 2 next superstar developers.

Our job specification is pretty straightforward.  You probably:

  • Want to be challenged intellectually
  • Be with a growing company with real customers and a real business model
  • Love Ruby on Rails (people with limited Rails experience need to have deep CS background and experience in a scripting language like Python)
  • Understand relational databases and how to make them scale – speed rocks
  • Play with javascript, prototype, scriptaculous, jQuery
  • Familiar with HTML/CSS and love creating pretty things

If this sounds like you or one of your friends, please get in touch with us.  Best way is to send an email to jobs@coupa.com.  It goes right to us.  The other good piece is that if you’re looking to move quickly, we’re ready.  We prioritize finding exceptional talent and making sure the interview process is quick, efficient and doesn’t waste your time (or ours).  Come join us!

Staying Agile in Customer Support & Development

2-plus years ago, when Coupa’s first customers came on board, we needed some way to communicate with them on issues and questions.  I had experience how Oracle did it and wasn’t about to go down that route.  Customers called in or logged issues directly into this homebuilt customer help desk system called Metalink.  Then Support spent days with a ticket trying to gather info and trying to reproduce.  If it was an actual code problem, maybe it got to Development a week later in another application called BugDB.  Support worked in Metalink, developers worked in BugDB and there was plenty hidden between the 2.  But that’s the support you get when paying 22% annually of your license fees.

At Coupa, we decided to go with a single system for help desk and issue/bug tracking.  Sometimes we don’t get it right the first time.  Our first solution was a SaaS system, but it lacked one of the main principles that Coupa was founded on:  simplicity.  Customers didn’t hate the system, but it wasn’t exactly user-friendly. Our development team detested it.  It slowed us down and that just couldn’t happen.  It was un-agile.

Graham, our development manager said (I’ll paraphrase), “It just blows….it’s fundamentally unintuitive.”

So one of our guys scanned the world via Google one night and stumbled upon a tiny company from Copenhagen named Zendesk.  They used Rails like us.  They were agile like us. They had free trials like us.  They were affordable like us.  In about 16 hours, our decision was made when our VP of Service Delivery sent an email:

From Ravi’s email…”this product is almost as cool as our app – freakin amazing”

We jettisoned that old system and turned on Zendesk immediately.  A couple months later, Zendesk named Coupa first in their list of customers in their seed investment press release.   It was great to see another Rails company succeeding.

Fast-forward to the present and Zendesk has grown significantly.  Now, headquartered in San Francisco, they have other great customers like Twitter, MSNBC, Scrib’d, IDEO and plenty others.   Most importantly, their solution is part of the collaboration that makes our customers successful and our development team agile.  The team doesn’t waste time on perfect classification of a ticket.  Instead, we get to work on it.  Besides that, it’s got simple and easy reporting, nice ways to communicate new features, and a collaborative forum for product enhancement ideas.  Our product roadmap gets influenced by the suggestions and the dialogue on our Zendesk site.  And we constantly track the percentage of suggestions that we implement and the pace that they’re done.

Without agile development, the cloud, Rails, and open source, I’m pretty sure that companies like Coupa and Zendesk wouldn’t have a fighting chance.  But with them, we’re changing the face of applications for businesses.

Don’t let IE7 ruin your weekend

Last Friday, around quitting time, fellow Coupa developer Ted and I were pairing up on some tickets and decided to attack one last issue before calling it a night. The ticket we landed on was one I had banged my head against for a few hours and only found dead ends. Even worse, it was a total show-stopper. Anyone using only our new Expensing application who clicks on the homepage navigation link in IE7 (which the majority of our customers use) is not directed to our home page – surprise! Instead they are prompted with a “File Download – Security Warning” dialog box and some text about “Type: Unknown File Type”. How bizarre! Clearly IE7 had no idea what to do with the response it received from our server. I clicked save from the prompt and examined the file download. It was our homepage, in all of its HTMLy wonder, now sitting alone and unrendered on the desktop.

My first thought had been – mime type! Having no way to evaluate response headers in IE, I took a shot in the dark and opened up Firebug. Unfortunately nothing was out of the ordinary. This wasn’t terribly surprising given that it does work in Firefox, but I suppose it had been worth a shot. Next I googled around and found that a similar issue exists (websites showing a download prompt instead of rendering in IE7) as a result of having Google Desktop Search installed and active. We don’t have that app (or much at all) installed on our IE-testing box, but whatever interaction had caused that sounded quite frightening. My mind was racing – could an unrelated process on this box be causing this link to download as a file? I calmed down a little when I remembered our QA team had reproduced this first on a different box. On a whim I tried manually appending the “.html” in the URL and – viola – the homepage rendered like a champ. At least I had a way to hack in a solution if I had to, but it was far from ideal.

This time what made the difference was a casual suggestion from Ted to use DebugBar to analyze the request/response Headers. I hadn’t heard of this tool but installed it immediately. A browser restart later I had it open, had reloaded the page and opened up the request and response headers. It was crazy – IE7 was asking for a jpeg (what?!) and we were responding with HTML but saying it was javascript (why!?). With that clue google quickly yielded this post from Hemant Khemani’s blog which clearly describes the exact problem we were having as well as the solution. Essentially IE was requesting a Content-Type of “images/jpeg”, which was not defined in our respond_to do block, so Rails chose our default format – which happens to be the first one you specify.

So the fix was simply to replace:

      respond_to do |format|
        format.js
        format.html {render :template => 'user/expense_only_home'}
      end

With:

      respond_to do |format|
        format.html {render :template => 'user/expense_only_home'}
        format.js
      end

This ensures that your default response header will be HTML. And from now on I will be checking that all of our respond_to blocks for this specific kind of sanity. So in the end, in about 20 minutes we had solved a problem I had previously struggled with for hours and all but given up on. A definite win for pair programming, for DebugBar and for my weekend.