enable coffeescript cache, step 1 of a big speedup

This commit is contained in:
John Bintz 2011-09-01 09:38:53 -04:00
parent bd8fa3e536
commit fa27ee715a
10 changed files with 238 additions and 137 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ moc_*.*
.DS_Store .DS_Store
hydra-runner.log hydra-runner.log
jhw-test jhw-test
.jhw-cache/

View File

@ -14,3 +14,4 @@ gem 'rake', '0.8.7'
gem 'mocha', '0.9.12' gem 'mocha', '0.9.12'
gem 'guard-jasmine-headless-webkit' gem 'guard-jasmine-headless-webkit'
gem 'facter' gem 'facter'

View File

@ -1,7 +1,6 @@
module Jasmine module Jasmine
module Headless module Headless
module Webkit autoload :CoffeeScriptCache, 'jasmine/headless/coffee_script_cache'
end
end end
end end

View File

@ -1,3 +1,4 @@
require 'jasmine/headless/coffee_script_cache'
require 'jasmine-core' require 'jasmine-core'
require 'iconv' require 'iconv'
@ -66,53 +67,26 @@ module Jasmine
private private
def to_html(files) def to_html(files)
coffeescript_run = []
files.collect { |file| files.collect { |file|
coffeescript_run << file if (ext = File.extname(file)) == '.coffee' case File.extname(file)
when '.coffee'
output = [] begin
if (files.last == file or ext != '.coffee') and !coffeescript_run.empty? %{<script type="text/javascript">#{Jasmine::Headless::CoffeeScriptCache.for(file)}</script>}
output << ensure_coffeescript_run!(coffeescript_run) rescue CoffeeScript::CompilationError => ne
puts "[%s] %s: %s" % [ 'coffeescript'.color(:red), file.color(:yellow), ne.message.to_s.color(:white) ]
raise ne
rescue StandardError => e
puts "[%s] Error in compiling one of the followng: %s" % [ 'coffeescript'.color(:red), files.join(' ').color(:yellow) ]
raise e
end end
if ext != '.coffee'
output << case File.extname(file)
when '.js' when '.js'
%{<script type="text/javascript" src="#{file}"></script>} %{<script type="text/javascript" src="#{file}"></script>}
when '.css' when '.css'
%{<link rel="stylesheet" href="#{file}" type="text/css" />} %{<link rel="stylesheet" href="#{file}" type="text/css" />}
end end
end
output
}.flatten.reject(&:empty?) }.flatten.reject(&:empty?)
end end
def ensure_coffeescript_run!(files)
data = StringIO.new
files.each { |file| data << File.read(file) << "\n" }
data.rewind
%{<script type="text/javascript">#{CoffeeScript.compile(data)}</script>}
rescue CoffeeScript::CompilationError => e
files.each do |file|
begin
CoffeeScript.compile(fh = File.open(file))
rescue CoffeeScript::CompilationError => ne
puts "[%s] %s: %s" % [ 'coffeescript'.color(:red), file.color(:yellow), ne.message.to_s.color(:white) ]
raise ne
ensure
fh.close
end
end
rescue StandardError => e
puts "[%s] Error in compiling one of the followng: %s" % [ 'coffeescript'.color(:red), files.join(' ').color(:yellow) ]
raise e
ensure
files.clear
end
def spec_filter def spec_filter
@spec_filter ||= (@options[:only] ? @options[:only].collect { |path| Dir[path] }.flatten : []) @spec_filter ||= (@options[:only] ? @options[:only].collect { |path| Dir[path] }.flatten : [])
end end

View File

@ -0,0 +1,66 @@
require 'coffee_script'
require 'digest/sha1'
require 'fileutils'
module Jasmine
module Headless
class CoffeeScriptCache
class << self
def enabled=(bool)
@enabled = bool
end
def enabled?
@enabled = true if @enabled == nil
@enabled
end
def cache_dir=(dir)
@cache_dir = dir
end
def cache_dir
@cache_dir ||= '.jhw-cache'
end
def for(file)
new(file).handle
end
end
attr_reader :file
def initialize(file)
@file = file
end
def handle
if self.class.enabled?
if fresh?
File.read(cache_file)
else
result = compile
FileUtils.mkdir_p self.class.cache_dir
File.open(cache_file, 'wb') { |fh| fh.print result }
result
end
else
compile
end
end
def cache_file
@cache_file ||= File.join(self.class.cache_dir, Digest::SHA1.hexdigest(file))
end
def fresh?
File.exist?(cache_file) && (File.mtime(file) < File.mtime(cache_file))
end
def compile
CoffeeScript.compile(File.read(file))
end
end
end
end

View File

@ -15,6 +15,7 @@ module Jasmine
:report => false, :report => false,
:do_list => false, :do_list => false,
:full_run => true, :full_run => true,
:enable_cache => true,
:files => [] :files => []
} }
@ -42,6 +43,10 @@ module Jasmine
@options[:colors] = true @options[:colors] = true
when '--no-colors', '-nc' when '--no-colors', '-nc'
@options[:colors] = false @options[:colors] = false
when '--cache'
@options[:enable_cache] = true
when '--no-cache'
@options[:enable_cache] = false
when '--keep' when '--keep'
@options[:remove_html_file] = false @options[:remove_html_file] = false
when '--report' when '--report'
@ -67,6 +72,8 @@ module Jasmine
command_line_args = GetoptLong.new( command_line_args = GetoptLong.new(
[ '--colors', '-c', GetoptLong::NO_ARGUMENT ], [ '--colors', '-c', GetoptLong::NO_ARGUMENT ],
[ '--no-colors', GetoptLong::NO_ARGUMENT ], [ '--no-colors', GetoptLong::NO_ARGUMENT ],
[ '--cache', GetoptLong::NO_ARGUMENT ],
[ '--no-t stcache', GetoptLong::NO_ARGUMENT ],
[ '--keep', GetoptLong::NO_ARGUMENT ], [ '--keep', GetoptLong::NO_ARGUMENT ],
[ '--report', GetoptLong::REQUIRED_ARGUMENT ], [ '--report', GetoptLong::REQUIRED_ARGUMENT ],
[ '--jasmine-config', '-j', GetoptLong::REQUIRED_ARGUMENT ], [ '--jasmine-config', '-j', GetoptLong::REQUIRED_ARGUMENT ],

View File

@ -61,6 +61,8 @@ module Jasmine
end end
def run def run
Jasmine::Headless::CoffeeScriptCache.enabled = @options[:enable_cache]
files_list = Jasmine::FilesList.new( files_list = Jasmine::FilesList.new(
:config => jasmine_config, :config => jasmine_config,
:only => @options[:files] :only => @options[:files]

View File

@ -147,7 +147,6 @@ describe Jasmine::FilesList do
describe '#.*files_to_html' do describe '#.*files_to_html' do
include FakeFS::SpecHelpers include FakeFS::SpecHelpers
context 'one coffeescript file' do
before do before do
files_list.instance_variable_set(:@files, [ files_list.instance_variable_set(:@files, [
'test.js', 'test.js',
@ -160,9 +159,7 @@ describe Jasmine::FilesList do
'test.coffee' 'test.coffee'
]) ])
File.open('test.coffee', 'w') { |fh| fh.print "first" } Jasmine::Headless::CoffeeScriptCache.stubs(:for).with('test.coffee').returns("i compiled")
CoffeeScript.stubs(:compile).with() { |field| field.read == "first\n" }.returns("i compiled")
end end
context '#files_to_html' do context '#files_to_html' do
@ -185,49 +182,6 @@ describe Jasmine::FilesList do
end end
end end
context 'two coffeescript files' do
before do
files_list.instance_variable_set(:@files, [
'test.js',
'test.coffee',
'test2.coffee',
'test.css'
])
files_list.instance_variable_set(:@filtered_files, [
'test.js',
'test.coffee'
])
File.open('test.coffee', 'w') { |fh| fh.print "first" }
File.open('test2.coffee', 'w') { |fh| fh.print "second" }
end
context '#files_to_html' do
it "should create the right HTML" do
CoffeeScript.stubs(:compile).with() { |field| field.read == "first\nsecond\n" }.returns("i compiled")
files_list.files_to_html.should == [
%{<script type="text/javascript" src="test.js"></script>},
%{<script type="text/javascript">i compiled</script>},
%{<link rel="stylesheet" href="test.css" type="text/css" />}
]
end
end
context '#filtered_files_to_html' do
it "should create the right HTML" do
CoffeeScript.stubs(:compile).with() { |field| field.read == "first\n" }.returns("i compiled")
files_list.filtered_files_to_html.should == [
%{<script type="text/javascript" src="test.js"></script>},
%{<script type="text/javascript">i compiled</script>}
]
end
end
end
end
describe '.get_spec_line_numbers' do describe '.get_spec_line_numbers' do
let(:line_numbers) do let(:line_numbers) do
described_class.get_spec_line_numbers(file) described_class.get_spec_line_numbers(file)

View File

@ -0,0 +1,87 @@
require 'spec_helper'
describe Jasmine::Headless::CoffeeScriptCache do
include FakeFS::SpecHelpers
let(:file) { 'file.coffee' }
let(:data) { 'data' }
let(:compiled) { 'compiled' }
before do
File.open(file, 'wb') { |fh| fh.print(data) }
described_class.cache_dir = cache_dir
end
let(:coffee_script_compiles!) do
CoffeeScript.expects(:compile).with(data).returns(compiled)
end
let(:cache_dir) { 'cache' }
let(:cache_file) { File.join(cache_dir, Digest::SHA1.hexdigest(file)) }
let(:cache_file_data) { File.read(cache_file) }
describe '.for' do
context 'cache disabled' do
before do
described_class.enabled = false
p described_class.enabled?
end
it 'should compile' do
coffee_script_compiles!
described_class.for(file).should == compiled
cache_file.should_not be_a_file
end
end
context 'cache enabled' do
before do
described_class.enabled = true
FileUtils.mkdir_p(cache_dir)
File.stubs(:mtime).with(file).returns(Time.at(10))
File.stubs(:mtime).with(cache_file).returns(Time.at(cache_file_mtime))
end
context 'cache empty' do
let(:cache_file_mtime) { 0 }
it 'should compile' do
coffee_script_compiles!
described_class.for(file).should == compiled
cache_file_data.should == compiled
end
end
context 'cache fresh' do
let(:cache_file_mtime) { 15 }
before do
File.open(cache_file, 'wb') { |fh| fh.print compiled }
end
it 'should not compile' do
coffee_script_compiles!.never
described_class.for(file).should == compiled
cache_file_data.should == compiled
end
end
context 'cache stale' do
let(:cache_file_mtime) { 5 }
it 'should compile' do
coffee_script_compiles!
described_class.for(file).should == compiled
cache_file_data.should == compiled
end
end
end
end
end

View File

@ -1,3 +1,6 @@
require 'jasmine-headless-webkit'
require 'fakefs/spec_helpers'
RSpec.configure do |c| RSpec.configure do |c|
c.mock_with :mocha c.mock_with :mocha
end end
@ -10,7 +13,8 @@ if !File.file?(specrunner)
end end
end end
RSpec::Matchers.define :be_a_report_containing do |total, fails, used_console| module RSpec::Matchers
define :be_a_report_containing do |total, fails, used_console|
match do |filename| match do |filename|
parts(filename).length.should == 4 parts(filename).length.should == 4
parts[0].should == total.to_s parts[0].should == total.to_s
@ -27,9 +31,9 @@ RSpec::Matchers.define :be_a_report_containing do |total, fails, used_console|
def parts(filename = nil) def parts(filename = nil)
@parts ||= File.readlines(filename).first.strip.split('/') @parts ||= File.readlines(filename).first.strip.split('/')
end end
end end
RSpec::Matchers.define :contain_a_failing_spec do |*parts| define :contain_a_failing_spec do |*parts|
match do |filename| match do |filename|
report(filename).include?(parts.join("||")).should be_true report(filename).include?(parts.join("||")).should be_true
end end
@ -37,5 +41,11 @@ RSpec::Matchers.define :contain_a_failing_spec do |*parts|
def report(filename) def report(filename)
@report ||= File.readlines(filename)[1..-1].collect(&:strip) @report ||= File.readlines(filename)[1..-1].collect(&:strip)
end end
end end
define :be_a_file do
match do |file|
File.file?(file)
end
end
end