merged version.yml
@ -4,4 +4,4 @@ rvm:
|
||||
- jruby
|
||||
- rbx
|
||||
- ree
|
||||
script: "bundle exec rake test"
|
||||
script: "bundle exec rake test features"
|
||||
|
2
Gemfile
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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}"
|
||||
|
79
lib/compass/commands/clean_project.rb
Normal 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
|
@ -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)
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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'
|
@ -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
|
||||
|
32
lib/compass/sass_extensions/sprites/image_methods.rb
Normal 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
|
@ -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
|
134
lib/compass/sass_extensions/sprites/sprite_methods.rb
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -1,5 +0,0 @@
|
||||
require 'spec_helper'
|
||||
require 'fakefs/spec_helpers'
|
||||
|
||||
describe Compass::Sprites do
|
||||
end
|
@ -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
|
@ -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
|
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB |
29
test/fixtures/stylesheets/busted_image_urls/config.rb
vendored
Normal 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
|
9
test/fixtures/stylesheets/busted_image_urls/css/screen.css
vendored
Normal 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'); }
|
BIN
test/fixtures/stylesheets/busted_image_urls/images/feed.png
vendored
Normal file
After Width: | Height: | Size: 691 B |
BIN
test/fixtures/stylesheets/busted_image_urls/images/flags/dk.png
vendored
Normal file
After Width: | Height: | Size: 808 B |
BIN
test/fixtures/stylesheets/busted_image_urls/images/grid.png
vendored
Normal file
After Width: | Height: | Size: 199 B |
14
test/fixtures/stylesheets/busted_image_urls/sass/screen.sass
vendored
Normal 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")
|
@ -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; }
|
||||
|
@ -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; }
|
||||
|
@ -7,5 +7,8 @@ $default-has-layout-approach: block;
|
||||
}
|
||||
|
||||
.pie-clearfix {
|
||||
@include legacy-pie-clearfix;
|
||||
}
|
||||
.simplified-pie-clearfix {
|
||||
@include pie-clearfix;
|
||||
}
|
||||
|
@ -5,7 +5,10 @@
|
||||
}
|
||||
|
||||
.pie-clearfix {
|
||||
@include pie-clearfix;
|
||||
@include legacy-pie-clearfix;
|
||||
}
|
||||
.simple-pie-clearfix {
|
||||
@include pie-clearfix;
|
||||
}
|
||||
|
||||
p.light { @include contrasted(#B0201E); }
|
||||
|
@ -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|
|
||||
|
@ -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
|
||||
|
43
test/units/sprites/engine_test.rb
Normal 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
|
@ -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
|
@ -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
|