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:
parent
e9fa71cf38
commit
6c718b21a8
8 changed files with 79 additions and 10 deletions
|
@ -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}'")
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}\".")
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue