Friday, December 16, 2011

Jasmine Server and Backbone.js + Require.js

‹prev | My Chain | next›

I can't quite tear myself away from Backbone.js (and really, why would I want to?). So tonight I am going to try to get a Backbone application built with require.js to run under the jasmine server (part of the jasmine ruby gem). Ultimately, I hope to parlay this into something that can be run under PhantomJS, but first things first.

The main problem with the jasmine server, with respect to require.js code, is that the jasmine server explicitly slurps in a bunch of Javascript files. It does this by checking the spec/javascripts/support/jasmine.yml file for the src_files entry. Back before require.js, that entry looked like:
src_files:
    - public/scripts/jquery.min.js
    - public/scripts/jquery-ui.min.js
    - public/scripts/underscore.js
    - public/scripts/backbone.js
    - public/scripts/**/*.js
Inserting those into the server's HTML is just going to cause require.js related trouble, so I empty out that entry.

But how do I get require.js into the server spec runner and, more importantly, how do I also add the data-main attribute? For now, I manually copy the run.html.erb file from the Ruby gem into my application's home directory. In there, I can manually add the require.js <script> tag:
<script data-main="/public/scripts/main"  src="/public/scripts/require.js"></script>
I was already able to get standalone jasmine to work with require.js the other night, so I can copy the same configuration into run.html.erb now:
<script data-main="/public/scripts/main"  src="/public/scripts/require.js"></script>

<script type="text/javascript">
require.config({
  baseUrl: '../public/scripts',
  paths: {
    'jquery': 'jquery.min',
    'jquery-ui': 'jquery-ui.min'
  }
});

require(['Calendar', 'backbone'], function(Calendar, Backbone){
  window.Cal = Calendar;
  window.Backbone = Backbone;

  var jasmineEnv = jasmine.getEnv();
  jasmineEnv.updateInterval = 1000;

  var trivialReporter = new jasmine.TrivialReporter();

  jasmineEnv.addReporter(trivialReporter);

  jasmineEnv.specFilter = function(spec) {
    return trivialReporter.specFilter(spec);
  };

  jasmineEnv.execute();
});
</script>
That is a combination of the usual Jasmine initialization and require.js configuration.

At this point, I have my spec runner ready to go, but the jasmine gem's server is still going to look for run.html.erb inside the library instead of using my new require.js copy. To get around that, I edit the Rakefile, to pull in a specialized jasmine server:
begin
  require 'jasmine'
  require './jasmine_server'
  load 'jasmine/tasks/jasmine.rake'
rescue LoadError
  task :jasmine do
    abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine"
  end
end
In the jasmine_server.rb file, I make use of Ruby's open classes to modify the server's run method:
module Jasmine
  class RunAdapter
    def run(focused_suite = nil)
      jasmine_files = @jasmine_files
      css_files = @jasmine_stylesheets + (@config.css_files || [])
      js_files = @config.js_files(focused_suite)
      body = ERB.new(File.read(File.join(File.dirname(__FILE__), "run.html.erb"))).result(binding)
      [
        200,
        { 'Content-Type' => 'text/html' },
        [body]
      ]
    end
  end
end
With that, I have my require.js jasmine server running my specs. Tomorrow, I will go through the exercise of fixing them:


The switch to the node-dirty data store broke those—not the switch to the jasmine server. For now, this is a good stopping point.

Day #236

1 comment:

  1. Great documentation - thanks so much for posting this!

    Since this posting, the jasmine gem has changed slightly (1.2.1) and instead of modifying RunAdapter, I modified ::Jasmine.runner_template. Might be helpful to the next person who runs across this.

    Again, thank you!

    ReplyDelete