Jan 25, 2009

PDF generation with Prawn in Merb

In my company, the customer service representatives asked me to generate a PDF file format of the price letter for our customers, because those are relative sensitive files, which was used to be in M$ word format.

I started using HTMLDOC at first, which is fine if you've already had the HTML version. Really, I like it. It is pretty simple to set up and running. You do not need to care about too much about the layout and format of the file, because it is relatively easy to do that in a html.erb file. But there is a catch on production servers, if you have, like me, several different types of environment, Red Hat, Ubuntu, OS X, you really have to be care of the PATH to the binary htmldoc command. Otherwise the Phusion Passenger will throw out very strange error message.

After I came back from Ruby Conf 2008(if you were there, you problaly knew Gregory Brown, he held a hack session with Prawn.), so I decide to give Prawn a try.

Here are the steps( of cause within Merb):

1. Add mime type in your config file
Merb::BootLoader.after_app_loads do
Merb.add_mime_type(:pdf, :to_pdf, %w[application/pdf], "Content-Encoding" => "gzip")
end
view raw init.rb hosted with ❤ by GitHub

2. In the view file, the form for the PDF request:
<%= form_for(@price_letter, :action => resource(:ordering, @price_letter, :priceletter, :format => "pdf"), :class => "form") do %>
# fields for ....
<% end =%>

Please notice the :format => 'pdf'
3. In controller, provides the pdf format:
def priceletter
provides :pdf
...
end


From here, you have two ways to send the PDF file:
In the controller/action
pdf = Prawn::Document.new(
:page_size => "A4",
:left_margin => 2.cm,
:right_margin => 2.cm,
:top_margin => 2.cm,
:bottom_margin => 1.5.cm
)
# logo
logo = image_dir + 'logo.jpg'
pdf.image logo, :position => :center
...
send_data pdf.render

Or, in a pdf.erb file
# price_letter.pdf.erb
<%=
require "prawn/measurement_extensions"
require "prawn/table"
pdf = Prawn::Document.new(
:page_size => "A4",
:left_margin => 2.cm,
:right_margin => 2.cm,
:top_margin => 2.cm,
:bottom_margin => 1.5.cm
)
# logo
logo = image_dir + 'logo.jpg'
pdf.image logo, :position => :center
pdf.font "Times-Roman"
...
pdf.render
%>

Now, you should be able to get a PDF file.

UPDATE(Feb-23-2009):
Someone asked what if you using 'GET' instead of post?
Well, it is pretty simple, you can just add the format like:
resource(:ordering, @price_letter, :print, :format => :pdf)
I am hoping this is helpful.

Jan 17, 2009

jQuery: unobtrusive javascript form submit with Ajax in Merb.

I was working on a project this week. There are several forms on one page, I want to submit any form without leaving the page. At first, this requirement really bothered me for a few minutes, after reading some documents from jQuery and Merb, it turns out pretty easy to implement:

1. View - file which have the forms you want to submit
In your form.html.erb file, you can create them like the normal forms.
<% @pos.each |purchase_order| do %>
<%= form_for(purchase_order, :action => url(:create_po_purchasing_shopping_lists)) do %>
<%= hidden_field :name => "counter", :value => count %>
<%= partial 'purchasing/purchase_orders/basic_form', :po => po %>
<button class="positive" type="submit">
<img alt="create" src="/images/icons/tick.png"></img>
Create
</button>
<% end =%>
<% end %>
view raw form.html hosted with ❤ by GitHub

2. Javascript(jQuery)
Here is the javascript to do the unobtrusive form submit.
<script type="text/javascript" >
jQuery.ajaxSetup({
'beforeSend': function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")}
});
jQuery.fn.submitWithAjax = function() {
this.submit(function() {
$.post(this.action + '.js', $(this).serialize(), null, "script");
return false;
})
return this;
};
$(document).ready(function() {
$("input").css({backgroundColor: "#FFFFFF"});
$("input").focus(function(){$(this).css({backgroundColor: "#FBE3E4"});});
$("input").blur(function(){$(this).css({backgroundColor: "#FFFFFF"});});
$("form").submitWithAjax();
});
</script>

3. Controller
In your controller's action, you have to specify provides :js
def create_po
provides :js
po = params[:purchase_order]
# do the creating job for the po
render
end
view raw controller.rb hosted with ❤ by GitHub

4. View - file which does the render
This is the good part I like Merb, you can mix Ruby code and javascript together to operate the DOM.
<% if @purchase_order %>
$("div.remote_<%= @counter %>").text("done");
<% else %>
<% if @params['vendor_name'] == "Enter the vendor's name" || @params['purchase_order'].nil? %>
$("#vendor_name").focus();
$("#error_<%=@counter%>").after('<span class="error">Vendor is required.</span>');
<% end %>
<% @params['cb3_ids'].each do |cb3_id| %>
<% %w{quantity unitsize price bt tcost}.each do |word| %>
<% if @params[word + '_' + cb3_id].to_f < 0 %>
$("input[name='<%= word %>_<%= cb3_id %>']").focus();
$("span.error").remove();
$("#error_<%=@counter%>").after('<span class="error"><%= word %> should > 0.</span>');
<% end %>
<% end %>
<% end %>
<% end %>


It is pretty easy and handy.

Jan 10, 2009

Create PDF file from image files

Recently, I had to create a PDF file from a lot of image files. Usually, I have to open the image in Preview, then save it to a pdf file. Once I get all the pdf files, combine them together to create one file. It is OK if you are only dealing with a few, but I am facing thousands of them. Finally, I find the Automator( Come with OS X). It is super helpful. Although this is the first time I am working with it, I love it.
Step 1: Select finder items


Step 2: Convert and combine


Step 3: Save as a finder plug in

Jan 4, 2009

Elaine is 6 month now!

Here is the video(HD) I edited during the new year holiday.
I'm 6-month now! from Elaine Zuo on Vimeo."