thoughts from brian samson

The worst comment I’ve ever written

I’m sitting here trying to debug a very complex piece of “meta” code. This code generates web forms that have fields that automatically show/hide based on the selections in other fields. It’s usually very nice to work in, and it makes creating dynamic forms easy, but it is insanely complex under the hood. I finally find the source of the bug is a hip, railsey, ruby one-liner. It’s not to hard to figure out what that line does (adds some kind of prefix to each key). However I have no idea why it would do that. Luckily when I wrote this code I knew this was confusing, so I included a comment:


if (@if)
#in case this is being rendered in a subsection, the key
@if.each {|andor, array| array.each {|h| h[:key] = field_name_prefix + h[:key] }}
end

Except I forgot to finish it. The key…. the key….. WHAT ABOUT THE KEY YOU IDIOT?? This is the worst comment I’ve ever written. It is worse than no comment. No comment wouldn’t have got my hopes up….and then crushed them. Why isn’t it lunch time yet?

Sharing internal Javascript libraries between Rails projects using SVN and Rake

I’ve been developing rails apps for over a year with Six Fried Rice, and we’ve been using ExtJS as our Javascript framework. As is quite common in industry, we’ve developed internal libraries for Ruby, Rails, vanilla Javascript, and ExtJS. When I used to write Java, we would have our shared libraries in separate source control projects, package them up as JARs and just distribute binaries, which we would check in to each project that used them. This worked fairly well because of 2 important reasons:

  1. Our libraries were mature
  2. They could be developed independently of a certain project

Number 1 is nice because the libraries don’t need to be updated often, so you usually only needed to spend time getting them set up at the start of a new project. Number 2 was true because our libraries usually revolved around authentication/single sign on and they were maintained by an entirely different group in IT. This is more typical of large enterprise development.

So when I wanted to do something similar for our rails/js libraries, I initially turned to gems and plugins. Either of these solutions would work fine for our modifications to Ruby, and we do currently maintain 2 internal rails plugins that handle our ruby convenience methods and also some changes we’ve made to rails (that haven’t made it into core rails yet….). We keep those plugins in separate subversion projects, and everything work great.

Now enter Javascript. We write lots of javascript. Many times as much as we write ruby. The Javascript also tends to be dirtier than ruby, and we run into a lot of problems that are much harder to reproduce so I usually prefer to test changes to the libraries in the context of the applications that use them. This JS code is also very immature and changes frequently, often daily. The traditional method of developing a library on the side and updating it occasionally breaks down for this case, so the situation for the JS libraries is the opposite of the traditional way I described above:

  1. Immature code that changes frequently
  2. Development depends on applications that use the library

It turns out that there is a nice property that is supported by subversion called externals. This allows you to set a directory in one repository to link right to another repository. You can make changes to the library right there, commit it back, and then go run update on a different project and it will go grab the changes. This is perfect, except for one thing. Say I branch off of trunk to write a big new feature that ends up adding a lot of code to the Javascript UI library. This branch may take a few weeks to finish and the whole time I’m committing new code to the javascript, which gets propagated to trunk everytime I update, which could certainly introduce bugs that we might not want in other branches.

This leads me (finally) to the point of this post. I solved this by writing a rake task to “freeze” and “unfreeze” the library. When it is frozen, the library just stays at a version we know to be stable. When you want the latest code, or if you need to make changes, you unfreeze it and test. We’ve been using this solution for a few weeks now and its works really well, so I decided to post the rakefile that handles installation and freezing of a javascript library, called ‘jslib’ by default.

external_js_library.rake

Now I can do cool stuff like

rake jslib:freeze

which makes sure that the library stays at whatever version it is at right now forever, or until I issue:

rake jslib:unfreeze

which sets it back to HEAD and updates. I don’t think this is a replacement for gems/rails plugins, but it does a very good job of handling our shared javascript library. Thoughts?

Capistrano: Calling tasks from other tasks

I wrote a new capistrano task for deploying a rails app the other day that would totally reset the old deployed database and give me a fresh new one with fixtures pre-loaded. This is useful for deploying different branches to the same location on a server. However the lack of proper capistrano documentation let to some wasted time figuring out how to invoke previously defined tasks from a new task. Most of them are just simple methods you can call, but to call the task deploy:migrations is a little more complex because it’s really a task called migrations in the deploy namespace.

So for my new “super deploy” task, deploy.rb looks something like this:

task :drop_db
  run "cd #{deploy_to}; rake db:drop"
end

task :create_db
  run "cd #{deploy_to}; rake db:drop"
end

task :super_deploy
  drop_db
  namespace :deploy
      migrations
  end
  create_db
end

Pretty simple but I got hung up it for a few minutes. So there you go.