1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

[Sass] Pass the options into literals returned by functions and operations.

This commit is contained in:
Nathan Weizenbaum 2009-12-03 12:28:13 -08:00
parent e9fa71cf38
commit 6c718b21a8
8 changed files with 79 additions and 10 deletions

View file

@ -41,7 +41,9 @@ module Sass
return Script::String.new("#{name}(#{args.map {|a| a.perform(environment)}.join(', ')})") return Script::String.new("#{name}(#{args.map {|a| a.perform(environment)}.join(', ')})")
end end
return Functions::EvaluationContext.new(environment.options).send(ruby_name, *args) result = Functions::EvaluationContext.new(environment.options).send(ruby_name, *args)
result.options = environment.options
return result
rescue ArgumentError => e rescue ArgumentError => e
raise e unless e.backtrace.any? {|t| t =~ /:in `(block in )?(#{name}|perform)'$/} raise e unless e.backtrace.any? {|t| t =~ /:in `(block in )?(#{name}|perform)'$/}
raise Sass::SyntaxError.new("#{e.message} for `#{name}'") raise Sass::SyntaxError.new("#{e.message} for `#{name}'")

View file

@ -87,6 +87,13 @@ module Sass::Script
# #
# Within one of the functions in this module, # Within one of the functions in this module,
# methods of {EvaluationContext} can be used. # methods of {EvaluationContext} can be used.
#
# ### Caveats
#
# When creating new {Literal} objects within functions,
# be aware that it's not safe to call {Literal#to_s #to_s}
# (or other methods that use the string representation)
# on those objects without first setting {Node#options= the #options attribute}.
module Functions module Functions
# The context in which methods in {Script::Functions} are evaluated. # The context in which methods in {Script::Functions} are evaluated.
# That means that all instance methods of {EvaluationContext} # That means that all instance methods of {EvaluationContext}

View file

@ -39,6 +39,23 @@ module Sass::Script
[] []
end end
# Returns the options hash for this node.
#
# @return [{Symbol => Object}]
# @raise [Sass::SyntaxError] if the options hash hasn't been set.
# This should only happen when the literal was created
# outside of the parser and \{#to\_s} was called on it
def options
opts = super
return opts if opts
raise Sass::SyntaxError.new(<<MSG)
The #options attribute is not set on this #{self.class}.
This error is probably occurring because #to_s was called
on this literal within a custom Sass function without first
setting the #option attribute.
MSG
end
# The SassScript `and` operation. # The SassScript `and` operation.
# #
# @param other [Literal] The right-hand side of the operator # @param other [Literal] The right-hand side of the operator
@ -181,5 +198,13 @@ module Sass::Script
# @raise [Sass::SyntaxError] if this literal isn't an integer # @raise [Sass::SyntaxError] if this literal isn't an integer
def assert_int!; to_i; end def assert_int!; to_i; end
# Returns the string representation of this literal
# as it would be output to the CSS document.
#
# @return [String]
def to_s
raise Sass::SyntaxError.new("[BUG] All subclasses of Sass::Literal must implement #to_s.")
end
end end
end end

View file

@ -35,7 +35,9 @@ module Sass::Script
literal1 = @operand1.perform(environment) literal1 = @operand1.perform(environment)
literal2 = @operand2.perform(environment) literal2 = @operand2.perform(environment)
begin begin
literal1.send(@operator, literal2) res = literal1.send(@operator, literal2)
res.options = environment.options
res
rescue NoMethodError => e rescue NoMethodError => e
raise e unless e.name.to_s == @operator.to_s raise e unless e.name.to_s == @operator.to_s
raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".") raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")

View file

@ -3,6 +3,12 @@ require File.dirname(__FILE__) + '/../test_helper'
require 'sass/engine' require 'sass/engine'
require 'stringio' require 'stringio'
module Sass::Script::Functions::UserFunctions
def option(name)
Sass::Script::String.new(@options[name.value.to_sym].to_s)
end
end
class SassEngineTest < Test::Unit::TestCase class SassEngineTest < Test::Unit::TestCase
# A map of erroneous Sass documents to the error messages they should produce. # A map of erroneous Sass documents to the error messages they should produce.
# The error messages may be arrays; # The error messages may be arrays;

View file

@ -2,16 +2,18 @@ require 'test/unit'
require File.dirname(__FILE__) + '/../../lib/sass' require File.dirname(__FILE__) + '/../../lib/sass'
require 'sass/script' require 'sass/script'
module UserFunctions module Sass::Script::Functions::UserFunctions
def call_options_on_new_literal
str = Sass::Script::String.new("foo")
str.options[:foo]
str
end
def user_defined def user_defined
Sass::Script::String.new("I'm a user-defined string!") Sass::Script::String.new("I'm a user-defined string!")
end end
end end
module Sass::Script::Functions
include UserFunctions
end
class SassFunctionTest < Test::Unit::TestCase class SassFunctionTest < Test::Unit::TestCase
# Tests taken from: # Tests taken from:
# http://www.w3.org/Style/CSS/Test/CSS3/Color/20070927/html4/t040204-hsl-h-rotating-b.htm # http://www.w3.org/Style/CSS/Test/CSS3/Color/20070927/html4/t040204-hsl-h-rotating-b.htm
@ -251,6 +253,15 @@ class SassFunctionTest < Test::Unit::TestCase
assert_equal("I'm a user-defined string!", evaluate("user_defined()")) assert_equal("I'm a user-defined string!", evaluate("user_defined()"))
end end
def test_options_on_new_literals_fails
assert_error_message(<<MSG, "call-options-on-new-literal()")
The #options attribute is not set on this Sass::Script::String.
This error is probably occurring because #to_s was called
on this literal within a custom Sass function without first
setting the #option attribute.
MSG
end
private private
def evaluate(value) def evaluate(value)

View file

@ -2,6 +2,13 @@
require File.dirname(__FILE__) + '/../test_helper' require File.dirname(__FILE__) + '/../test_helper'
require 'sass/engine' require 'sass/engine'
module Sass::Script::Functions::UserFunctions
def assert_options(val)
val.options[:foo]
Sass::Script::String.new("Options defined!")
end
end
class SassScriptTest < Test::Unit::TestCase class SassScriptTest < Test::Unit::TestCase
include Sass::Script include Sass::Script
@ -157,6 +164,11 @@ WARN
assert_equal 'blam(foo)', resolve('blam("foo")') assert_equal 'blam(foo)', resolve('blam("foo")')
end end
def test_function_results_have_options
assert_equal "Options defined!", resolve("assert_options(abs(1))")
assert_equal "Options defined!", resolve("assert_options(round(1.2))")
end
def test_hyphenated_variables def test_hyphenated_variables
assert_equal("a-b", resolve("!a-b", {}, env("a-b" => Sass::Script::String.new("a-b")))) assert_equal("a-b", resolve("!a-b", {}, env("a-b" => Sass::Script::String.new("a-b"))))
end end
@ -266,6 +278,11 @@ WARN
assert_equal "true", resolve("1.1cm == 11mm") assert_equal "true", resolve("1.1cm == 11mm")
end end
def test_operations_have_options
assert_equal "Options defined!", resolve("assert_options(1 + 1)")
assert_equal "Options defined!", resolve("assert_options('bar' + 'baz')")
end
# Regression Tests # Regression Tests
def test_funcall_has_higher_precedence_than_color_name def test_funcall_has_higher_precedence_than_color_name

View file

@ -10,9 +10,8 @@ require 'sass'
Sass::RAILS_LOADED = true unless defined?(Sass::RAILS_LOADED) Sass::RAILS_LOADED = true unless defined?(Sass::RAILS_LOADED)
module Sass::Script::Functions module Sass::Script::Functions
def option(name) module UserFunctions; end
Sass::Script::String.new(@options[name.value.to_sym].to_s) include UserFunctions
end
end end
class Test::Unit::TestCase class Test::Unit::TestCase