Rendering nested HAML templates with blocks as parameters - ruby

I am currently using an helper method to render a HAML nested partial/template in Sinatra. For the sake of simplicity I just wrote a minimal example but of course my code is much larger. The point is that I want to factorise inner_template instead of copy/pasting everywhere:
require 'sinatra'
require 'haml'
helpers do
def inner_template(&block)
haml_tag('div', :class => 'title', &block)
end
end
get '/' do
haml :index
end
__END__
## layout
%html
= yield
## index
%div.div1
- inner_template do
%span Item 1
- inner_template do
%span Item 2
This correctly renders the page like this:
<html>
<div class='div1'>
<div class='title'>
<span>Item 1</span>
</div>
<div class='title'>
<span>Item 2</span>
</div>
</div>
</html>
The problem is that my real life inner_template method has a consequent number of tags, and I find it quite inelegant (and cumbersome when I must edit it). I also want to keep all the inner-most blocks (my items) on the same page, otherwise I'll end up with dozens of small files and it will be a headache to maintain that.
I am under the impression that, since I can use an helper method which only uses haml_tag methods, there must be a way to make everything work in pure HAML. But I cannot figure out how to do it properly. For instance if I just try the obvious way:
require 'sinatra'
require 'haml'
get '/' do
haml :index
end
__END__
## layout
%html
= yield
## index
%div.div1
= haml :inner_template do
%span Item 1
= haml :inner_template do
%span Item 2
## inner_template
%div.title
= yield
This doesn't work, the inner_template is rendered after its "inner" block (as to what exactly yields the value 1, I do not know):
<html>
<div class='div1'>
<span>Item 1</span>
<div class='title'>
1
</div>
<span>Item 2</span>
<div class='title'>
1
</div>
</div>
</html>
I tried a very long list of hacks, I also tried to use content_for or similar solutions (which only seem to apply to old versions of Sinatra anyway), but I cannot find examples that match my approach (which could be a hint that it's just not possible). And it looks like the block is not passed at all to the HAML renderer when calling haml.
So I would like to know whether or not this can be done in pure HAML (and how, or why not)?

Related

Linking to search results with Ruby

I'm a complete novice in Ruby and Nanoc, but a project has been put in my lap. Basically, the code for the page brings back individual URLs for each item linking them to the manual. I'm trying to create a URL that will list all of the manuals in one search. Any help is appreciated.
Here's the code:
<div>
<%
manuals = #items.find_all('/manuals/autos/*')
.select {|item| item[:tag] == 'suv' }
.sort_by {|item| item[:search] }
manuals.each_slice((manuals.size / 4.0).ceil).each do |manuals_column|
%>
<div>
<% manual_column.each do |manual| %>
<div>
<a href="<%= app_url "/SearchManual/\"#{manual[:search]}\"" %>">
<%= manual[:search] %>
</a>
</div>
<% end %>
</div>
<% end %>
</div>
As you didn't specify what items is returning, I did an general example:
require 'uri'
# let suppose that your items query has the follow output
manuals = ["Chevy", "GMC", "BMW"]
# build the url base
url = "www.mycars.com/search/?list_of_cars="
# build the parameter that will be passed by the url
manuals.each do |car|
url += car + ","
end
# remove the last added comma
url.slice!(-1)
your_new_url = URI::encode(url)
# www.mycars.com/?list_of_cars=Chevy,GMC,BMW
# In your controller, you will be able to get the parameter with
# URI::decode(params[:list_of_cars]) and it will be a string:
# "Chevy,GMC,BMW".split(',') method to get each value.
Some considerations:
I don't know if you are gonna use this on view or controller, if will be in view, than wrap the code with the <% %> syntax.
About the URL format, you can find more choices of how to build it in:
Passing array through URLs
When writing question on SO, please, put more work on that. You will help us find a quick answer to your question, and you, for wait less for an answer.
If you need something more specific, just ask and I can see if I can answer.

making a page break when pdf generated with wicked_pdf

sample.erb.html
<p>Page 1</p1>
<p>Page 2</p2>
So, everything after "Page 1" I want to print on the 2nd page.
How can I do this?
There's one solution in SO but it didn't work for me.
For example, in case of Prawn, it has a nice feature called start_new_page
in your css
p{page-break-after: always;}
Update
After a couple of questions, I will expand my answer and how I use it in my apps.
1 Some times, the wickedpdf helpers doesn't work, so I add an initializer
_config/initializers/wiked_pdf.rb_
module WickedPdfHelper
def wicked_pdf_stylesheet_link_tag(*sources)
sources.collect { |source|
"<style type='text/css'>#{Rails.application.assets.find_asset("#{source}.css")}</style>"
}.join("\n").gsub(/url\(['"](.+)['"]\)(.+)/,%[url("#{wicked_pdf_image_location("\\1")}")\\2]).html_safe
end
def wicked_pdf_image_tag(img, options={})
image_tag wicked_pdf_image_location(img), options
end
def wicked_pdf_image_location(img)
"file://#{Rails.root.join('app', 'assets', 'images', img)}"
end
def wicked_pdf_javascript_src_tag(source)
"<script type='text/javascript'>#{Rails.application.assets.find_asset("#{source}.js").body}</script>"
end
def wicked_pdf_javascript_include_tag(*sources)
sources.collect{ |source| wicked_pdf_javascript_src_tag(source) }.join("\n").html_safe
end
WickedPdf.config = {
}
end
2 In application controller create a config method with the general config params
_app/controllers/application_controller.rb_
class ApplicationController < ActionController::Base
def pdf_config
WickedPdf.config = {
:wkhtmltopdf => "/usr/local/bin/wkhtmltopdf",
:orientation => 'Landscape',
:layout => "pdf.html",
:footer => {
:left => "Rectores Lideres Transformadores",
#:left => "#{Entidad.find(#current_user.entidad).nombre}",
:right => "#{Time.now}",
:font_size => 5,
:center => '[page] de [topage]'
},
:disposition => 'attachment'
}
end
end
3 Create a common layout for all of your pdf files. Here I use my application css in order to maintain the same look and feel of web page in pdf reports, only I have to use the same classes and id's
app/layouts/pdf.html.erb
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<%= wicked_pdf_stylesheet_link_tag "application" %> ----- HERE YOUR APPLICATION CSS -----
</head>
<div id="content">
<%= yield %>
</div>
</body>
</html>
4 Add pdf redirection in your controllers
_app/controllers/users_controller.rb_
def index
#users = User.all
respond_to do |format|
format.pdf do
pdf_config
render :pdf => "filename"
end
end
end
5 Now, in your css, choose which html id is the page brake
#brake{page-break-after: always;}
I had the same problem and I discovered something that might help. This was my page break CSS code:
.page-break {
display: block;
clear: both;
page-break-after: always;
}
This didn't work because of TWO reasons:
I. In one of the SASS imported file I had this line of code:
html, body
overflow-x: hidden !important
II. The other problem was bootstrap
#import "bootstrap"
It looks like because of the float: left in:
.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
float: left;
}
the page break is no longer working. So, just add this after you import bootstrap.
.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
float: initial !important;
}
For anyone still having this problem but none of these answers, like for me, just aren't working:
I gave up on using CSS to fix the page breaks and instead generated 2 pdfs and merged them together and used the resulting file instead, that way there is no possible way for a page break to not exist.
To merge files, an array of pdf file names, I used
system("pdftk #{files.join(' ')} cat output merged_file_name.pdf")
Update
I don't remember where I generated 2 pdfs but I did manage to do these page breaks in a single pdf file by manually counting the pixels in the .html.erb files.
<% #pixel_height = 0 %> and <% #page_height = 980 %>. view the pdf as html to see how many pixels each section takes up. Add to #pixel_height.
In places a page break would make sense, I check #pixel_height + 20 >= #page_height (20 being the number of pixels a <tr> took up for most of our pdfs) and rendering a manual page break and resetting #pixel_height to 0. The manual page break closes all the html tags, adds a 0 pixel tall div with a page-break-after: always, and opens the html tags again.
I've only had 2 problems with this method:
If some text in the pdf is too long, it will line-break and throw off the #pixel_count causing a automatic page break in an odd spot and a manual page break also in an odd spot
WickedPdf is slow
To combat these 2 issues, we've been slowly migrating our pdfs to Prawn, specifically Prawn::Table. It is much faster and it calculates the height of each row before it draws the pdf so page breaks are more predictable and consistent
One quick method is to use below HTML:
<div style="page-break-before: always;"></div>
From this line onwards HTML Content will come in next page

Rails 3.1.4 - Render :text

I'm updating my rails 2 apps to rails 3 and find that the use of 'render :text' does not behave the same anymore.
#results is an array. In my controller:
render :text => "<ul>#{#results}</ul>"
It's returning the whole array as a string rather than iterating through each value:
<ul>
["
<li>Steve</li>
", "
<li>John</li>
"]
</ul>
Worked fine in Rails 2.x but not in 3. How do I fix this?
I'm expecting a result of:
<ul>
<li>Steve</li>
<li>John</li>
</ul>
I know this question is for Rails 3.1.4 only.
But those who come here and are on a more recent version, starting with Rails 5.1 we'll do this:
render plain: "I'm like everyone else."
The string contains HTML tags so you will need to mark it as safe so that Rails doesn't escape the tags.
render :text => "<ul>#{#results}</ul>".html_safe
NOTE: Unless there is a valid reason to have HTML in your controller, I recommend moving the list items to a view.
6/23/2014 UPDATE: In retrospect, I don't like having this string parsing logic in the controller. The #results suggests there is HTML embedded in an object somewhere. I recommend using a presentation object and call a method like #results.list. The Draper gem is well-suited for this.
Cite
https://github.com/drapergem/draper
I would suggest doing the following instead of render :text
render :partial => "result", :collection => #results
and add the file: _result.html.erb with
<ul>
<%= result %>
</ul>
or even better if you can remove the li tags from #results
<ul>
<li><%= result %></li>
</ul>
The Rails 3 docs say render text should be used for NON HTML text, which does not fit your use case. Using render :partial :collection is a better and more rails 3 way to iterate through your list.

Is sprintf incompatible with sinatra?

Say I have this:
class Account
...
property :charge, Decimal, :precision => 7, :scale => 2
...
classy stuff
...
def self.balance(prefix)
x = Account.get(prefix.to_sym).order(:fields => [:charge]).sum(:charge)
sprintf("%5.2f", x)
end
end
(Edit: The value of all :charge fields is 0.13E2 (0.1E2 + 0.3E1). This is correctly returned. Only in a View does it seem to get borked from sprintf)
In IRB Account.balance(:AAA) returns => "13.00"
if I call Account.balance(:AAA) from a view I get TypeError at /accounts
can't convert nil into Float
Account.balance(:AAA) works anywhere I call it except in a view. If I remove sprintf("%5.2f", x) I get 0.13E2 in my view. (using Account.balance(:AAA).to_f in a view gives me 13.0)
Is sinatra incompatible with sprintf? or am I not understanding how to use sprintf?
(Edit: This is the offending view:)
<section>
<% #accounts.each do |account| %>
<article>
<h2><%= account.prefix %></h2>
<span><p>This account belongs to <%= account.name %> & has a balance of $<%= Account.balance(account.prefix) %>.</p></span>
</article>
<% end %>
</section>
Wouldn't it make more sense to define balance as an instance method rather than a class method? It looks from your example like you're calling balance in an account-specific way anyway, so why not make it:
# the model
class Account
#...
def balance
amount = self.order(:fields => [:charge]).sum(:charge)
sprintf "%5.2f", amount
# or the infix version:
"%5.2f" % amount
end
end
,
# the view
...balance of $<%= account.balance %>...
I know that this doesn't address sprintf per se, but the problem is more likely to be coming from the slightly convoluted lookup than from a built-in method. Even if my specific code doesn't suit your application, it might be worth simplifying the lookup step, even if that involves a few more lines of code.
The advantage of this approach is that there is no doubt that you'll be getting the right Account record.
tested it with a little sinatra app and it worked for me
app.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
get '/' do
#x = 10.23
erb :index
end
views/index.erb
<%= sprintf("%5.2f", #x) %>
output:
10.23
ruby 1.9.2 / sinatra 1.3.1
I think there is another error before the sprintf because of your error message:
can't convert nil into Float
seems like your x is nil. try to be sure that x is not nil there, then sprintf should work as expected.

Best way to parse a table in Ruby

I'd like to parse a simple table into a Ruby data structure. The table looks like this:
alt text http://img232.imageshack.us/img232/446/picture5cls.png http://img232.imageshack.us/img232/446/picture5cls.png
Edit: Here is the HTML
and I'd like to parse it into an array of hashes. E.g.,:
schedule[0]['NEW HAVEN'] == '4:12AM'
schedule[0]['Travel Time In Minutes'] == '95'
Any thoughts on how to do this? Perl has HTML::TableExtract, which I think would do the job, but I can't find any similar library for Ruby.
You might like to try Hpricot (gem install hpricot, prepend the usual sudo for *nix systems)
I placed your HTML into input.html, then ran this:
require 'hpricot'
doc = Hpricot.XML(open('input.html'))
table = doc/:table
(table/:tr).each do |row|
(row/:td).each do |cell|
puts cell.inner_html
end
end
which, for the first row, gives me
<span class="black">12:17AM </span>
<span class="black">
</span>
<span class="black">1:22AM </span>
<span class="black">
</span>
<span class="black">65</span>
<span class="black">TRANSFER AT STAMFORD (AR 1:01AM & LV 1:05AM) </span>
<span class="black">
N
</span>
So already we're down to the content of the TD tags. A little more work and you're about there.
(BTW, the HTML looks a little malformed: you have <th> tags in <tbody>, which seems a bit perverse: <tbody> is fairly pointless if it's just going to be another level within <table>. It makes much more sense if your <tr><th>...</th></tr> stuff is in a separate <thead> section within the table. But it may not be "your" HTML, of course!)
In case there isn't a library to do that for ruby, here's some code to get you started writing this yourself:
require 'nokogiri'
doc=Nokogiri("<table><tr><th>la</th><th><b>lu</b></th></tr><tr><td>lala</td><td>lulu</td></tr><tr><td><b>lila</b></td><td>lolu</td></tr></table>")
header, *rest = (doc/"tr").map do |row|
row.children.map do |c|
c.text
end
end
header.map! do |str| str.to_sym end
item_struct = Struct.new(*header)
table = rest.map do |row|
item_struct.new(*row)
end
table[1].lu #=> "lolu"
This code is far from perfect, obviously, but it should get you started.

Resources