merged version.yml

This commit is contained in:
Scott Davis 2011-06-27 00:56:29 -04:00
commit cc81fe6fac
60 changed files with 666 additions and 1157 deletions

View File

@ -4,4 +4,4 @@ rvm:
- jruby
- rbx
- ree
script: "bundle exec rake test"
script: "bundle exec rake test features"

View File

@ -5,7 +5,7 @@ gemspec
gem "cucumber", "~> 0.9.2"
gem "rspec", "~>2.0.0"
gem "rails", "~>3.0.0.rc"
gem "compass-validator", "3.0.0"
gem "compass-validator", "3.0.1"
gem "css_parser", "~> 1.0.1"
gem "sass", "~>3.1"
gem "haml", "~> 3.1"

View File

@ -7,8 +7,8 @@ GIT
PATH
remote: .
specs:
compass (0.11.1.f248c22)
chunky_png (~> 1.1)
compass (0.11.3.b352e8b)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
@ -51,7 +51,7 @@ GEM
sys-uname
builder (2.1.2)
chunky_png (1.2.0)
compass-validator (3.0.0)
compass-validator (3.0.1)
css_parser (1.0.1)
cucumber (0.9.4)
builder (~> 2.1.2)
@ -136,7 +136,7 @@ DEPENDENCIES
autotest
autotest-fsevent
compass!
compass-validator (= 3.0.0)
compass-validator (= 3.0.1)
css_parser (~> 1.0.1)
cucumber (~> 0.9.2)
diff-lcs (~> 1.1.2)

View File

@ -16,7 +16,7 @@ Gem::Specification.new do |gemspec|
gemspec.summary = %q{A Real Stylesheet Framework}
gemspec.add_dependency 'sass', '~> 3.1'
gemspec.add_dependency 'chunky_png', '~> 1.1'
gemspec.add_dependency 'chunky_png', '~> 1.2'
gemspec.add_dependency 'fssm', '>= 0.2.7'
gemspec.files = %w(README.markdown LICENSE.markdown VERSION.yml Rakefile)

View File

