From 5f1c35d1717dac8872a4c75c8f1844377f975a65 Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Wed, 20 Apr 2011 19:23:21 -0700 Subject: [PATCH 01/10] Only compute the sass engine options once in the update_project command -- this is called repeatedly by the watcher command. --- lib/compass/commands/update_project.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/compass/commands/update_project.rb b/lib/compass/commands/update_project.rb index 7ce101f2..9c47fa5c 100644 --- a/lib/compass/commands/update_project.rb +++ b/lib/compass/commands/update_project.rb @@ -49,17 +49,20 @@ module Compass end def new_compiler_instance(additional_options = {}) - compiler_opts = Compass.sass_engine_options - compiler_opts.merge!(:force => options[:force], - :sass_files => explicit_sass_files, - :dry_run => options[:dry_run]) - compiler_opts[:quiet] = options[:quiet] if options[:quiet] - compiler_opts[:time] = options[:time] if options[:time] - compiler_opts.merge!(additional_options) + @compiler_opts ||= begin + compiler_opts = Compass.sass_engine_options + compiler_opts.merge!(:force => options[:force], + :sass_files => explicit_sass_files, + :dry_run => options[:dry_run]) + compiler_opts[:quiet] = options[:quiet] if options[:quiet] + compiler_opts[:time] = options[:time] if options[:time] + Sass::Engine.normalize_options(compiler_opts) + end + Compass::Compiler.new(working_path, Compass.configuration.sass_path, Compass.configuration.css_path, - compiler_opts) + @compiler_opts.merge(additional_options)) end def explicit_sass_files From 4c814f97dd703782386d72905f5035ce5d763a67 Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Wed, 20 Apr 2011 19:26:07 -0700 Subject: [PATCH 02/10] Use Sass's Chain and Memory caching for faster caching, store this once in the configuration when watching so Sass doesn't do this with each iteration. --- lib/compass/commands/watch_project.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/compass/commands/watch_project.rb b/lib/compass/commands/watch_project.rb index 1a3fd66c..fea0cfb4 100644 --- a/lib/compass/commands/watch_project.rb +++ b/lib/compass/commands/watch_project.rb @@ -36,11 +36,24 @@ module Compass exit 0 end + unless Compass.sass_engine_options[:cache_store] + Compass.configuration.sass_options ||= {} + Compass.configuration.sass_options[:cache_store] = + Sass::CacheStores::Chain.new( + Sass::CacheStores::Memory.new, + Sass::CacheStores::Filesystem.new( + Compass.sass_engine_options[:cache_location] || + Sass::Engine::DEFAULT_OPTIONS[:cache_location] + ) + ) + end + check_for_sass_files!(new_compiler_instance) recompile require 'fssm' + if options[:poll] require "fssm/backends/polling" # have to silence the ruby warning about chaning a constant. From 134160885cabc6be08550c8f2124d9e08edb542b Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Wed, 20 Apr 2011 19:27:11 -0700 Subject: [PATCH 03/10] Store the compiler importer in the Sass options so sass doesn't make one. --- lib/compass/compiler.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/compass/compiler.rb b/lib/compass/compiler.rb index d8a24fcf..a611f6ef 100644 --- a/lib/compass/compiler.rb +++ b/lib/compass/compiler.rb @@ -12,7 +12,8 @@ module Compass self.options = options self.options[:cache_location] ||= determine_cache_location Compass.configure_sass_plugin! - self.importer = Sass::Importers::Filesystem.new(from) + + self.options[:importer] = self.importer = Sass::Importers::Filesystem.new(from) self.staleness_checker = Sass::Plugin::StalenessChecker.new(options) end From 9cafbc642c8d223a929d6c849bf557f4be3b09c7 Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Wed, 20 Apr 2011 19:27:53 -0700 Subject: [PATCH 04/10] Consistent hashing of the Sprite importer object --- lib/compass/sass_extensions/sprites/sprites.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/compass/sass_extensions/sprites/sprites.rb b/lib/compass/sass_extensions/sprites/sprites.rb index 12bfd9d4..c0c5dc21 100644 --- a/lib/compass/sass_extensions/sprites/sprites.rb +++ b/lib/compass/sass_extensions/sprites/sprites.rb @@ -49,5 +49,14 @@ module Compass def to_s "" end + + def hash + self.class.name.hash + end + + def eql?(other) + other.class == self.class + end + end end From f4ac295894be74848c2fd62d855f7bf47345be0a Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Wed, 20 Apr 2011 22:28:46 -0700 Subject: [PATCH 05/10] Refactor the gradient literals. --- .../functions/gradient_support.rb | 134 ++++++++++-------- 1 file changed, 74 insertions(+), 60 deletions(-) diff --git a/lib/compass/sass_extensions/functions/gradient_support.rb b/lib/compass/sass_extensions/functions/gradient_support.rb index 52711e6b..30a6ca20 100644 --- a/lib/compass/sass_extensions/functions/gradient_support.rb +++ b/lib/compass/sass_extensions/functions/gradient_support.rb @@ -33,82 +33,109 @@ module Compass::SassExtensions::Functions::GradientSupport end end - class RadialGradient < Sass::Script::Literal - attr_accessor :position_or_angle, :shape_and_size, :color_stops - def children - [color_stops, position_or_angle, shape_and_size].compact + module Gradient + + def self.included(base) + base.extend ClassMethods end - def initialize(position_or_angle, shape_and_size, color_stops) - unless color_stops.value.size >= 2 - raise Sass::SyntaxError, "At least two color stops are required for a radial-gradient" + + module ClassMethods + def standardized_prefix(prefix) + class_eval %Q{ + def to_#{prefix}(options = self.options) + Sass::Script::String.new("-#{prefix}-\#{to_s(options)}") + end + } end - self.position_or_angle = position_or_angle - self.shape_and_size = shape_and_size - self.color_stops = color_stops end + def inspect to_s end + + def supports?(aspect) + GRADIENT_ASPECTS.include?(aspect) + end + + def has_aspect? + true + end + + def angle?(value) + value.is_a?(Sass::Script::Number) && value.numerator_units.size == 1 && value.numerator_units.first == "deg" && value.denominator_units.empty? + end + + end + + class RadialGradient < Sass::Script::Literal + include Gradient + + attr_accessor :position, :shape_and_size, :color_stops + + def children + [color_stops, position, shape_and_size].compact + end + + def initialize(position, shape_and_size, color_stops) + unless color_stops.value.size >= 2 + raise Sass::SyntaxError, "At least two color stops are required for a radial-gradient" + end + if angle?(position) + raise Sass::SyntaxError, "CSS no longer allows angles in radial-gradients." + end + self.position = position + self.shape_and_size = shape_and_size + self.color_stops = color_stops + end + def to_s(options = self.options) s = "radial-gradient(" - s << position_or_angle.to_s(options) << ", " if position_or_angle + s << position.to_s(options) << ", " if position s << shape_and_size.to_s(options) << ", " if shape_and_size s << color_stops.to_s(options) s << ")" end - def supports?(aspect) - if aspect == "svg" - if position_or_angle.nil? || is_position(position_or_angle).to_bool || is_position_list(position_or_angle).to_bool - true - else - Compass::Util.compass_warn("Warning: Angle-based gradients are not yet supported in SVG. Found: #{position_or_angle}") - false - end - else - GRADIENT_ASPECTS.include?(aspect) - end - end - def has_aspect? - true - end - def to_webkit(options = self.options) - Sass::Script::String.new("-webkit-#{to_s(options)}") - end + + standardized_prefix :webkit + standardized_prefix :moz + standardized_prefix :o + def to_owg(options = self.options) args = [ - grad_point(position_or_angle || _center_position), + grad_point(position || _center_position), Sass::Script::String.new("0"), - grad_point(position_or_angle || _center_position), + grad_point(position || _center_position), grad_end_position(color_stops, Sass::Script::Bool.new(true)), grad_color_stops(color_stops) ] args.each {|a| a.options = options} Sass::Script::String.new("-webkit-gradient(radial, #{args.join(', ')})") end - def to_moz(options = self.options) - Sass::Script::String.new("-moz-#{to_s(options)}") - end - def to_o(options = self.options) - Sass::Script::String.new("-o-#{to_s(options)}") - end + def to_svg(options = self.options) # XXX Add shape support if possible - radial_svg_gradient(color_stops, position_or_angle || _center_position) + radial_svg_gradient(color_stops, position || _center_position) end + def to_pie(options = self.options) Compass::Logger.new.record(:warning, "PIE does not support radial-gradient.") Sass::Script::String.new("-pie-radial-gradient(unsupported)") end + def to_css2(options = self.options) Sass::Script::String.new("") end end class LinearGradient < Sass::Script::Literal + include Gradient + attr_accessor :color_stops, :position_or_angle + def children [color_stops, position_or_angle].compact end + def initialize(position_or_angle, color_stops) unless color_stops.value.size >= 2 raise Sass::SyntaxError, "At least two color stops are required for a linear-gradient" @@ -116,24 +143,18 @@ module Compass::SassExtensions::Functions::GradientSupport self.position_or_angle = position_or_angle self.color_stops = color_stops end - def inspect - to_s - end + def to_s(options = self.options) s = "linear-gradient(" s << position_or_angle.to_s(options) << ", " if position_or_angle s << color_stops.to_s(options) s << ")" end - def supports?(aspect) - GRADIENT_ASPECTS.include?(aspect) - end - def has_aspect? - true - end - def to_webkit(options = self.options) - Sass::Script::String.new("-webkit-#{to_s(options)}") - end + + standardized_prefix :webkit + standardized_prefix :moz + standardized_prefix :o + # Output the original webkit gradient syntax def to_owg(options = self.options) args = [] @@ -143,20 +164,17 @@ module Compass::SassExtensions::Functions::GradientSupport args.each{|a| a.options = options} Sass::Script::String.new("-webkit-gradient(linear, #{args.join(', ')})") end - def to_moz(options = self.options) - Sass::Script::String.new("-moz-#{to_s(options)}") - end - def to_o(options = self.options) - Sass::Script::String.new("-o-#{to_s(options)}") - end + def to_svg(options = self.options) linear_svg_gradient(color_stops, position_or_angle || Sass::Script::String.new("top")) end + def to_pie(options = self.options) # PIE just uses the standard rep, but the property is prefixed so # the presence of this attribute helps flag when to render a special rule. Sass::Script::String.new to_s(options) end + def to_css2(options = self.options) Sass::Script::String.new("") end @@ -437,10 +455,6 @@ module Compass::SassExtensions::Functions::GradientSupport end end - def angle? - position_or_angle.is_a?(Sass::Script::Number) && position_or_angle.numerator_units == ["deg"] && position_or_angle.denominator_units.empty? - end - def linear_svg(color_stops, x1, y1, x2, y2) transform = '' if angle? From 78dc4a5bfb8c62b0bd48c12778842e0927847aa8 Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Thu, 21 Apr 2011 12:32:55 -0700 Subject: [PATCH 06/10] Pick up bug fix in FSSM. --- Gemfile.lock | 6 +++--- compass.gemspec | 2 +- doc-src/Gemfile | 2 +- doc-src/Gemfile.lock | 8 +++++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 081ebfb3..d499d5b4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,9 +7,9 @@ GIT PATH remote: . specs: - compass (0.11.beta.7.ac623c6) + compass (0.11.beta.7.f4ac295) chunky_png (~> 1.1.0) - fssm (~> 0.2) + fssm (>= 0.2.7) sass (>= 3.1.0.alpha.249) GEM @@ -65,7 +65,7 @@ GEM erubis (2.6.6) abstract (>= 1.0.0) eventmachine (0.12.10) - fssm (0.2.6.1) + fssm (0.2.7) gherkin (2.2.9) json (~> 1.4.6) term-ansicolor (~> 1.0.5) diff --git a/compass.gemspec b/compass.gemspec index bd561598..539aabca 100644 --- a/compass.gemspec +++ b/compass.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |gemspec| gemspec.add_dependency 'sass', '>= 3.1.0.alpha.249' gemspec.add_dependency 'chunky_png', '~> 1.1.0' - gemspec.add_dependency 'fssm', '~> 0.2' + gemspec.add_dependency 'fssm', '>= 0.2.7' gemspec.files = %w(README.markdown LICENSE.markdown VERSION.yml Rakefile) gemspec.files += Dir.glob("bin/*") diff --git a/doc-src/Gemfile b/doc-src/Gemfile index 5ad23a24..1b09d7ac 100644 --- a/doc-src/Gemfile +++ b/doc-src/Gemfile @@ -9,7 +9,6 @@ gem 'serve', "1.0.0" gem 'nokogiri' gem 'coderay' gem 'sass', ">= 3.1.0.alpha.249" -gem 'fssm', '0.1.2' gem 'haml', ">= 3.1.0.alpha.36" gem 'rake' gem 'compass', :path => ".." @@ -18,4 +17,5 @@ gem 'css-slideshow', "0.2.0" gem 'json' gem 'css_parser', "1.0.1" gem 'ruby-prof' +gem 'rb-fsevent' diff --git a/doc-src/Gemfile.lock b/doc-src/Gemfile.lock index 1355f1a0..46916319 100644 --- a/doc-src/Gemfile.lock +++ b/doc-src/Gemfile.lock @@ -8,8 +8,9 @@ GIT PATH remote: .. specs: - compass (0.11.beta.6.7c5f831) + compass (0.11.beta.7.f4ac295) chunky_png (~> 1.1.0) + fssm (>= 0.2.7) sass (>= 3.1.0.alpha.249) GEM @@ -24,7 +25,7 @@ GEM css-slideshow (0.2.0) compass (>= 0.10.0.rc3) css_parser (1.0.1) - fssm (0.1.2) + fssm (0.2.7) haml (3.1.0.alpha.147) i18n (0.4.2) json (1.5.1) @@ -32,6 +33,7 @@ GEM nokogiri (1.4.4) rack (1.2.2) rake (0.8.7) + rb-fsevent (0.4.0) rdiscount (1.6.8) ruby-prof (0.9.2) sass (3.1.0.alpha.252) @@ -52,7 +54,6 @@ DEPENDENCIES compass-susy-plugin (>= 0.7.0.pre8) css-slideshow (= 0.2.0) css_parser (= 1.0.1) - fssm (= 0.1.2) haml (>= 3.1.0.alpha.36) json mime-types @@ -60,6 +61,7 @@ DEPENDENCIES nokogiri rack rake + rb-fsevent rdiscount ruby-prof sass (>= 3.1.0.alpha.249) From a8a74995bf137adc0babb81f70a4cd86213d7591 Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Fri, 22 Apr 2011 09:19:32 -0700 Subject: [PATCH 07/10] A module to help debug memory leaks in the watcher. --- lib/compass/commands/watch_project.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/compass/commands/watch_project.rb b/lib/compass/commands/watch_project.rb index fea0cfb4..668b6b81 100644 --- a/lib/compass/commands/watch_project.rb +++ b/lib/compass/commands/watch_project.rb @@ -24,12 +24,37 @@ module Compass end end + module MemoryDebugger + def report_on_instances(type, options = {}) + @@runs ||= 0 + @@runs += 1 + @@object_id_tracker ||= {} + @@object_id_tracker[type] ||= [] + GC.start + sleep options.fetch(:gc_pause, 1) + count = ObjectSpace.each_object(type) do |obj| + if @@runs > 2 + if options.fetch(:verbose, true) && !@@object_id_tracker[type].include?(obj.object_id) + begin + puts obj.inspect + rescue + end + puts "#{obj.class.name}:#{obj.object_id}" + end + end + @@object_id_tracker[type] << obj.object_id + end + puts "#{type}: #{count} instances." + end + end class WatchProject < UpdateProject register :watch attr_accessor :last_update_time, :last_sass_files + include MemoryDebugger + def perform Signal.trap("INT") do puts "" @@ -116,6 +141,7 @@ module Compass begin puts ">>> Change detected to: #{file}" compiler.run + # report_on_instances(Sass::Importers::Base, :verbose => false) rescue StandardError => e ::Compass::Exec::Helpers.report_error(e, options) end From 9e54d7bcb7cb1d066058ee63155e11b075466874 Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Fri, 22 Apr 2011 09:21:09 -0700 Subject: [PATCH 08/10] Unnecessary normalization -- sass will do it later. --- lib/compass/commands/update_project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compass/commands/update_project.rb b/lib/compass/commands/update_project.rb index 9c47fa5c..c451db1d 100644 --- a/lib/compass/commands/update_project.rb +++ b/lib/compass/commands/update_project.rb @@ -56,7 +56,7 @@ module Compass :dry_run => options[:dry_run]) compiler_opts[:quiet] = options[:quiet] if options[:quiet] compiler_opts[:time] = options[:time] if options[:time] - Sass::Engine.normalize_options(compiler_opts) + compiler_opts end Compass::Compiler.new(working_path, From ece275a05439e5d28849c354af67f451f35e3d81 Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Fri, 22 Apr 2011 09:27:51 -0700 Subject: [PATCH 09/10] Reset the memory cache before each watcher run. --- lib/compass/commands/watch_project.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/compass/commands/watch_project.rb b/lib/compass/commands/watch_project.rb index 668b6b81..7a079b74 100644 --- a/lib/compass/commands/watch_project.rb +++ b/lib/compass/commands/watch_project.rb @@ -62,15 +62,16 @@ module Compass end unless Compass.sass_engine_options[:cache_store] + @memory_cache = Sass::CacheStores::Memory.new Compass.configuration.sass_options ||= {} - Compass.configuration.sass_options[:cache_store] = - Sass::CacheStores::Chain.new( - Sass::CacheStores::Memory.new, - Sass::CacheStores::Filesystem.new( - Compass.sass_engine_options[:cache_location] || - Sass::Engine::DEFAULT_OPTIONS[:cache_location] - ) + Sass::CacheStores::Chain.new( + @memory_cache, + Sass::CacheStores::Filesystem.new( + Compass.sass_engine_options[:cache_location] || + Sass::Engine::DEFAULT_OPTIONS[:cache_location] ) + ) + end check_for_sass_files!(new_compiler_instance) @@ -136,6 +137,7 @@ module Compass end def recompile(base = nil, relative = nil) + @memory_cache.reset! if @memory_cache compiler = new_compiler_instance(:quiet => true) if file = compiler.out_of_date? begin From c7a1c2a911c8e333199f162442f0772b3534f723 Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Fri, 22 Apr 2011 09:28:56 -0700 Subject: [PATCH 10/10] Correctly report the changed file. --- lib/compass/commands/watch_project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compass/commands/watch_project.rb b/lib/compass/commands/watch_project.rb index 7a079b74..4fcaea8e 100644 --- a/lib/compass/commands/watch_project.rb +++ b/lib/compass/commands/watch_project.rb @@ -141,7 +141,7 @@ module Compass compiler = new_compiler_instance(:quiet => true) if file = compiler.out_of_date? begin - puts ">>> Change detected to: #{file}" + puts ">>> Change detected to: #{relative}" compiler.run # report_on_instances(Sass::Importers::Base, :verbose => false) rescue StandardError => e