How to generate a PDF mail merge with Ruby on Rails? - ruby-on-rails

I'm working on a very simple mail merge system with Ruby on Rails. I have 2 models (members and letters).
First I create a html-formatted letter with some special fields (like {NAME}, {ADDRESS} or {CITY}.
Then, I must generate a PDF with one letter per member. So, if I have 100 members in the database, the PDF must have 100 letters, each one customized (replacing the special fields with the data in the database).
I know how to create the letters in html, but I don't know how to generate the PDF. Can you help me with that? I know there are some gems to create PDFs, but I don't know how to add the letters on different pages.
Thanks for your help!

I have used Prawn to generate PDFs and it sounds like it would work for you. There’s a wiki article on using Prawn with Rails.
The go_to_page method will let you position elements on a particular page after you have used add_pages to create them.

Related

Inserting external PDF into Prawn generated document

How can I insert an existing PDF into a Prawn generated document? I am generating a pdf for a bill (as a view), and that bill can have many attachments (png, jpg, or pdf). How can I insert/embed/include those external pdf attachments in my generated document? I've read the manual, looked over the source code, and searched online, but no luck so far.
The closest hint I've found is to use ImageMagick or something similar to convert the pdf to another format, but since I don't need to resize/manipulate the document, that seems wasteful. The old way to do it seems to be through templates, but my understanding is that the code for templating is unstable.
Does anyone know how to include PDF pages in a Prawn generated PDF? If Prawn won't do this, do you know of any supplementary gems that will? If someone can point me towards something like prawn-templates but more reliable, that would be awesome.
Edit: I am using prawnto and prawn to render PDF views in Rails 4.2.0 with Ruby 2.2.0.
Strategies that I've found but that seem inapplicable/too messy:
Create a jpg preview of a PDF on upload, include that in the generated document (downsides: no text selection/searching, expensive). This is currently my favorite option, but I don't like it.
prawn-templates (downside: unstable, unmaintained codebase; this is a business-critical application)
Merge PDFs through a gem like 'combine-pdf'–I can't figure out how to make this work for rendering a view with the external PDFs inserted at specific places (the generated pdf is a collection of bills, and I need them to follow the bill they're attached to)
You're right about the lack of existing documentation for this - I found only this issue from 2010 which uses the outdated methods you describe. I also found this SO answer which does not work now since Prawn dropped support for templates.
However, the good news is that there is a way to do what you want with Ruby! What you will be doing is merging the PDFs together, not "inserting" PDFs into the original PDF.
I would recommend this library, combine_pdf, to do so. The documentation is good, so doing what you want would be as simple as:
my_prawn_pdf = CombinePDF.new
my_prawn_pdf << CombinePDF.new("my_bill_pdf.pdf")
my_prawn_pdf << CombinePDF.new("attachment.pdf")
my_prawn_pdf.save "combined.pdf"
Edit
In response to your questions:
I'm using Prawn to render a pdf view in Rails, which means that I don't think I get that kind of post-processing
You do! If you look at the documentation for combine_pdf, you'll see that loading from memory is the fastest way to use the gem - the documentation even explicitly says that Prawn can be used as input.
I'm not just tacking the PDFs to the end: a bill attachment must directly follow the generated page(s) for a bill
The combine_pdf gem isn't just for adding pages on the end. As the documentation shows, you can cycle through a PDF adding pages when you want to, for example:
my_pdf # previously defined
new_pdf = CombinePDF.new
my_pdf.pages.each.do |page|
i += 1
new_pdf << my_pdf if i == bill_number # or however you want to handle this logic
end
new_pdf.save "new_pdf.pdf"

How to confirm partial line of text in Ruby

I'm writing a test to confirm that a csv file has hit my downloads folder. As the title of the csv file is set to include the date and time of the download, it's impractical to keep changing the name of the file in my feature. Example filename: fleet_123456_20140707_103015.csv
Can I include in my ruby code, something that will just confirm that the "fleet_123456" is present as it's the only generic part of the name that will appear on every download?
At the moment I have:
Then /^I should get a download with the filename "(.*?)"$/ do |file_name|
page.response_headers['Content-Disposition'].should include("filename=\"#{file_name}\"")
end
I'm thinking that the "#{file_name}\"") needs tweeking, just not sure where.
Any help would be great, thank you
You asked:
Can I include in my ruby code, something that will just confirm that the "fleet_123456" is present as it's the only generic part of the name that will appear on every download?
Yes, you can. One way would be to replace the include matcher with a regex-based one. For example, instead of
page.response_headers['Content-Disposition'].should include("filename=\"#{file_name}\"")
you could write
page.response_headers['Content-Disposition'].should match(/filename="fleet_[\d_]+.csv"/)
, which would match "fleet_123456" followed by any combination of numbers and underscores, followed ".csv". Another possibility, if you want to be a little more specific, is
page.response_headers['Content-Disposition'].should match(/filename="fleet_123456_\d+_\d+.csv"/)
which matches the specific arrangement of groups of numbers separated by underscores. You can read about regular expressions in Ruby here and play with them here.