@ -14,6 +14,29 @@ The Documentation for the [latest stable release](http://compass-style.org/docs/
The Documentation for the [latest preview release](http://beta.compass-style.org/)
0.11.3 (06/11/2011)
-------------------
**Note:** Due to some internal changes to compass you may have issue with your sass cache. Run `compass clean` to clear your cache.
* The `pie-clearfix` mixin has been updated. If you have to
support Firefox < 3.5, please update your stylesheets
to use `legacy-pie-clearfix` instead.
* Added a new command: `compass clean` which removes any generated
css files and clears the sass cache.
* Enable IE 10 support for flexible box with the -ms prefix.
* A small change to how generated sprites are named for better
rails 3.1 compatibility.
* Fixes for the compass --quiet mode.
* It is now possible to generate cache buster urls that manipulate
the path of the image instead of the query string. This makes
images work better with proxies, but will require some web server
configuration. [Docs](/help/tutorials/configuration-reference/#asset-cache-buster)
* Numerous small bug fixes to sprites.
* Sprite Engines are now classes see [Docs](/help/tutorials/extending) for more information
* Sprite classes have bee re-factored into modules for readability
* Sprites will no longer cause `undefined method 'find' for #<Compass::SpriteMap>` when adding or removing sprite files
0.11.2 (06/10/2011)
-------------------
* Sprites will now by default remove any old versions of the sprite. A new configuration

View File

@ -307,10 +307,14 @@ the asset host configuration is ignored.
---
<a name="asset-cache-buster"></a>
**`asset_cache_buster`** Pass this function a block of code that defines the
cache buster strategy to be used. The block must return nil or a string that can
be appended to a url as a query parameter. The returned string must not include
the starting `?`. The block will be passed the root-relative url of the asset.
cache buster strategy to be used. The block must return nil, a string or a hash.
If the returned value is a hash the values of :path and/or :query is used to generate
a cache busted path to the asset. If a string value is returned, it is added as a query string.
The returned values for query strings must not include the starting `?`.
The block will be passed the root-relative url of the asset.
If the block accepts two arguments, it will also be passed a path
that points to the asset on disk — which may or may not exist.
@ -324,6 +328,18 @@ that points to the asset on disk — which may or may not exist.
end
end
Busting the cache via path:
asset_cache_buster do |path, real_path|
if File.exists?(real_path)
pathname = Pathname.new(path)
modified_time = File.mtime(real_path).strftime("%s")
new_path = "%s/%s-%s%s" % [pathname.dirname, pathname.basename(pathname.extname), modified_time, pathname.extname]
{:path => new_path, :query => nil}
end
end
To disable the asset cache buster:
asset_cache_buster :none

View File

@ -14,11 +14,9 @@ The sprite engine is the work horse of sprite generation it's the interface for
### Requirements
A sprite engine requires only one method and that is `construct_sprite` which must return an object that responds to `save(filepath)`
A sprite engine requires two methods `construct_sprite`, and `save(filename)`
Once inside this method you have access to `images` which is a collection of [Compass::SassExtensions::Sprites::Image](http://rdoc.info/github/chriseppstein/compass/dda7c9/Compass/SassExtensions/Sprites/Image)
Since the Engine module extends base you also have access to all methods in [Compass::SassExtensions::Sprites::Base](http://rdoc.info/github/chriseppstein/compass/dda7c9/Compass/SassExtensions/Sprites/Base)
Once inside the class you have access to `images` which is a collection of [Compass::SassExtensions::Sprites::Image](http://rdoc.info/github/chriseppstein/compass/dda7c9/Compass/SassExtensions/Sprites/Image)
### Configuration
@ -26,7 +24,7 @@ To enable your sprite engine from the config file set
sprite_engine = :<engine name>
The example below will load `Compass::SassExtension::Sprites::ChunkyPngEngine`
The example below will load `Compass::SassExtension::Sprites::ChunkyPngEngine.new(width, height, images)`
sprite_engine = :chunky_png
@ -35,13 +33,16 @@ The example below will load `Compass::SassExtension::Sprites::ChunkyPngEngine`
module Compass
module SassExtensions
module Sprites
module <engine name>Engine
class ChunkyPngEngine < Compass::SassExtensions::Sprites::Engine
# Returns an object
def construct_sprite
#must return a image object that responds to save(filename)
#do something
end
def save(filename)
#save file
end
end
end
end

View File

@ -34,7 +34,7 @@ And you'll get the following CSS output:
.icon-delete,
.icon-edit,
.icon-new,
.icon-save { background: url('/images/icon-34fe0604ab.png') no-repeat; }
.icon-save { background: url('/images/icon-s34fe0604ab.png') no-repeat; }
.icon-delete { background-position: 0 0; }
.icon-edit { background-position: 0 -32px; }
@ -74,7 +74,7 @@ And your stylesheet will compile to:
.actions .new,
.actions .edit,
.actions .save,
.actions .delete { background: url('/images/icon-34fe0604ab.png') no-repeat; }
.actions .delete { background: url('/images/icon-s34fe0604ab.png') no-repeat; }
.actions .new { background-position: 0 -64px; }
.actions .edit { background-position: 0 -32px; }
@ -122,7 +122,7 @@ Now in our sass file we add:
And your stylesheet will compile to:
.selectors-sprite, a {
background: url('/selectors-edfef809e2.png') no-repeat;
background: url('/selectors-sedfef809e2.png') no-repeat;
}
a {
@ -141,7 +141,7 @@ And your stylesheet will compile to:
Alternatively you can use the `@include all-selectors-sprites;` after the import and get the following output:
.selectors-sprite, .selectors-ten-by-ten {
background: url('/selectors-edfef809e2.png') no-repeat;
background: url('/selectors-sedfef809e2.png') no-repeat;
}
.selectors-ten-by-ten {

View File

@ -145,6 +145,7 @@ Feature: Command Line
Scenario: Basic help
When I run: compass help
Then I should see the following "primary" commands:
| clean |
| compile |
| create |
| init |
@ -179,6 +180,27 @@ Feature: Command Line
And I run: compass compile
And a css file tmp/layout.css is reported overwritten
Scenario: Cleaning a project
Given I am using the existing project in test/fixtures/stylesheets/compass
When I run: compass compile
And I run: compass clean
Then the following files are reported removed:
| .sass-cache/ |
| tmp/border_radius.css |
| tmp/box.css |
| tmp/box_shadow.css |
| tmp/columns.css |
| tmp/fonts.css |
| images/flag-s03c3b29b35.png |
And the following files are removed:
| .sass-cache/ |
| tmp/border_radius.css |
| tmp/box.css |
| tmp/box_shadow.css |
| tmp/columns.css |
| tmp/fonts.css |
| images/flag-s03c3b29b35.png |
Scenario: Watching a project for changes
Given ruby supports fork
Given I am using the existing project in test/fixtures/stylesheets/compass
@ -218,7 +240,6 @@ Feature: Command Line
| sass_dir | sass |
| css_dir | assets/css |
@now
Scenario Outline: Print out a configuration value
Given I am using the existing project in test/fixtures/stylesheets/compass
When I run: compass config -p <property>

View File

@ -76,7 +76,7 @@ When /^I run in a separate process: compass ([^\s]+) ?(.+)?$/ do |command, args|
file.puts $stdout.string
end
open('/tmp/last_error.compass_test.txt', 'w') do |file|
file.puts @stderr.string
file.puts $stderr.string
end
exit!
end
@ -116,10 +116,30 @@ Then /^a directory ([^ ]+) is (not )?created$/ do |directory, negated|
File.directory?(directory).should == !negated
end
Then /an? \w+ file ([^ ]+) is (not )?removed/ do |filename, negated|
File.exists?(filename).should == !!negated
end
Then /an? \w+ file ([^ ]+) is (not )?created/ do |filename, negated|
File.exists?(filename).should == !negated
end
Then "the following files are reported removed:" do |table|
table.rows.each do |css_file|
Then %Q{a css file #{css_file.first} is reported removed}
end
end
Then "the following files are removed:" do |table|
table.rows.each do |css_file|
Then %Q{a css file #{css_file.first} is removed}
end
end
Then /an? \w+ file ([^ ]+) is reported removed/ do |filename|
@last_result.should =~ /remove.*#{Regexp.escape(filename)}/
end
Then /an? \w+ file ([^ ]+) is reported created/ do |filename|
@last_result.should =~ /create.*#{Regexp.escape(filename)}/
end

View File

@ -18,7 +18,7 @@
// [Easy Clearing](http://www.positioniseverything.net/easyclearing.html)
// has the advantage of allowing positioned elements to hang
// outside the bounds of the container at the expense of more tricky CSS.
@mixin pie-clearfix {
@mixin legacy-pie-clearfix {
&:after {
content : "\0020";
display : block;
@ -29,3 +29,16 @@
}
@include has-layout;
}
// This is an updated version of the PIE clearfix method that reduces the amount of CSS output.
// If you need to support Firefox before 3.5 you need to use `legacy-pie-clearfix` instead.
//
// Adapted from: [A new micro clearfix hack](http://nicolasgallagher.com/micro-clearfix-hack/)
@mixin pie-clearfix {
&:after {
content: "";
display: table;
clear: both;
}
@include has-layout;
}

View File

@ -65,7 +65,10 @@ module Compass
end
def remove(file_name)
if File.exists?(file_name)
if File.directory?(file_name)
FileUtils.rm_rf file_name
log_action :remove, basename(file_name)+"/", options
elsif File.exists?(file_name)
File.unlink file_name
log_action :remove, basename(file_name), options
end

View File

@ -4,7 +4,7 @@ end
require 'compass/commands/registry'
%w(base generate_grid_background default help list_frameworks project_base
update_project watch_project create_project imports installer_command
update_project watch_project create_project clean_project imports installer_command
print_version project_stats stamp_pattern sprite validate_project
write_configuration interactive unpack_extension).each do |lib|
require "compass/commands/#{lib}"

View File

@ -0,0 +1,79 @@
require 'compass/commands/project_base'
require 'compass/compiler'
module Compass
module Commands
module CleanProjectOptionsParser
def set_options(opts)
opts.banner = %Q{
Usage: compass clean [path/to/project] [options]
Description:
Remove generated files and the sass cache.
Options:
}.split("\n").map{|l| l.gsub(/^ */,'')}.join("\n")
super
end
end
class CleanProject < UpdateProject
register :clean
def initialize(working_path, options)
super
assert_project_directory_exists!
end
def perform
compiler = new_compiler_instance
compiler.clean!
Compass::SpriteImporter.find_all_sprite_map_files(Compass.configuration.images_path).each do |sprite|
remove sprite
end
end
def determine_cache_location
Compass.configuration.cache_path || Sass::Plugin.options[:cache_location] || File.join(working_path, ".sass-cache")
end
class << self
def option_parser(arguments)
parser = Compass::Exec::CommandOptionParser.new(arguments)
parser.extend(Compass::Exec::GlobalOptionsParser)
parser.extend(Compass::Exec::ProjectOptionsParser)
parser.extend(CleanProjectOptionsParser)
end
def usage
option_parser([]).to_s
end
def primary; true; end
def description(command)
"Remove generated files and the sass cache"
end
def parse!(arguments)
parser = option_parser(arguments)
parser.parse!
parse_arguments!(parser, arguments)
parser.options
end
def parse_arguments!(parser, arguments)
if arguments.size > 0
parser.options[:project_name] = arguments.shift if File.directory?(arguments.first)
unless arguments.empty?
parser.options[:sass_files] = arguments.dup
parser.options[:force] = true
end
end
end
end
end
end
end

View File

@ -16,8 +16,10 @@ module Compass::Commands
matching.first
elsif name =~ /^-/
nil
else
elsif matching.size > 1
raise Compass::Error, "Ambiguous abbreviation '#{name}'. Did you mean one of: #{matching.join(", ")}"
else
raise Compass::Error, "Command not found: #{name}"
end
end
def abbreviation?(name)

View File

@ -39,7 +39,7 @@ module Compass
def perform
relative_uri = options[:uri].gsub(/^#{Compass.configuration.images_dir}\//, '')
sprites = Compass::SpriteImporter.new(relative_uri, Compass.sass_engine_options)
sprites = Compass::SpriteImporter.new(:uri => relative_uri, :options => Compass.sass_engine_options)
options[:output_file] ||= File.join(Compass.configuration.sass_path, "sprites", "_#{sprites.name}.#{Compass.configuration.preferred_syntax}")
options[:skip_overrides] ||= false
contents = sprites.content_for_images(options[:skip_overrides])

View File

@ -51,12 +51,10 @@ module Compass
def new_compiler_instance(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]
compiler_opts = {:sass => Compass.sass_engine_options}
compiler_opts.merge!(options)
compiler_opts[:sass_files] = explicit_sass_files
compiler_opts[:cache_location] = determine_cache_location
compiler_opts
end

View File

@ -3,16 +3,19 @@ module Compass
include Actions
attr_accessor :working_path, :from, :to, :options, :staleness_checker, :importer
attr_accessor :working_path, :from, :to, :options, :sass_options, :staleness_checker, :importer
def initialize(working_path, from, to, options)
self.working_path = working_path
self.from, self.to = from.gsub('./', ''), to
self.logger = options.delete(:logger)
sass_opts = options.delete(:sass) || {}
self.options = options
self.options[:cache_location] ||= determine_cache_location
options[:importer] = self.importer = Sass::Importers::Filesystem.new(from)
self.staleness_checker = Sass::Plugin::StalenessChecker.new(options)
self.sass_options = options.dup
self.sass_options.update(sass_opts)
self.sass_options[:cache_location] ||= determine_cache_location
self.sass_options[:importer] = self.importer = Sass::Importers::Filesystem.new(from)
self.staleness_checker = Sass::Plugin::StalenessChecker.new(sass_options)
end
def determine_cache_location
@ -72,16 +75,16 @@ module Compass
end
def clean!
FileUtils.rm_rf options[:cache_location]
remove options[:cache_location]
css_files.each do |css_file|
FileUtils.rm_f css_file
remove css_file
end
end
def run
if new_config?
# Wipe out the cache and force compilation if the configuration has changed.
FileUtils.rm_rf options[:cache_location]
remove options[:cache_location]
options[:force] = true
end
@ -142,7 +145,7 @@ module Compass
# A sass engine for compiling a single file.
def engine(sass_filename, css_filename)
syntax = (sass_filename =~ /\.(s[ac]ss)$/) && $1.to_sym || :sass
opts = options.merge :filename => sass_filename, :css_filename => css_filename, :syntax => syntax
opts = sass_options.merge(:filename => sass_filename, :css_filename => css_filename, :syntax => syntax)
Sass::Engine.new(open(sass_filename).read, opts)
end

View File

@ -71,8 +71,10 @@ module Compass
end
# When called with a block, defines the cache buster strategy to be used.
# The block must return nil or a string that can be appended to a url as a query parameter.
# The returned string must not include the starting '?'.
# If the block returns nil or a string, then it is appended to the url as a query parameter.
# In this case, the returned string must not include the starting '?'.
# The block may also return a hash with :path and/or :query values and it
# will replace the original path and query string with the busted values returned.
# The block will be passed the root-relative url of the asset.
# If the block accepts two arguments, it will also be passed a File object
# that points to the asset on disk -- which may or may not exist.

View File

@ -13,6 +13,7 @@ module Compass::Exec
def run!
begin
perform!
return 0
rescue Exception => e
raise e if e.is_a? SystemExit
if e.is_a?(::Compass::Error) || e.is_a?(OptionParser::ParseError)
@ -22,7 +23,6 @@ module Compass::Exec
end
return 1
end
return 0
end
protected

View File

@ -91,9 +91,7 @@ module Compass::SassExtensions::Functions::Urls
if cache_buster.is_a?(Sass::Script::String)
path += "?#{cache_buster.value}"
else
if buster = compute_cache_buster(path, real_path)
path += "?#{buster}"
end
path = cache_busted_path(path, real_path)
end
end
@ -137,6 +135,23 @@ module Compass::SassExtensions::Functions::Urls
end
end
def cache_busted_path(path, real_path)
cache_buster = compute_cache_buster(path, real_path)
if cache_buster.nil?
return path
elsif cache_buster.is_a?(String)
cache_buster = {:query => cache_buster}
else
path = cache_buster[:path] if cache_buster[:path]
end
if cache_buster[:query]
"%s?%s" % [path, cache_buster[:query]]
else
path
end
end
def compute_cache_buster(path, real_path)
if Compass.configuration.asset_cache_buster
args = [path]

View File

@ -9,6 +9,8 @@ module Compass
end
require 'compass/sass_extensions/sprites/image'
require 'compass/sass_extensions/sprites/sprite_methods'
require 'compass/sass_extensions/sprites/image_methods'
require 'compass/sass_extensions/sprites/sprite_map'
require 'compass/sass_extensions/sprites/engines'

View File

@ -1 +1,25 @@
module Compass
module SassExtensions
module Sprites
class Engine
attr_accessor :width, :height, :images, :canvas
def initialize(width, height, images)
@width, @height, @images = width, height, images
@canvas = nil
end
def construct_sprite
raise ::Compass::Error, "You must impliment construct_sprite"
end
def save(filename)
raise ::Compass::Error, "You must impliment save(filename)"
end
end
end
end
end
require 'compass/sass_extensions/sprites/engines/chunky_png_engine'

View File

@ -7,20 +7,19 @@ end
module Compass
module SassExtensions
module Sprites
module ChunkyPngEngine
# Returns a PNG object
class ChunkyPngEngine < Compass::SassExtensions::Sprites::Engine
def construct_sprite
output_png = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT)
@canvas = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT)
images.each do |image|
input_png = ChunkyPNG::Image.from_file(image.file)
if image.repeat == "no-repeat"
output_png.replace! input_png, image.left, image.top
canvas.replace! input_png, image.left, image.top
else
x = image.left - (image.left / image.width).ceil * image.width
while x < width do
begin
output_png.replace! input_png, x, image.top
canvas.replace! input_png, x, image.top
x += image.width
rescue ChunkyPNG::OutOfBounds
break;
@ -28,8 +27,15 @@ module Compass
end
end
end
output_png
end
def save(filename)
if canvas.nil?
construct_sprite
end
canvas.save(filename, :best_compression)
end
end
end
end

View File

@ -0,0 +1,32 @@
module Compass
module SassExtensions
module Sprites
module ImageMethods
# Fetches the Sprite::Image object for the supplied name
def image_for(name)
@images.detect { |img| img.name == name}
end
# Returns true if the image name has a hover selector image
def has_hover?(name)
!image_for("#{name}_hover").nil?
end
# Returns true if the image name has a target selector image
def has_target?(name)
!image_for("#{name}_target").nil?
end
# Returns true if the image name has an active selector image
def has_active?(name)
!image_for("#{name}_active").nil?
end
# Return and array of image names that make up this sprite
def sprite_names
image_names.map { |f| File.basename(f, '.png') }
end
end
end
end
end

View File

@ -2,8 +2,13 @@ module Compass
module SassExtensions
module Sprites
class SpriteMap < Sass::Script::Literal
attr_accessor :image_names, :path, :name, :map, :kwargs
attr_accessor :images, :width, :height, :engine
include SpriteMethods
include ImageMethods
# Initialize a new sprite object from a relative file path
# the path is relative to the <tt>images_path</tt> confguration option
def self.from_uri(uri, context, kwargs)
@ -13,22 +18,8 @@ module Compass
end
new(sprites, importer.path, importer.name, context, kwargs)
end
# Loads the sprite engine
def require_engine!
self.class.send(:include, eval("::Compass::SassExtensions::Sprites::#{modulize}Engine"))
end
# Changing this string will invalidate all previously generated sprite images.
# We should do so only when the packing algorithm changes
SPRITE_VERSION = "1"
attr_accessor :image_names, :path, :name, :kwargs
attr_accessor :images, :width, :height
def initialize(sprites, path, name, context, kwargs)
require_engine!
@image_names = sprites
@path = path
@name = name
@ -37,150 +28,12 @@ module Compass
@images = nil
@width = nil
@height = nil
@engine = nil
@evaluation_context = context
validate!
compute_image_metadata!
end
# Calculate the size of the sprite
def size
[width, height]
end
# Calculates the overal image dimensions
# collects image sizes and input parameters for each sprite
# Calculates the height
def compute_image_metadata!
@width = 0
init_images
compute_image_positions!
@height = @images.last.top + @images.last.height
end
# Creates the Sprite::Image objects for each image and calculates the width
def init_images
@images = image_names.collect do |relative_file|
image = Compass::SassExtensions::Sprites::Image.new(self, relative_file, kwargs)
@width = [ @width, image.width + image.offset ].max
image
end
end
# Calculates the overal image dimensions
# collects image sizes and input parameters for each sprite
def compute_image_positions!
@images.each_with_index do |image, index|
image.left = image.position.unit_str == "%" ? (@width - image.width) * (image.position.value / 100) : image.position.value
next if index == 0
last_image = @images[index-1]
image.top = last_image.top + last_image.height + [image.spacing, last_image.spacing].max
end
end
# Fetches the Sprite::Image object for the supplied name
def image_for(name)
@images.detect { |img| img.name == name}
end
# Returns true if the image name has a hover selector image
def has_hover?(name)
!image_for("#{name}_hover").nil?
end
# Returns true if the image name has a target selector image
def has_target?(name)
!image_for("#{name}_target").nil?
end
# Returns true if the image name has an active selector image
def has_active?(name)
!image_for("#{name}_active").nil?
end
# Return and array of image names that make up this sprite
def sprite_names
image_names.map { |f| File.basename(f, '.png') }
end
# Validates that the sprite_names are valid sass
def validate!
for sprite_name in sprite_names
unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/
raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier"
end
end
end
# The on-the-disk filename of the sprite
def filename
File.join(Compass.configuration.images_path, "#{path}-s#{uniqueness_hash}.png")
end
# Generate a sprite image if necessary
def generate
if generation_required?
if kwargs.get_var('cleanup').value
cleanup_old_sprites
end
sprite_data = construct_sprite
save!(sprite_data)
Compass.configuration.run_callback(:sprite_generated, sprite_data)
end
end
def cleanup_old_sprites
Dir[File.join(Compass.configuration.images_path, "#{path}-*.png")].each do |file|
FileUtils.rm file
end
end
# Does this sprite need to be generated
def generation_required?
!File.exists?(filename) || outdated?
end
# Returns the uniqueness hash for this sprite object
def uniqueness_hash
@uniqueness_hash ||= begin
sum = Digest::MD5.new
sum << SPRITE_VERSION
sum << path
images.each do |image|
[:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr|
sum << image.send(attr).to_s
end
end
sum.hexdigest[0...10]
end
@uniqueness_hash
end
# Saves the sprite engine
def save!(output_png)
saved = output_png.save filename
Compass.configuration.run_callback(:sprite_saved, filename)
saved
end
# All the full-path filenames involved in this sprite
def image_filenames
@images.map(&:file)
end
# Checks whether this sprite is outdated
def outdated?
if File.exists?(filename)
return @images.map(&:mtime).any? { |imtime| imtime.to_i > self.mtime.to_i }
end
true
end
# Mtime of the sprite file
def mtime
@mtime ||= File.mtime(filename)
end
def inspect
to_s
end
@ -200,14 +53,14 @@ module Compass
super
end
end
private
def modulize
@modulize ||= Compass::configuration.sprite_engine.to_s.scan(/([^_.]+)/).flatten.map {|chunk| "#{chunk[0].chr.upcase}#{chunk[1..-1]}" }.join
end
end
end
end
end
end

View File

@ -0,0 +1,134 @@
module Compass
module SassExtensions
module Sprites
module SpriteMethods
# Changing this string will invalidate all previously generated sprite images.
# We should do so only when the packing algorithm changes
SPRITE_VERSION = "1"
# Calculates the overal image dimensions
# collects image sizes and input parameters for each sprite
# Calculates the height
def compute_image_metadata!
@width = 0
init_images
compute_image_positions!
@height = @images.last.top + @images.last.height
init_engine
end
def init_engine
@engine = eval("::Compass::SassExtensions::Sprites::#{modulize}Engine.new(nil, nil, nil)")
@engine.width = @width
@engine.height = @height
@engine.images = @images
end
# Creates the Sprite::Image objects for each image and calculates the width
def init_images
@images = image_names.collect do |relative_file|
image = Compass::SassExtensions::Sprites::Image.new(self, relative_file, kwargs)
@width = [ @width, image.width + image.offset ].max
image
end
end
# Calculates the overal image dimensions
# collects image sizes and input parameters for each sprite
def compute_image_positions!
@images.each_with_index do |image, index|
image.left = image.position.unit_str == "%" ? (@width - image.width) * (image.position.value / 100) : image.position.value
next if index == 0
last_image = @images[index-1]
image.top = last_image.top + last_image.height + [image.spacing, last_image.spacing].max
end
end
# Validates that the sprite_names are valid sass
def validate!
for sprite_name in sprite_names
unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/
raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier"
end
end
end
# The on-the-disk filename of the sprite
def filename
File.join(Compass.configuration.images_path, "#{path}-s#{uniqueness_hash}.png")
end
# Generate a sprite image if necessary
def generate
if generation_required?
if kwargs.get_var('cleanup').value
cleanup_old_sprites
end
engine.construct_sprite
Compass.configuration.run_callback(:sprite_generated, engine.canvas)
save!
end
end
def cleanup_old_sprites
Dir[File.join(Compass.configuration.images_path, "#{path}-*.png")].each do |file|
FileUtils.rm file
end
end
# Does this sprite need to be generated
def generation_required?
!File.exists?(filename) || outdated?
end
# Returns the uniqueness hash for this sprite object
def uniqueness_hash
@uniqueness_hash ||= begin
sum = Digest::MD5.new
sum << SPRITE_VERSION
sum << path
images.each do |image|
[:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr|
sum << image.send(attr).to_s
end
end
sum.hexdigest[0...10]
end
@uniqueness_hash
end
# Saves the sprite engine
def save!
saved = engine.save(filename)
Compass.configuration.run_callback(:sprite_saved, filename)
saved
end
# All the full-path filenames involved in this sprite
def image_filenames
@images.map(&:file)
end
# Checks whether this sprite is outdated
def outdated?
if File.exists?(filename)
return @images.map(&:mtime).any? { |imtime| imtime.to_i > self.mtime.to_i }
end
true
end
# Mtime of the sprite file
def mtime
@mtime ||= File.mtime(filename)
end
# Calculate the size of the sprite
def size
[width, height]
end
end
end
end
end

View File

@ -4,7 +4,14 @@ module Compass
VAILD_FILE_NAME = /\A#{Sass::SCSS::RX::IDENT}\Z/
SPRITE_IMPORTER_REGEX = %r{((.+/)?([^\*.]+))/(.+?)\.png}
VALID_EXTENSIONS = ['.png']
# finds all sprite files
def self.find_all_sprite_map_files(path)
hex = "[0-9a-f]"
glob = "*-{,s}#{hex*10}{#{VALID_EXTENSIONS.join(",")}}"
Dir.glob(File.join(path, "**", glob))
end
def self.load(uri, options)
klass = Compass::SpriteImporter.new
klass.uri, klass.options = uri, options
@ -31,7 +38,7 @@ module Compass
end
def to_s
content_for_images
self.class.name
end
def hash

View File

@ -1,9 +1,16 @@
begin
require 'rubygems'
require 'compass-validator'
rescue LoadError
raise Compass::MissingDependency, %Q{The Compass CSS Validator could not be loaded. Please install it:
rescue LoadError => e
if e.message =~ /core_ext/
raise Compass::MissingDependency, <<-ERRORMSG
The Compass CSS Validator is out of date. Please upgrade it:
sudo gem install compass-validator --version ">= 3.0.1"
ERRORMSG
else
raise Compass::MissingDependency, <<-ERRORMSG
The Compass CSS Validator could not be loaded. Please install it:
sudo gem install compass-validator
}
ERRORMSG
end
end

View File

@ -1,57 +0,0 @@
require 'spec_helper'
describe Compass::SassExtensions::Sprites::Base 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')
FileUtils.cp_r @images_src_path, @images_tmp_path
config = Compass::Configuration::Data.new('config')
config.images_path = @images_tmp_path
Compass.add_configuration(config)
Compass.configure_sass_plugin!
#fix this eww
options = Compass.sass_engine_options.extend Compass::SassExtensions::Functions::Sprites::VariableReader
@map = Compass::SpriteImporter.new("selectors/*.png", options)
@base = Compass::SassExtensions::Sprites::Base.new(@map.sprite_names.map{|n| "selectors/#{n}.png"}, @map.path, 'selectors', @map.sass_engine, @map.options)
end
after :each do
FileUtils.rm_r @images_tmp_path
end
subject { @base }
its(:size) { should == [10,40] }
its(:sprite_names) { should == @map.sprite_names }
its(:image_filenames) { should == Dir["#{@images_tmp_path}/selectors/*.png"].sort }
its(:generation_required?) { should be_true }
its(:uniqueness_hash) { should == 'ef52c5c63a'}
its(:outdated?) { should be_true }
its(:filename) { should == File.join(@images_tmp_path, "#{@base.path}-s#{@base.uniqueness_hash}.png")}
it "should return the 'ten-by-ten' image" do
subject.image_for('ten-by-ten').name.should == 'ten-by-ten'
subject.image_for('ten-by-ten').should be_a Compass::SassExtensions::Sprites::Image
end
%w(target hover active).each do |selector|
it "should have a #{selector}" do
subject.send(:"has_#{selector}?", 'ten-by-ten').should be_true
end
it "should return #{selector} image class" do
subject.image_for('ten-by-ten').send(:"#{selector}").name.should == "ten-by-ten_#{selector}"
end
end
context "#generate" do
before { @base.generate }
it "should generate sprite" do
File.exists?(@base.filename).should be_true
end
its(:generation_required?) { should be_false }
its(:outdated?) { should be_false }
end
end

View File

@ -1,161 +0,0 @@
require 'spec_helper'
require 'compass/sass_extensions/sprites/image'
describe Compass::SassExtensions::Sprites::Image do
let(:sprite_filename) { 'squares/ten-by-ten.png' }
let(:sprite_path) { File.join(images_src_path, sprite_filename) }
let(:sprite_name) { File.basename(sprite_filename, '.png') }
let(:parent) do
mock
end
before do
parent.stubs(:image_for).with('ten-by-ten').returns(image)
parent.stubs(:image_for).with('ten-by-ten_hover').returns(hover_image)
end
let(:image) { self.class.describes.new(parent, File.join(sprite_filename), options)}
let(:hover_image) { self.class.describes.new(parent, File.join('selectors/ten-by-ten_hover.png'), options)}
let(:digest) { Digest::MD5.file(sprite_path).hexdigest }
subject { image }
before {
file = StringIO.new("images_path = #{images_src_path.inspect}\n")
Compass.add_configuration(file, "sprite_config")
}
describe '#initialize' do
its(:name) { should == sprite_name }
its(:file) { should == sprite_path }
its(:relative_file) { should == sprite_filename }
its(:width) { should == 10 }
its(:height) { should == 10 }
its(:digest) { should == digest }
its(:top) { should == 0 }
its(:left) { should == 0 }
end
let(:get_var_expects) { nil }
let(:get_var_return) { nil }
let(:options) {
options = mock
options.stubs(:get_var).with(anything).returns(nil)
options.stubs(:get_var).with(get_var_expects).returns(get_var_return)
options
}
describe '#parent' do
context '_hover' do
subject { hover_image }
its(:parent) { should == image }
end
context 'no parent' do
subject { image }
its(:parent) { should be_nil }
end
end
describe '#repeat' do
let(:type) { nil }
let(:get_var_return) { OpenStruct.new(:value => type) }
context 'specific image' do
let(:type) { 'specific' }
let(:get_var_expects) { "#{sprite_name}-repeat" }
its(:repeat) { should == type }
end
context 'global' do
let(:type) { 'global' }
let(:get_var_expects) { 'repeat' }
its(:repeat) { should == type }
end
context 'default' do
let(:get_var_expects) { nil }
its(:repeat) { should == "no-repeat" }
end
end
describe '#position' do
let(:type) { nil }
let(:get_var_return) { type }
context 'specific image' do
let(:type) { 'specific' }
let(:get_var_expects) { "#{sprite_name}-position" }
its(:position) { should == type }
end
context 'global' do
let(:type) { 'global' }
let(:get_var_expects) { 'position' }
its(:position) { should == type }
end
context 'default' do
let(:get_var_expects) { nil }
its(:position) { should == Sass::Script::Number.new(0, ["px"]) }
end
end
describe '#spacing' do
let(:type) { nil }
let(:get_var_return) { OpenStruct.new(:value => type) }
context 'specific image' do
let(:type) { 'specific' }
let(:get_var_expects) { "#{sprite_name}-spacing" }
its(:spacing) { should == type }
end
context 'global' do
let(:type) { 'global' }
let(:get_var_expects) { 'spacing' }
its(:spacing) { should == type }
end
context 'default' do
let(:get_var_expects) { nil }
its(:spacing) { should == Sass::Script::Number.new(0).value }
end
end
describe '#offset' do
before { image.stubs(:position).returns(stub_position) }
let(:offset) { 100 }
let(:stub_position) {
stub(:value => offset)
}
context 'unitless' do
before { stub_position.stubs(:unitless?).returns(true) }
before { stub_position.stubs(:unit_str).returns('em') }
its(:offset) { should == offset }
end
context 'pixels' do
before { stub_position.stubs(:unitless?).returns(false) }
before { stub_position.stubs(:unit_str).returns('px') }
its(:offset) { should == offset }
end
context 'neither, use 0' do
before { stub_position.stubs(:unitless?).returns(false) }
before { stub_position.stubs(:unit_str).returns('em') }
its(:offset) { should == 0 }
end
end
end

View File

@ -1,54 +0,0 @@
require 'spec_helper'
require 'fakefs/spec_helpers'
require 'timecop'
describe Compass::SpriteImporter do
include FakeFS::SpecHelpers
let(:sprite_map) { self.class.describes.new(uri, options) }
let(:options) { { :test => :test2 } }
subject { sprite_map }
let(:path) { 'path' }
let(:dir) { "dir/#{name}" }
let(:name) { 'subdir' }
let(:sprite_path) { File.join(path, dir) }
let(:files) { (1..3).collect { |i| File.join(sprite_path, "#{i}.png") } }
let(:expanded_files) { files.collect { |file| File.expand_path(file) } }
let(:configuration) { stub(:images_path => path) }
let(:mtime) { Time.now - 30 }
before {
Compass.stubs(:configuration).returns(configuration)
FileUtils.mkdir_p(sprite_path)
Timecop.freeze(mtime) do
files.each { |file| File.open(file, 'w') }
end
Timecop.return
}
describe '#initialize' do
let(:uri) { 'dir/subdir/*.png' }
its(:uri) { should == uri }
its(:path) { should == dir }
its(:name) { should == name }
its(:files) { should == expanded_files }
its(:sass_options) { should == options.merge(:filename => name, :syntax => :scss, :importer => sprite_map) }
it "should have a correct mtime" do
sprite_map.mtime(uri, subject.sass_options).should == mtime
end
it "should have a test for the sass engine" do
pending 'sass'
end
end
end

View File

@ -1,5 +0,0 @@
require 'spec_helper'
require 'fakefs/spec_helpers'
describe Compass::Sprites do
end

View File

@ -1,37 +0,0 @@
$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'rubygems'
require 'compass'
require 'rspec'
require 'rspec/autorun'
require 'mocha'
module CompassGlobalInclude
class << self
def included(klass)
klass.instance_eval do
let(:images_src_path) { File.join(File.dirname(__FILE__), 'test_project', 'public', 'images') }
end
end
end
end
module CompassSpriteHelpers
def create_sprite_temp
::FileUtils.cp_r @images_src_path, @images_tmp_path
end
def clean_up_sprites
::FileUtils.rm_r @images_tmp_path
end
end
RSpec.configure do |config|
config.include(CompassGlobalInclude)
config.include(CompassSpriteHelpers)
config.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')
end
config.mock_with :mocha
end

View File

@ -1,571 +0,0 @@
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
require 'digest/md5'
describe Compass::Sprites do
before :each do
create_sprite_temp
file = StringIO.new("images_path = #{@images_tmp_path.inspect}\n")
Compass.add_configuration(file, "sprite_config")
Compass.configure_sass_plugin!
end
after :each do
clean_up_sprites
end
def map_location(file)
Dir.glob(File.join(@images_tmp_path, file)).first
end
def image_size(file)
IO.read(map_location(file))[0x10..0x18].unpack('NN')
end
def image_md5(file)
md5 = Digest::MD5.new
md5.update IO.read(map_location(file))
md5.hexdigest
end
def render(scss)
scss = %Q(@import "compass"; #{scss})
options = Compass.sass_engine_options
options[:line_comments] = false
options[:style] = :expanded
options[:syntax] = :scss
css = Sass::Engine.new(scss, options).render
# reformat to fit result of heredoc:
" #{css.gsub('@charset "UTF-8";', '').gsub(/\n/, "\n ").strip}\n"
end
#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";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-161c60ad78.png') no-repeat;
}
.squares-ten-by-ten {
background-position: 0 0;
}
.squares-twenty-by-twenty {
background-position: 0 -10px;
}
CSS
image_size('squares-*.png').should == [20, 30]
image_md5('squares-*.png').should == 'fcc93d7b279c2ad6898fbca49cbd01e1'
end
it "should generate sprite classes with dimensions" do
css = render <<-SCSS
$squares-sprite-dimensions: true;
@import "squares/*.png";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-161c60ad78.png') no-repeat;
}
.squares-ten-by-ten {
background-position: 0 0;
height: 10px;
width: 10px;
}
.squares-twenty-by-twenty {
background-position: 0 -10px;
height: 20px;
width: 20px;
}
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);
}
SCSS
css.should == <<-CSS
.squares-sprite, .cubicle, .large-cube {
background: url('/squares-161c60ad78.png') no-repeat;
}
.cubicle {
background-position: 0 0;
}
.large-cube {
background-position: 0 -10px;
height: 20px;
width: 20px;
}
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";
@import "squares/*.png";
SCSS
css.should == <<-CSS
.circles {
background: url('/squares-161c60ad78.png') no-repeat;
}
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;
@import "squares/*.png";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-89450808af.png') no-repeat;
}
.squares-ten-by-ten {
background-position: 0 0;
}
.squares-twenty-by-twenty {
background-position: 0 -43px;
}
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;
@import "squares/*.png";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-673837183a.png') no-repeat;
}
.squares-ten-by-ten {
background-position: 0 0;
}
.squares-twenty-by-twenty {
background-position: 0 -43px;
}
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;
$squares-twenty-by-twenty-spacing: 33px;
@import "squares/*.png";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-1cd84c9068.png') no-repeat;
}
.squares-ten-by-ten {
background-position: 0 0;
}
.squares-twenty-by-twenty {
background-position: 0 -54px;
}
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;
$squares-twenty-by-twenty-spacing: 44px;
@import "squares/*.png";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-f25b7090ca.png') no-repeat;
}
.squares-ten-by-ten {
background-position: 0 0;
}
.squares-twenty-by-twenty {
background-position: 0 -54px;
}
CSS
image_size('squares-*.png').should == [20, 74]
end
it "should calculate the default spacing between images" do
css = render <<-SCSS
$squares-spacing: 22px;
@import "squares/*.png";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-d66bf24bab.png') no-repeat;
}
.squares-ten-by-ten {
background-position: 0 0;
}
.squares-twenty-by-twenty {
background-position: 0 -32px;
}
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);
}
SCSS
css.should == <<-CSS
.squares-sprite {
background: url('/squares-8e490168dd.png') no-repeat;
}
.adjusted-percentage {
background-position: 100% 0;
}
.adjusted-px-1 {
background-position: -6px 0;
}
.adjusted-px-2 {
background-position: -3px -8px;
}
CSS
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);
}
SCSS
css.should == <<-CSS
.squares-sprite, .adjusted-percentage, .adjusted-px-1, .adjusted-px-2 {
background: url('/squares-8e490168dd.png') no-repeat;
}
.adjusted-percentage {
background-position: 100% 0;
}
.adjusted-px-1 {
background-position: -6px 0;
}
.adjusted-px-2 {
background-position: -3px -8px;
}
CSS
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;
@import "squares/*.png";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-a5550fd132.png') no-repeat;
}
.squares-ten-by-ten {
background-position: 0 0;
}
.squares-twenty-by-twenty {
background-position: 0 -10px;
}
CSS
image_size('squares-*.png').should == [20, 30]
image_md5('squares-*.png').should == '94abae8440f1b58617f52920b70aaed2'
end
it "should allow the position of a sprite to be specified in absolute pixels" do
css = render <<-SCSS
$squares-ten-by-ten-position: 10px;
$squares-twenty-by-twenty-position: 10px;
@import "squares/*.png";
@include all-squares-sprites;
SCSS
css.should == <<-CSS
.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty {
background: url('/squares-89a274044e.png') no-repeat;
}
.squares-ten-by-ten {
background-position: -10px 0;
}
.squares-twenty-by-twenty {
background-position: -10px -10px;
}
CSS
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
.squares {
background: sprite-url("squares/*.png") no-repeat;
}
SCSS
end.should raise_error Sass::SyntaxError,
%q(The first argument to sprite-url() must be a sprite map. See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
proc do
render <<-SCSS
.squares {
background: sprite-image("squares/twenty-by-twenty.png") no-repeat;
}
SCSS
end.should raise_error Sass::SyntaxError,
%q(The sprite-image() function has been replaced by sprite(). See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
proc do
render <<-SCSS
@import "squares/*.png";
.squares {
background: sprite-position("squares/twenty-by-twenty.png") no-repeat;
}
SCSS
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 {
background: sprite(sprite-map("squares/*.png"), twenty-by-twenty) no-repeat;
}
SCSS
actual_css.should == <<-CSS
.squares {
background: url('/squares-145869726f.png') 0 -10px no-repeat;
}
CSS
end
it "should calculate corret sprite demsions when givin spacing via issue#253" do
css = render <<-SCSS
$squares-spacing: 10px;
@import "squares/*.png";
.foo {
@include sprite-background-position($squares-sprites, "twenty-by-twenty");
}
.bar {
@include sprite-background-position($squares-sprites, "ten-by-ten");
}
SCSS
image_size('squares-*.png').should == [20, 40]
css.should == <<-CSS
.squares-sprite {
background: url('/squares-e3c68372d9.png') no-repeat;
}
.foo {
background-position: 0 -20px;
}
.bar {
background-position: 0 0;
}
CSS
end
it "should render corret sprite with css selectors via issue#248" do
css = render <<-SCSS
@import "selectors/*.png";
@include all-selectors-sprites;
SCSS
css.should == <<-CSS
.selectors-sprite, .selectors-ten-by-ten {
background: url('/selectors-edfef809e2.png') no-repeat;
}
.selectors-ten-by-ten {
background-position: 0 0;
}
.selectors-ten-by-ten:hover, .selectors-ten-by-ten.ten-by-ten_hover, .selectors-ten-by-ten.ten-by-ten-hover {
background-position: 0 -20px;
}
.selectors-ten-by-ten:target, .selectors-ten-by-ten.ten-by-ten_target, .selectors-ten-by-ten.ten-by-ten-target {
background-position: 0 -30px;
}
.selectors-ten-by-ten:active, .selectors-ten-by-ten.ten-by-ten_active, .selectors-ten-by-ten.ten-by-ten-active {
background-position: 0 -10px;
}
CSS
end
it "should render corret sprite with css selectors via magic mixin" do
css = render <<-SCSS
@import "selectors/*.png";
a {
@include selectors-sprite(ten-by-ten)
}
SCSS
css.should == <<-CSS
.selectors-sprite, a {
background: url('/selectors-edfef809e2.png') no-repeat;
}
a {
background-position: 0 0;
}
a:hover, a.ten-by-ten_hover, a.ten-by-ten-hover {
background-position: 0 -20px;
}
a:target, a.ten-by-ten_target, a.ten-by-ten-target {
background-position: 0 -30px;
}
a:active, a.ten-by-ten_active, a.ten-by-ten-active {
background-position: 0 -10px;
}
CSS
end
it "should not render corret sprite with css selectors via magic mixin" do
css = render <<-SCSS
@import "selectors/*.png";
a {
$disable-magic-sprite-selectors:true;
@include selectors-sprite(ten-by-ten)
}
SCSS
css.should == <<-CSS
.selectors-sprite, a {
background: url('/selectors-edfef809e2.png') no-repeat;
}
a {
background-position: 0 0;
}
CSS
end
it "should raise error on filenames that are not valid sass syntax" do
lambda do
render <<-SCSS
@import "prefix/*.png";
a {
@include squares-sprite(20-by-20);
}
SCSS
end.should raise_error Compass::Error
end
it "should generate sprite with bad repeat-x dimensions" do
css = render <<-SCSS
$ko-starbg26x27-repeat: repeat-x;
@import "ko/*.png";
@include all-ko-sprites;
SCSS
css.should == <<-CSS
.ko-sprite, .ko-default_background, .ko-starbg26x27 {
background: url('/ko-cc3f80660d.png') no-repeat;
}
.ko-default_background {
background-position: 0 0;
}
.ko-starbg26x27 {
background-position: 0 -128px;
}
CSS
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,29 @@
# Require any additional compass plugins here.
project_type = :stand_alone
css_dir = "tmp"
sass_dir = "sass"
images_dir = "images"
output_style = :compact
# To enable relative image paths using the images_url() function:
# http_images_path = :relative
http_images_path = "/images"
line_comments = false
asset_cache_buster do |path, file|
pathname = Pathname.new(path)
case pathname.basename(pathname.extname).to_s
when "grid"
new_path = "%s/%s-BUSTED%s" % [pathname.dirname, pathname.basename(pathname.extname), pathname.extname]
{:path => new_path, :query => nil}
when "feed"
"query_string"
when "dk"
{:query => "query_string"}
end
end
asset_host do |path|
"http://assets%d.example.com" % (path.size % 4)
end

View File

@ -0,0 +1,9 @@
.showgrid { background-image: url('http://assets0.example.com/images/grid-BUSTED.png'); }
.inlinegrid { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAUEAYAAACv1qP4AAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAAZ0lEQVRYw+3QwQ2AIBAFUTEUwI3+uzN7gDscsIgxEuO8An52J11X73OudfxMraXkzHfO3Y98nQEhA0IGhAwIGRAyIGRAyICQASEDQgaEDAgZEDIgZEDIgJABoZzSGK3tPuN9ERFP7Nw4fg+c5g8V1wAAAABJRU5ErkJggg=='); }
.no-buster { background-image: url('http://assets0.example.com/images/grid.png'); }
.feed { background-image: url('http://assets0.example.com/images/feed.png?query_string'); }
.dk { background-image: url('http://assets0.example.com/images/flags/dk.png?query_string'); }

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

View File

@ -0,0 +1,14 @@
.showgrid
background-image: image-url("grid.png")
.inlinegrid
background-image: inline-image("grid.png")
.no-buster
background-image: image-url("grid.png", $only-path: false, $cache-buster: false)
.feed
background-image: image-url("feed.png")
.dk
background-image: image-url("flags/dk.png")

View File

@ -15,3 +15,12 @@
visibility: hidden; }
.pie-clearfix {
display: block; }
.simplified-pie-clearfix {
display: inline-block; }
.simplified-pie-clearfix:after {
content: "";
display: table;
clear: both; }
.simplified-pie-clearfix {
display: block; }

View File

@ -12,6 +12,13 @@
overflow: hidden;
visibility: hidden; }
.simple-pie-clearfix {
*zoom: 1; }
.simple-pie-clearfix:after {
content: "";
display: table;
clear: both; }
p.light {
background-color: #b0201e;
color: black; }

View File

@ -7,5 +7,8 @@ $default-has-layout-approach: block;
}
.pie-clearfix {
@include legacy-pie-clearfix;
}
.simplified-pie-clearfix {
@include pie-clearfix;
}

