Merge branch 'master' into rails31

* master: (23 commits)
  updated change log
  fixed horizontal height and width calculation
  horizontal layout now respects positions and spacing correctly
  Changed the descriptions of the sin, cos, and tan to be more descriptive.
  derp double require
  smart pack docs - forgot something
  smart pack docs
  smart packing implimentation thanks @johnbintz
  tests for smart packing
  smart packing helper classes
  new tests
  docs for diagonal layout
  layout for diagonal sprite
  added test for diagonal layout
  fixed trig functions closes #498
  patched image functions to accept a real path
  Fix failing test for change to http_path in rails
  Failing test for changing http_path with rails
  reverted sorting back to one liner
  more importer tests
  ...

Conflicts:
	Gemfile.lock
This commit is contained in:
Chris Eppstein 2011-08-19 14:42:42 -07:00
commit 000e4e1ed7
30 changed files with 631 additions and 157 deletions

View File

@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
compass (0.12.0.alpha.0.33263ca) compass (0.12.0.alpha.0.91a748a)
chunky_png (~> 1.2) chunky_png (~> 1.2)
fssm (>= 0.2.7) fssm (>= 0.2.7)
sass (~> 3.1) sass (~> 3.1)
@ -10,12 +10,12 @@ GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
abstract (1.0.0) abstract (1.0.0)
actionmailer (3.0.9) actionmailer (3.0.10)
actionpack (= 3.0.9) actionpack (= 3.0.10)
mail (~> 2.2.19) mail (~> 2.2.19)
actionpack (3.0.9) actionpack (3.0.10)
activemodel (= 3.0.9) activemodel (= 3.0.10)
activesupport (= 3.0.9) activesupport (= 3.0.10)
builder (~> 2.1.2) builder (~> 2.1.2)
erubis (~> 2.6.6) erubis (~> 2.6.6)
i18n (~> 0.5.0) i18n (~> 0.5.0)
@ -23,23 +23,23 @@ GEM
rack-mount (~> 0.6.14) rack-mount (~> 0.6.14)
rack-test (~> 0.5.7) rack-test (~> 0.5.7)
tzinfo (~> 0.3.23) tzinfo (~> 0.3.23)
activemodel (3.0.9) activemodel (3.0.10)
activesupport (= 3.0.9) activesupport (= 3.0.10)
builder (~> 2.1.2) builder (~> 2.1.2)
i18n (~> 0.5.0) i18n (~> 0.5.0)
activerecord (3.0.9) activerecord (3.0.10)
activemodel (= 3.0.9) activemodel (= 3.0.10)
activesupport (= 3.0.9) activesupport (= 3.0.10)
arel (~> 2.0.10) arel (~> 2.0.10)
tzinfo (~> 0.3.23) tzinfo (~> 0.3.23)
activeresource (3.0.9) activeresource (3.0.10)
activemodel (= 3.0.9) activemodel (= 3.0.10)
activesupport (= 3.0.9) activesupport (= 3.0.10)
activesupport (3.0.9) activesupport (3.0.10)
addressable (2.2.6) addressable (2.2.6)
arel (2.0.10) arel (2.0.10)
builder (2.1.2) builder (2.1.2)
chunky_png (1.2.0) chunky_png (1.2.1)
compass-validator (3.0.1) compass-validator (3.0.1)
css_parser (1.0.1) css_parser (1.0.1)
cucumber (0.9.4) cucumber (0.9.4)
@ -50,7 +50,7 @@ GEM
term-ansicolor (~> 1.0.5) term-ansicolor (~> 1.0.5)
diff-lcs (1.1.2) diff-lcs (1.1.2)
em-dir-watcher (0.9.4) em-dir-watcher (0.9.4)
em-websocket (0.3.0) em-websocket (0.3.1)
addressable (>= 2.1.1) addressable (>= 2.1.1)
eventmachine (>= 0.12.9) eventmachine (>= 0.12.9)
erubis (2.6.6) erubis (2.6.6)
@ -74,29 +74,29 @@ GEM
treetop (~> 1.4.8) treetop (~> 1.4.8)
mime-types (1.16) mime-types (1.16)
mocha (0.9.12) mocha (0.9.12)
polyglot (0.3.1) polyglot (0.3.2)
rack (1.2.3) rack (1.2.3)
rack-mount (0.6.14) rack-mount (0.6.14)
rack (>= 1.0.0) rack (>= 1.0.0)
rack-test (0.5.7) rack-test (0.5.7)
rack (>= 1.0) rack (>= 1.0)
rails (3.0.9) rails (3.0.10)
actionmailer (= 3.0.9) actionmailer (= 3.0.10)
actionpack (= 3.0.9) actionpack (= 3.0.10)
activerecord (= 3.0.9) activerecord (= 3.0.10)
activeresource (= 3.0.9) activeresource (= 3.0.10)
activesupport (= 3.0.9) activesupport (= 3.0.10)
bundler (~> 1.0) bundler (~> 1.0)
railties (= 3.0.9) railties (= 3.0.10)
railties (3.0.9) railties (3.0.10)
actionpack (= 3.0.9) actionpack (= 3.0.10)
activesupport (= 3.0.9) activesupport (= 3.0.10)
rake (>= 0.8.7) rake (>= 0.8.7)
rdoc (~> 3.4) rdoc (~> 3.4)
thor (~> 0.14.4) thor (~> 0.14.4)
rake (0.8.7) rake (0.8.7)
rcov (0.9.9) rcov (0.9.10)
rdoc (3.8) rdoc (3.9.2)
rspec (2.0.1) rspec (2.0.1)
rspec-core (~> 2.0.1) rspec-core (~> 2.0.1)
rspec-expectations (~> 2.0.1) rspec-expectations (~> 2.0.1)
@ -110,11 +110,12 @@ GEM
ruby-json (1.1.2) ruby-json (1.1.2)
ruby-prof (0.10.8) ruby-prof (0.10.8)
rubyzip (0.9.4) rubyzip (0.9.4)
sass (3.1.5) sass (3.1.7)
term-ansicolor (1.0.6) term-ansicolor (1.0.6)
thor (0.14.6) thor (0.14.6)
timecop (0.3.5) timecop (0.3.5)
treetop (1.4.9) treetop (1.4.10)
polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
tzinfo (0.3.29) tzinfo (0.3.29)

