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

Merge branch 'master' into yard

This commit is contained in:
Nathan Weizenbaum 2009-04-23 18:03:03 -07:00
commit 2c0d5a6bfa
25 changed files with 392 additions and 199 deletions

1
TODO
View file

@ -22,7 +22,6 @@
Figure out alternate attribute syntax
** Sass
CSS Terminology? (https://mail.google.com/mail/#label/Haml/1202d75234b3ce6b)
Sass::Engine load_paths option should be set by executable
[2.4] Allow semicolons for multiple props on one line
[2.4] Pull in Compass watcher stuff

View file

@ -7,6 +7,23 @@ module Haml
# that a Haml template is parsed in, so all these methods are at your
# disposal from within the template.
module Helpers
# An object that raises an error when #to_s is called.
# It's used to raise an error when the return value of a helper is used
# when it shouldn't be.
class ErrorReturn
def initialize(message)
@message = message
end
def to_s
raise Haml::Error.new(@message)
end
def inspect
"Haml::Helpers::ErrorReturn(#{@message.inspect})"
end
end
self.extend self
@@action_view_defined = defined?(ActionView)
@ -281,7 +298,7 @@ DEPRECATION WARNING:
The Haml #puts helper is deprecated and will be removed in version 2.4.
Use the #haml_concat helper instead.
END
haml_concat *args
haml_concat(*args)
end
# Outputs text directly to the Haml buffer, with the proper tabulation
@ -342,6 +359,11 @@ END
# </table>
#
def haml_tag(name, *rest, &block)
ret = ErrorReturn.new(<<MESSAGE)
haml_tag outputs directly to the Haml template.
Disregard its return value and use the - operator.
MESSAGE
name = name.to_s
text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
flags = []
@ -352,7 +374,7 @@ END
if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
haml_concat "<#{name}#{attributes} />"
return nil
return ret
end
if flags.include?(:/)
@ -364,7 +386,7 @@ END
if block.nil?
tag << text.to_s << "</#{name}>"
haml_concat tag
return
return ret
end
if text
@ -374,7 +396,7 @@ END
if flags.include?(:<)
tag << capture_haml(&block).strip << "</#{name}>"
haml_concat tag
return
return ret
end
haml_concat tag
@ -382,10 +404,8 @@ END
block.call
tab_down
haml_concat "</#{name}>"
<<MESSAGE
<h1><code>haml_tag</code> outputs directly to the Haml template.
Disregard its return value and use the <code>-</code> operator.</h1>
MESSAGE
ret
end
# Characters that need to be escaped to HTML entities from user input

View file

@ -195,7 +195,10 @@ END
@index = index + 1
case text[0]
when DIV_CLASS, DIV_ID; render_div(text)
when DIV_CLASS; render_div(text)
when DIV_ID
return push_plain(text) if text[1] == ?{
render_div(text)
when ELEMENT; render_tag(text)
when COMMENT; render_comment(text[1..-1].strip)
when SANITIZE

View file

@ -1,20 +1,11 @@
require 'haml/engine'
module Haml
class Template
class << self
@@options = {}
module Template
extend self
# Gets various options for Haml. See README.rdoc for details.
def options
@@options
end
# Sets various options for Haml. See README.rdoc for details.
def options=(value)
@@options = value
end
end
@options = {}
attr_accessor :options
end
end

View file

@ -4,7 +4,7 @@ require 'enumerator'
module Haml
module Util
class << self; include Haml::Util; end
extend self
RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}

View file

@ -94,7 +94,7 @@ module Sass
:load_paths => ['.']
}.merge! options
@template = template
@environment = Environment.new
@environment = Environment.new(nil, @options)
@environment.set_var("important", Script::String.new("!important"))
end
@ -131,7 +131,7 @@ module Sass
tab_str = nil
first = true
enum_with_index(string.gsub(/\r|\n|\r\n|\r\n/, "\n").scan(/^.*?$/)).map do |line, index|
index += 1
index += (@options[:line] || 1)
next if line.strip.empty?
line_tab_str = line[/^\s*/]
@ -229,7 +229,7 @@ END
case child
when Tree::MixinDefNode
raise SyntaxError.new("Mixins may only be defined at the root of a document.", line.index)
when Tree::DirectiveNode
when Tree::DirectiveNode, Tree::FileNode
raise SyntaxError.new("Import directives may only be used at the root of a document.", line.index)
end
end
@ -430,7 +430,7 @@ END
end
def import_paths
paths = @options[:load_paths] || []
paths = (@options[:load_paths] || []).dup
paths.unshift(File.dirname(@options[:filename])) if @options[:filename]
paths
end

