gems.compass-0.12.2.lib.compass.commands.watch_project.rb Maven / Gradle / Ivy
require 'fileutils'
require 'pathname'
require 'compass/commands/base'
require 'compass/commands/update_project'
require 'sass/plugin'
module Compass
module Commands
module WatchProjectOptionsParser
def set_options(opts)
super
opts.banner = %Q{
Usage: compass watch [path/to/project] [path/to/project/src/file.sass ...] [options]
Description:
watch the project for changes and recompile when they occur.
Options:
}.split("\n").map{|l| l.gsub(/^ */,'')}.join("\n")
opts.on("--poll", :NONE, "Check periodically if there's been changes.") do
self.options[:poll] = 1 # check every 1 second.
end
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 options.fetch(:verbose, true)
if @@runs > 2
if !@@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
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 ""
exit 0
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.
stderr, $stderr = $stderr, StringIO.new
FSSM::Backends.const_set("Default", FSSM::Backends::Polling)
$stderr = stderr
end
action = FSSM::Backends::Default.to_s == "FSSM::Backends::Polling" ? "polling" : "watching"
puts ">>> Compass is #{action} for changes. Press Ctrl-C to Stop."
$stdout.flush
begin
FSSM.monitor do |monitor|
Compass.configuration.sass_load_paths.each do |load_path|
load_path = load_path.root if load_path.respond_to?(:root)
next unless load_path.is_a? String
monitor.path load_path do |path|
path.glob '**/*.s[ac]ss'
path.update &method(:recompile)
path.delete {|base, relative| remove_obsolete_css(base,relative); recompile(base, relative)}
path.create &method(:recompile)
end
end
Compass.configuration.watches.each do |glob, callback|
monitor.path Compass.configuration.project_path do |path|
path.glob glob
path.update do |base, relative|
puts ">>> Change detected to: #{relative}"
$stdout.flush
callback.call(base, relative)
end
path.create do |base, relative|
puts ">>> New file detected: #{relative}"
$stdout.flush
callback.call(base, relative)
end
path.delete do |base, relative|
puts ">>> File Removed: #{relative}"
$stdout.flush
callback.call(base, relative)
end
end
end
end
rescue FSSM::CallbackError => e
# FSSM catches exit? WTF.
if e.message =~ /exit/
exit
end
end
end
def remove_obsolete_css(base = nil, relative = nil)
compiler = new_compiler_instance(:quiet => true)
sass_files = compiler.sass_files
deleted_sass_files = (last_sass_files || []) - sass_files
deleted_sass_files.each do |deleted_sass_file|
css_file = compiler.corresponding_css_file(deleted_sass_file)
remove(css_file) if File.exists?(css_file)
end
self.last_sass_files = sass_files
end
def recompile(base = nil, relative = nil)
@memory_cache.reset! if @memory_cache
compiler = new_compiler_instance(:quiet => true, :loud => [:identical, :overwrite, :create])
if file = compiler.out_of_date?
begin
puts ">>> Change detected at "+Time.now.strftime("%T")+" to: #{relative || compiler.relative_stylesheet_name(file)}"
$stdout.flush
compiler.run
GC.start
rescue StandardError => e
::Compass::Exec::Helpers.report_error(e, options)
end
end
end
class << self
def description(command)
"Compile Sass stylesheets to CSS when they change"
end
def option_parser(arguments)
parser = Compass::Exec::CommandOptionParser.new(arguments)
parser.extend(Compass::Exec::GlobalOptionsParser)
parser.extend(Compass::Exec::ProjectOptionsParser)
parser.extend(CompileProjectOptionsParser)
parser.extend(WatchProjectOptionsParser)
end
end
end
end
end