View File

@ -5,7 +5,10 @@
}
.pie-clearfix {
@include pie-clearfix;
@include legacy-pie-clearfix;
}
.simple-pie-clearfix {
@include pie-clearfix;
}
p.light { @include contrasted(#B0201E); }

View File

@ -73,6 +73,17 @@ class CompassTest < Test::Unit::TestCase
end
end
def test_busted_image_urls
within_project('busted_image_urls') do |proj|
each_css_file(proj.css_path) do |css_file|
assert_no_errors css_file, 'busted_image_urls'
end
each_sass_file do |sass_file|
assert_renders_correctly sass_file
end
end
end
def test_image_urls
within_project('image_urls') do |proj|
each_css_file(proj.css_path) do |css_file|

View File

@ -71,7 +71,7 @@ class SpritesTest < Test::Unit::TestCase
}
CSS
assert_equal image_size('squares-s*.png'), [20, 30]
assert_equal image_md5('squares-s*.png'), 'fcc93d7b279c2ad6898fbca49cbd01e1'
assert_equal image_md5('squares-s*.png'), '7349a0f4e88ea80abddcf6ac2486abe3'
end
it "should generate sprite classes with dimensions" do
@ -294,7 +294,7 @@ class SpritesTest < Test::Unit::TestCase
}
CSS
assert_equal image_size('squares-s*.png'), [20, 30]
assert_equal image_md5('squares-s*.png'), '652b67f5e9092520d6f26caae7e18012'
assert_equal image_md5('squares-s*.png'), '9cc7ce48cfaf304381c2d08adefd2fb6'
end
it "should use position adjustments in mixins" do
@ -332,7 +332,7 @@ class SpritesTest < Test::Unit::TestCase
}
CSS
assert_equal image_size('squares-s*.png'), [20, 30]
assert_equal image_md5('squares-s*.png'), '652b67f5e9092520d6f26caae7e18012'
assert_equal image_md5('squares-s*.png'), '9cc7ce48cfaf304381c2d08adefd2fb6'
end
it "should repeat the image" do
@ -355,7 +355,7 @@ class SpritesTest < Test::Unit::TestCase
}
CSS
assert_equal image_size('squares-s*.png'), [20, 30]
assert_equal image_md5('squares-s*.png'), '94abae8440f1b58617f52920b70aaed2'
assert_equal image_md5('squares-s*.png'), 'a77a2fd43f04d791722b706aa7c9f1c1'
end
it "should allow the position of a sprite to be specified in absolute pixels" do
@ -379,7 +379,7 @@ class SpritesTest < Test::Unit::TestCase
}
CSS
assert_equal image_size('squares-s*.png'), [30, 30]
assert_equal image_md5('squares-s*.png'), '2fb19ef9c83018c93c6f147af3a56cb2'
assert_equal image_md5('squares-s*.png'), '9856ced9e8211b6b28ff782019a0d905'
end
it "should provide a nice errors for lemonade's old users" do

