5 Things to Know When Migrating Prototype to jQuery

2 Comments

I just finished moving 1000+ lines of javascript from Prototype to jQuery. At first it went slow, but after a while it went relatively fast because the code was pretty basic. In the end, there were only 5 things that I needed to “translate”.

Prototype adds methods to the internal Javascript classes, while jQuery simply provides and a new function jQuery and a new type of object, the jQuery object, but otherwise Prototype and jQuery appear very similar. Both use the “$” in their syntax, but I prefer the alternate “jQuery” syntax below.

1. Selecting DOM elements

Prototype’s selector returns DOM elements.

$(element_id) => DOM element

jQuery does not.

jQuery('#'+element_id) => jQuery object which may include 0-n DOM elements.

jQuery('#'+element_id)[0] => first(and likely only) DOM element

The one gotcha is remembering when you’re dealing with a jQuery object or a DOM element.

2. Binding functions to contexts

Prototype has a bind function.

function.bind(context)

jQuery binds with a proxy.

jQuery.proxy(function, context)

3. Iterating over an array

Prototype adds an each method to Array.

Array.each(function(item){ ... })

jQuery can iterate over arrays.

jQuery.each(Array, function(item){ ... })

But usually you want to iterate over the results of a jQuery selector:

jQuery(".className").each(function(index, element){ ... })

4. Adding event handlers

Prototype adds the observe method to DOM elements

$(element_id).observe("click", function(event){ ... })

jQuery has the bind method.

jQuery('#'+element_id).bind("click", function(jQueryEvent){ ... })

…or the shorthand click (and others).

jQuery('#'+element_id).click(function(jQueryEvent){ ... })

The jQueryEvent object is not the same as a regular event object, but target() both work the same.

5. On DOM Ready

Initializing on DOM ready is done with Prototype’s observe method as:

document.observe("dom:loaded", function(){ ... })

In jQuery:
jQuery(function(){ ... })

I’m not a Prototype or jQuery expert, so your milage may vary. I’m mostly writing this for the next time I have to migrate Prototype to jQuery.

A Sinatra-Sequel-RSpec Template

3 Comments

I’m just getting to the fun part of writing an app, the part where I’m actually writing the app and not just trying to get my environment setup. A couple days ago, I would’ve loved to have found a post that explains how to get Sinatra, Sequel, and RSpec working together plus have all the files organized so that I could just dive in.

If that’s what you’re looking for and all you want to do is get hacking, just download the repo. It provides an example folder structure, migrations, model, helper, routes and specs to help you quickly get started writing your own RESTful app.

Get the Sinatra-Sequel-RSpec Template on Github.

If you’d like an in-depth walk-thru, here’s the (admittedly boring) play-by-play.

Sinatra-Sequel-RSpec Template Walk-Thru

Step 1: Hello Sinatra

I started with the “Hello World” example on the Sinatra site. I literally cut and pasted the code on the homepage into a new file called app.rb.

> mkdir sinatra-sequel-rspec
> cd sinatra-sequel-rspec
> sudo gem install sinatra

I created an app.rb file:

1
2
3
4
5
6
require 'rubygems'
require 'sinatra'
 
get '/hi' do
  "Hello World!"
end

I also created a readme, before I pushed my first commit to github. If you’re following along at home and have the repo, git checkout 3eeb602.

I highly recommend the shotgun gem for running sinatra locally.

> sudo gem install shotgun
> shotgun app.rb

Try it out by going to http://localhost:9393/hi

Step 2: Hello Sequel

Next, I added in some Sequel. Ryan Tomayko’s sinatra-sequel gem made it a snap. I really like to keep my files small and organized, so I created a config folder for the database configuration and migrations.

> sudo gem install sequel sinatra-sequel
> sudo gem install sqlite3
> mkdir config

And then I created the config/init.rb file:

1
2
3
4
5
6
7
8
9
10
11
require 'sinatra/sequel'
require 'sqlite3'
 
configure :development do
  set :database, 'sqlite://tmp/development.sqlite'
end
configure :test do
  set :database, 'sqlite3::memory:'
end
 
require 'config/migrations'

You don’t have to use sqlite, Sequel supports connecting to a lot of databases.

Next I created my config/migrations.rb file.

> touch config/migrations.rb

Now that we have the config/init.rb file in place, app.rb needs to require it.

1
2
3
4
require 'rubygems'
require 'sinatra'
require 'config/init'
...

And that concluded my second commit. If you’re following along at home and have the repo git checkout a45f06e.

Step 3: Hello RSpec

> sudo gem install rspec
> sudo gem install rack-test
> mkdir spec

spec/spec_helper.rb will run the tests.

1
2
3
4
5
6
7
8
9
10
11
require File.join(File.dirname(__FILE__), '..', 'app.rb')
 
require 'rack/test'
require 'ruby-debug'
require 'spec'
 
# set test environment
set :environment, :test
set :run, false
set :raise_errors, true
set :logging, false

