While working on Careplane recently, I ran into a problem that was made easier by JSONPath. I was already enjoying the ability to test my JavaScript with Node.js, Jasmine, and other npm packages. It would be nice if I could get the same package system I have with npm and use it with my client-side JavaScript.
From the command-line, I can create a single JavaScript file (I’ll call it application.js) that contains my code and includes the JSONPath package. I can even bake jQuery into application.js!
N.B. Not all npm packages will work client-side, especially ones that depend on core Node.js modules like fs or http.
Organize Your Code
Careplane has a ton of JavaScript split into a separate file for each class. Previously, I was using simple concatenation and a hand-crafted file list to construct an application.js and to determine script load order. Now it can be managed automatically by browserify because each of my class files can require other class files.
For instance, the Kayak class in src/drivers/Kayak.js depends on the Driver class in src/Driver.js. I can now do this:
I like to run my Jasmine JavaScript tests from the command-line and so does our Continuous Integration system. With jasmine-node, I had to maintain a list of files in my src directory that were loaded in a certain order in order to run my tests. Now, each spec file can use Node.js’ CommonJS require statement to require files from my src directory, and all dependencies are automatically managed.
When I run my specs from the command-line with Node.js (I have an examples rake task that runs node and jasmine), Node’s built-in CommonJS require is used and it recognizes the require statements I wrote into src/drivers/Kayak.js.
I can also run my Jasmine specs from the standard Jasmine web server. All I have to do is create a browserified script that is included before Jasmine runs its tests in my browser.
12
/* src/jasmine.js */varKayak=require('./drivers/Kayak');// browserify needs a starting point to resolve all dependencies
> ./node_modules/.bin/browserify -e src/jasmine.js -o spec/javascripts/support/application.js
> rake jasmine:server
your tests are here:
http://localhost:8888/
[2011-08-04 16:15:56] INFO WEBrick 1.3.1
[2011-08-04 16:15:56] INFO ruby 1.9.2 (2011-02-18)[x86_64-darwin10.4.0][2011-08-04 16:15:56] INFO WEBrick::HTTPServer#start: pid=61833 port=8888
When I visit http://localhost:8888 in my browser, my Jasmine tests all run!
The only drawback is that my code has to be re-browserified whenever it changes. This only adds about a second of overhead while I wait for my watchr script to run the browserification.
More
If you’d like to take a look at the environment I set up for Careplane, you can check out the source.
While working on Hootroot and Careplane, I found myself getting frustrated with the way I was having handling events. Over time, however, I stopped fighting the language and learned a pattern that I believe is easiest to test and read.
I’ll work with a simple example to show you my thought process.
Initially, I started handling my events with standard closures:
//Rocket.jsRocket=function(location){this.location=location;};Rocket.prototype.ignite=function(){/* ... */};Rocket.prototype.scrub=function(){/* ... */};Rocket.prototype.launch=function(){varrocket=this;$.ajax('/launch_code',{data:{location:this.location},success:function(data){if(rocket.isReady()){alert('Launching from '+rocket.location);rocket.ignite(data.launchCode);}else{alert('Not ready to launch!');}},error:function(){alert('Failed to get launch code for '+rocket.location);rocket.scrub();}});};//RocketSpec.jsdescribe('Rocket',function(){describe('#launch',function(){varrocket;beforeEach(function(){rocket=newRocket('Cape Canaveral, FL');fakeAjax({urls:{'/launch_code':{successData:'{ "launchCode": 12345 }'}}})spyOn(rocket,'ignite');spyOn(rocket,'scrub');});it('ignites if ready',function(){rocket.isReady=function(){returntrue;};rocket.launch();expect(rocket.ignite).toHaveBeenCalled();});it('waits if not ready',function(){rocket.isReady=function(){returnfalse;};rocket.launch();expect(rocket.ignite).not.toHaveBeenCalled();});it('scrubs if a bad launch code is given',function(){fakeAjax({urls:{'/launch_code':{errorMessage:'{ "error": "too bad" }'}}})rocket.launch();expect(rocket.scrub).toHaveBeenCalled();});});});
Because of the way this works in JavaScript, I had to assign the this that referred to the current instance of Rocket to a temporary variable that is referenced in the event handlers. This seemed kludgy to me, and I soon discovered the $.proxy() method that jQuery (and other frameworks similarly) provide:
//Rocket.jsRocket=function(location){this.location=location;};Rocket.prototype.ignite=function(){/* ... */};Rocket.prototype.scrub=function(){/* ... */};Rocket.prototype.launch=function(){$.ajax('/launch_code',{data:{location:this.location},success:$.proxy(function(data){if(this.isReady()){alert('Launching from '+this.location);this.ignite(data.launchCode)}else{alert('Not ready to launch!');}},this),error:$.proxy(function(){alert('Failed to get launch code for '+this.location);this.scrub();},this)});};//RocketSpec.jsdescribe('Rocket',function(){describe('#launch',function(){varrocket;beforeEach(function(){rocket=newRocket('Cape Canaveral, FL');fakeAjax({urls:{'/launch_code':{successData:'{ "launchCode": 12345 }'}}})spyOn(rocket,'ignite');spyOn(rocket,'scrub');});it('ignites if ready',function(){rocket.isReady=function(){returntrue;};rocket.launch();expect(rocket.ignite).toHaveBeenCalled();});it('waits if not ready',function(){rocket.isReady=function(){returnfalse;};rocket.launch();expect(rocket.ignite).not.toHaveBeenCalled();});it('scrubs if a bad launch code is given',function(){fakeAjax({urls:{'/launch_code':{errorMessage:'{ "error": "too bad" }'}}})rocket.launch();expect(rocket.scrub).toHaveBeenCalled();});});});
The problem now is there are all sorts of functions hanging around within Rocket#launch() that are a bit difficult to test in a straightforward manner. Solution: create some functions on Rocket that act as event handlers.
// Rocket.jsRocket=function(){this.location='Cape Canaveral, FL';};Rocket.prototype.igniteWhenReady=function(data){if(this.isReady()){alert('Launching from '+this.location);this.ignite(data.launchCode);}else{alert('Not ready to launch!');}};Rocket.prototype.invalidLaunchCode=function(){alert('Failed to get launch code for '+this.location);this.scrub();};Rocket.prototype.launch=function(){$.ajax('/launch_code',{data:{location:this.location},success:$.proxy(this.igniteWhenReady,this),error:$.proxy(this.invalidLaunchCode,this)});};//RocketSpec.jsdescribe('Rocket',function(){varrocket;beforeEach(function(){rocket=newRocket('Cape Canaveral, FL');spyOn(rocket,'ignite');spyOn(rocket,'scrub');});describe('#launch',function(){// we don't need to test launch() because we'd really just be testing $.ajax});describe('#igniteWhenReady',function(){it('ignites if ready',function(){rocket.isReady=function(){returntrue;};rocket.igniteWhenReady({launchCode:12345});expect(rocket.ignite).toHaveBeenCalled();});it('does not ignite if not ready',function(){rocket.isReady=function(){returnfalse;};rocket.igniteWhenReady();expect(rocket.ignite).not.toHaveBeenCalled();});});describe('#invalidLaunchCode',function(){it('scrubs if a bad launch code is given',function(){rocket.invalidLaunchCode();expect(rocket.scrub).toHaveBeenCalled();});});});
This is much cleaner and easier to test, but those lingering $.proxy() calls were bugging me. They also made debugging a bit more tedious when having to step through the calls to $.proxy.
My solution: stop fighting with this and create my own event proxy pattern. Testing is now much cleaner.
//Rocket.jsRocket=function(location){this.location=location;};Rocket.prototype.ignite=function(){/* ... */};Rocket.prototype.scrub=function(){/* ... */};Rocket.events={igniteWhenReady:function(rocket){returnfunction(data)if(rocket.isReady()){alert('Launching from '+rocket.location);rocket.ignite(data.launchCode);}else{alert('Not ready to launch!');}};},invalidLaunchCode:function(rocket){returnfunction(){alert('Failed to get launch code for '+rocket.location);rocket.scrub();};}};Rocket.prototype.launch=function(){$.ajax('/launch_code',{data:{location:this.location},success:Rocket.events.igniteWhenReady(this),error:Rocket.events.invalidLaunchCode(this)});};//RocketSpec.jsdescribe('Rocket',function(){varrocket,igniteWhenReady,invalidLaunchCode;beforeEach(function(){rocket=newRocket('Cape Canaveral, FL');spyOn(rocket,'ignite');spyOn(rocket,'scrub');igniteWhenReady=Rocket.events.igniteWhenReady(rocket);invalidLaunchCode=Rocket.events.invalidLaunchCode(rocket);});describe('.events',function(){describe('.igniteWhenReady',function(){it('ignites if ready',function(){rocket.isReady=function(){returntrue;};igniteWhenReady({launchCode:12345});expect(rocket.ignite).toHaveBeenCalled();});it('does not ignite if not ready',function(){rocket.isReady=function(){returnfalse;};igniteWhenReady();expect(rocket.ignite).not.toHaveBeenCalled();});});describe('.invalidLaunchCode',function(){it('scrubs if a bad launch code is given',function(){invalidLaunchCode();expect(rocket.scrub).toHaveBeenCalled();});});});});
The result is much more readable code, easier debugging (when absolutely necessary), and simpler testing without all those nested closures and AJAX stubs. As an added bonus, you get to keep the this in your event handlers that refers to the event itself.
This experience has led me to believe that a lot of the problems CoffeeScript tries to solve (like function binding) can really just be solved using good, simple JavaScript coding practices. I’m happy to hear from anyone who has a better pattern or has had similar experiences.
Lately, I’ve been working on a Google Maps mashup that calculates CO2 emissions for automobile, bus, and rail trips. Since Google Maps has such a great JavaScript API, I decided to write the application almost entirely in JavaScript. Thus, Carbon.js was born!
Carbon.js makes it simple to calculate emissions. Its design is similar to the Ruby carbon gem. For any class that represents an “emission activity”, e.g. an automobile trip, you can extend it with Carbon.js to give it the ability to perform calculations.
Let’s look at an example.
12345678910111213141516
RentalCar=function(){};Carbon.emitter(RentalCar,function(emitter){emitter.emitAs('automobile');emitter.provide('make_model');emitter.provide('weekly_distance');emitter.provide('timeframe');});varfocus=newRentalCar;focus.make_model='Ford Focus';focus.weekly_distance=436;focus.timeframe='2010-03-08/2010-03-14';focus.getEmissionEstimate(function(estimate){alert("My Focus' emissions for the rental period of March 8th-14th are: "+estimate.value());});
As you can see, the configuration defined in the Carbon.emitter call maps properties on an instance of RentalCar to characteristics defined in automobile’s characteristics API. In order to calculate emissions, a call to getEmissionEstimate with a callback function parameter is required. Within that callback, you can update HTML on a page with the result.
Future Improvements
Carbon only maps properties of an object to characteristics. It will be simple to allow an instance function to be mapped to a characteristic.
Carbon.js requires that you use jQuery. One improvement would be to use platform-agnostic XHR and proxy functions, or intelligently detect available libraries, like Prototype, MooTools, etc.
For DRYness sake, Carbon.js operates on classes. It may be helpful to be able to decorate any JavaScript object on the fly.
Recently, we spiffed up some of our emitters with enhanced documentation using Rocco, a Ruby port of Docco. We combined this with github’s ability to set up static html pages for a repo. By setting up a branch called gh-pages, Github will serve any html files in that branch. We use this feature to display Rocco-generated documentation of our carbon models (check out the flight emitter for an example).
This was great, but we were missing a way to automate the process by which Rocco will generate its documentation from the code in the master branch and place them in the gh-pages branch. Ryan Tomayko came to the rescue and wrote a rake task that creates a docs directory within the project and initializes a new git repository within the directory, which points to the project’s gh-pages branch. When documentation is generated, it is copied to the docs folder and pushed to gh-pages.
What intrigued me about the rake task was its use of file tasks. It’s a feature of rake I had never noticed before, but it’s pretty slick. A file task says, “if the specified path does not exist, execute the following code.” Since many unix tools use files for configuration, this feature plays well with many utilities, such as git, your favorite editor, etc.
For example, you could define a rake task that will create a .rvmrc for your project using your current RVM-installed ruby:
When you run rake .rvmrc, your .rvmrc will be generated. Try it out!
There is all kinds of magic you can work with a file task. A novel way in which Ryan’s Rocco tasks use file tasks is when deciding whether to create a git remote based on whether there is a file referencing the remote in the .git configuration directory:
I just got back from JRubyConf in Columbus, Ohio. I had a great time meeting other Rubyists and learned a great deal about how Ruby is being integrated into enterprise environments via JRuby. It’s really exciting to see “the big guys” embracing a language and ecosystem that we at Brighter Planet enjoy using on a daily basis.
When I got back home, I decided this would be a good opportunity to try out our new meeting and lodging emissions models to come up with a carbon footprint for my trip to the conference.
I started out with the meeting space itself. I fired up Google Earth and measured the square footage of the conference center we were at - about 1,150 square meters. With the area of the space and the location, I came up with 3.08 tons of CO2e generated by the heating, cooling, and electricity usage of the space: methodology. Of course, if we considered my individual footprint, it would be 150th of that amount, given that 150 of us shared the space. This comes out to 0.02 tons.
I then looked at my hotel stay for two nights at the Hampton Inn and came up with 0.09 tons of CO2e: methodology. I looked up the lodging class from our data repository
To get to the conference, I carpooled with a couple other Rubyists from Ann Arbor, Michigan. I used 18 gallons of gas, which comes out to 68.1L. When we developed the automobile trip emitter I was surprised to learn from Seamus and Ian that the type of engine you have doesn’t make a significant difference in the amount of CO2 emitted, it really comes down to how much gasoline is burned. Therefore, our automobile trip emitter looks at the type of fuel and the quantity burned. Transportation to and from the conference came out to 0.18 tons of CO2e: methodology. Divided by the three of us, my personal transportation footprint was 0.06 tons.
Of course, no conference weekend is complete without a few good meals with fellow programmers. Over the course of the weekend, I mostly ate vegetarian, but I splurged on a trip to City Barbecue for a tasty beef brisket sandwich. I looked at all the food I ate, and calculated the share of each of the food groups I ate. My foodprint for the weekend was 0.02 tons of CO2e: methodology.
Overall, my total footprint was 0.19 tons of CO2e. Had I not carpooled, the biggest factor would have been the car trip to the venue. With carpooling, most of my emissions came from the hotel stay.
Over the past few years, there have been more and more regional Ruby conferences, and it’s great to see the community buzzing. I think it would be really cool if someone could whip up a web app that analyzes Ruby (or other) conference footprints, perhaps based on data gathered from attendees. We could make a competition out of it to see which conference has the best average per-attendee footprint!
I have been spending the past few weeks creating and refactoring our carbon model gems, with the goal of making them easy to enhance, fix, and test by climate scientists and Ruby developers. I wanted to make contributing a simple process and bundler fit the bill quite well.
A not-so-widely-known feature of the Rubygems API is the ability to declare a gem’s development dependencies, along with its runtime dependencies. If one planned on making changes to one of the emitter gems and testing it, she could run gem install <emitter_gem> --development and have any needed testing gems installed for the emitter gem.
This is all fine and good, but I chose to use bundler to manage our dependencies, as it adds a few extras that have been a tremendous help to us. To contribute to any of our gems, a developer can follow a simple process:
12345
$ git clone git://github.com/brighterplanet/<gem>.git
$ cd <gem>
$ gem install bundler --pre # this is needed until bundler 1.0 is released$ bundle install
$ rake
And Bob’s your uncle!
Bundler + Gemspecs
The first goodie that bundler provides is the ability to use the gem’s own gemspec to define the dependencies needed for development. For instance, our flight gem has a gemspec with dependencies:
Instead of defining these dependencies in both flight.gemspec and in Gemfile, we can instead give the following directive in our Gemfile:
1
gemspec:path=>'.'
Bundler + Paths
We have a chain of gem dependencies, where an emitter gem depends on the sniff gem for development, which in turn depends on the earth gem for data models. In the olden days (like, 4 months ago) if I made a change to sniff, I would have to rebuild the gem and reinstall it. With bundler, I can simply tell my emitter gem to use a path to my local sniff repo as the gem source:
1
gem'sniff',:path=>'../sniff'
Now, any changes I make to sniff instantly appear in the emitter gem!
I had to add some special logic (a hack, if you will) to my gemspec definition for this to work, because the above gem statement in my Gemfile would conflict with the dependency listed in my gemspec (remember, I’m using my gemspec to tell bundler what gems I need). To get around this, I added an if clause to my gemspec definition that checks for an environment variable. If this variable exists, the gemspec will not request the gem and bundler will instead use my custom gem requirement that uses a local path:
123456
# Rakefile (we use jeweler to generate our gemspecs)Jeweler::Tasks.newdo|gem|# ...gem.add_development_dependency'sniff','=0.0.10'unlessENV['LOCAL_SNIFF']# ...end
So now, if I want to make some changes to the sniff gem and test them out in my emitter, I do:
123456789
$ cd sniff
# work work work$ cd ../[emitter]$ export LOCAL_SNIFF=~/sniff
$ rake gemspec
$ bundle update
# ...sniff (0.0.13) using path /Users/dkastner/sniff
# ...
And then Bob is my uncle.
Bundler + Rakefile
This next idea has some drawbacks in terms of code cleanliness, but I think it offers a good way to point contributers in the right direction. One thing that frustrated me about Jeweler was that if I wanted to contribute to a gem, my typical work flow went like:
12345678
$ cd[project]# work work work$ rake testLoadError: No such file: 'jeweler'$ gem install jeweler
$ rake testLoadError: No such file: 'shoulda'# etc etc
I attempted to simplify this process, so a new developer who doesn’t read the README should be able to just do:
12345678
$ cd[emitter]# work work work$ rake testYou need to `gem install bundler` and then run `bundle install` to run rake tasks
$ gem install bundler
$ bundle install
$ rake testAll tests pass!
I achieved this by adding the following code to the top of the Rakefile:
1234567
require'rubygems'beginrequire'bundler'Bundler.setuprescueLoadErrorputs'You must `gem install bundler` and `bundle install` to run rake tasks'end
This was convenient, but it created a chicken and egg problem: in order to generate a gemspec for the first time, bundler needed to know which dependencies it needed, which meant that it needed the gemspec, which is generated by the Rakefile, which requires bundler, which requires the gemspec, etc. etc. I overcame this problem by allowing an override:
123456789
require'rubygems'unlessENV['NOBUNDLE']beginrequire'bundler'Bundler.setuprescueLoadErrorputs'You must `gem install bundler` and `bundle install` to run rake tasks'endend
So, if you’re really desparate, you can run rake test NOBUNDLE=true
More on Local Gems
Now that I had a way to easily tell bundler to use an actual gem or a local repo holding the gem, I wanted a way to quickly “flip the switch.” I wrote up a quick function in my ~/.bash_profile:
I don’t follow Yehuda Katz’ blog (though I should) but he posted an article on basic gem configuration and build steps. It irks me to no end when I clone a git repo and run through the following process:
123456789101112131415161718
> gem build somegem.gemspec
> gem install ./
[write code using gem]> run code
#ERROR# some gem code is missing a file in "require_once"> ls /path/to/gem/file/that/is/missing
File not found: /path/to/gem/file/that/is/missing
> cd /path/to/gem/repo
> ls /path/to/gem/repo/path/to/gem/file/that/is/missing
missing.rb
> rake build
#ERROR# missing gem my_crappy_test_library> gem install my_crappy_test_library
> rake build
#ERROR# missing gem my_crappy_mock_library> rake build
OK
> gem install pkg/
When it could simply be:
1234
> gem build somegem.gemspec
> gem install ./
[write code using gem]> run code
The first scenario happens when someone uses a tool like jeweler to build their gemspec and forgets to commit the latest generated (or what should have been generated) gemspec. Jewler likes to list every single file packaged with the gem, so the gemspec must be refreshed whenever a file is added or removed. When writing a gemspec by hand one, can simply say “all the files in the lib directory are part of this gem.” You can use a handy tool like a .gitignore to prevent unwanted files from appearing in your gem.
Why is this such a big issue? Because, as Yehuda wrote, writing your own gemspec isn’t that hard. In fact, it’s kind of enjoyable, being that it’s a Ruby DSL. Ruby was designed to cause the lowest amount of pain for developers. Our Ruby code should reflect that ethos.
My wife is a graduate student who spends most of her time reading a ton of articles for her classes. Once a week, she has to drive an hour and a half to Detroit for work. One night, she was worried that she’d have enough time to work in Detroit and read all of her articles. The proverbial light bulb went off in my head and I remembered a feature that Mac OS X has had since Jaguar (2002): Text to Speech.
Previously, I could just select a block of text, then go to the current application menu, click “Services,” then “Speak selected text.” However, Apple seems to have removed this feature in Snow Leopard. After a little digging, I found it again. Here’s how to get it working:
(Note: click the images to get a full view)
Open System Preferences (either click the spotlight search magnifying glass in the top left and type “System Preferences” or find System Preferences in the Applications folder).
Click the “Keyboard” icon toward the middle/top.
Click the “Keyboard Shortcuts” tab at the top.
In the left pane, select the “Services” item. In the right pane, scroll down and check the box next to “Add to iTunes as a Spoken Track.”
This will add an item to the “Services” submenu when you open the main application menu in any program.
For example, let’s open a page in Safari and convert the text to speech.
In Safari, browse to http://www.jabberwocky.com/carroll/walrus.html
Highlight all of the text. It’s OK if some images get in there, they should be ignored by the Text to Speech conversion.
In the top menu (next to the Apple) click “Safari”, then click “Services” and then click “Add to iTunes as a Spoken Track.”
You should see a spinning cog in your top menu bar as Mac OS creates an audio file for you and putting it in iTunes. Click the spinning cog if you want to cancel.
When the cog disappears, there should be a new playlist in iTunes called “Spoken Text.” In it, you’ll see a track called “Text to Speech.” You can rename this to “The Walrus and the Carpenter” and copy the track to your iPod.
And that’s it! Enjoy your free audio books!
Bonus:
If you’d like to change the voice of the computer’s reader, there are six standard voices you can choose from. Simply go to System Preferences, click “Speech”, then click the “Text to Speech” tab, then select the voice you want from the “System voice” drop-down list. The voice you select here will be used the next time you use the “Add to iTunes as a Spoken Track” service menu item.
Friday was my last day working in Ann Arbor (1hr away, by car, 4 by train/bike) at Greenview Data. At Greenview I had helped to build an email archiving system, writing mainly Ruby and Perl code using Agile (XP) development practices. Two of the best years of my career were spent there - I learned so much and discovered who I was as a software craftsman. My coworkers there helped keep me accountable and we had a great time learning together new concepts and some neat tools. In the end, we have an email archiving system that provides discoverability and redundancy across 3 data centers in the US.
The biggest change in the last year has been my migration from graphic IDEs and text editors to vim. Once I discovered it’s underlying philosophy, I was hooked. Now Linux has returned as my development platform of choice. Linux especially works well when working with Ruby C extensions.
This next week I’ll be finishing some work for the State of Michigan and then February begins my work at BrighterPlanet! Conservation, sustainability, and environmental stewardship have become hot issues for me over the past three years. I’m really excited to be working on that front as a Rails developer developing apps that help people start thinking about their relationship with the environment and helping some grassroots environmental projects get started through the company’s project fund. When I found BrighterPlanet’s open telecommuting position, I couldn’t pass it up.
So now I will be working from home during the day. Julie and I have been setting up the office as our coworking facility and I’ve been tinkering around with my old PC to see if it’s usable as a Linux development platform. I also got a nice 22” Dell monitor (same components as Apple LCDs) to reduce neck straining with my laptop. It also doubles as our TV :)
2010 is already shaping up to be an excellent year!
My wife is a grad student who does quantitative studies from time to time. Her advisor recommended that she use SPSS to help her generate statistics from her research data. Of course, SPSS is a gigantic, bloated Java app developed by IBM. Being an IBM product targeted toward large institutions, it also costs an arm and a leg. I really wish R was more well known and widely used because it is free software.
My wife recently found that after I upgraded her laptop to Snow Leopard, it rendered SPSS unusable. Mac OS reported that the application is incompatible with Snow Leopard. For a brief time, SPSS offered a free patch that allows you to use SPSS with Snow Leopard, but it expired December 31st, 2009 and you are now told to buy an upgrade for $100. A poster mentioned in a help forum that the only reason SPSS 17 didn’t work with Snow Leopard was that SPSS 17 was designed for Java 1.5 and Snow Leopard only comes with Java 1.6 installed. My blood boiled as I realized that SPSS was attempting to charge me $100 for not having an older version of Java installed. I was also suspicious of the fact that Mac OS warned of an incompatibility before the application even attempted to launch. How was it so sure?
I found some tutorials for getting SPSS 17 to work with Snow Leopard, but they involved magic and cargo culting. Therefore, I give you the simplest instructions for installing it:
Extract the package by double-clicking the downloaded file. A “1.5.0” folder will appear.
Drag the 1.5.0 folder into Macintosh HD/Library/System/Frameworks/JavaVM.framework/versions. Say yes you want to overwrite and authenticate with your user name and password.
In spotlight (click the magnifying glass in the top right) type “Java Preferences” and click the top hit.
In the bottom-most box, drag Java 1.5 32-bit to the top of the box, ahead of Java 1.6. This tells OS X to try and use Java 1.5 to open your apps before it tries the newer 1.6 version.
This is the ridiculous part. Snow Leopard ships with a black list of applications that it “knows” are incompatible. Apple is trying to protect us, here. The only way it knows you’re trying to run an incompatible application is that it checks the name of the application you try to run against its blacklist. Simply rename your SPSS program by going to Applications/SPSSInc/Statistics17, highlight SPSSStatistics17.0, press return, then rename it to something like SPSS. Now Mac OS will not recognize that you’re running SPSS 17.