Removing whitespace in a block in Haml - ruby-on-rails

I have this:
= link_to user_path(f.object.user) do
%span.hourly-rate>= f.object.user.hourly_rate.to_currency
\/hour
Which renders as:
<a href="/users/44"><span class='hourly-rate'>$16</span>/hour
</a>
The close tag is on a separate line. This results in the link looking funny when there is underlining (eg. on hover)
The solution is to have the markup look like:
<span class='hourly-rate'>$16</span>/hour
But I haven't found a way of doing that in Haml. I'd prefer to keep using the block form of link_to if possible (I imagine not using the block would result in even messier code, but at this point I'm open to anything).
Tangent: I imagine this would be fixed if I was using the :ugly Haml option. I have Haml::Template.options[:ugly] = true in my config/environments/development.rb, and I have haml-rails in my Gemfile (I know it doesn't include :ugly, but it's worth mentioning), and I have an initializer with;
# config/initializers/haml.rb
Haml::Template.options[:format] = :html5
Haml::Template.options[:ugly] = true
... but the code still isn't ugly :( Any ideas why not?

If you can live with the content wrapped inside another div, you can use this:
= link_to user_path(f.object.user) do
%div<>
%span.hourly-rate>= f.object.user.hourly_rate.to_currency
\/hour

The span was being used by javascript, to get its value (in this case, the user's hourly rate) - it wasn't being used for CSS at all.
Thus, I ended up removing the span and adding the user's hourly rate as a data- attribute of a separate field. So the final markup for the link was
= link_to f.object.user.hourly_rate.to_currency + "/hour", user_path(f.object.user)

Related

rails 5.x: add nofollow to all links in 'sanitize'

I am working on a Rails application whose HAML templates frequently make use of a routine called sanitize. I have deduced from context that this routine sanitizes user-controlled HTML. Example:
# views/feed_items/_about.html.haml
%h3 Summary:
.description
= sanitize #feed_item.description
I want to make this routine add 'rel=nofollow' to all outbound links, in addition to what it's already doing. What is the most straightforward way to do that?
N.B. I am not having any luck finding the definition of this method, or the official configuration knobs for it. The vendor directory has two different HTML sanitizer gems in it and I can't even figure out which one is being used. This is a large, complicated web application that I did not write, and I barely understand Ruby, let alone all of Rails' extensions to it. Please assume I do not know any of the things that you think are obvious.
The sanitizer will strip out the rel tags if they exist.
I ran into a similar issue and added an additional helper method - clean_links to the ApplicationHelper module, and called it after sanitizing the content.
# application_helper.rb
def clean_links html
html.gsub!(/\\2')
html.html_safe
end
This method looks for all <a> tags, and adds rel="nofollow". The html_safe method is necessary or else the HTML will be displayed as a string (it's already been sanitized).
This solution treats all links equally, so if you only want this for links pointing outside the domain, you'll have to update the REGEX accordingly.
In your view: <%= clean_links sanitize(#something) %>
So, first the content is sanitized, then you add the rel="nofollow" tag before displaying the link.
Actually there's a built-in way:
sanitize "your input", scrubber: Loofah::Scrubbers::NoFollow.new

Can Haml omit tags where the Ruby content evaluates to nil?

If I have a line in a Rails template that evaluates to nil, is there a way to have Haml not bother to generate output for that line rather than create an empty tag? For instance given this:
%h4= #my_hash[:optional]
...imagine that there's no data for that hash-key. I don't want to end up with:
<h4></h4>
I'd like no output at all from that line because empty tags can still affect page layout, particularly if you're using something CSS-heavy like Bootstrap.
I can write this everywhere:
-if #my_hash[:optional]
%h4= #my_hash[:optional]
but it's long-winded and ugly.
(It seems like lots of people would want to do this, but I couldn't find any mention of either a way to do it in the Haml docs, or people on SO or elsewhere asking how it could be done. So clearly everybody already knows how to do it except me, right?)
Update: Thanks for the suggestions. The thing is, it doesn't seem to matter what you put after the equals sign here:
%h4= amazing_intelligent_helper_method_but_sadly_too_late()
because Haml has already decided to output a tag at that point, the only question is what goes in the tag? If the expression after the equals sign evaluates to nil, Haml doesn't put any content in the tag - but it still outputs the tag itself.
%h4= nil #output: <h4></h4>
%h4= '' #output: <h4></h4>
%h4= false #unexpected output: <h4>false</h4>
%h4= #Haml::SyntaxError 'There's no Ruby code for = to evaluate.'
So at the moment, I don't have a one-line way of omitting empty tags. But surely I'm not the only one who wants to do this? When you look at some of the esoteric options Haml supports, I'd have really expected this feature to already be in there somewhere.
Just append the condition to the line ala Ruby statement modifiers:
%h4= #my_hash[:optional] if #my_hash[:optional]
Still long winded but at least its on one line now.
Here are some other techniques that were discussed but they're not any shorter or prettier: HAML: Create container/wrapper element only if condition is true
Try this code
%h4= #my_hash[:optional] unless #my_hash[:optional].blank?
Even though this is my question I thought I'd suggest a semi-OK-ish answer I've just thought of in the hope that someone will look at it and say 'Aha! I can do better than that.'
I could put this in the view
!= h4_if #my_hash[:options]
and I throw this in my helper file:
TAGS_TO_DELETE_WHEN_EMPTY = ['h1', 'h2', 'h3', 'h4', 'p', 'span']
TAGS_TO_DELETE_WHEN_EMPTY.each do |tag|
new_method_name = (tag + '_if').to_sym
define_method new_method_name do |content = nil|
"<#{tag}>#{content}</#{tag}" if content
end
end
then I only get an h4 tag if there's content for it. I can use these little conditional helper methods, coupled with Haml's unescaped ruby evaluator '!=', to get what I'm looking for. Downsides include: even though it's very concise in the view, and easy to extend, it doesn't look much like regular Haml. Also, I'm not 100% sure it won't upset Haml in some way if I use much of it.
Plus I'm still hoping someone will tell me there's an option in Haml to not output empty tags, because then I don't need to write any additional code at all (which is my favourite solution to problems).

set data-* attribute

I have a situation where I need to set dinamically the value for "data-*" on a div. The code I'm using actually is this one:
= tag('div', { class: 'agile-carousel',\
data: { carousel_list: url_for(format: :json) } }, true)
= "</div>".html_safe
Which is horrible. Also rails closes the div in a <div /> fashion, which chrome and other browsers definitely don't like (creating some disasters).
So my solution is that one. I would like to ask if there is another way to do this.
Update 1:
Small issue I have, this code:
span class==I18n.locale == locale_used ? "inactive" : nil
= link_to(I18n.t('locale_name', locale: locale_used),\
url_for(locale: locale_used))
If I remove link_to it works. Notice that if I insert even something like | text it returns an error: syntax error, unexpected keyword_ensure, expecting $end
You can use content_tag instead of tag to produce a <div></div> instead of a <div>, but you shouldn't.
You're using Slim, so use Slim:
.agile-carouesel data-carousel_list=url_for(format: :json)
Output:
<div class="agile-carouesel" data-carousel_list="some URL"></div>
The whole point of Slim and HAML is that you have this fantastically terse syntax for writing tags. You should use it, rather than resorting to Rails' tag helpers when they're not needed. By using them, you're reintroducing all the punctuation clutter that they're supposed to remove.

Weird problem with href attribute of anchors in Rails 3.1.0.rc4

In my Rails 3.1.0.rc4 application, if I do something like this:
<%= link_to "Back to Feature Index", features_path %>
I get this in the view :
Back to Feature Index (/features)
SO, I tried this:
HTML link
and I got this:
HTML link (blah)
I don't know why is this happening.
UPDATE:
In the first case, the HTML generated is:
Back to Feature Index
and the same is with the second case.
SO, I guess its something to do with the CSS. How can I fix this?
In the two examples you gave, what are the values of 1) the linked text, and 2) the href in the generated HTML?
If the hrefs are correct, but the linked text is not, it's possible that a stylesheet is adding the (href) displayed portion.
Try adding a:after { content: ""; } to your stylesheet.

Correct coding convention for embedded code on web page templates

I had come experience with PHP a time ago and now I'm learning to use Ruby on Rails. But one simple question bothered me in both these languages, so I think I can cross-post it to both tags.
As you know, one of the concepts there is that one can embed PHP or Ruby code into web page template. Then these statements are executed and result of its execution is inserted in certain places of the page, marked with "brackets" <%= ... %>.
Or... wait. We program Ruby/PHP, but not HTML. Maybe we should treat template as Ruby/PHP code, into which sometimes HTML markup is inserted? So the process is treated like that HTML are inserted into ruby code into the "brackets" %> ... <%.
These are two different approaches:
HTML page is the primary entity, and it is affected by code execution; or
code is the primary entity, and it is executed, while HTML snippets are inserted in certain places.
This philosophy leads to different possibilities in coding conventions: result of code execution influences the page If we adhere the first insight, then the code would look like this:
<p>
<% (1..10).foreach do |i| %>
Iteration number <strong><%= i %></strong>. <br/>
<% end %>
</p>
But if we stick to the second alternative, the code would be formatted like this:
<%
%><p><%
(1..10).foreach do |i|
%>Iteration number <strong><%
%><%= i %><%
%></strong>. <br/><%
end
%>
How should the concept of templates be construed? What concepts do you, way more experienced Web developers, account for the coding convention?
If this is in your View layer (and it should be), then the HTML is the primary entity. It's the most pertinent part of that layer -- marking up your data to display in meaningful ways to the user.
Even aside from that, your second example is nearly unreadable. I see what you're doing, but it took me a minute to wrap my brain around it. I've also never, ever seen View-layer code like your second example (and I would make it one of my priorities to change it wherever I saw it if it was in a project I was working on).
To be more concise: you're putting the emphasis on the wrong thing. In my opinion, readability trumps just about everything else. The coding style that produces the most readable code is therefore the most superior (ceteris paribus and YMMV, of course).
Maybe you should look into Haml? I don't know if there's a php equivalent, but as far as Rails goes, it's somewhere in between the two schemes. It's not quite code centric. But when used right, all the raw html is prepared programatically.
In short everything is considered text to be directly outputted, unless prefixed with either a %, - or =. Which translate to html-tag, ruby code that doesn't output. Ruby code that does output. Haml then uses whitespacing to nest things properly, much like python does. Raw html outputs untouched but using % to specify a tag handles closing tags.
Sample:
#outer-div
- #items.each do |i|
%span.item
= i
%br
Outputs
<div id="outer-div">
<span class="item">
item
</span>
<br>
</div>
See the haml tutorial for more information.
To answer the central question. The bulk of any page is going to be HTML or raw text. We reduce the bulk of that text with includes and helpers, but it's still there. If there were a truly code centered approach my use of it would depend on the ratio of program logic to html. Personally I'd rather go with the html centered approach.
If you are interested in a code-oriented view, this is something you might try implementing as a pure Ruby DSL:
tag :p, :class => 'iterations-container' do
(1..10).each do |i|
text "Iteration number "
tag :strong { text i }
text "."
tag :br
end
end
Or perhaps instead of tag :p do ... end, you may favor tag.p do ... end.
I recommend doing only very simple logic in your template files. That way designers who can edit HTML can easily edit even those files to alter the layout if need be.

Resources