And you might want a spec/spec.opts with your preferred options.

1
2
3
4
--colour
--format progress
--loadby mtime
--reverse

And finally I needed a rake task to run my specs, so I added Rakefile:

1
2
3
4
5
6
7
8
desc "Run those specs"
task :spec do
require 'spec/rake/spectask'
 
Spec::Rake::SpecTask.new do |t|
t.spec_files = FileList['spec/**/*_spec.rb']
end
end

And finally, I finally I wrote my first spec in spec/app_spec.rb.

1
2
3
4
5
6
7
8
9
10
require File.dirname(__FILE__) + '/spec_helper'
 
describe "App" do
  include Rack::Test::Methods
 
  it "should respond to /" do
    get '/'
    last_response.should be_ok
  end
end

I then ran rake spec and my test FAILED! Which is exactly what I wanted. I fixed the route in app.rb, got it green, and proved RSpec was working.

My third commit now had all three building blocks, Sinatra, Sequel, and RSpec. If you’re following along at home and have the repo git checkout bd1fbf5.

Step 4: Hello People

My simple app says hello to people. Next, l added a person model, specs, and a migration.

Let’s do the migration first. Edit config/migrations.rb and add…

1
2
3
4
5
6
7
8
9
10
11
12
# Migrations will run automatically. The DSL like wrapper syntax is courtesy
# of sinatra-sequel
#
# For details on sequel's schema modifications, check out:
# http://sequel.rubyforge.org/rdoc/files/doc/schema_rdoc.html
 
migration "create the people table" do
  database.create_table :people do
    primary_key :id
    string      :name
  end
end

This migration will run automatically. Don’t change it after it’s run. More robust up and down ActiveRecord-like migrations are available for Sequel, but since this is a really simple app, really simple migrations will do.

Next, I created the folders for my model and its specs.

> mkdir models
> mkdir spec/models

To load the models, append config/init.rb with…

12
13
14
Dir["models/**/*.rb"].each{|model|
  require model
}

My specs for the Person model will go in spec/models/person_spec.rb.

1
2
3
4
5
6
7
8
9
10
11
require File.dirname(__FILE__) + '/../spec_helper'
 
describe Person do
  describe "validations" do
    it "should require a name" do
      Person.new().should_not be_valid
      Person.new(:name => '').should_not be_valid
      Person.new(:name => "Maggie").should be_valid
    end
  end
end

I started autospec before creatig models/person_rb.

1
2
3
4
5
class Person < Sequel::Model
  def validate
  errors.add(:name, "can't be empty") if name.nil? || name.empty?
  end
end

Note that like ActiveRecord, Sequel does pluralizations. So the person records go in the people table.

Before I commited, I needed a .gitignore:

1
*.sqlite

I then pushed my fourth commit. If you’re following along at home and have the repo git checkout 10901eb.

Step 5: Hello Everyone!

After that things got easy. I continued to run autospec, write tests, and implemented code. Look at the individual commits or play with the final code.

  • commit 07bfdf5: Add a public folder for css, a layout and our first view.
  • commit a9845f9: Add the restful controller for people and helpers to get partials.
  • commit 4b3837c: Added the readme and license.

I hope this template and walk-thru help you get started with Sinatra, Sequel and RSpec faster. I’m sure the template’s not perfect, so you’re welcome to fork it.

I Don’t Want to Be Agile, I Just Want to Create Value

3 Comments

Goldstar spent a few months at the end of 2009 revamping its project management system. We were previously managing everything in Mingle by creating a large number of lanes and moving cards left to right. Some cards were huge features. Other cards were tiny bugs. The system was one-size fits all, which is the same as one-size fits none. There was no concept of velocity or iteration, though we tried to measure that separately with a wet finger in the air.

We—and by we, I mean Chrissy Deters, Pat Maddox, BJ Clark, and I—made a series of changes switching out of Mingle into what we have today, a Kanban-style system that is 1 part physical cards on board, 1 part Basecamp and 1 part Pivotal Tracker. It’s not 100% Agile or Kanban (or waterfall), and that doesn’t bother me right now. My goal—my team’s goal—isn’t to be Agile, it’s to create value. Any means to an end should not be confused with the end. Of course, there’s differing opinions among us about this new process. Each of us has suggestions or tweaks that we’d like to make, inadequacies that we see. But, for now, this new process has proven to be more effective for us. It’s a big step in the right direction.

So what is our process:

The physical board is the “idea board” with all the features we “want”. Using a sharpie and a 3×5 card, you can request a feature… if there’s room on the board. There are big ideas and small ideas on the board, but you can’t get more detailed than a sharpie and a 3×5 card allows, and you can’t add a new board. It’s not convenient, which is a good thing, and it’s not infinite, which is a better thing. It is physical which is awesome—you’re physically limited to not being able to do much with a new idea. On the idea board are 5 spaces for “ideas” that are in Basecamp and 5 more for things in Pivotal Tracker being coded. Everything else is just in the pool not being worked on. It’s a three-lane super-simple Kanban board. True Kanban is different, you have enqueue and in progress lanes. If you want you can consider the Idea Pool to be the queue for Basecamp, and Basecamp to be the queue for Pivotal Tracker. The important part is that we’re capping the number of things that can be in progress.

