71085

Auto-removing all newlines from Haml output

Question:

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 <em>want</em> 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:

<ol><li>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.</li> <li>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.</li> </ol>

Any pointers or other suggestions appreciated!

<strong>Update:</strong> 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 <em>not</em> 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! :)

Answer1:

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 <a href="http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#whitespace_removal__and_" rel="nofollow">whitespace removal</a> 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. :)

Answer2:

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}

Answer3:

Similar to @yfeldblum's answer, I decided to simply split on newlines and join on spaces, in order to avoid newlines in html rendered into js. For example:

- content = capture_haml do %div %ul %li Stuff # ...

and then later,

:javascript var stuff = "#{content.split("\n").join(" ")}"; // ...

Answer4:

Okay, I think I'll try to self-answer my question -- there's probably no clean/sensible way to remove all newlines, but perhaps I don't actually need that. For typical XPath test expressions to work nicely, it's enough if <em>inline</em> elements don't have newlines around them.

So I guess I'll just put an inner-whitespace-eater ("<") after any tag that contains inline elements (p, li, span, a, etc.). This seems to be working reasonably well so far. E.g.

%div %ul %li< %span< stuff...

And all the other (i.e. most) newlines can stay and won't do any harm.

Sorry everyone for the messy question!

Recommend

  • Set horizontal scrollbar for 32 bit scrolling in a C# RichTextBox
  • What happens to return value from goroutine
  • How to get rich text formatting from asana task API
  • 8086 assembly division
  • Rails: Storing a 256 bit checksum as binary in database
  • How to replace all hash keys having a '.'?
  • how to unescape \\\\ in a regex that has been escaped by Rails?
  • Processing (too) many XML files (with TagSoup)
  • ember js subviews and didinsertelement event
  • How do I remove the last vowel in a string in Ruby?
  • Anybody have any idea how the divs are emitted in pinterest.com?
  • AWS S3 implementation for serving private files
  • EF6 and pre-generated views
  • Sun.security couldn't find after upgrading Java
  • Setting color in a row of a Jtable
  • Conditional render in tagfile depending on whether the attribute is specified or not
  • How to discover a file is changed in GIT during a push
  • Does argparse support multiple exclusive arguments?
  • Codeigniter Hooks advance
  • Cannot update firebase version [duplicate]
  • Autohotkey script running program with command line arguments
  • Is there a way to choose which files are displayed to the user via the standard OPENFILE dialogs?
  • Issue with routerLink directive
  • Setting WPF Window Background to Resource Dictionary Brush User Setting
  • Validate child input components on submit with Vee-Validate and vue js 2
  • Reduction and collapse clauses in OMP have some confusing points
  • MongoDb aggregation
  • How to use remove-erase idiom for removing empty vectors in a vector?
  • jQuery .attr() and value
  • Rearranging Cells in UITableView Bug & Saving Changes
  • Circular dependency while pushing http interceptor
  • How to get icons for entities from eclipse?
  • Linker errors when using intrinsic function via function pointer
  • WPF Applying a trigger on binding failure
  • Proper way to use connect-multiparty with express.js?
  • Load html files in TinyMce
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Memory offsets in inline assembly
  • How can I remove ASP.NET Designer.cs files?
  • JaxB to read class hierarchy