From 5a945e1533c10d049500d49bbbe86eb1bf9f5944 Mon Sep 17 00:00:00 2001 From: Nathan Weizenbaum Date: Wed, 22 Apr 2009 00:02:07 -0700 Subject: [PATCH] [Sass] Move import- and precompiled-file management to a separate module. --- lib/sass/engine.rb | 91 ++----------------------------------- lib/sass/files.rb | 97 ++++++++++++++++++++++++++++++++++++++++ lib/sass/plugin.rb | 4 +- test/sass/engine_test.rb | 3 +- 4 files changed, 104 insertions(+), 91 deletions(-) create mode 100644 lib/sass/files.rb diff --git a/lib/sass/engine.rb b/lib/sass/engine.rb index 29415120..759e938f 100644 --- a/lib/sass/engine.rb +++ b/lib/sass/engine.rb @@ -16,6 +16,7 @@ require 'sass/tree/file_node' require 'sass/environment' require 'sass/script' require 'sass/error' +require 'sass/files' require 'haml/shared' module Sass @@ -429,101 +430,15 @@ END engine = nil begin - filename = self.class.find_file_to_import(filename, import_paths) + filename = Sass::Files.find_file_to_import(filename, import_paths) rescue Exception => e raise SyntaxError.new(e.message, @line) end next Tree::DirectiveNode.new("@import url(#{filename})") if filename =~ /\.css$/ - Tree::FileNode.new(filename, self.class.tree_for(filename, @options).children) + Tree::FileNode.new(filename, Sass::Files.tree_for(filename, @options).children) end.flatten end - - def self.tree_for(filename, options) - options = DEFAULT_OPTIONS.merge(options) - compiled_filename = sassc_filename(filename, options) - text = File.read(filename) - sha = Digest::SHA1.hexdigest(text) - - if dump = try_to_read_sassc(filename, compiled_filename, sha) - return Marshal.load(dump) - end - - engine = Sass::Engine.new(text, options.merge(:filename => filename)) - - begin - root = engine.to_tree - rescue Sass::SyntaxError => err - err.add_backtrace_entry(filename) - raise err - end - - try_to_write_sassc root, compiled_filename, sha, options - - root - end - - def self.sassc_filename(filename, options) - File.join(options[:precompiled_location], - Digest::SHA1.hexdigest(File.dirname(File.expand_path(filename))), - File.basename(filename) + 'c') - end - - def self.try_to_read_sassc(filename, compiled_filename, sha) - return unless File.readable?(compiled_filename) - - File.open(compiled_filename) do |f| - return unless f.readline("\n").strip == Sass::VERSION - return unless f.readline("\n").strip == sha - return f.read - end - end - - def self.try_to_write_sassc(root, compiled_filename, sha, options) - return unless File.writable?(File.dirname(options[:precompiled_location])) - return if File.exists?(options[:precompiled_location]) && !File.writable?(options[:precompiled_location]) - return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename)) - return if File.exists?(compiled_filename) && !File.writable?(compiled_filename) - FileUtils.mkdir_p(File.dirname(compiled_filename)) - File.open(compiled_filename, "w") do |f| - f.puts(Sass::VERSION) - f.puts(sha) - f.write(Marshal.dump(root)) - end - end - - def self.find_file_to_import(filename, load_paths) - was_sass = false - original_filename = filename - - if filename[-5..-1] == ".sass" - filename = filename[0...-5] - was_sass = true - elsif filename[-4..-1] == ".css" - return filename - end - - new_filename = find_full_path("#{filename}.sass", load_paths) - - return new_filename if new_filename - return filename + '.css' unless was_sass - raise SyntaxError.new("File to import not found or unreadable: #{original_filename}.", @line) - end - - def self.find_full_path(filename, load_paths) - segments = filename.split(File::SEPARATOR) - segments.push "_#{segments.pop}" - partial_name = segments.join(File::SEPARATOR) - load_paths.each do |path| - [partial_name, filename].each do |name| - full_path = File.join(path, name) - if File.readable?(full_path) - return full_path - end - end - end - nil - end end end diff --git a/lib/sass/files.rb b/lib/sass/files.rb new file mode 100644 index 00000000..17843d77 --- /dev/null +++ b/lib/sass/files.rb @@ -0,0 +1,97 @@ +require 'digest/sha1' + +module Sass + # This module contains various bits of functionality + # relatd to finding and precompiling Sass files. + module Files + extend self + + def tree_for(filename, options) + options = Sass::Engine::DEFAULT_OPTIONS.merge(options) + compiled_filename = sassc_filename(filename, options) + text = File.read(filename) + sha = Digest::SHA1.hexdigest(text) + + if dump = try_to_read_sassc(filename, compiled_filename, sha) + return Marshal.load(dump) + end + + engine = Sass::Engine.new(text, options.merge(:filename => filename)) + + begin + root = engine.to_tree + rescue Sass::SyntaxError => err + err.add_backtrace_entry(filename) + raise err + end + + try_to_write_sassc root, compiled_filename, sha, options + + root + end + + def find_file_to_import(filename, load_paths) + was_sass = false + original_filename = filename + + if filename[-5..-1] == ".sass" + filename = filename[0...-5] + was_sass = true + elsif filename[-4..-1] == ".css" + return filename + end + + new_filename = find_full_path("#{filename}.sass", load_paths) + + return new_filename if new_filename + return filename + '.css' unless was_sass + raise SyntaxError.new("File to import not found or unreadable: #{original_filename}.", @line) + end + + private + + def sassc_filename(filename, options) + File.join(options[:precompiled_location], + Digest::SHA1.hexdigest(File.dirname(File.expand_path(filename))), + File.basename(filename) + 'c') + end + + def try_to_read_sassc(filename, compiled_filename, sha) + return unless File.readable?(compiled_filename) + + File.open(compiled_filename) do |f| + return unless f.readline("\n").strip == Sass::VERSION + return unless f.readline("\n").strip == sha + return f.read + end + end + + def try_to_write_sassc(root, compiled_filename, sha, options) + return unless File.writable?(File.dirname(options[:precompiled_location])) + return if File.exists?(options[:precompiled_location]) && !File.writable?(options[:precompiled_location]) + return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename)) + return if File.exists?(compiled_filename) && !File.writable?(compiled_filename) + FileUtils.mkdir_p(File.dirname(compiled_filename)) + File.open(compiled_filename, "w") do |f| + f.puts(Sass::VERSION) + f.puts(sha) + f.write(Marshal.dump(root)) + end + end + + def find_full_path(filename, load_paths) + segments = filename.split(File::SEPARATOR) + segments.push "_#{segments.pop}" + partial_name = segments.join(File::SEPARATOR) + load_paths.each do |path| + [partial_name, filename].each do |name| + full_path = File.join(path, name) + if File.readable?(full_path) + return full_path + end + end + end + nil + end + end +end diff --git a/lib/sass/plugin.rb b/lib/sass/plugin.rb index 851c88ae..b9a7059c 100644 --- a/lib/sass/plugin.rb +++ b/lib/sass/plugin.rb @@ -70,7 +70,7 @@ module Sass filename = template_filename(name, template_location) result = begin - Engine.tree_for(filename, engine_options(:css_filename => css, :filename => filename)). + Sass::Files.tree_for(filename, engine_options(:css_filename => css, :filename => filename)). perform(Environment.new).to_s rescue Exception => e exception_string(e) @@ -190,7 +190,7 @@ END def dependencies(filename) File.readlines(filename).grep(/^@import /).map do |line| line[8..-1].split(',').map do |inc| - Sass::Engine.find_file_to_import(inc.strip, [File.dirname(filename)] + load_paths) + Sass::Files.find_file_to_import(inc.strip, [File.dirname(filename)] + load_paths) end end.flatten.grep(/\.sass$/) end diff --git a/test/sass/engine_test.rb b/test/sass/engine_test.rb index 01054711..ef44737e 100755 --- a/test/sass/engine_test.rb +++ b/test/sass/engine_test.rb @@ -189,7 +189,8 @@ SASS end def test_sass_import - sassc_path = Sass::Engine.sassc_filename(File.join(File.dirname(__FILE__) + "/templates/importee.sass"), Sass::Engine::DEFAULT_OPTIONS) + sassc_path = File.join(File.dirname(__FILE__) + "/templates/importee.sass") + sassc_path = Sass::Files.send(:sassc_filename, sassc_path, Sass::Engine::DEFAULT_OPTIONS) assert !File.exists?(sassc_path) renders_correctly "import", { :style => :compact, :load_paths => [File.dirname(__FILE__) + "/templates"] } assert File.exists?(sassc_path)