Idea Board -> Basecamp -> Pivotal Tracker -> Done Done

Some ideas from the board are simple enough that they skip right over Basecamp, but many become Basecamp projects. There they are discussed, refined, wireframed, mocked-up, tweaked and ultimately moved in PT tracker. Initially we looked at Basecamp as a replacement for Mingle. I didn’t like it. I didn’t get it. I didn’t feel like I could express the priorities of the company with it. That was something that I needed PT for. But with PT’s forced granularity, I thought we were getting too detailed too fast and we didn’t have a forum to discuss some of the larger concepts of an idea like branding and the flow between related features. Deciding to put Basecamp on top of PT filled the void. With Basecamp, it’s so easy to communicate and collaborate on tasks, designs and decisions. You can’t break those tasks up into iterations or measure velocity inside Basecamp (maybe with a plugin, maybe?), but from its collaboration features we get beautifully designed features with ease.

As parts of an idea come out of Basecamp and are ready to be coded, they go into PT. This may seem awkward to some but what we’re doing is getting more granular in each phase. One idea on the board becames a project in Basecamp, that project becomes a series of to-dos, and each to-do task then becomes a bunch of stories in PT. We’re also free to skip any steps that aren’t applicable. This makes perfect sense to me given how we work. We start with a idea, we break it up once, and we break it up again. We’re not writing up massive requirement docs that become outdated before they’re even read. One idea from the board will become dozen of PT story cards and span a coupel iterations. It’s also important to note that if we’re being granular enough, a single feature will be in basecamp and PT at the same time. It’ll be iterated multiple times in an agile way.

Pivotal Tracker doesn’t offer a lot of choices (feature!), so we use it as everyone else does. We have stories. They are features, chores, and bugs. The developers estimate them, start them, finish them, deliver them. The customers (product managers) prioritize them, accept them, reject them. We work in weekly iterations and mark releases. We try to keep the backlog small–ideally we have nothing but an iteration’s worth of stories. The icebox should be empty because that’s what the Idea Board is for, but for now we use it as a bug tracker.

PT enforces a lot of agile practices on us. We have to estimate. We have no choice but to track velocity. We have to keep the stories small. We deliver things in pieces and get immediate feedback. We also pair programmer and do BDD most of the time, so we’re getting more agile outside PT too.

We replaced Mingle with 3 tools and one of them is something we have to touch…with our fingers! Having so many tools, is bad right? Well, I once knew a contractor that only had a hammer… no, that’s a lie. It’s absurd to think that a professional contractor would only have a hammer. In every professional, you have many tools and you use them for different tasks. Using a Kanban-esque physical board, Basecamp, and Pivotal Tracker isn’t using too many tools when they’re the right tools for the job. In fact, I think we’re still missing 1 tool (a topic for another post).

In the end, we’ve accomplished a couple things:

We have fewer things in progress with Kanban-style forced lane limits. Mingle had an infinite backlog and we wasted a lot of time filling it, organizing it, only so that we could efficiently ignore it. Some of those Mingle cards that we ignored had documents or mock-ups attached… powerpoint presentations on why we should do them. All wasted work. One of the purposes of Kanban is to move things quickly from start to finish, not to get more done, but to simply reduce the time from when you begin investing effort to when you receive rewards.

We don’t get too detailed too soon. When we moved a card through the lanes of Mingle, the card never changed. If we wanted to be detailed in our estimates, we have to be detailed creating cards, which meant putting efforts into things that would never get built. By moving things from the Idea Board to Basecamp to PT, we get more detailed when we need to, and it’s obvious when we should–each tool demands the appropriate amount of granularity for that development phase. You start with 3 words written on an index card and finish with 20 feature stories accepted in PT.

We get better, more efficient collaboration on features upfront in Basecamp. We talk more but only when we’re ready to talk. Basecamp is all about commenting and collaborating. It’s easy to get input from a various stakeholders upfront. We learn more. We vet more. And when we move things into PT, we’re more prepared.

We’re forced with PT to estimate stories, track velocity, and prioritize everything. Stories have to be accepted, and there’s no confusion about what’s done. It’s done, when the customer says it’s accepted.

This has made a significant difference in the development cycle at Goldstar. Yet, it remains a work in progress. There are at least two areas that I want to address with our team, and I hope to cover them in future posts too:

  1. Why bugs are usually about psychology when they should be about economics.
  2. The folly of rewarding velocity while hoping for value.

In the end, I’m not suggesting that you use our process. Maybe you should be 100% Agile. I am saying that you should never, never, never start with the goal of being Agile. You should start with the goal of creating value, and you should look at Agile as a tool to achieve that.