diff --git a/doc-src/content/help/tutorials/configuration-reference.markdown b/doc-src/content/help/tutorials/configuration-reference.markdown
index 8bf0d11b..0f6475e9 100644
--- a/doc-src/content/help/tutorials/configuration-reference.markdown
+++ b/doc-src/content/help/tutorials/configuration-reference.markdown
@@ -217,13 +217,13 @@ later on.
approach.
-
- disable_warnings |
- Boolean |
+
+ disable_warnings |
+ Boolean |
Set this to true to silence deprecation warnings.
- |
-
+
+
sass_options |
Hash |
@@ -330,4 +330,37 @@ more than once. Example:
end
This code will be called if the file is added, updated, or removed. Be sure to check for existence
-to avoid crashing the watcher in the case where the file has been removed.
\ No newline at end of file
+to avoid crashing the watcher in the case where the file has been removed.
+
+## Callbacks
+
+**`on_sprite_saved`** -- Pass this function a block of code that gets executed after a sprite is saved to disk. The block will be passed the filename. Can be invoked more then once. Example:
+
+ on_sprite_saved do |filename|
+ post_process(filename) if File.exists?(filename)
+ end
+
+**`on_sprite_generated`** -- Pass this function a block of code that gets executed after a sprite is generated but before its saved to disk. The block will be passed an instance of `ChunkyPNG::Image`. Can be invoked more then once. Example:
+
+ on_sprite_generated do |sprite_data|
+ sprite_data.metadata['Caption'] = "This Image is © My Company 2011"
+ end
+
+**`on_stylesheet_saved`** -- Pass this function a block of code that gets executed after a stylesheet is processed. The block will be passed the filename. Can be invoked more then once. Example:
+
+ on_stylesheet_saved do |filename|
+ Growl.notify {
+ self.message "#{filename} updated!"
+ self.icon = '/path/to/success.jpg'
+ }
+ end
+
+**`on_stylesheet_error`** -- Pass this function a block of code that gets executed if a stylesheet has an error while processing. The block will be passed the filename and the error message. Can be invoked more then once. Example:
+
+ on_stylesheet_error do |filename, message|
+ Growl.notify {
+ self.message = "#{filename}: #{message}"
+ self.icon = '/path/to/fail.jpg'
+ sticky!
+ }
+ end
\ No newline at end of file
diff --git a/lib/compass.rb b/lib/compass.rb
index 2f0b7ec7..5ce7268d 100644
--- a/lib/compass.rb
+++ b/lib/compass.rb
@@ -5,6 +5,8 @@ end
require "compass/#{lib}"
end
+require 'sass/callbacks'
+
module Compass
def base_directory
File.expand_path(File.join(File.dirname(__FILE__), '..'))
diff --git a/lib/compass/compiler.rb b/lib/compass/compiler.rb
index b259dfce..bccb6489 100644
--- a/lib/compass/compiler.rb
+++ b/lib/compass/compiler.rb
@@ -119,6 +119,7 @@ module Compass
end
duration = additional_options[:time] ? "(#{(css_content.__duration * 1000).round / 1000.0}s)" : ""
write_file(css_filename, css_content, options.merge(:force => true, :extra => duration))
+ Compass.configuration.send(:run_stylesheet_saved, File.basename(css_filename)) #run callback
end
def should_compile?(sass_filename, css_filename)
@@ -136,7 +137,10 @@ module Compass
# formatted to display in the browser (in development mode)
# if there's an error.
def handle_exception(sass_filename, css_filename, e)
- logger.record :error, basename(sass_filename), "(Line #{e.sass_line}: #{e.message})"
+ formatted_error = "(Line #{e.sass_line}: #{e.message})"
+ file = basename(sass_filename)
+ logger.record :error, file, formatted_error
+ Compass.configuration.send(:run_styesheet_error, file, formatted_error) #run callback
write_file css_filename, error_contents(e, sass_filename), options.merge(:force => true)
end
diff --git a/lib/compass/configuration.rb b/lib/compass/configuration.rb
index c51e9a88..f094570b 100644
--- a/lib/compass/configuration.rb
+++ b/lib/compass/configuration.rb
@@ -43,6 +43,6 @@ module Compass
end
end
-['adapters', 'comments', 'defaults', 'helpers', 'inheritance', 'serialization', 'paths', 'data'].each do |lib|
+['adapters', 'callbacks', 'comments', 'defaults', 'helpers', 'inheritance', 'serialization', 'paths', 'data'].each do |lib|
require "compass/configuration/#{lib}"
end
diff --git a/lib/compass/configuration/callbacks.rb b/lib/compass/configuration/callbacks.rb
new file mode 100644
index 00000000..2f034ee9
--- /dev/null
+++ b/lib/compass/configuration/callbacks.rb
@@ -0,0 +1,25 @@
+module Compass
+ module Configuration
+ module CallbackMethods
+ extend ::Sass::Callbacks
+
+ # on_sprite_generated
+ # yields the filename
+ # usage: on_sprite_save {|filename| do_somethign(filename) }
+ define_callback :sprite_saved
+ # on_sprite_generated
+ # yields 'ChunkyPNG::Image'
+ # usage: on_sprite_generated {|sprite_data| do_something(sprite_data) }
+ define_callback :sprite_generated
+
+ define_callback :stylesheet_saved
+ define_callback :stylesheet_error
+
+ end
+
+ class Callbacks
+ extend CallbackMethods
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/lib/compass/configuration/inheritance.rb b/lib/compass/configuration/inheritance.rb
index b44954ca..24de6b4d 100644
--- a/lib/compass/configuration/inheritance.rb
+++ b/lib/compass/configuration/inheritance.rb
@@ -141,9 +141,11 @@ module Compass
end
end
- def method_missing(meth)
+ def method_missing(meth, *args, &block)
if inherited_data
- inherited_data.send(meth)
+ inherited_data.send(meth, *args, &block)
+ elsif Callbacks.respond_to?(meth, true)
+ Callbacks.send(meth, *args, &block)
else
raise NoMethodError, meth.to_s
end
diff --git a/lib/compass/sass_extensions/functions/sprites.rb b/lib/compass/sass_extensions/functions/sprites.rb
index 7df04e20..45343df9 100644
--- a/lib/compass/sass_extensions/functions/sprites.rb
+++ b/lib/compass/sass_extensions/functions/sprites.rb
@@ -2,7 +2,7 @@ require 'digest/md5'
module Compass::SassExtensions::Functions::Sprites
ZERO = Sass::Script::Number::new(0)
-
+
# Provides a consistent interface for getting a variable in ruby
# from a keyword argument hash that accounts for underscores/dash equivalence
# and allows the caller to pass a symbol instead of a string.
@@ -124,6 +124,7 @@ module Compass::SassExtensions::Functions::Sprites
def generate
if generation_required?
save!(construct_sprite)
+ Compass.configuration.send(:run_sprite_generated, construct_sprite)
end
end
@@ -155,7 +156,7 @@ module Compass::SassExtensions::Functions::Sprites
end
end
end
- output_png
+ output_png
end
# The on-the-disk filename of the sprite
@@ -180,7 +181,9 @@ module Compass::SassExtensions::Functions::Sprites
# saves the sprite for later retrieval
def save!(output_png)
- output_png.save filename
+ saved = output_png.save filename
+ Compass.configuration.send(:run_sprite_saved, filename)
+ saved
end
# All the full-path filenames involved in this sprite
@@ -285,7 +288,7 @@ module Compass::SassExtensions::Functions::Sprites
end
end
Sass::Script::Functions.declare :sprite_file, [:map, :sprite]
-
+
# Returns a url to the sprite image.
def sprite_url(map)
unless map.is_a?(SpriteMap)
diff --git a/spec/sprites_spec.rb b/spec/sprites_spec.rb
index c619e9f5..9f2b2f12 100644
--- a/spec/sprites_spec.rb
+++ b/spec/sprites_spec.rb
@@ -3,7 +3,7 @@ require "compass/sprites"
require 'digest/md5'
describe Compass::Sprites do
-
+
before :each do
@images_src_path = File.join(File.dirname(__FILE__), 'test_project', 'public', 'images')
@images_tmp_path = File.join(File.dirname(__FILE__), 'test_project', 'public', 'images-tmp')
@@ -29,7 +29,7 @@ describe Compass::Sprites do
md5.update IO.read(map_location(file))
md5.hexdigest
end
-
+
def render(scss)
scss = %Q(@import "compass"; #{scss})
options = Compass.sass_engine_options
@@ -40,9 +40,34 @@ describe Compass::Sprites do
# reformat to fit result of heredoc:
" #{css.gsub('@charset "UTF-8";', '').gsub(/\n/, "\n ").strip}\n"
end
-
- # DEFAULT USAGE:
+ #Callbacks
+ describe 'callbacks' do
+ it "should fire on_sprite_saved" do
+ saved = false
+ path = nil
+ Compass.configuration.on_sprite_saved {|filepath| path = filepath; saved = true }
+ render <<-SCSS
+ @import "squares/*.png";
+ @include all-squares-sprites;
+ SCSS
+ saved.should eq true
+ path.should be_kind_of String
+ end
+ it "should fire on_sprite_generated" do
+ saved = false
+ sprite_data = nil
+ Compass.configuration.on_sprite_generated {|data| sprite_data = data; saved = true }
+ render <<-SCSS
+ @import "squares/*.png";
+ @include all-squares-sprites;
+ SCSS
+ sprite_data.should be_kind_of ChunkyPNG::Image
+ saved.should eq true
+ end
+ end
+
+ # DEFAULT USAGE:
it "should generate sprite classes" do
css = render <<-SCSS
@import "squares/*.png";
@@ -90,15 +115,15 @@ describe Compass::Sprites do
CSS
image_size('squares-*.png').should == [20, 30]
end
-
+
it "should provide sprite mixin" do
css = render <<-SCSS
@import "squares/*.png";
-
+
.cubicle {
@include squares-sprite("ten-by-ten");
}
-
+
.large-cube {
@include squares-sprite("twenty-by-twenty", true);
}
@@ -120,9 +145,9 @@ describe Compass::Sprites do
CSS
image_size('squares-*.png').should == [20, 30]
end
-
+
# CUSTOMIZATIONS:
-
+
it "should be possible to change the base class" do
css = render <<-SCSS
$squares-sprite-base-class: ".circles";
@@ -135,7 +160,7 @@ describe Compass::Sprites do
CSS
image_size('squares-*.png').should == [20, 30]
end
-
+
it "should calculate the spacing between images but not before first image" do
css = render <<-SCSS
$squares-ten-by-ten-spacing: 33px;
@@ -157,7 +182,7 @@ describe Compass::Sprites do
CSS
image_size('squares-*.png').should == [20, 63]
end
-
+
it "should calculate the spacing between images" do
css = render <<-SCSS
$squares-twenty-by-twenty-spacing: 33px;
@@ -179,7 +204,7 @@ describe Compass::Sprites do
CSS
image_size('squares-*.png').should == [20, 63]
end
-
+
it "should calculate the maximum spacing between images" do
css = render <<-SCSS
$squares-ten-by-ten-spacing: 44px;
@@ -202,7 +227,7 @@ describe Compass::Sprites do
CSS
image_size('squares-*.png').should == [20, 74]
end
-
+
it "should calculate the maximum spacing between images in reversed order" do
css = render <<-SCSS
$squares-ten-by-ten-spacing: 33px;
@@ -225,7 +250,7 @@ describe Compass::Sprites do
CSS
image_size('squares-*.png').should == [20, 74]
end
-
+
it "should calculate the default spacing between images" do
css = render <<-SCSS
$squares-spacing: 22px;
@@ -247,22 +272,22 @@ describe Compass::Sprites do
CSS
image_size('squares-*.png').should == [20, 52]
end
-
+
it "should use position adjustments in functions" do
css = render <<-SCSS
$squares: sprite-map("squares/*.png", $position: 100%);
.squares-sprite {
background: $squares no-repeat;
}
-
+
.adjusted-percentage {
background-position: sprite-position($squares, ten-by-ten, 100%);
}
-
+
.adjusted-px-1 {
background-position: sprite-position($squares, ten-by-ten, 4px);
}
-
+
.adjusted-px-2 {
background-position: sprite-position($squares, twenty-by-twenty, -3px, 2px);
}
@@ -287,20 +312,20 @@ describe Compass::Sprites do
image_size('squares-*.png').should == [20, 30]
image_md5('squares-*.png').should == '652b67f5e9092520d6f26caae7e18012'
end
-
+
it "should use position adjustments in mixins" do
css = render <<-SCSS
$squares-position: 100%;
@import "squares/*.png";
-
+
.adjusted-percentage {
@include squares-sprite("ten-by-ten", $offset-x: 100%);
}
-
+
.adjusted-px-1 {
@include squares-sprite("ten-by-ten", $offset-x: 4px);
}
-
+
.adjusted-px-2 {
@include squares-sprite("twenty-by-twenty", $offset-x: -3px, $offset-y: 2px);
}
@@ -325,7 +350,7 @@ describe Compass::Sprites do
image_size('squares-*.png').should == [20, 30]
image_md5('squares-*.png').should == '652b67f5e9092520d6f26caae7e18012'
end
-
+
it "should repeat the image" do
css = render <<-SCSS
$squares-repeat: repeat;
@@ -372,7 +397,7 @@ describe Compass::Sprites do
image_size('squares-*.png').should == [30, 30]
image_md5('squares-*.png').should == '2fb19ef9c83018c93c6f147af3a56cb2'
end
-
+
it "should provide a nice errors for lemonade's old users" do
proc do
render <<-SCSS
@@ -393,7 +418,7 @@ describe Compass::Sprites do
proc do
render <<-SCSS
@import "squares/*.png";
-
+
.squares {
background: sprite-position("squares/twenty-by-twenty.png") no-repeat;
}
@@ -401,7 +426,7 @@ describe Compass::Sprites do
end.should raise_error Sass::SyntaxError,
%q(The first argument to sprite-position() must be a sprite map. See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
end
-
+
it "should work even if @import is missing" do
actual_css = render <<-SCSS
.squares {
@@ -414,5 +439,5 @@ describe Compass::Sprites do
}
CSS
end
-
+
end
\ No newline at end of file
diff --git a/test/compass_test.rb b/test/compass_test.rb
index f86f94c3..ed3b8581 100644
--- a/test/compass_test.rb
+++ b/test/compass_test.rb
@@ -20,6 +20,26 @@ class CompassTest < Test::Unit::TestCase
end
end
+ def test_on_stylesheet_saved_callback
+ saved = false
+ filepath = nil
+ Compass.configuration.on_stylesheet_saved {|filepath| path = filepath; saved = true }
+ within_project(:blueprint) { } #requires a block but we don't need to pass anything - sdavis
+ assert saved, "Stylesheet callback didn't get called"
+ assert filepath.is_a?(String), "Path is not a string"
+ end
+
+ # no project with errors exists to test aginst - leep of FAITH!
+ # *chriseppstein flogs himself*
+ # def test_on_stylesheet_error_callback
+ # error = false
+ # file = nil
+ # Compass.configuration.on_stylesheet_error {|filename, message| file = filename; error = true }
+ # within_project(:error) { } #requires a block but we don't need to pass anything - sdavis
+ # assert error, "Project did not throw a compile error"
+ # assert file.is_a?(String), "Filename was not a string"
+ # end
+
def test_empty_project
# With no sass files, we should have no css files.
within_project(:empty) do |proj|
@@ -114,10 +134,10 @@ private
end
yield Compass.configuration
rescue
- save_output(project_name)
+ save_output(project_name)
raise
end
-
+
def each_css_file(dir, &block)
Dir.glob("#{dir}/**/*.css").each(&block)
end
@@ -145,15 +165,15 @@ private
def tempfile_path(project_name)
File.join(project_path(project_name), "tmp")
end
-
+
def template_path(project_name)
File.join(project_path(project_name), "sass")
end
-
+
def result_path(project_name)
File.join(project_path(project_name), "css")
end
-
+
def save_path(project_name)
File.join(project_path(project_name), "saved")
end