View file

@ -2,10 +2,15 @@ module Sass
class Environment
attr_reader :parent
def initialize(parent = nil)
def initialize(parent = nil, options = nil)
@vars = {}
@mixins = {}
@parent = parent
@options = options
end
def options
@options || (parent && parent.options) || {}
end
def self.inherited_hash(name)

View file

@ -1,24 +1,24 @@
require 'sass/engine'
require 'pathname'
module Sass
# This module contains methods to aid in using Sass
# as a stylesheet-rendering plugin for various systems.
# Currently Rails/ActionController and Merb are supported out of the box.
module Plugin
class << self
@@options = {
extend self
@options = {
:css_location => './public/stylesheets',
:always_update => false,
:always_check => true,
:full_exception => true
}
@@checked_for_updates = false
@checked_for_updates = false
# Whether or not Sass has *ever* checked if the stylesheets need updates
# (in this Ruby instance).
def checked_for_updates
@@checked_for_updates
@checked_for_updates
end
# Gets various options for Sass. See README.rdoc for details.
@ -26,12 +26,12 @@ module Sass
# TODO: *DOCUMENT OPTIONS*
#++
def options
@@options
@options
end
# Sets various options for Sass.
def options=(value)
@@options.merge!(value)
@options.merge!(value)
end
# Get the options ready to be passed to the Sass::Engine
@ -49,7 +49,7 @@ module Sass
def update_stylesheets
return if options[:never_update]
@@checked_for_updates = true
@checked_for_updates = true
template_locations.zip(css_locations).each do |template_location, css_location|
Dir.glob(File.join(template_location, "**", "*.sass")).each do |file|
@ -172,14 +172,16 @@ END
def stylesheet_needs_update?(name, template_path, css_path)
css_file = css_filename(name, css_path)
template_file = template_filename(name, template_path)
if !File.exists?(css_file)
return true
else
exact_stylesheet_needs_update?(css_file, template_file)
end
def exact_stylesheet_needs_update?(css_file, template_file)
return true unless File.exists?(css_file)
css_mtime = File.mtime(css_file)
File.mtime(template_file) > css_mtime ||
dependencies(template_file).any?(&dependency_updated?(css_mtime))
end
end
def dependency_updated?(css_mtime)
lambda do |dep|
@ -197,7 +199,6 @@ END
end
end
end
end
require 'sass/plugin/rails' if defined?(ActionController)
require 'sass/plugin/merb' if defined?(Merb::Plugins)

View file

@ -1,6 +1,18 @@
require File.join(File.dirname(__FILE__), 'functions')
module Sass
module Script
class Funcall # :nodoc:
class EvaluationContext # :nodoc:
include Sass::Script::Functions
attr_reader :options
def initialize(options)
@options = options
end
end
attr_reader :name, :args
def initialize(name, args)
@ -18,7 +30,7 @@ module Sass
return Script::String.new("#{name}(#{args.map {|a| a.perform(environment)}.join(', ')})")
end
return Functions.send(name, *args)
return EvaluationContext.new(environment.options).send(name, *args)
rescue ArgumentError => e
raise e unless e.backtrace.first =~ /:in `(#{name}|perform)'$/
raise Sass::SyntaxError.new("#{e.message} for `#{name}'")

View file

@ -19,6 +19,9 @@ module Sass::Script
# and then left as static CSS files.
# Any dynamic CSS should be left in <style> tags in the HTML.
#
# Within a sass function you can call the options method to gain access to the
# options hash that was used to create the Sass::Engine that is processing the function call.
#
# The following functions are provided:
# * +hsl+ - converts an <tt>hsl(hue, saturation, lightness)</tt> triplet into a color.
#

View file

@ -141,7 +141,7 @@ module Sass
end
def last_match_position
current_position - @scanner.matchedsize
current_position - @scanner.matched_size
end
def after_interpolation?

View file

@ -20,7 +20,7 @@ module Sass::Script
elsif other.is_a?(Color)
other.plus(self)
else
Sass::Script::String.new(self.to_s + other.to_s)
super
end
end

View file

@ -18,8 +18,8 @@ module Sass::Tree
range = Range.new(from, to, @exclusive)
children = []
range.each do |i|
environment = Sass::Environment.new(environment)
range.each do |i|
environment.set_local_var(@var, Sass::Script::Number.new(i))
children += perform_children(environment)
end

View file

@ -73,7 +73,7 @@ module Sass
res << "\\" * (escapes - 1) << '#{'
else
res << "\\" * [0, escapes - 1].max
res << Script::Parser.new(scan, line, scan.pos - scan.matchedsize, filename).
res << Script::Parser.new(scan, line, scan.pos - scan.matched_size, filename).
parse_interpolated.perform(environment).to_s
end
end

View file

@ -1,3 +1,5 @@
require 'pathname'
module Sass::Tree
class RuleNode < Node
# The character used to include the parent selector

View file

@ -11,8 +11,9 @@ module Sass::Tree
def _perform(environment)
children = []
new_environment = Sass::Environment.new(environment)
while @expr.perform(environment).to_bool
children += perform_children(Sass::Environment.new(environment))
children += perform_children(new_environment)
end
children
end

View file

@ -129,14 +129,19 @@ class EngineTest < Test::Unit::TestCase
assert_equal("<strong>Hi there!</strong>\n", engine.to_html)
end
def test_double_equals
assert_equal("<p>Hello World</p>\n", render('%p== Hello #{who}', :locals => {:who => 'World'}))
assert_equal("<p>\n Hello World\n</p>\n", render("%p\n == Hello \#{who}", :locals => {:who => 'World'}))
def test_interpolation
assert_equal("<p>Hello World</p>\n", render('%p Hello #{who}', :locals => {:who => 'World'}))
assert_equal("<p>\n Hello World\n</p>\n", render("%p\n Hello \#{who}", :locals => {:who => 'World'}))
end
def test_double_equals_in_the_middle_of_a_string
def test_interpolation_in_the_middle_of_a_string
assert_equal("\"title 'Title'. \"\n",
render("== \"title '\#{\"Title\"}'. \""))
render("\"title '\#{\"Title\"}'. \""))
end
def test_interpolation_at_the_beginning_of_a_line
assert_equal("<p>2</p>\n", render('%p #{1 + 1}'))
assert_equal("<p>\n 2\n</p>\n", render("%p\n \#{1 + 1}"))
end
def test_nil_tag_value_should_render_as_empty

