I’m currently working on a Rails project that needs to generate Report PDFs. Ruport, of course, is an obvious choice for this task. It took me a little bit to get used to how Ruport does things (read: wants you to do things). I don’t want to dive too deep into getting started with Ruport. If you’re new to it, check out these two great guides: The Dimwit’s Guide and O’Reilly Network Ruport Guide. Please note, however, that the tutorials and guides often mention “Renderers”: they are called “Controllers” in the current version of Ruport. Just keep that in mind.
So back to my PDF columns problem: I wanted to be able to have all the text in the first column left-aligned and the text in all the other columns right-aligned. This turned out to be a little harder than I thought.
Let’s do this step by step (I assume you have installed the Ruport Gem and it’s dependencies). This will also show you how to use Ruport in your Rails app without acts_as_reportable:
- Create a "reports" directory inside your app directory.
- Add it to your Rails load path by adding this inside the Rails::Initializer.run block in your environment.rb:
config.load_paths += %W( #{RAILS_ROOT}/app/reports )
- For the next step, we assume to have class that looks something like this:
class MyReport < ActiveRecord::Base validates_presence_of :report_title, :report_subtitle
So we see our report can have n rows with n columns and two attributes: "report_title" and "report_subtitle".ColumnHeader has an attribute “name”
has_many :column_headers
We assume they come in the right order, and a row has_many columns.
A Column has an attribute “value”
has_many :rows end
- Create a file called "my_report_renderer.rb" in your app/reports directory.
- Put this in the file:
require 'ruport'
OK, now we have a renderer (or controller) that renders our report with all columns right-justified except for the first one.class MyReportRenderer < Ruport::Controller stage :report_header, :report_body finalize :report
class PDF < Ruport::Formatter::PDF renders :pdf, :for => MyReportRenderer
def build_report_header # data is an instance of your MyReport class add_text "My Great Report", :font_size => 20, :justification => :center add_text data.report_title, :font_size => 18 add_text data.report_subtitle, :font_size => 16 end def build_report_body # we have to build the table headers = [] # gather the column header strings in an array data.column_headers.each do |ch| headers << ch.name end table = Table(headers) do |t| data.rows.each do |row| columns = [] # gather the actual column values for each row in an array row.columns.each do |col| columns << col.value end # add the columns for the current row to the table t << columns end end # create column option that right justify all columns but the first one col_opts = {} header_index = 0 for header_index in (0..(headers.length - 1)) # we'll skip the first one, since :left is the default if header_index != 0 # the options for each column are indexed by it's name (headers array) col_opts[headers[header_index]] = {:justification => :right} end end pad (20) { draw_table(table, :column_options => col_opts) } end def finalize_report render_pdf end
end end
- Now just add an action to your MyReport Rails controller:
def download_pdf begin @report = MyReport.find(params[:id]) send_data(MyReportRenderer.render(:pdf, :data => @report), :filename => “report#{params[:id]}.pdf”, :type => “application/pdf”, :disposition => ‘inline’) rescue ActiveRecord::RecordNotFound flash[:error] = “Report not found.” redirect_to :action => :index end end
- That’s it.
If this was useful for you, please take a minute and recommend me:
Thank you!