View File

@ -14,6 +14,14 @@ The Documentation for the [latest stable release](http://compass-style.org/docs/
The Documentation for the [latest preview release](http://beta.compass-style.org/) The Documentation for the [latest preview release](http://beta.compass-style.org/)
0.12.0 pending
-------------------
* Added support for diagonal, horizontal, and smart sprite layout
* Fixed a bug with spacing in horizontal layout
* Changed the descriptions of the sin, cos, and tan to be more descriptive
* Fixed trig functions via issue #498
* Fixed the default `http_path` in rails
* Sprites can now have a `sprite_seach_path` that is an array of directories that contain source images for sprites handy for using sprites in extensions of gems
0.11.5 (07/10/2011) 0.11.5 (07/10/2011)
------------------- -------------------

View File

@ -55,11 +55,21 @@ is located within it.
<a name="layout-control"></a> <a name="layout-control"></a>
## Layout Control ## Layout Control
If you prefer horizontal sprites to the `vertical` default just set the magic variable `$<sprite>-layout` variable. Set the `$<sprite>-layout` variable to the preferred layout method.
* vertical - default
* horizontal - lays images out side by side
* diagonal - lays images out corner to corner none of the spacing of padding options are taken into account
* smart - lays the images out using a bin packing algorithm which allows for much smaller surface areas in your sprite files - does not support spacing or padding
Example:
$icon-layout:horizontal; $icon-layout:horizontal;
@import "icon/*.png"; @import "icon/*.png";
$dropcap-layout:diagonal
@import "dropcap/*.png";
<a name="selector-control"></a> <a name="selector-control"></a>
## Selector Control ## Selector Control

View File

@ -35,8 +35,9 @@ documented_functions:
sin($number) sin($number)
.details .details
%p %p
Takes the sine of a number. If the number is unitless or has a unit of <code>deg</code> Returns the sine of a number. If the number is unitless or has a unit of <code>deg</code>
then it will return a unitless result. Degrees will first be converted to radians. then it will return a unitless result. Unless the number has a unit of <code>deg</code> it
will be evaluated as radians. Degrees will first be converted to radians.
If the number is any other unit, the units will be passed thru to the result, If the number is any other unit, the units will be passed thru to the result,
and the number will be treated as radians. and the number will be treated as radians.
@ -46,8 +47,9 @@ documented_functions:
cos($number) cos($number)
.details .details
%p %p
Takes the cosine of a number. If the number is unitless or has a unit of <code>deg</code> Returns the cosine of a number. If the number is unitless or has a unit of <code>deg</code>
then it will return a unitless result. Degrees will first be converted to radians. then it will return a unitless result. Unless the number has a unit of <code>deg</code> it
will be evaluated as radians. Degrees will first be converted to radians.
If the number is any other unit, the units will be passed thru to the result, If the number is any other unit, the units will be passed thru to the result,
and the number will be treated as radians. and the number will be treated as radians.
#tan.helper #tan.helper
@ -56,7 +58,8 @@ documented_functions:
tan($number) tan($number)
.details .details
%p %p
Takes the tangent of a number. If the number is unitless or has a unit of <code>deg</code> Returns the tangent of a number. If the number is unitless or has a unit of <code>deg</code>
then it will return a unitless result. Degrees will first be converted to radians. then it will return a unitless result. Unless the number has a unit of <code>deg</code> it
will be evaluated as radians. Degrees will first be converted to radians.
If the number is any other unit, the units will be passed thru to the result, If the number is any other unit, the units will be passed thru to the result,
and the number will be treated as radians. and the number will be treated as radians.

View File

@ -40,10 +40,7 @@ module Compass
initializer "compass.initialize_rails" do |app| initializer "compass.initialize_rails" do |app|
# Configure compass for use within rails, and provide the project configuration # Configure compass for use within rails, and provide the project configuration
# that came via the rails boot process. # that came via the rails boot process.
Compass::AppIntegration::Rails.check_for_double_boot! Compass::AppIntegration::Rails.initialize!(app.config.compass)
Compass.discover_extensions!
Compass.configure_sass_plugin!
Compass.handle_configuration_change!
end end
end end
end end

View File

@ -70,19 +70,21 @@ module Compass
end end
def default_http_images_path def default_http_images_path
"#{top_level.http_path}images" # Relies on the fact that this will be loaded after the "normal"
# defaults, so that method_missing finds http_root_relative
http_root_relative "images"
end end
def default_http_javascripts_path def default_http_javascripts_path
"#{top_level.http_path}javascripts" http_root_relative "javascripts"
end end
def default_http_fonts_path def default_http_fonts_path
"#{top_level.http_path}fonts" http_root_relative "fonts"
end end
def default_http_stylesheets_path def default_http_stylesheets_path
"#{top_level.http_path}stylesheets" http_root_relative "stylesheets"
end end
def default_extensions_dir def default_extensions_dir

View File

@ -24,7 +24,7 @@ module Compass
attributes_for_directory(:fonts), attributes_for_directory(:fonts),
attributes_for_directory(:extensions, nil), attributes_for_directory(:extensions, nil),
# Compilation options # Compilation options
:sprite_search_path, :sprite_load_path,
:output_style, :output_style,
:environment, :environment,
:relative_assets, :relative_assets,

View File

@ -89,7 +89,7 @@ module Compass
top_level.images_dir top_level.images_dir
end end
def default_sprite_search_path def default_sprite_load_path
[top_level.images_path] [top_level.images_path]
end end

View File

@ -1,14 +1,22 @@
module Compass::SassExtensions::Functions::ImageSize module Compass::SassExtensions::Functions::ImageSize
# Returns the width of the image relative to the images directory # Returns the width of the image relative to the images directory
def image_width(image_file) def image_width(image_file)
image_path = real_path(image_file) image_path = if File.exists?(image_file.value)
image_file.value
else
real_path(image_file)
end
width = ImageProperties.new(image_path).size.first width = ImageProperties.new(image_path).size.first
Sass::Script::Number.new(width,["px"]) Sass::Script::Number.new(width,["px"])
end end
# Returns the height of the image relative to the images directory # Returns the height of the image relative to the images directory
def image_height(image_file) def image_height(image_file)
image_path = real_path(image_file) image_path = if File.exists?(image_file.value)
image_file.value
else
real_path(image_file)
end
height = ImageProperties.new(image_path).size.last height = ImageProperties.new(image_path).size.last
Sass::Script::Number.new(height, ["px"]) Sass::Script::Number.new(height, ["px"])
end end

View File

@ -61,7 +61,7 @@ module Compass::SassExtensions::Functions::Sprites
verify_map(map, "sprite") verify_map(map, "sprite")
verify_sprite(sprite) verify_sprite(sprite)
if image = map.image_for(sprite.value) if image = map.image_for(sprite.value)
Sass::Script::String.new(image.relative_file) Sass::Script::String.new(image.file)
else else
missing_image!(map, sprite) missing_image!(map, sprite)
end end

View File

@ -19,9 +19,10 @@ module Compass::SassExtensions::Functions::Trig
private private
def trig(operation, number) def trig(operation, number)
if number.numerator_units == ["deg"] && number.denominator_units == [] if number.numerator_units == ["deg"] && number.denominator_units == []
Sass::Script::Number.new(Math.send(operation, Math::PI * number.value / 360)) Sass::Script::Number.new(Math.send(operation, (number.value * Math::PI / 180)))
else else
Sass::Script::Number.new(Math.send(operation, number.value), number.numerator_units, number.denominator_units) Sass::Script::Number.new(Math.send(operation, number.value), number.numerator_units, number.denominator_units)
end end
end end
end end

View File

@ -7,7 +7,8 @@ module Compass
end end
end end
end end
require 'compass/sass_extensions/sprites/image_row'
require 'compass/sass_extensions/sprites/row_fitter'
require 'compass/sass_extensions/sprites/image' require 'compass/sass_extensions/sprites/image'
require 'compass/sass_extensions/sprites/layout_methods' require 'compass/sass_extensions/sprites/layout_methods'
require 'compass/sass_extensions/sprites/sprite_methods' require 'compass/sass_extensions/sprites/sprite_methods'

View File

@ -17,7 +17,16 @@ module Compass
# The Full path to the image # The Full path to the image
def file def file
File.join(Compass.configuration.images_path, relative_file) @file ||= find_file
end
def find_file
Compass.configuration.sprite_load_path.each do |path|
f = File.join(path, relative_file)
if File.exists?(f)
return f
end
end
end end
# Width of the image # Width of the image
@ -25,6 +34,10 @@ module Compass
dimensions.first dimensions.first
end end
def size
@size ||= File.size(file)
end
# Height of the image # Height of the image
def height def height
dimensions.last dimensions.last
@ -35,19 +48,18 @@ module Compass
File.basename(relative_file, '.png') File.basename(relative_file, '.png')
end end
# Value of <tt> $#{name}-repeat </tt> or <tt> $repeat </tt> def get_var_file(var)
def repeat options.get_var "#{name}_#{var}"
[ "#{name}-repeat", "repeat" ].each { |which|
if var = options.get_var(which)
return var.value
end
}
"no-repeat"
end end
# Value of <tt> $#{name}-position </tt> or <tt> $position </tt> defaults o <tt>0px</tt> # Value of <tt> $#{name}-repeat </tt> or <tt> $repeat </tt>
def repeat
(get_var_file("repeat") || options.get_var("repeat") || Sass::Script::String.new("no-repeat")).value
end
# Value of <tt> $#{name}-position </tt> or <tt> $position </tt> defaults to <tt>0px</tt>
def position def position
options.get_var("#{name}-position") || options.get_var("position") || Sass::Script::Number.new(0, ["px"]) get_var_file("position") || options.get_var("position") || Sass::Script::Number.new(0, ["px"])
end end
# Offset within the sprite # Offset within the sprite
@ -57,7 +69,7 @@ module Compass
# Spacing between this image and the next # Spacing between this image and the next
def spacing def spacing
(options.get_var("#{name}-spacing") || options.get_var("spacing") || Sass::Script::Number.new(0)).value (get_var_file("spacing") || options.get_var("spacing") || Sass::Script::Number.new(0, ['px'])).value
end end
# MD5 hash of this file # MD5 hash of this file

View File

@ -0,0 +1,47 @@
require 'forwardable'
module Compass
module SassExtensions
module Sprites
class ImageRow
extend Forwardable
attr_reader :images, :max_width
def_delegators :@images, :last, :delete, :empty?, :length
def initialize(max_width)
@images = []
@max_width = max_width
end
def add(image)
return false if !will_fit?(image)
@images << image
true
end
alias :<< :add
def height
images.map(&:height).max
end
def width
images.map(&:width).max
end
def total_width
images.inject(0) {|sum, img| sum + img.width }
end
def efficiency
1 - (total_width.to_f / max_width.to_f)
end
def will_fit?(image)
(total_width + image.width) <= max_width
end
end
end
end
end

View File

@ -3,23 +3,76 @@ module Compass
module Sprites module Sprites
module LayoutMethods module LayoutMethods
HORIZONTAL = 'horizontal' HORIZONTAL = 'horizontal'
DIAGONAL = 'diagonal'
SMART = 'smart'
def smart?
@kwargs.get_var('layout').value == SMART
end
def horizontal? def horizontal?
@kwargs.get_var('layout').value == HORIZONTAL @kwargs.get_var('layout').value == HORIZONTAL
end end
def diagonal?
@kwargs.get_var('layout').value == DIAGONAL
end
# Calculates the overal image dimensions # Calculates the overal image dimensions
# collects image sizes and input parameters for each sprite # collects image sizes and input parameters for each sprite
def compute_image_positions! def compute_image_positions!
if horizontal? case @kwargs.get_var('layout').value
calculate_height when SMART
calculate_smart_positions
when DIAGONAL
calculate_diagonal_dimensions
calculate_diagonal_positions
when HORIZONTAL
@height = height_for_horizontal_layout
calculate_horizontal_positions calculate_horizontal_positions
calculate_width @width = width_for_horizontal_layout
else else
@images.sort! {|a,b| File.size(b.file) <=> File.size(a.file)} #put small images first @images.sort! {|a,b| b.size <=> a.size}
calculate_width @width = width_for_vertical_layout
calulate_vertical_postions calulate_vertical_postions
calculate_height @height = height_for_vertical_layout
end
end
def calculate_smart_positions
fitter = ::Compass::SassExtensions::Sprites::RowFitter.new(@images)
current_y = 0
fitter.fit!.each do |row|
current_x = 0
row.images.each_with_index do |image, index|
image.left = current_x
image.top = current_y
current_x += image.width
end
current_y += row.height
end
@width = fitter.width
@height = fitter.height
end
def calculate_diagonal_dimensions
@width = @images.map {|image| image.width}.inject(0) {|width, sum| sum + width}
@height = @images.map {|image| image.height}.inject(0) {|height, sum| sum + height}
end
def calculate_diagonal_positions
previous = nil
@images.each_with_index do |image, index|
if previous.nil?
previous = image
image.top = 0
image.left = 0
next
end
image.top = previous.top + previous.height
image.left = previous.left + previous.width
previous = image
end end
end end
@ -28,7 +81,7 @@ module Compass
image.top = image.position.unit_str == '%' ? (@height - image.height) * (image.position.value / 100.0) : image.position.value image.top = image.position.unit_str == '%' ? (@height - image.height) * (image.position.value / 100.0) : image.position.value
next if index == 0 next if index == 0
last_image = @images[index-1] last_image = @images[index-1]
image.left = last_image.left + last_image.width + [image.offset, last_image.offset].max image.left = last_image.left + last_image.width + [image.spacing, last_image.spacing].max
end end
end end
@ -41,39 +94,17 @@ module Compass
end end
end end
def calculate_dimensions!
calculate_width
calculate_height
end
def calculate_height
@height = if horizontal?
height_for_horizontal_layout
else
height_for_vertical_layout
end
end
def height_for_vertical_layout def height_for_vertical_layout
last = @images.last last = @images.last
last.top + last.height last.top + last.height
end end
def height_for_horizontal_layout def height_for_horizontal_layout
@height = @images.map {|image| image.height + image.spacing}.max @height = @images.map {|image| image.height + image.offset}.max
end
def calculate_width
@width = if horizontal?
width_for_horizontal_layout
else
width_for_vertical_layout
end
end end
def width_for_horizontal_layout def width_for_horizontal_layout
@images.inject(0) { |sum, image| sum += (image.width + image.offset) } @images.inject(0) { |sum, image| sum += (image.width + image.spacing) }
end end
def width_for_vertical_layout def width_for_vertical_layout

View File

@ -0,0 +1,86 @@
require 'forwardable'
module Compass
module SassExtensions
module Sprites
class RowFitter
extend Forwardable
attr_reader :images, :rows
def_delegators :rows, :[]
def initialize(images)
@images = images.sort {|a,b| a.height <=> b.height }
@rows = []
end
def fit!(style = :scan)
send("#{style}_fit")
@rows
end
def width
@width ||= @images.collect(&:width).max
end
def height
@height ||= @rows.inject(0) {|sum, row| sum += row.height}
end
def efficiency
@rows.inject(0) { |sum, row| sum += row.efficiency } ** @rows.length
end
private
def new_row(image = nil)
row = Compass::SassExtensions::Sprites::ImageRow.new(width)
row.add(image) if image
row
end
def fast_fit
row = new_row
@images.each do |image|
if !row.add(image)
@rows << row
row = new_row(image)
end
end
@rows << row
end
def scan_fit
fast_fit
moved_images = []
begin
removed = false
catch :done do
@rows.each do |row|
(@rows - [ row ]).each do |other_row|
other_row.images.each do |image|
if !moved_images.include?(image)
if row.will_fit?(image)
other_row.delete(image)
row << image
@rows.delete(other_row) if other_row.empty?
removed = true
moved_images << image
throw :done
end
end
end
end
end
end
end while removed
end
end
end
end
end

View File

@ -17,11 +17,21 @@ module Compass
name, path = Compass::SpriteImporter.path_and_name(uri) name, path = Compass::SpriteImporter.path_and_name(uri)
files = Compass::SpriteImporter.files(uri) files = Compass::SpriteImporter.files(uri)
sprites = files.map do |sprite| sprites = files.map do |sprite|
sprite.gsub("#{Compass.configuration.images_path}/", "") relative_name(sprite)
end end
new(sprites, path, name, context, kwargs) new(sprites, path, name, context, kwargs)
end end
def self.relative_name(sprite)
sprite = File.expand_path(sprite)
Compass.configuration.sprite_load_path.each do |path|
path = File.expand_path(path)
if sprite.include?(path)
return sprite.gsub("#{path}/", "")
end
end
end
def initialize(sprites, path, name, context, kwargs) def initialize(sprites, path, name, context, kwargs)
@image_names = sprites @image_names = sprites
@path = path @path = path

View File

@ -66,7 +66,7 @@ module Compass
# Returns the Glob of image files for the uri # Returns the Glob of image files for the uri
def self.files(uri) def self.files(uri)
Compass.configuration.sprite_search_path.each do |folder| Compass.configuration.sprite_load_path.each do |folder|
files = Dir[File.join(folder, uri)].sort files = Dir[File.join(folder, uri)].sort
next if files.empty? next if files.empty?
return files return files

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,50 @@
require 'test_helper'
require 'compass'
require 'stringio'
class ConfigurationTest < Test::Unit::TestCase
setup do
Compass.reset_configuration!
end
after do
Compass.reset_configuration!
end
def test_defaults
contents = StringIO.new(<<-CONFIG)
project_type = :rails
CONFIG
config = Compass.configuration_for(contents, "config/compass.rb")
Compass.add_project_configuration(config, :project_type => "rails")
assert_equal 'public/images', Compass.configuration.images_dir
assert_equal 'public/stylesheets', Compass.configuration.css_dir
assert_equal 'public/fonts', Compass.configuration.fonts_dir
assert_equal '/', Compass.configuration.http_path
assert_equal '/images', Compass.configuration.http_images_path
assert_equal '/stylesheets', Compass.configuration.http_stylesheets_path
assert_equal '/fonts', Compass.configuration.http_fonts_path
# Other default values must wait until I have a better idea of how to mock Sass::Util.app_geq
end
def test_http_path_change
contents = StringIO.new(<<-CONFIG)
project_type = :rails
http_path = "/test/alternative_path"
CONFIG
config = Compass.configuration_for(contents, "config/compass.rb")
Compass.add_project_configuration(config, :project_type => "rails")
assert_equal '/test/alternative_path', Compass.configuration.http_path
assert_equal '/test/alternative_path/images', Compass.configuration.http_images_path
assert_equal '/test/alternative_path/stylesheets', Compass.configuration.http_stylesheets_path
assert_equal '/test/alternative_path/fonts', Compass.configuration.http_fonts_path
end
end

View File

@ -65,18 +65,21 @@ class SassExtensionsTest < Test::Unit::TestCase
end end
def test_trig_functions def test_trig_functions
assert_equal "0.841", evaluate("sin(1)")
assert_equal "0.841px", evaluate("sin(1px)") assert_equal "0.841px", evaluate("sin(1px)")
assert_equal "0.0", evaluate("sin(pi())") assert_equal "0.0", evaluate("sin(pi())")
assert_equal "1", evaluate("sin(pi() / 2)") assert_equal "1", evaluate("sin(pi() / 2)")
assert_equal "1", evaluate("sin(180deg)") assert_equal "0.0", evaluate("sin(180deg)")
assert_equal "-1", evaluate("sin(3* pi() / 2)") assert_equal "-1", evaluate("sin(3* pi() / 2)")
assert_equal "-1", evaluate("cos(pi())") assert_equal "-1", evaluate("cos(pi())")
assert_equal "-1", evaluate("cos(360deg)") assert_equal "1", evaluate("cos(360deg)")
assert_equal "-0.176", evaluate("sin(270)")
assert_equal "1", evaluate("cos(2*pi())") assert_equal "1", evaluate("cos(2*pi())")
assert_equal "0.0", evaluate("cos(pi() / 2)") assert_equal "0.0", evaluate("cos(pi() / 2)")
assert_equal "0.0", evaluate("cos(3* pi() / 2)") assert_equal "0.0", evaluate("cos(3* pi() / 2)")
assert_equal "0.0", evaluate("tan(pi())") assert_equal "0.0", evaluate("tan(pi())")
assert_equal "0.0", evaluate("tan(360deg)") assert_equal "0.0", evaluate("tan(360deg)")
assert_equal "0.959", evaluate("sin(360)")
assert evaluate("tan(pi()/2 - 0.0001)").to_f > 1000, evaluate("tan(pi()/2 - 0.0001)") assert evaluate("tan(pi()/2 - 0.0001)").to_f > 1000, evaluate("tan(pi()/2 - 0.0001)")
assert evaluate("tan(pi()/2 + 0.0001)").to_f < -1000, evaluate("tan(pi()/2 - 0.0001)") assert evaluate("tan(pi()/2 + 0.0001)").to_f < -1000, evaluate("tan(pi()/2 - 0.0001)")
end end

View File

@ -0,0 +1,57 @@
require 'test_helper'
class ImageRowTest < Test::Unit::TestCase
include SpriteHelper
def setup
create_sprite_temp
file = StringIO.new("images_path = #{@images_src_path.inspect}\n")
Compass.add_configuration(file, "sprite_config")
@filenames = %w(large.png large_square.png medium.png tall.png small.png)
@image_files = Dir["#{@images_src_path}/image_row/*.png"].sort
@images = @image_files.map do |img|
img.gsub!("#{@images_src_path}/", '')
Compass::SassExtensions::Sprites::Image.new(nil, img, {})
end
image_row(1000)
end
def teardown
clean_up_sprites
end
def image_row(max)
@image_row = Compass::SassExtensions::Sprites::ImageRow.new(max)
end
def populate_row
@images.each do |image|
assert @image_row.add(image)
end
end
it "should return false if image will not fit in row" do
image_row(100)
img = Compass::SassExtensions::Sprites::Image.new(nil, File.join('image_row', 'large.png'), {})
assert !@image_row.add(img)
end
it "should have 5 images" do
populate_row
assert_equal 5, @image_row.images.size
end
it "should return max image width" do
populate_row
assert_equal 400, @image_row.width
end
it "should return max image height" do
populate_row
assert_equal 40, @image_row.height
end
it "should have an efficiency rating" do
populate_row
assert_equal 1 - (580.0 / 1000.0), @image_row.efficiency
end
end

View File

@ -6,41 +6,36 @@ class SpritesImageTest < Test::Unit::TestCase
include SpriteHelper include SpriteHelper
def setup def setup
create_sprite_temp create_sprite_temp
file = StringIO.new("images_path = #{@images_src_path.inspect}\n")
Compass.add_configuration(file, "sprite_config")
@repeat = 'no-repeat'
@spacing = 0
@position = 100
@offset = 100
end end
let(:sprite_filename) { 'squares/ten-by-ten.png' } SPRITE_FILENAME = 'selectors/ten-by-ten.png'
let(:sprite_path) { File.join(@images_src_path, sprite_filename) }
let(:sprite_name) { File.basename(sprite_filename, '.png') }
def sprite_path
def options File.join(@images_tmp_path, SPRITE_FILENAME)
options = {:offset => @offset}
options.stubs(:get_var).with(anything).returns(nil)
::OpenStruct.any_instance.stubs(:unitless?).returns(true)
options.stubs(:get_var).with("#{sprite_name}-repeat").returns(::OpenStruct.new(:value => @repeat))
options.stubs(:get_var).with("#{sprite_name}-spacing").returns(::OpenStruct.new(:value => @spacing))
options.stubs(:get_var).with("#{sprite_name}-position").returns(::OpenStruct.new(:value => @position))
options.stubs(:get_var).with("layout").returns(::OpenStruct.new(:value => 'vertical'))
options
end end
def sprite_name
File.basename(SPRITE_FILENAME, '.png')
end
def digest
Digest::MD5.file(sprite_path).hexdigest
end
let(:digest) { Digest::MD5.file(sprite_path).hexdigest } def test_map(options ={})
options = {'cleanup' => Sass::Script::Bool.new(true), 'layout' => Sass::Script::String.new('vertical')}.merge(options)
map = sprite_map_test(options)
end
def test_image(options ={})
let(:image) { Compass::SassExtensions::Sprites::Image.new(sprite_map_test(options), File.join(sprite_filename), options)} test_map(options).images.first
end
test 'initialize' do test 'initialize' do
image = test_image
assert_equal sprite_name, image.name assert_equal sprite_name, image.name
assert_equal sprite_path, image.file assert_equal sprite_path, image.file
assert_equal sprite_filename, image.relative_file assert_equal SPRITE_FILENAME, image.relative_file
assert_equal 10, image.width assert_equal 10, image.width
assert_equal 10, image.height assert_equal 10, image.height
assert_equal digest, image.digest assert_equal digest, image.digest
@ -49,46 +44,62 @@ class SpritesImageTest < Test::Unit::TestCase
end end
test 'hover' do test 'hover' do
assert_equal 'ten-by-ten_hover', image.hover.name assert_equal 'ten-by-ten_hover', test_image.hover.name
end end
test 'no parent' do test 'no parent' do
assert_nil image.parent assert_nil test_image.parent
end end
test 'image type is nil' do
@repeat = nil
assert_nil image.repeat
end
test 'image type is "global"' do test 'image type is "global"' do
@repeat = 'global' image = test_image "ten_by_ten_repeat" => Sass::Script::String.new('global')
assert_equal @repeat, image.repeat assert_equal 'global', image.repeat
end end
test 'image type is "no-repeat"' do test 'image type is "no-repeat"' do
assert_equal 'no-repeat', image.repeat assert_equal 'no-repeat', test_image.repeat
end end
test 'image position' do test 'image position' do
assert_equal Sass::Script::Number.new(100, ["px"]).value, image.position.value image = test_image "ten_by_ten_position" => Sass::Script::Number.new(100, ["px"])
assert_equal 100, image.position.value
end end
test 'image spacing' do test 'image spacing' do
@spacing = 10 @spacing = 10
assert_equal @spacing, image.spacing image = test_image "spacing" => Sass::Script::Number.new(100, ["px"])
assert_equal 100, image.spacing
end end
test 'offset' do test 'offset' do
assert_equal @offset, image.offset image = test_image "ten_by_ten_position" => Sass::Script::Number.new(100, ["px"])
assert_equal 100, image.offset
end end
test 'neither, uses 0' do test 'neither, uses 0' do
@offset = 0 img = test_image
img = image
img.position.stubs(:unitless?).returns(false) img.position.stubs(:unitless?).returns(false)
assert_equal 0, img.offset assert_equal 0, img.offset
end end
test 'gets name for sprite in search path' do
Compass.reset_configuration!
uri = 'foo/*.png'
other_folder = File.join(@images_tmp_path, '../other-temp')
FileUtils.mkdir_p other_folder
FileUtils.mkdir_p File.join(other_folder, 'foo')
%w(my bar).each do |file|
FileUtils.touch(File.join(other_folder, "foo/#{file}.png"))
end
config = Compass::Configuration::Data.new('config')
config.images_path = @images_tmp_path
config.sprite_load_path = [@images_tmp_path, other_folder]
Compass.add_configuration(config, "sprite_config")
image = Compass::SassExtensions::Sprites::Image.new(test_map, "foo/my.png", {})
assert_equal File.join(other_folder, 'foo/my.png'), image.file
assert_equal 0, image.size
FileUtils.rm_rf other_folder
end
end end

View File

@ -29,11 +29,11 @@ class ImporterTest < Test::Unit::TestCase
end end
config = Compass::Configuration::Data.new('config') config = Compass::Configuration::Data.new('config')
config.images_path = @images_tmp_path config.images_path = @images_tmp_path
config.sprite_search_path = [@images_tmp_path, other_folder] config.sprite_load_path = [@images_tmp_path, other_folder]
Compass.add_configuration(config, "sprite_config") Compass.add_configuration(config, "sprite_config")
importer = Compass::SpriteImporter.new importer = Compass::SpriteImporter.new
assert_equal 2, Compass.configuration.sprite_search_path.compact.size assert_equal 2, Compass.configuration.sprite_load_path.compact.size
assert Compass.configuration.sprite_search_path.include?(other_folder) assert Compass.configuration.sprite_load_path.include?(other_folder)
assert_equal ["bar", "my"], Compass::SpriteImporter.sprite_names(uri) assert_equal ["bar", "my"], Compass::SpriteImporter.sprite_names(uri)
FileUtils.rm_rf other_folder FileUtils.rm_rf other_folder

View File

@ -0,0 +1,66 @@
require 'test_helper'
require 'compass/sass_extensions/sprites/row_fitter'
class RowFitterTest < Test::Unit::TestCase
include SpriteHelper
def setup
file = StringIO.new("images_path = #{@images_src_path.inspect}\n")
Compass.add_configuration(file, "sprite_config")
end
def row_fitter(images = nil)
@row_fitter ||= Compass::SassExtensions::Sprites::RowFitter.new(images)
end
def teardown
@row_fitter = nil
end
def create_images(dims)
dims.collect { |width, height|
image = Compass::SassExtensions::Sprites::Image.new('blah', 'blah', {})
image.stubs(:width => width, :height => height)
image
}
end
def basic_dims
[
[ 100, 10 ],
[ 80, 10 ],
[ 50, 10 ],
[ 35, 10 ],
[ 20, 10 ]
]
end
it 'should use the fast placement algorithm' do
images = create_images(basic_dims)
row_fitter(images)
assert_equal 100, row_fitter.width
row_fitter.fit!(:fast)
assert_equal 4, row_fitter.rows.length
assert_equal [ images[0] ], row_fitter[0].images
assert_equal [ images[1] ], row_fitter[1].images
assert_equal [ images[2], images[3] ], row_fitter[2].images
assert_equal [ images[4] ], row_fitter[3].images
end
it 'should use the scan placement algorithm' do
images = create_images(basic_dims)
row_fitter(images)
row_fitter.fit!(:scan)
assert_equal 3, row_fitter.rows.length
assert_equal [ images[0] ], row_fitter[0].images
assert_equal [ images[1], images[4] ], row_fitter[1].images
assert_equal [ images[2], images[3] ], row_fitter[2].images
end
end

View File

@ -5,20 +5,16 @@ class SpriteMapTest < Test::Unit::TestCase
def setup def setup
Hash.send(:include, Compass::SassExtensions::Functions::Sprites::VariableReader) Hash.send(:include, Compass::SassExtensions::Functions::Sprites::VariableReader)
@images_src_path = File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'sprites', 'public', 'images') create_sprite_temp
@images_tmp_path = File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'sprites', 'public', 'images-tmp') file = StringIO.new("images_path = #{@images_tmp_path.inspect}\n")
FileUtils.cp_r @images_src_path, @images_tmp_path Compass.add_configuration(file, "sprite_config")
config = Compass::Configuration::Data.new('config')
config.images_path = @images_tmp_path
config.sprite_search_path = [@images_tmp_path]
Compass.add_configuration(config)
Compass.configure_sass_plugin! Compass.configure_sass_plugin!
@options = {'cleanup' => Sass::Script::Bool.new(true), 'layout' => Sass::Script::String.new('vertical')} @options = {'cleanup' => Sass::Script::Bool.new(true), 'layout' => Sass::Script::String.new('vertical')}
@base = sprite_map_test(@options) @base = sprite_map_test(@options)
end end
def teardown def teardown
FileUtils.rm_r @images_tmp_path clean_up_sprites
@base = nil @base = nil
end end
@ -90,10 +86,55 @@ class SpriteMapTest < Test::Unit::TestCase
assert_equal [0, 0, 0, 0], @base.images.map(&:left) assert_equal [0, 0, 0, 0], @base.images.map(&:left)
end end
it "should have a vertical layout with spacing" do
base = sprite_map_test(@options.merge({"spacing" => Sass::Script::Number.new(10, ['px'])}))
assert_equal [0, 20, 40, 60], base.images.map(&:top)
end
it "should layout vertical with position" do
base = sprite_map_test("ten_by_ten_active_position" => Sass::Script::Number.new(10, ['px']))
assert_equal [0, 10, 0, 0], base.images.map(&:left)
end
def smart
options = @options.merge("layout" => Sass::Script::String.new('smart'))
importer = Compass::SpriteImporter.new
uri = "image_row/*.png"
path, name = Compass::SpriteImporter.path_and_name(uri)
sprite_names = Compass::SpriteImporter.sprite_names(uri)
sass_engine = Compass::SpriteImporter.sass_engine(uri, name, importer, options)
Compass::SassExtensions::Sprites::SpriteMap.new(sprite_names.map {|n| "image_row/#{n}.png"}, path, name, sass_engine, options)
end
it "should have a smart layout" do
base = smart
base.generate
assert_equal 400, base.width
assert_equal 60, base.height
assert_equal [[0, 0], [20, 120], [20, 20], [20, 0], [20, 160]], base.images.map {|i| [i.top, i.left]}
assert File.exists?(base.filename)
FileUtils.rm base.filename
end
def diagonal
opts = @options.merge("layout" => Sass::Script::String.new('diagonal'))
sprite_map_test(opts)
end
it "should generate a diagonal sprite" do
base = diagonal
base.generate
assert_equal 40, base.width
assert_equal 40, base.height
assert_equal [[0,0], [10,10], [20,20], [30,30]], base.images.map {|i| [i.top, i.left]}
assert File.exists?(base.filename)
FileUtils.rm base.filename
end
# Horizontal tests # Horizontal tests
def horizontal def horizontal(options= {})
opts = @options.merge("layout" => Sass::Script::String.new('horizontal')) opts = @options.merge("layout" => Sass::Script::String.new('horizontal'))
opts.merge!(options)
sprite_map_test(opts) sprite_map_test(opts)
end end
@ -109,6 +150,18 @@ class SpriteMapTest < Test::Unit::TestCase
assert_equal [0, 0, 0, 0], base.images.map(&:top) assert_equal [0, 0, 0, 0], base.images.map(&:top)
end end
it "should layout horizontaly with spacing" do
base = horizontal("spacing" => Sass::Script::Number.new(10, ['px']))
assert_equal [0, 20, 40, 60], base.images.map(&:left)
assert_equal [0, 0, 0, 0], base.images.map(&:top)
assert_equal 80, base.width
end
it "should layout horizontaly with position" do
base = horizontal("ten_by_ten_active_position" => Sass::Script::Number.new(10, ['px']))
assert_equal [0, 10, 0, 0], base.images.map(&:top)
end
it "should generate a horrizontal sprite" do it "should generate a horrizontal sprite" do
base = horizontal base = horizontal
base.generate base.generate
@ -122,5 +175,22 @@ class SpriteMapTest < Test::Unit::TestCase
assert_equal sizes.max, File.size(@base.images.last.file) assert_equal sizes.max, File.size(@base.images.last.file)
end end
test "should get correct relative_name" do
Compass.reset_configuration!
uri = 'foo/*.png'
other_folder = File.join(@images_tmp_path, '../other-temp')
FileUtils.mkdir_p other_folder
FileUtils.mkdir_p File.join(other_folder, 'foo')
%w(my bar).each do |file|
FileUtils.touch(File.join(other_folder, "foo/#{file}.png"))
end
config = Compass::Configuration::Data.new('config')
config.images_path = @images_tmp_path
config.sprite_load_path = [@images_tmp_path, other_folder]
Compass.add_configuration(config, "sprite_config")
assert_equal 'foo/my.png', Compass::SassExtensions::Sprites::SpriteMap.relative_name(File.join(other_folder, 'foo/my.png'))
FileUtils.rm_rf other_folder
end
end end