View File

@ -0,0 +1,43 @@
require 'test_helper'
class EngineTest < Test::Unit::TestCase
def setup
sprite_filename = 'squares/ten-by-ten.png'
@images = [
Compass::SassExtensions::Sprites::Image.new(nil, File.join(sprite_filename), {})
]
@engine = Compass::SassExtensions::Sprites::Engine.new(100, 100, @images)
end
test "should have width of 100" do
assert_equal 100, @engine.width
end
test "should have height of 100" do
assert_equal 100, @engine.height
end
test "should have correct images" do
assert_equal @images, @engine.images
end
test "raises Compass::Error when calling save" do
begin
@engine.save('foo')
assert false, '#save did not raise an exception'
rescue Compass::Error
assert true
end
end
test "raises Compass::Error when calling construct_sprite" do
begin
@engine.construct_sprite
assert false, '#construct_sprite did not raise an exception'
rescue Compass::Error
assert true
end
end
end

View File

@ -9,6 +9,10 @@ class ImporterTest < Test::Unit::TestCase
Compass.add_configuration(file, "sprite_config")
@importer = Compass::SpriteImporter.new(:uri => URI, :options => options)
end
def teardown
Compass.reset_configuration!
end
def options
{:foo => 'bar'}
@ -46,21 +50,17 @@ class ImporterTest < Test::Unit::TestCase
assert_equal 'bar', @importer.sass_options[:foo]
end
test "should fail givin bad sprite extensions" do
test "should fail given bad sprite extensions" do
@images_src_path = File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'sprites', 'public', 'images')
file = StringIO.new("images_path = #{@images_src_path.inspect}\n")
Compass.add_configuration(file, "sprite_config")
importer = Compass::SpriteImporter.new(:uri => 'bad_extensions/*.jpg', :options => options)
begin
importer.sass_engine
assert false, "Somthing happened an invalid sprite file made it past validation"
assert false, "An invalid sprite file made it past validation."
rescue Compass::Error => e
assert e.message.include?('.png')
end
end
def taredown
Compass.reset_configuration!
end
end

