Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto-removing all newlines from Haml output

I'm using Haml in a Rails 3 app, and its newlines drive me nuts! For example,

%span
  foo

renders as

<span>
foo
</span>

But I'd much rather want

<span>foo</foo>

The reason (apart from being cleaner XML) is that extra newlines are a problem for my Selenium tests because they mess up the ability to do XPath queries like "//span[.='foo']". So I'd have to write '\nfoo\n' instead (ew!), or use //span[contains(text(), 'foo')], which matches too broadly.

I know I could use the alligator operators ("<" and ">") to strip whitespace, but since I don't ever have a case where I want the newlines to appear in the output, I'd just end up adding them mechanically at the end of each line. And that just seems very unDRY.

So now I see two solutions:

  1. Force Haml to never emit newlines unless they come from a Ruby expression. I see some nuke_inner_whitespace / nuke_outer_whitespace variables spread around the Haml code that might do the job, but I'm not sure how to change them without resorting to gratuitous monkey-patching.
  2. Hook into Rails to apply a gsub!("\n", "") to all rendered HTML. (For textarea's and pre's, I could still use ~ "foo\nbar" to make Haml emit foo&#x000A;bar.) But where's the right place to hook into Rails? I'm a little lost in the code.

Any pointers or other suggestions appreciated!

Update: I've used Jason's monkey patch below for a while now and I'm beginning to think that it's not worth it. E.g. if I want to get <span>[del]</span> <span>foo</span>, it's difficult to not have the blank space in between nuked. Even the following will render as [del]foo on the page:

%span
  = '[del] '
%span
  foo

So I think I'm going back to adding alligator operators manually (see my self-answer down below). Live and learn.

Thanks again to Jason! :)

like image 798
Jo Liss Avatar asked Dec 08 '22 00:12

Jo Liss


2 Answers

If you place a less-than sign at the end of the element name the whitespace around the content will be suppressed:

%span<
  foo

See whitespace removal in the Haml reference for more details.

There doesn't appear to be any clean way to force these flags on for all tags but the following monkey patch works fine with Haml 3.0.24:

module Haml::Precompiler
  def parse_tag_with_nuked_whitespace(line)
    result = parse_tag_without_nuked_whitespace line
    unless result.size == 9 && [false,true].include?(result[4]) && [false,true].include?(result[5])
      raise "Unexpected parse_tag output: #{result.inspect}"
    end
    result[4] = true # nuke_outer_whitespace
    result[5] = true # nuke_inner_whitespace
    result
  end
  alias_method_chain :parse_tag, :nuked_whitespace
end

It probably wouldn't be hard to fork Haml and add an option to the Engine options to always nuke whitespace. The parse_tag method could check this option if enabled and set the inner and outer flags to true. I'll leave this as an exercise for the reader. :)

like image 179
Jason Weathered Avatar answered Dec 24 '22 07:12

Jason Weathered


A few ways to do it:

%span foo

%span= "foo"

- foo = ["f", "o", "o"].join("")
%span= foo

%span #{"foo"}

- foo = ["f", "o", "o"].join("")
%span #{foo}
like image 44
yfeldblum Avatar answered Dec 24 '22 06:12

yfeldblum