Auto-removing all newlines from Haml output - ruby-on-rails

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:
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.
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
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! :)

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. :)

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}

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(" ")}";
// ...

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 inline 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!

Related

Rails: join an array with a new line between each component

I am trying to make an app in Rails 4.
I have this method:
def full_address
[self.first_line, middle_line, last_line, country_name].compact.join("\n")
end
When I try this, it prints a space between components.
I've read several posts on here that say that single quotation marks are the problem (I've also tried those). I also tried:
def full_address
[self.first_line, middle_line, last_line, country_name].compact.join("<br>")
end
When I try this, it prints 'br' between components.
How do I do this? I've seen responses in several other languages that talk about '+' signs being the solution. I've tried them, it doesnt work. I need help in rails please.
I'm trying to use this in my views show page as:
<%= #address.full_address %>
irb(main):004:0> puts ["a", "b", "c", "d"].join("\n")
a
b
c
d
You didn't show that part of the code but I suspect that the problem is that you're trying to put the result string in an HTML page. HTML ignores whitespace and that is why you don't get the newlines. You can try the solution you proposed with the <br> and try to add .html_safe

Rails prevent HAML from escaping a link (w/ helper haml_tag)

I'm trying to create a simple menu and I came across this problem: HAML keeps escaping my links to html entities. I have a helper that is supposed to generate a menu:
def buildMainMenu(file=Rails.root.join("config","menu.yaml"))
... some operations ...
link = url_for par.merge({:controller=>mitem["controller"], :action=>mitem["action"]})
... some more operations yay ...
haml_tag :a, mitem["label"], :href=>link
end
par is {"testPARAM1"=>"testVAL1","testPARAM2"=>"testVAL2"}
Sadly the output is
<a href='/test/test1?testPARAM1=testVAL1&testPARAM2=testVAL2'>Test2</a>
I've looked for a while now and I can't seem to find how to force HAML to NOT escape my strings :(
I know this isn't 100% what you are looking for, but personally I would refactor this - this is going to cause you headaches and anyway, rendering html within a helper isn't ideal at all.
I'd change the helper function to get your YAML file, or whatever is going on there, and output a final array with the correct items.
Make a _header.html.haml partial (put it in a directory 'shared'), the partial will call the helper function, get the array, and since you are in a view you can loop with normal techniques, and use link_to, etc, and all your problems are solved.
This is a much cleaner way of doing things.
Just figured it out (I wish I've found it before spending over an hour on it but hey).
For anyone interested:
There are two functions html_safe and raw that do the trick. Used as follow:
haml_tag :a, mitem["label"], :href=>link.html_safe
haml_tag :a, mitem["label"], :href=>raw(link)

Globally delimit numbers in rails app?

Is it possible to format all numbers in a rails app to be delimited?
I don't really think this is an i18n issue, as I'm fine with the default delimiter/separator characters. I'm simply trying to avoid putting number_with_delimiter(value) all over my views.
I always want numbers to be displayed as delimited. Always.
So far I've tried extending the Fixnum class with code cribbed from the number_with_delimiter method:
class Fixnum
def delimit(delimiter=",", separator=".")
begin
parts = self.to_s.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
parts.join separator
rescue
self
end
end
end
> 98734578.delimit
=> "98,734,578"
> 223.delimit => "223"
So this is a step in the right direction- I like the dot notation as well as the slightly shorter method name. But I'd like to apply this to all instances of a Fixnum inside of a view without having to call .delimit.
Is this a bad idea? Should I be worried about implications this will have on numbers outside of the view context? Is there a better way to accomplish this goal?

Rails way to offer modified attributes

The case is simple: I have markdown in my database, and want it parsed on output(*).
#post.body is mapped to the posts.body column in the database. Simple, default Activerecord ORM. That column stores the markdown text a user inserts.
Now, I see four ways to offer the markdown rendered version to my views:
First, in app/models/post.rb:
# ...
def body
markdown = RDiscount.new(body)
markdown.to_html
end
Allowing me to simply call #post.body and get an already rendered version. I do see lots of potential problems with that, e.g. on edit the textfield being pre-filled with the rendered HMTL instead of the markdown code.
Second option would be a new attribute in the form of a method
In app/models/post.rb:
# ...
def body_mardownified
markdown = RDiscount.new(body)
markdown.to_html
end
Seems cleanest to me.
Or, third in a helper in app/helpers/application_helper.rb
def markdownify(string)
markdown = RDiscount.new(string)
markdown.to_html
end
Which is used in the view, instead of <%= body %>, <%= mardownify(body) %>.
The fourth way, would be to parse this in the PostsController.
def index
#posts = Post.find(:all)
#posts.each do |p|
p.body = RDiscount.new(string).to_html
#rendered_posts << p
end
end
I am not too familiar with Rails 3 proper method and attribute architecture. How should I go with this? Is there a fifth option? Should I be aware of gotchas, pitfalls or performance issues with one or another of these options?
(*) In future, potentially updated with a database caching layer, or even special columns for rendered versions. But that is beyond the point, merely pointing out, so to avoid discussion on filter-on-output versus filter-on-input :).
The first option you've described won't work as-is. It will cause an infinite loop because when you call RDiscount.new(body) it will use the body method you've just defined to pass into RDiscount (which in turn will call itself again, and again, and so on). If you want to do it this way, you'd need to use RDiscount.new(read_attribute('body')) instead.
Apart from this fact, I think the first option would be confusing for someone new looking at your app as it would not be instantly clear when they see in your view #post.body that this is in fact a modified version of the body.
Personally, I'd go for the second or third options. If you're going to provide it from the model, having a method which describes what it's doing to the body will make it very obvious to anyone else what is going on. If the html version of body will only ever be used in views or mailers (which would be logical), I'd argue that it makes more sense to have the logic in a helper as it seems like the more logical place to have a method that outputs html.
Do not put it in the controller as in your fourth idea, it's really not the right place for it.
Yet another way would be extending the String class with a to_markdown method. This has the benefit of working on any string anywhere in your application
class String
def to_markdown
RDiscount.new(self)
end
end
#post.body.to_markdown
normal bold italic
If you were using HAML, for example in app/views/posts/show.html.haml
:markdown
= #post.body
http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#markdown-filter
How about a reader for body that accepts a parse_with parameter?
def body(parse_with=nil)
b = read_attribute('body')
case parse_with
when :markdown then RDiscount.new(b)
when :escape then CGI.escape(b)
else b
end
end
This way, a regular call to body will function as it used to, and you can pass a parameter to specify what to render with:
#post.body
normal **bold** *italic*
#post.body(:markdown)
normal bold italic

Difference between t(:str) and t :str in ROR

i am new to ROR.. i am having a doubt in internationalization commands.
in some case we were using <%=t :str_use%>
and in some cases we were using <%= t(:str_use) %>
what is the difference between these two
when should i have to use 1st and when to use the second one..
Pls give some ideas regarding this.
i am having a view file with in that i am having lot of strings i wanna to internationalization them.
in some cases i am having like <td>Use</td>
and in some cases
<% if use %> Use <br />
<% else %>
This is ruby's syntax, not specifically ror ;)
Both are the same. Ruby can guess the parenthesis even if they're not there.
So it's completely up to you.
There's no difference between t :str and t(:str) — they both call the method t with the symbol :str as an argument. In Ruby, parentheses around arguments are optional.
But these are both different from t: str, which is Ruby 1.9 shorthand for the hash {:t => str}.
When to use t(:str)? When you want to chain further methods.
Try this in Rails:
t 'some.translation'.capitalize
t('some.translation').capitalize
First approach will return:
Translation
..as in the second half the identifier after the period, instead of translating the whole text. This, from what I can tell, is because you are passing the argument 'some.translation'.capitalize, not calling capitalize on the return, like the second example.

Resources