View File

@ -1,32 +1,11 @@
require 'spec_helper'
require 'compass/commands'
require 'compass/exec'
require 'compass/commands/sprite'
describe Compass::Commands::Sprite do
def config_data
return <<-CONFIG
images_path = #{@images_tmp_path.inspect}
CONFIG
end
require 'test_helper'
class SpriteCommandTest < Test::Unit::TestCase
attr_reader :test_dir
def create_temp_cli_dir
directory = File.join(File.expand_path('../', __FILE__), 'test')
::FileUtils.mkdir_p directory
@test_dir = directory
end
def run_compass_with_options(options)
output = 'foo'
::Dir.chdir @test_dir
%x{compass #{options.join(' ')}}
end
def options_to_cli(options)
options.map.flatten!
end
let(:test_dir) { @test_dir }
before :each do
def setup
@images_src_path = File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'sprites', 'public', 'images')
@images_tmp_path = File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'sprites', 'public', 'images-tmp')
@before_dir = ::Dir.pwd
create_temp_cli_dir
create_sprite_temp
@ -34,22 +13,48 @@ describe Compass::Commands::Sprite do
f << config_data
end
end
after :each do
def create_sprite_temp
::FileUtils.cp_r @images_src_path, @images_tmp_path
end
def clean_up_sprites
::FileUtils.rm_r @images_tmp_path
end
def config_data
return <<-CONFIG
images_path = #{@images_tmp_path.inspect}
CONFIG
end
def create_temp_cli_dir
directory = File.join(File.expand_path('../', __FILE__), 'test')
::FileUtils.mkdir_p directory
@test_dir = directory
end
def run_compass_with_options(options)
output = 'foo'
::Dir.chdir @test_dir
%x{compass #{options.join(' ')}}
end
def options_to_cli(options)
options.map.flatten!
end
def teardown
::Dir.chdir @before_dir
clean_up_sprites
if File.exists?(@test_dir)
::FileUtils.rm_r @test_dir
end
end
it "should create sprite file" do
run_compass_with_options(['sprite', "-f", "stylesheet.scss", "'#{@images_tmp_path}/*.png'"]).to_i.should == 0
File.exists?(File.join(test_dir, 'stylesheet.scss')).should be_true
assert_equal 0, run_compass_with_options(['sprite', "-f", 'stylesheet.scss', "'#{@images_tmp_path}/*.png'"]).to_i
assert File.exists?(File.join(test_dir, 'stylesheet.scss'))
end
it "should fail gracfuly when giving bad arguments" do
pending
end
end