View file

@ -141,6 +141,10 @@ HAML
assert_raise(Haml::Error) { render("- haml_tag :p, :/ do\n foo") }
end
def test_haml_tag_error_return
assert_raise(Haml::Error) { render("= haml_tag :p") }
end
def test_is_haml
assert(!ActionView::Base.new.is_haml?)
assert_equal("true\n", render("= is_haml?"))

View file

@ -13,6 +13,126 @@
20
</div>
<div id='body'> Quotes should be loved! Just like people!</div>
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
Wow.|
<p>
Holy cow multiline tags! A pipe (|) even!

View file

@ -1,5 +1,5 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US'>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
<head>
<title>Hampton Catlin Is Totally Awesome</title>
<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
@ -38,7 +38,7 @@
</div>
<ul class='really cool'>
<% ('a'..'f').each do |a|%>
<li><%= a %>
<li><%= a %></li>
<% end %>
<div class='of_divs_with_underscore' id='combo'><%= @should_eval = "with this text" %></div>
<%= "foo".each_line do |line|

View file

@ -1,5 +1,5 @@
!!!
%html{html_attrs}
%html{:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en-US", "lang" => "en-US"}
%head
%title Hampton Catlin Is Totally Awesome
%meta{"http-equiv" => "Content-Type", :content => "text/html; charset=utf-8"}
@ -12,7 +12,7 @@
= 1 + 9 + 8 + 2 #numbers should work and this should be ignored
#body= " Quotes should be loved! Just like people!"
- 120.times do |number|
- number
= number
Wow.|
%p
= "Holy cow " + |

View file

@ -37,6 +37,7 @@ class UtilTest < Test::Unit::TestCase
end
def test_powerset
return unless Set[Set[]] == Set[Set[]] # There's a bug in Ruby 1.8.6 that breaks nested set equality
assert_equal([[].to_set].to_set,
powerset([]))
assert_equal([[].to_set, [1].to_set].to_set,

View file

@ -44,6 +44,7 @@ class SassEngineTest < Test::Unit::TestCase
"@import foo.sass" => "File to import not found or unreadable: foo.sass.",
"@import templates/basic\n foo" => "Illegal nesting: Nothing may be nested beneath import directives.",
"foo\n @import templates/basic" => "Import directives may only be used at the root of a document.",
"foo\n @import #{File.dirname(__FILE__)}/templates/basic" => "Import directives may only be used at the root of a document.",
%Q{!foo = "bar" "baz" !} => %Q{Syntax error in '"bar" "baz" !' at character 20.},
"=foo\n :color red\n.bar\n +bang" => "Undefined mixin 'bang'.",
".bar\n =foo\n :color red\n" => ["Mixins may only be defined at the root of a document.", 2],
@ -114,14 +115,16 @@ class SassEngineTest < Test::Unit::TestCase
def test_exceptions
EXCEPTION_MAP.each do |key, value|
line = 10
begin
Sass::Engine.new(key).render
Sass::Engine.new(key, :filename => __FILE__, :line => line).render
rescue Sass::SyntaxError => err
value = [value] unless value.is_a?(Array)
assert_equal(value.first, err.message, "Line: #{key}")
assert_equal(value[1] || key.split("\n").length, err.sass_line, "Line: #{key}")
assert_match(/\(sass\):[0-9]+/, err.backtrace[0], "Line: #{key}")
assert_equal(__FILE__, err.sass_filename)
assert_equal((value[1] || key.split("\n").length) + line - 1, err.sass_line, "Line: #{key}")
assert_match(/#{Regexp.escape(__FILE__)}:[0-9]+/, err.backtrace[0], "Line: #{key}")
else
assert(false, "Exception not raised for\n#{key}")
end
@ -145,6 +148,24 @@ SASS
end
end
def test_exception_location
to_render = <<SASS
rule
:attr val
// comment!
:broken
SASS
begin
Sass::Engine.new(to_render, :filename => __FILE__, :line => (__LINE__-7)).render
rescue Sass::SyntaxError => err
assert_equal(__FILE__, err.sass_filename)
assert_equal((__LINE__-6), err.sass_line)
else
assert(false, "Exception not raised for '#{to_render}'!")
end
end
def test_imported_exception
[nil, 2].each do |i|
begin

View file

@ -96,7 +96,7 @@ class SassFunctionTest < Test::Unit::TestCase
end
def evaluate(value)
Sass::Script::Parser.parse(value, 0, 0).perform({}).to_s
Sass::Script::Parser.parse(value, 0, 0).perform(Sass::Environment.new).to_s
end
def assert_error_message(message, value)

View file

@ -139,6 +139,11 @@ class SassPluginTest < Test::Unit::TestCase
tempfile_name = arguments.shift || result_name
expected_lines = File.read(result_loc(result_name, prefix)).split("\n")
actual_lines = File.read(tempfile_loc(tempfile_name, prefix)).split("\n")
if actual_lines.first == "/*" && expected_lines.first != "/*"
assert(false, actual_lines[0..actual_lines.enum_with_index.find {|l, i| l == "*/"}.last].join("\n"))
end
expected_lines.zip(actual_lines).each_with_index do |pair, line|
message = "template: #{result_name}\nline: #{line + 1}"
assert_equal(pair.first, pair.last, message)