diff --git a/lib/haml/engine.rb b/lib/haml/engine.rb index feb3e0d5..9e74922f 100644 --- a/lib/haml/engine.rb +++ b/lib/haml/engine.rb @@ -17,16 +17,16 @@ module Haml include Precompiler # Allow reading and writing of the options hash - attr :options, true + attr_accessor :options # This string contains the source code that is evaluated # to produce the Haml document. - attr :precompiled, true + attr_accessor :precompiled # A string containing the indentation used for the Haml document. # nil if the indentation is ambiguous # (for example, for a single-level document). - attr :indentation, true + attr_accessor :indentation # True if the format is XHTML def xhtml? diff --git a/lib/haml/helpers.rb b/lib/haml/helpers.rb index ee9ce623..2a805c23 100644 --- a/lib/haml/helpers.rb +++ b/lib/haml/helpers.rb @@ -78,14 +78,14 @@ module Haml # call-seq: # find_and_preserve(input, tags = haml_buffer.options[:preserve]) - # find_and_preserve {...} + # find_and_preserve(tags = haml_buffer.options[:preserve]) {...} # # Uses preserve to convert any newlines inside whitespace-sensitive tags # into the HTML entities for endlines. # +tags+ is an array of tags to preserve. # It defaults to the value of the :preserve option. - def find_and_preserve(input = '', tags = haml_buffer.options[:preserve], &block) - return find_and_preserve(capture_haml(&block)) if block + def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block) + return find_and_preserve(capture_haml(&block), input || tags) if block input = input.to_s input.gsub(/<(#{tags.map(&Regexp.method(:escape)).join('|')})([^>]*)>(.*?)(<\/\1>)/im) do diff --git a/lib/haml/html.rb b/lib/haml/html.rb index 5cf49769..7e269058 100644 --- a/lib/haml/html.rb +++ b/lib/haml/html.rb @@ -14,7 +14,7 @@ module Haml # which can either be a string containing HTML or an Hpricot node, # to a Haml string when +render+ is called. def initialize(template, options = {}) - @@options = options + @options = options if template.is_a? Hpricot::Node @template = template @@ -23,12 +23,12 @@ module Haml template = template.read end - if @@options[:rhtml] + if @options[:rhtml] match_to_html(template, /<%=(.*?)-?%>/m, 'loud') match_to_html(template, /<%-?(.*?)-?%>/m, 'silent') end - method = @@options[:xhtml] ? Hpricot.method(:XML) : method(:Hpricot) + method = @options[:xhtml] ? Hpricot.method(:XML) : method(:Hpricot) @template = method.call(template.gsub('&', '&')) end end @@ -36,14 +36,14 @@ module Haml # Processes the document and returns the result as a string # containing the Haml template. def render - @template.to_haml(0) + @template.to_haml(0, @options) end alias_method :to_haml, :render module ::Hpricot::Node # Returns the Haml representation of the given node, # at the given tabulation. - def to_haml(tabs = 0) + def to_haml(tabs, options) parse_text(self.to_s, tabs) end @@ -70,32 +70,28 @@ module Haml # :stopdoc: - def self.options - @@options - end - TEXT_REGEXP = /^(\s*).*$/ class ::Hpricot::Doc - def to_haml(tabs = 0) - (children || []).inject('') {|s, c| s << c.to_haml(0)} + def to_haml(tabs, options) + (children || []).inject('') {|s, c| s << c.to_haml(0, options)} end end class ::Hpricot::XMLDecl - def to_haml(tabs = 0) + def to_haml(tabs, options) "#{tabulate(tabs)}!!! XML\n" end end class ::Hpricot::CData - def to_haml(tabs = 0) + def to_haml(tabs, options) "#{tabulate(tabs)}:cdata\n#{parse_text(self.content, tabs + 1)}" end end class ::Hpricot::DocType - def to_haml(tabs = 0) + def to_haml(tabs, options) attrs = public_id.scan(/DTD\s+([^\s]+)\s*([^\s]*)\s*([^\s]*)\s*\/\//)[0] if attrs == nil raise Exception.new("Invalid doctype") @@ -126,34 +122,35 @@ module Haml end class ::Hpricot::Comment - def to_haml(tabs = 0) + def to_haml(tabs, options) "#{tabulate(tabs)}/\n#{parse_text(self.content, tabs + 1)}" end end class ::Hpricot::Elem - def to_haml(tabs = 0) + def to_haml(tabs, options) output = "#{tabulate(tabs)}" - if HTML.options[:rhtml] && name[0...5] == 'haml:' + if options[:rhtml] && name[0...5] == 'haml:' return output + HTML.send("haml_tag_#{name[5..-1]}", CGI.unescapeHTML(self.inner_text)) end - output += "%#{name}" unless name == 'div' && (static_id? || static_classname?) + output += "%#{name}" unless name == 'div' && + (static_id?(options) || static_classname?(options)) if attributes - if static_id? + if static_id?(options) output += "##{attributes['id']}" remove_attribute('id') end - if static_classname? + if static_classname?(options) attributes['class'].split(' ').each { |c| output += ".#{c}" } remove_attribute('class') end - output += haml_attributes if attributes.length > 0 + output += haml_attributes(options) if attributes.length > 0 end (self.children || []).inject(output + "\n") do |output, child| - output + child.to_haml(tabs + 1) + output + child.to_haml(tabs + 1, options) end end @@ -174,27 +171,27 @@ module Haml end end - def static_attribute?(name) - attributes[name] and !dynamic_attribute?(name) + def static_attribute?(name, options) + attributes[name] and !dynamic_attribute?(name, options) end - def dynamic_attribute?(name) - HTML.options[:rhtml] and dynamic_attributes.key?(name) + def dynamic_attribute?(name, options) + options[:rhtml] and dynamic_attributes.key?(name) end - def static_id? - static_attribute? 'id' + def static_id?(options) + static_attribute?('id', options) end - def static_classname? - static_attribute? 'class' + def static_classname?(options) + static_attribute?('class', options) end # Returns a string representation of an attributes hash # that's prettier than that produced by Hash#inspect - def haml_attributes + def haml_attributes(options) attrs = attributes.map do |name, value| - value = dynamic_attribute?(name) ? dynamic_attributes[name] : value.inspect + value = dynamic_attribute?(name, options) ? dynamic_attributes[name] : value.inspect name = name.index(/\W/) ? name.inspect : ":#{name}" "#{name} => #{value}" end diff --git a/lib/haml/template.rb b/lib/haml/template.rb index 9ceb4b74..3fa989a0 100644 --- a/lib/haml/template.rb +++ b/lib/haml/template.rb @@ -31,7 +31,7 @@ if defined?(RAILS_ROOT) # because the new init file is sufficiently flexible # to not need updating. rails_init_file = File.join(RAILS_ROOT, 'vendor', 'plugins', 'haml', 'init.rb') - haml_init_file = Haml.scope('init.rb') + haml_init_file = Haml::Util.scope('init.rb') begin if File.exists?(rails_init_file) require 'fileutils' diff --git a/lib/haml/util.rb b/lib/haml/util.rb index 7b37db8f..e7e123b1 100644 --- a/lib/haml/util.rb +++ b/lib/haml/util.rb @@ -8,6 +8,11 @@ module Haml RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i} + # Returns the path of file relative to the Haml root. + def scope(file) + File.expand_path File.join(File.dirname(__FILE__), '..', '..', file) + end + def to_hash(arr) arr.compact.inject({}) {|h, (k, v)| h[k] = v; h} end diff --git a/lib/haml/version.rb b/lib/haml/version.rb index 6e822076..e2b33c37 100644 --- a/lib/haml/version.rb +++ b/lib/haml/version.rb @@ -1,5 +1,9 @@ +require 'haml/util' + module Haml module Version + include Haml::Util + # Returns a hash representing the version of Haml. # The :major, :minor, and :teeny keys have their respective numbers. # The :string key contains a human-readable string representation of the version. @@ -38,10 +42,5 @@ module Haml @@version end - - # Returns the path of file relative to the Haml root. - def scope(file) # :nodoc: - File.expand_path File.join(File.dirname(__FILE__), '..', '..', file) - end end end diff --git a/lib/sass/tree/attr_node.rb b/lib/sass/tree/attr_node.rb index 5aaf9f87..f5bba4a4 100644 --- a/lib/sass/tree/attr_node.rb +++ b/lib/sass/tree/attr_node.rb @@ -69,6 +69,7 @@ module Sass::Tree end children.each do |kid| + next if kid.invisible? to_return << kid.to_s(tabs, real_name) << join_string end diff --git a/lib/sass/tree/comment_node.rb b/lib/sass/tree/comment_node.rb index c517f31b..2776b10f 100644 --- a/lib/sass/tree/comment_node.rb +++ b/lib/sass/tree/comment_node.rb @@ -44,11 +44,22 @@ module Sass::Tree # @param tabs [Fixnum] The level of indentation for the CSS # @return [String] The resulting CSS def to_s(tabs = 0, parent_name = nil) - return if (style == :compressed || @silent) + return if invisible? spaces = ' ' * (tabs - 1) spaces + "/* " + ([value] + lines.map {|l| l.text}). map{|l| l.sub(%r{ ?\*/ *$},'')}.join(style == :compact ? ' ' : "\n#{spaces} * ") + " */" end + + def invisible? + style == :compressed || @silent + end + + protected + + def _perform(environment) + return [] if @silent + self + end end end diff --git a/lib/sass/tree/directive_node.rb b/lib/sass/tree/directive_node.rb index 0a59d6cf..ebb26ea8 100644 --- a/lib/sass/tree/directive_node.rb +++ b/lib/sass/tree/directive_node.rb @@ -39,6 +39,7 @@ module Sass::Tree was_attr = false first = true children.each do |child| + next if child.invisible? if style == :compact if child.is_a?(AttrNode) result << "#{child.to_s(first || was_attr ? 1 : tabs + 1)} " diff --git a/lib/sass/tree/node.rb b/lib/sass/tree/node.rb index 8bf03011..64a39e3c 100644 --- a/lib/sass/tree/node.rb +++ b/lib/sass/tree/node.rb @@ -103,6 +103,8 @@ module Sass perform(Environment.new).to_s end + def invisible?; false; end + # Computes the CSS corresponding to this Sass tree. # # Only static-node subclasses need to implement \{#to\_s}. @@ -116,8 +118,8 @@ module Sass if child.is_a? AttrNode raise Sass::SyntaxError.new('Attributes aren\'t allowed at the root of a document.', child.line) else + next if child.invisible? child_str = child.to_s(1) - next unless child_str && child_str.length > 0 result << child_str + (style == :compressed ? '' : "\n") end end diff --git a/lib/sass/tree/rule_node.rb b/lib/sass/tree/rule_node.rb index c68d1aa2..431b0ee0 100644 --- a/lib/sass/tree/rule_node.rb +++ b/lib/sass/tree/rule_node.rb @@ -88,6 +88,7 @@ module Sass::Tree end.join(line_separator) children.each do |child| + next if child.invisible? if child.is_a? RuleNode sub_rules << child else diff --git a/test/haml/helper_test.rb b/test/haml/helper_test.rb index 093d6ecd..287100d9 100644 --- a/test/haml/helper_test.rb +++ b/test/haml/helper_test.rb @@ -182,6 +182,11 @@ HAML render("= find_and_preserve do\n %pre\n Foo\n Bar\n Foo\n Bar")) end + def test_find_and_preserve_with_block_and_tags + assert_equal("
Foo\nBar
\nFoo\nBar\n", + render("= find_and_preserve([]) do\n %pre\n Foo\n Bar\n Foo\n Bar")) + end + def test_preserve_with_block assert_equal("
Foo
Bar
Foo Bar\n", render("= preserve do\n %pre\n Foo\n Bar\n Foo\n Bar")) diff --git a/test/haml/util_test.rb b/test/haml/util_test.rb index 834f8482..6151426d 100644 --- a/test/haml/util_test.rb +++ b/test/haml/util_test.rb @@ -1,9 +1,14 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../test_helper' +require 'pathname' class UtilTest < Test::Unit::TestCase include Haml::Util + def test_scope + assert(File.exist?(scope("Rakefile"))) + end + def test_to_hash assert_equal({ :foo => 1, diff --git a/test/sass/engine_test.rb b/test/sass/engine_test.rb index 65cced3d..e771dae6 100755 --- a/test/sass/engine_test.rb +++ b/test/sass/engine_test.rb @@ -696,6 +696,50 @@ a SASS end + # Regression tests + + def test_comment_beneath_attr + assert_equal(< :compressed)) +.box{border-style:solid} +RESULT +.box + :border + /*:color black + :style solid +SOURCE + end + + def test_compressed_comment_beneath_directive + assert_equal(< :compressed)) +@foo{a:b} +RESULT +@foo + a: b + /*b: c +SOURCE + end + private def render(sass, options = {})