Rails comments system with bb-code

In my rails 4 app i want to add comments to my articles, but i want to add functional as most forum-engines do (like SMF), and i need to add bb-code for it.
Are there any good gem for it? With rails 4 support? How then in controller i can translate [quote] to some div with some style?
Also how is it good to store html data in database?
For example if i use haml, and somebody post comment as
- current_user.id
or something similar to this, how to secure my app from "bad boys" ? Sure i can change comments system to something like: quote_parent_id, but if i have multiple quotes in one comment? so it is hard to realise, better is to store html, but to secure it somehow.
Could i do this? And how? Please give good ideas, tutorials, gem-links.
Look into https://github.com/veger/ruby-bbcode
Since it converts to HTML and does not excecute user input as Ruby code - you'll be fairly safe. However, I havent tried the gem and its possible it introduces some XSS vulnerabilities.
Have you considered Markdown as an option?
You should also look into https://github.com/asceth/bbcoder ( I should note I am the original author ).
In the controller, changing a string such as "[quote=user]My post of epic importance[/quote]" into a div etc is just doing:
# assume params[:comment] is the text you are converting
params[:comment].bbcode_to_html
As for storing html in a database, there is no right or wrong answer. If you want to allow users to edit their posts later then I would lean towards not storing the html version but storing their original bbcode version. This way when you allow them to edit you aren't having to convert html back to bbcode.
To make sure you aren't open to XSS and other attacks I recommend combining other gems like sanitize.
Sanitize.clean(text.to_s).bbcode_to_html
Some more notes:
Multiple tags and nested tags are parsed as they are seen without any additional steps required. So a comment or post with lots of bbcode tags, multiple quotes, b tags or anything else is dealt with by just calling bbcode_to_html on the variable/string.
If a user tries to use haml in their post it should appear as-is. haml shouldn't try to eval the string unless you specifically tell it to which I'm not even sure how to do that unless haml as a special filter or operator.

Custom URL format for news in Expression Engine

Our site is migrating from MovableType to ExpressionEngine, and there is one small issue we are having. MT uses a date based URL structure, e.g. www.site.com/2012/03/post-title.html, while EE uses a category based structure, e.g. www.site.com/index.php/news/comments/post-title. The issue is that our MT page used Disqus for comments, and as such comments are tied to a specific URL, meaning that we'd lose all of our comments if we were to migrate. I am wondering if there's a way to change the URL structure in EE to match MT's, thus allowing us to keep the comments. Thanks in advance.
Correction: EE uses a Template Group/Template based structure for URLs, not categories - just to clarify.
You've got a couple of options here.
One is to create an .htaccess rule which internally redirects all requests matching YYYY/MM/ to your EE template which displays your posts (say, /news/entry/). I don't know exactly what those rewrite rules would look like off the top of my head, my mod_rewrite-fu is pretty shallow. But it could definitely work.
Another is to export all of your comments from Disqus via their XML export tool, then do a grep-based find and replace using something like BBEdit, replacing all /YYYY/MM/ strings in that file with /news/entry/; delete all of your existing comments on Disqus; then import your newly-modifed XML file.

If using Ruby on Rails, how to use 1 code base for 2 websites for the HTML elements' IDs and class names to be different?

If using Ruby on Rails, we can set a config file to have all the different header titles, phrases, so as to different phrases on 2 different websites.
But how about the IDs and class names (in .erb or .haml, and in .sass or .css)?
(The reason to use particular category names for the IDs and classes is so that search engines can better index the site (to see what actual content is on the site))
In .haml it is something like
#sport-watches-list
which is the same as
<div id="sport-watches-list">
and there is corresponding .sass or .css styles that go with it. So if the other website is
#fancy-cars-list
How do we make it work on Ruby on Rails? It is because there can be many such names in one page, and there are several types of pages for each website, with either watches or cars in the name:
#fancy-cars-list
#fancy-car-row
#fancy-car-photo
#fancy-car-name
#fancy-car-rating
We can have a general file with
#product-category-list
and all the markups, and use a script to replace all of them to either watches or cars, and do it also to the .sass files, but it will be quite messy. Is there a better way?
The reason to use particular category names for the IDs and classes is so that search engines can better index the site (to see what actual content is on the site)
There is no need to structure your id's and class names for Search Engines as search engines do not use this markup. You're time is better spent ensuring the content of each site is great.
Build sites for users not search engines, and the search engines will follow ;)

Resources