Analog of template toolkit block-include in erb rails - ruby-on-rails

What is the analog of template toolkit block-include in erb rails?
Or how to translate this code to rails erb (only one single file):
[% BLOCK hello %]
Hello [% var %]!
[% END %]
[% INCLUDE hello var='World' %]
result:
Hello World!

Related

Iterate over hashref Template Toolkit

The data that I'm passing to the template is as follows:
This is the 'tickets' hashref used in the following foreach:
$VAR1 = {
'1234' => {'request_time' => '1405392890', 'id' => '1234'},
'9993' => {'request_time' => '1485035309', 'id' => '9993'}
};
I am doing the following:
[% FOREACH ticket IN tickets %]
<td>[% ticket.request_time %]</td>
<td>[% ticket.id %]</td>
[% END -%]
But this doesn't seem to display anything. Could someone point out where I'm wrong?
You also need to be careful that TT doesn't treat a numeric hash-key as an array-element reference. If there's a risk of confusion (or if you have a clash between hash-keys and vmethod names), then the vmethod item() is particularly useful:
[% FOREACH ticket IN tickets.keys.nsort %]
<td>[% tickets.item(ticket).request_time %]</td>
<td>[% ticket %]</td>
[% END -%]
I wasn't aware you had to use the 'keys' vmethod:
[% FOREACH ticket IN tickets.keys %]
<td>[% tickets.$ticket.request_time %]</td>
<td>[% tickets.$ticket.id %]</td>
[% END -%]

How do I scrape HTML between two HTML comments using Nokogiri?

I have some HTML pages where the contents to be extracted are marked with HTML comments like below.
<html>
.....
<!-- begin content -->
<div>some text</div>
<div><p>Some more elements</p></div>
<!-- end content -->
...
</html>
I am using Nokogiri and trying to extract the HTML between the <!-- begin content --> and <!-- end content --> comments.
I want to extract the full elements between these two HTML comments:
<div>some text</div>
<div><p>Some more elements</p></div>
I can get the text-only version using this characters callback:
class TextExtractor < Nokogiri::XML::SAX::Document
def initialize
#interesting = false
#text = ""
#html = ""
end
def comment(string)
case string.strip # strip leading and trailing whitespaces
when /^begin content/ # match starting comment
#interesting = true
when /^end content/
#interesting = false # match closing comment
end
def characters(string)
#text << string if #interesting
end
end
I get the text-only version with #text but I need the full HTML stored in #html.
Extracting content between two nodes is not a normal thing we'd do; Normally we'd want content inside a particular node. Comments are nodes, they're just special types of nodes.
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<body>
<!-- begin content -->
<div>some text</div>
<div><p>Some more elements</p></div>
<!-- end content -->
</body>
EOT
By looking for a comment containing the specified text it's possible to find a starting node:
start_comment = doc.at("//comment()[contains(.,'begin content')]") # => #<Nokogiri::XML::Comment:0x3fe94994268c " begin content ">
Once that's found then a loop is needed that stores the current node, then looks for the next sibling until it finds another comment:
content = Nokogiri::XML::NodeSet.new(doc)
contained_node = start_comment.next_sibling
loop do
break if contained_node.comment?
content << contained_node
contained_node = contained_node.next_sibling
end
content.to_html # => "\n <div>some text</div>\n <div><p>Some more elements</p></div>\n"

How do I remove white space between HTML nodes?

