Callbacks for the following events:

* sprite_saved
* sprite_generated
* stylesheet_saved
* stylesheet_error

From the compass configuration file you can attach code to run when the
event occurs like so:

on_stylesheet_error do |filename, message|
  # do something
end
This commit is contained in:
Scott Davis 2011-02-17 02:30:11 -05:00 committed by Chris Eppstein
parent 9fd7ac6e52
commit 2142430735
9 changed files with 160 additions and 46 deletions

View File

@ -217,13 +217,13 @@ later on.
approach.
</td>
</tr>
<tr>
<td style="vertical-align:top;"><code>disable_warnings</code> </td>
<td style="vertical-align:top;">Boolean </td>
<tr>
<td style="vertical-align:top;"><code>disable_warnings</code> </td>
<td style="vertical-align:top;">Boolean </td>
<td style="vertical-align:top;">
Set this to true to silence deprecation warnings.
</td>
</tr>
</td>
</tr>
<tr>
<td style="vertical-align:top;"><code>sass_options</code> </td>
<td style="vertical-align:top;">Hash </td>
@ -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.
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 &copy; 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

View File

@ -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__), '..'))

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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