Creating a grouped output using Template toolkit - 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.

Related

Getting {", value= "=>"on"} as a paramater when I want an Object's id

I have a program that is supposed to simulate a delivery POS. A Delivery Object belongs_to a Meal Object, which has_many items. The form renders a bunch of items with a checkbox next to each item using helper methods like this...
#inside the app/models/item.rb file
def self.meal_field_maker(foods)
rStr = ""
foods.each do | sel |
rStr += "<p><input type= 'checkbox' name= 'meal[items][], value= '#{sel.id}'> #{sel.show} </p>"
end
return rStr
end
#inside the app/helpers/application_helper.rb file
def the_new_meal_form
foodgroups = Item.get_foodgroups #Gets each food group
rStr = ""
foodgroups.each do | sel |
rStr+= "\n<h3>#{sel}</h3>" #Adds the name of each Food Group as a header before checkboxes
groupedFoods = Item.list_items_of_group(sel) #Gets every item of a food group
rStr += Item.meal_field_maker(groupedFoods) #makes a checkbox input tag for each item in groupedFoods
end
return (rStr)
end
And this all renders properly in the form which looks like this...
<form method= "post" action= "new_user_delivery">
<input type= "hidden" name= "delivery[user]" value= <%= #user.id %>
<%= user_delivery_address_field(session).html_safe %>
<p>(Optional) Meal Name: <input type= "text" name="delivery[meal][name]"></p>
<p>----------------------------------------------------</p>
<%= the_new_meal_form.html_safe %>
<p>----------------------------------------------------</p>
<p>Proceed to Payment <input type= "submit" value= "Proceed">
</form>
And It looks like it all works perfectly, but on the next action the params look like this...
<ActionController::Parameters {"delivery"=>{"user"=>"11", "address"=>"98 Linden Ave", "meal"=>{"name"=>"FirstMeal"}}, "meal"=>{"items"=>[{", value= "=>"on"}, {", value= "=>"on"}, {", value= "=>"on"}, {", value= "=>"on"}, {", value= "=>"on"}]}, "controller"=>"deliveries", "action"=>"payment_options", "id"=>"11"} permitted: false>
The issue here is clearly that for every item I select, I just get {", value= "=>"on"}, which of course gives me no indication which Item Objects were chose, as you see this is ~supposed~ to return the Item's ID as the parameter value. How do I fix this?
Your line here:
rStr += "<p><input type= 'checkbox' name= 'meal[items][], value= '#{sel.id}'> #{sel.show} </p>"
is missing a single quote after 'meal[items][]. Also make sure you don't have spaces immediately after equals signs:
rStr += "<p><input type='checkbox' name='meal[items][]', value='#{sel.id}'> #{sel.show} </p>"
I would say that it's generally a code smell if you have HTML building code inside your model. It's usually better to keep those in views or helpers, but that's outside the scope of this question.

Applying the value of one field to multiple records in rails

I want to know the best way to apply the value of one form field to multiple records in rails.
I will have a view that renders partials where each partial will create a record. The record will have an association, quantity, and category. I want the user to be able to type in one category for multiple records but I cannot find any way to do this.
Effectively, I want the user to use a form like:
Cat1 Item1 1
Item2 2
Item3 3
Cat2 Item4 4
Item5 5
Cat3 Item6 6
Cat4 Item7 7
and have it create the records like:
Cat1 Item1 1
Cat1 Item2 2
Cat1 Item3 3
Cat2 Item4 4
Cat2 Item5 5
Cat3 Item6 6
Cat4 Item7 7
I do something like this in my project, but it's a bit complicated.
I use javascript on the page so that whenever the user enters a new item under a category then another blank filed appears in the form for the next item. Each item's field names and ids are suffixed with "__n" where n is a counter that increments so all the names and ids are unique and matching for the items, something like this:
<div class="category-input" id="cat1" >
<h2>Category 1</h2>
<div class="record-input">
<input type="text" name="cat1-item-name__0" />
<input type="number" name="cat1-item-amount__0" />
</div
<div class="record-input">
<input type="text" name="cat1-item-name__1" />
<input type="number" name="cat1-item-amount__1" />
</div
</div>
Then in the controller I have to sort through all the parameters and create records for each. I put the values into hashes to make it easier like this:
categories.each do |cat|
items = Hash[params.select{ |param| param[/^#{cat}-item-name__\d+$/] }.map{ |k,v| [k.split('__').last,v] }]
amounts = Hash[params.select{ |param| param[/^#{cat}-item-amount__\d+$/] }.map{ |k,v| [k.split('__').last,v] }]
# make records for category `cat` from these hashes
end

Analog of template toolkit block-include in erb 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!

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

Resources