I'm trying to remove whitespace from an HTML fragment between <p> tags
<p>Foo Bar</p> <p>bar bar bar</p> <p>bla</p>
as you can see, there always is a blank space between the <p> </p> tags.
The problem is that the blank spaces create <br> tags when saving the string into my database.
Methods like strip or gsub only remove the whitespace in the nodes, resulting in:
<p>FooBar</p> <p>barbarbar</p> <p>bla</p>
whereas I'd like to have:
<p>Foo Bar</p><p>bar bar bar</p><p>bla</p>
I'm using:
Nokogiri 1.5.6
Ruby 1.9.3
Rails
UPDATE:
Occasionally there are children nodes of the <p>Tags that generate the same problem: white space between
Sample Code
Note: the Code normally is in one Line, I reformatted it because it would be unbearable otherwise...
<p>
<p>
<strong>Selling an Appartment</strong>
</p>
<ul>
<li>
<p>beautiful apartment!</p>
</li>
<li>
<p>near the train station</p>
</li>
.
.
.
</ul>
<ul>
<li>
<p>10 minutes away from a shopping mall </p>
</li>
<li>
<p>nice view</p>
</li>
</ul>
.
.
.
</p>
How would I strip those white spaces aswell?
SOLUTION
It turns out that I messed up using the gsub method and didn't further investigate the possibility of using gsub with regex...
The simple solution was adding
data = data.gsub(/>\s+</, "><")
It deleted whitespace between all different kinds of nodes... Regex ftw!
This is how I'd write the code:
require 'nokogiri'
doc = Nokogiri::HTML::DocumentFragment.parse(<<EOT)
<p>Foo Bar</p> <p>bar bar bar</p> <p>bla</p>
EOT
doc.search('p, ul, li').each { |node|
next_node = node.next_sibling
next_node.remove if next_node && next_node.text.strip == ''
}
puts doc.to_html
It results in:
<p>Foo Bar</p><p>bar bar bar</p><p>bla</p>
Breaking it down:
doc.search('p')
looks for only the <p> nodes in the document. Nokogiri returns a NodeSet from search, or a nil if nothing matched. The code loops over the NodeSet, looking at each node in turn.
next_node = node.next_sibling
gets the pointer to the next node following the current <p> node.
next_node.remove if next_node && next_node.text.strip == ''
next_node.remove removes the current next_node from the DOM if the next node isn't nil and its text isn't empty when stripped, in otherwords, if the node has only whitespace.
There are other techniques to locate only the TextNodes if all of them should be stripped from the document. That's risky, because it can end up deleting all blanks between tags, causing run-on sentences and joined words, which probably isn't what you want.
A first solution can be to remove empty text nodes, a quick way to do this for your exact case can be:
require 'nokogiri'
doc = Nokogiri::HTML("<p>Foo Bar</p> <p>bar bar bar</p> <p>bla</p>")
doc.css('body').first.children.map{|node| node.to_s.strip}.compact.join
This won't work for nested elements as-is but should give you a good path for start.
UPDATE:
You can actually optimise a little with:
require 'nokogiri'
doc = Nokogiri::HTML::DocumentFragment.parse("<p>Foo Bar</p> <p>bar bar bar</p> <p>bla</p>")
doc.children.map{|node| node.to_s.strip}.compact.join
Here is all the possible task you can be looking for which deals with unnecessary whitespaces(including unicode one) in parsing output.
html = "<p>A paragraph.<em> </em> <br><br><em>
</em></p><p><em> </em>
</p><p><em>
</em><strong><em>\" Quoted Text \" </em></strong></p>
<ul><li><p>List 1</p></li><li><p>List 2</p></li><li><p>List 3 </p>
<p><br></p><p><br><em> </em><br>
A text content.<br><em><br>
</em></p></li></ul>"
doc = Nokogiri::HTML.fragment(html)
doc.traverse { |node|
# removes any whitespace node
node.remove if node.text.gsub(/[[:space:]]/, '') == ''
# replace mutiple consecutive spaces with single space
node.content = node.text.gsub(/[[:space:]]{2,}/, ' ') if node.text?
}
# Gives you html without any text node including <br> or multiple spaces anywhere in the text of html
puts doc.to_html
# Gives text of html, concatenating li items with a space between them
# By default li items text are concatenated without the space
Nokogiri::HTML(doc.to_html).xpath('//text()').map(&:text).join(' ')
#Output
# "A paragraph. \" Quoted Text \" \n List 1 \n List 2 \n \n List 3 \n A text content. \n \n"
# To Remove newline character '\n'
Nokogiri::HTML(doc.to_html).xpath('//text()').map(&:text).join(' ').gsub(/\n+/,'')
#Output
# "A paragraph. \" Quoted Text \" List 1 List 2 List 3 A text content."
Note: If you are not using fragment in case of a complete html doc then you might have to replace traverse with other function like search.
data.squish does the same thing and is way more readable.

Escape ruby code inside HTML string

I have an object in my controller
#data="<meta property=\"og:url\"
content=\"<%=SITE_URL%><%=request.request_uri%>\"
/>".html_safe
When I print this in my view file by <%=#data%> the html that it generates is
<meta property="og:url"
content="<%=SITE_URL%><%=request.request_uri%>"
/>
How do i print out/escape the ruby code withing the string?
You're looking for string interpolation, which means that in the string, it substitutes the value of the variable. Surround the variables with #{} like this:
http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals#Interpolation
#data="<meta property=\"og:url\"
content=\"#{SITE_URL} #{request.request_uri}\"
/>".html_safe
Now, when you do <%= #data %>, it should show up normally.
#data="<meta property=\"og:url\"
content=\"<%= #{SITE_URL}%><%= #{request.request_uri} %>\"
/>"
In your views use raw helper method
<%= raw(#data) %>
You can try h helper like this.....
<%=h(#data) %>
also raw can be use
<%=raw(#data) %>

Creating a grouped output using Template toolkit

I have a datafile
city : name
London : John
London : George
Paris : Jean
And I would like output
London
John
George
Paris
Jean
I feel i want something like
[% USE namelist = datafile( 'namelist.txt' ) %]
[% FOREACH city = unique namelist.city ??? %]
[% city %]
[% FOREACH name =???? %]
[% name %]
[%END %]
[%END %]
It is probably best to do this kind of data munging in your controller. Template toolkit's job is really to lay things out and make them pretty, not do "calculations".
What you want is something more like:
[% SET list = formatter.group_them('namelist.txt') %]
[% FOREACH item IN list %]
[% item.key %]
[% FOREACH name IN item.value %]
[% name %]
[% END %]
[% END %]
It is possible to do what you want in a variety of ways. You can use VMethods http://template-toolkit.org/docs/manual/VMethods.html to split, create an array, split again, build a hash:
[% data = INSERT 'namelist.txt' %]
[% lines = data.split("\n") %]\
[% list = {} %]
[% FOREACH line IN lines %]
[% pair = line.split(': ') %]
[% city = pair.0; name = pair.1; list.city.push(name) %]
[% END %]
OK, I have to admit, I would be mortified to see this in a template I inherited. But sometimes we do things that mortify others for a good reason... at the time... :-)
A slightly better approach is to insert
[% RAWPERL %]
block. At least then, you are admitting, you have code within the template and doing the operations in a natural and efficient way.

Resources