From 26e7fc7ff44defeaf0339fae5e6b06e460fc2b4f Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Mon, 13 Oct 2008 09:32:17 -0700 Subject: [PATCH 1/3] Unit tests and documentation for the percentage function. --- lib/sass/script/functions.rb | 3 +++ test/sass/functions_test.rb | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/sass/script/functions.rb b/lib/sass/script/functions.rb index 7072a1dd..ab651fd3 100644 --- a/lib/sass/script/functions.rb +++ b/lib/sass/script/functions.rb @@ -40,6 +40,9 @@ module Sass::Script hue_to_rgb(m1, m2, h - 1.0/3)].map { |c| (c * 0xff).round }) end + # Converts a unitless number into a percent and multiplies the number by 100. + # E.g. percentage(100px / 50px) => 200% + # Some may find this more natural than: 1% * 100px / 50px def percentage(value) unless value.is_a?(Sass::Script::Number) && value.unitless? raise ArgumentError.new("Value is not a unitless number") diff --git a/test/sass/functions_test.rb b/test/sass/functions_test.rb index ab43de96..9e67eb82 100644 --- a/test/sass/functions_test.rb +++ b/test/sass/functions_test.rb @@ -43,9 +43,23 @@ class SassFunctionTest < Test::Unit::TestCase assert_rgb_hsl(purple, ['2820', '100%', '50%']) end + def test_percentage + assert_equal("50%", percentage(".5")) + assert_equal("100%", percentage("1")) + assert_equal("25%", percentage("25px / 100.0px")) + assert_raises(ArgumentError) {percentage("25px")} + assert_raises(ArgumentError) {percentage("#ccc")} + assert_raises(ArgumentError) {percentage("string")} + end + private def assert_rgb_hsl(rgb, hsl) assert_equal(rgb, Sass::Script::Functions.hsl(*hsl.map(&Sass::Script::Parser.method(:parse))).value) end + + def percentage(value) + Sass::Constant::Functions.percentage(Sass::Constant::Parser.parse(value)).to_s + end + end From 00e953df48e959b9e46eae85e49a2e12fa3f86ef Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Mon, 13 Oct 2008 11:47:44 -0700 Subject: [PATCH 2/3] New numeric transformation functions for SassScript: round, ceil, floor, abs. --- lib/sass/script/functions.rb | 33 ++++++++++++++++++++++++++- test/sass/functions_test.rb | 43 +++++++++++++++++++++++++++++------- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/lib/sass/script/functions.rb b/lib/sass/script/functions.rb index ab651fd3..e647815c 100644 --- a/lib/sass/script/functions.rb +++ b/lib/sass/script/functions.rb @@ -45,13 +45,44 @@ module Sass::Script # Some may find this more natural than: 1% * 100px / 50px def percentage(value) unless value.is_a?(Sass::Script::Number) && value.unitless? - raise ArgumentError.new("Value is not a unitless number") + raise ArgumentError.new("#{value} is not a unitless number") end Sass::Script::Number.new(value.value * 100, '%') end + # Rounds a number to the nearest whole number. + def round(value) + numeric_transformation(value) {|n| n.round} + end + + # Rounds up to the nearest whole number. + def ceil(value) + numeric_transformation(value) {|n| n.ceil} + end + + # Rounds down to the nearest whole number. + def floor(value) + numeric_transformation(value) {|n| n.floor} + end + + # Returns the absolute value of a number. + def abs(value) + numeric_transformation(value) {|n| n.abs} + end + private + # This method implements the pattern of transforming a numeric value into + # another numeric value with the same units. + # It yields a number to a block to perform the operation and return a number + def numeric_transformation(value) + unless value.is_a?(Sass::Script::Number) + calling_function = caller.first.scan(/`([^']+)'/).first.first + raise Sass::SyntaxError.new("#{value} is not a number for `#{calling_function}'") + end + Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units) + end + def hue_to_rgb(m1, m2, h) h += 1 if h < 0 h -= 1 if h > 1 diff --git a/test/sass/functions_test.rb b/test/sass/functions_test.rb index 9e67eb82..3b93f0c5 100644 --- a/test/sass/functions_test.rb +++ b/test/sass/functions_test.rb @@ -44,12 +44,30 @@ class SassFunctionTest < Test::Unit::TestCase end def test_percentage - assert_equal("50%", percentage(".5")) - assert_equal("100%", percentage("1")) - assert_equal("25%", percentage("25px / 100.0px")) - assert_raises(ArgumentError) {percentage("25px")} - assert_raises(ArgumentError) {percentage("#ccc")} - assert_raises(ArgumentError) {percentage("string")} + assert_equal("50%", evaluate("percentage(.5)")) + assert_equal("100%", evaluate("percentage(1)")) + assert_equal("25%", evaluate("percentage(25px / 100px)")) + assert_error_message("25px is not a unitless number for `percentage'", "percentage(25px)") + assert_error_message("#cccccc is not a unitless number for `percentage'", "percentage(#ccc)") + assert_error_message("string is not a unitless number for `percentage'", "percentage(string)") + end + + def test_numeric_transformations + assert_equal("5", evaluate("round(4.8)")) + assert_equal("5px", evaluate("round(4.8px)")) + assert_equal("5px", evaluate("round(5.49px)")) + assert_equal("4", evaluate("floor(4.8)")) + assert_equal("4px", evaluate("floor(4.8px)")) + assert_equal("5", evaluate("ceil(4.1)")) + assert_equal("5px", evaluate("ceil(4.8px)")) + assert_equal("5", evaluate("abs(-5)")) + assert_equal("5px", evaluate("abs(-5px)")) + assert_equal("5", evaluate("abs(5)")) + assert_equal("5px", evaluate("abs(5px)")) + assert_error_message("#cccccc is not a number for `round'", "round(#ccc)") + assert_error_message("foo is not a number for `floor'", "floor(foo)") + assert_error_message("'a' is not a number for `ceil'", "ceil('a')") + assert_error_message("#aaaaaa is not a number for `abs'", "abs(#aaa)") end private @@ -58,8 +76,17 @@ class SassFunctionTest < Test::Unit::TestCase assert_equal(rgb, Sass::Script::Functions.hsl(*hsl.map(&Sass::Script::Parser.method(:parse))).value) end - def percentage(value) - Sass::Constant::Functions.percentage(Sass::Constant::Parser.parse(value)).to_s + def evaluate(value) + Sass::Script::Parser.parse(value).perform({}).to_s + end + + def assert_error_message(message, value) + begin + evaluate(value) + flunk("Error message expected but not raised: #{message}") + rescue Sass::SyntaxError => e + assert_equal(message, e.message) + end end end From 689bb9e68305a09a84533e714cea0f319369a87f Mon Sep 17 00:00:00 2001 From: Chris Eppstein Date: Mon, 13 Oct 2008 11:44:20 -0700 Subject: [PATCH 3/3] No longer perform division using integer division if only integers are used. If you want integer-based division you should now do the following instead: floor(14/5) which will return 2 instead of 2.8. --- lib/sass/script/number.rb | 4 +++- test/sass/results/script.css | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/sass/script/number.rb b/lib/sass/script/number.rb index d1a01f03..c91d6528 100644 --- a/lib/sass/script/number.rb +++ b/lib/sass/script/number.rb @@ -129,8 +129,10 @@ module Sass::Script other = other.coerce(numerator_units, denominator_units) end end + # avoid integer division + value = (:/ == operation) ? this.value.to_f : this.value + result = value.send(operation, other.value) - result = this.value.send(operation, other.value) if result.is_a?(Numeric) Number.new(result, *compute_units(this, other, operation)) else # Boolean op diff --git a/test/sass/results/script.css b/test/sass/results/script.css index 2e814a89..9f3b146e 100644 --- a/test/sass/results/script.css +++ b/test/sass/results/script.css @@ -7,7 +7,7 @@ #times { num-num: 7; num-col: #7496b8; col-num: #092345; col-col: #243648; } -#div { num-num: 3.333; num-num2: 3; col-num: #092345; col-col: #0b0d0f; comp: 1px; } +#div { num-num: 3.333; num-num2: 3.333; col-num: #092345; col-col: #0b0d0f; comp: 1px; } #mod { num-num: 2; col-col: #0f0e05; col-num: #020001; }