How to create PDF’s and Images from your website in Rails

I am going to show you how to generate both a pdf and image from a single action in a controller using the awesome, wkhtmltopdf library. This also uses PDFKit and WebSnap gems available on GitHub. This example assumes the following:

  • wkhtmltopdf and wkhtmltoimage are already installed and accessible on in the PATH.
  • You have an html page setup to display the record.
  • You have created a pdf CSS file to help display the pdf, if you so choose.
# config/initializers/mime_types.rb
Mime::Type.register "application/pdf", :pdf
Mime::Type.register "image/png", :png

# app/controllers/items_controller.rb
def show
  @item = Item.find(params[:id])
  
  respond_to do |format|
    format.html { }
    format.pdf {
      html = render(:action => "show.html.erb")
      
      kit = PDFKit.new( html, :zoom => 0.75 )
      kit.stylesheets << File.join(RAILS_ROOT,
                                   "public",
                                   "stylesheets",
                                   "pdf.css")
      
      send_data kit.to_pdf, :filename => "item.pdf",
                            :type => "application/pdf",
                            :disposition => "inline"
    }
    format.png {
      html = render :action => "show.html.erb",
                    :layout => "application.html.erb"

      # I am nil’ing these options out because my version of wkhtmltoimage does
      # not support the scale options and I do not want to crop the image.
      snap = WebSnap.new(html, :format => "png",
                               :"scale-h" => nil,
                               :"scale-w" => nil,
                               :"crop-h" => nil,
                               :"crop-w" => nil,
                               :quality => 100,
                               :"crop-x" => nil,
                               :"crop-y" => nil)

      send_data snap.to_bytes, :filename => "item.png",
                               :type => "image/png",
                               :disposition => "inline"
    }
  end
end

Now you should be able to access three distinct views, each producing a different result

http://example.com/items/1 # => Generates an html page.
http://example.com/items/1.pdf # => Generates a pdf of the html page.
http://example.com/items/1.png # => Generates a png of the html page.

You could easily also add more image types by just created another block for each format, and changing the :format to whatever one you would like.

Including methods and associations in a JSON Data set with Rails

I was poking around while working with creating an application specifically for web services. We decided to use JSON as the methods of transportation of data, but the problem came when I wanted to include custom methods, or associations in my data set. The solution was fairly simple, using the to_json method. Suppose you have the following classes:

class Client < ActiveRecord::Base
  has_many :employees
end

class Employee < ActiveRecord::Base
  belongs_to :client

  def full_name
    "#{first_name} #{last_name}"
  end
end

We want the controller to return a client with association employees and the full name in the database. Here is how we would go about doing that:

def show
  @client = Client.find(params[:id])

  respond_to do |format|
    format.json { render :json => @client.to_json(:include => { :employee => { :only => :email, :methods => [:full_name]}})}
  end
end

You will end up with the following data set:

{
  client: {
    name: "Some client",
    employee: {
      email: "test@test.com",
      full_name: "John Doe"
    }
  }
}

Forgive me if I messed up the json output…doing it from memory 🙂 There are of course way easier uses for this too, but I just decided to spit out a more complex one.

ActiveRecord – Making :include and :select play nice!

So I have been using a nice plug-in that will allow me to using :select when using :include, and not have it pull the entire data set. You can add the plug-in to your app like this:

script/plugin install git://github.com/blythedunham/eload-select.git

Here are some ways to use the plug-in:

Employee.find :all,
  :select => "addresses.city, address.state, employees.*",
  :include => :address

Employee.find :first,
  :select => "now() as current_time, addresses.city, DATE(addresses.created_at) as addresses.created_at, employee.*",
  :include => :address

Employee.find :all,
  :select => "addresses.city, employees.name, employees.start_date",
  :include => :address

Examples taken from:

http://www.snowgiraffe.com/tech/329/eager-loading-select-plugin-when-select-plays-nice-with-include/

Turn off Rails sessions by request type

Here is a solution to turn sessions off based on a request type. I added this to application.rb file:

session :off,
  :if => Proc.new { |req| ['xml', 'rss'].include?(req.params[:format]) }

You could ideally just add this to an individual controller, and add :only or :except to it as well, but I wanted to eliminate all of the controllers and actions.

Easy True/False Select Tag with Rails

It’s been a while since my last post. I have been pretty busy with other things, but thats no excuse. Anyway, I came across a problem where I needed to create a select tag with just a true and false option in it and came up with the following quick and easy solution. (This would presumably work with more/different options as well): First, create a file in the config/initializers directory. This is where your global variables will go for use in the select tags. I called mine select_tag_globals.rb:

TRUE_FALSE = { 'True'=>true, 'False'=>false }

Make sure the values are set to whatever the fields data type is. In this case I want the labels to say True or False, and the value saved to the database will be true or false. Now all we have to do is use this in a select tag in a view like so:

<%= f.select :database_field, TRUE_FALSE %>

This should give you a normal select box with True and False labels. Yay rails!

ActiveRecord and Transactions!

If you ever have to do multiple actions with activerecord, you should group it into a transaction like so:

Post.transaction do
  posts.each do |post|
    post = Post.new(:body=>'Whatever')
    post.save!
  end
end

This will rollback too if the save fails.

Restful Authentication and Basic HTTP Auth

I had an application that was prompting a HTTP Authentication box in IE after using the restful authentication plugin. The way I fixed it was to replace the access_denied method in lib/authenticated_system.rb to:

def access_denied
  respond_to do |format|
    format.any do
      #format.html do
      store_location
      redirect_to new_session_path
    end
    #format.any do
      #  request_http_basic_authentication 'Web Password'
    #end
  end
end

Noticed I commented some lines out. This was so that if i ever need to revert, the code will still be there and I wont have to go looking for it. Hope this helps!

Rails route helpers – route_url vs route_path

If you weren’t already aware of this, there IS a difference between using route_url and route_path. Here are what they return:

<%= posts_url %>  # => http://localhost:3000/posts
<%= posts_path %> # => /posts

As a general rule of thumb, you would want to use posts_url in your controllers, and posts_path in your views.

href jumps to top of page

While this isn’t a ruby on rails specific issue, I do tend to do this a lot, and never thought twice about it, until it starts bugging the crap out of me. The issue is that you have a link that is used to do some type of javascript, instead of linking. So you add an onclick event, but you need something to link to. I usually put in a # sign:

Do Something

 
<%= link_to 'Do Something', '#', :onclick=>'dosomething()' %>

This is great, except that it jumps you to the top of the page. Very annoying. Here is the solution:

Do Something

 
<%= link_to 'Do Something', '#nogo', :onclick=>'dosomething()' %>

Notice I added “nogo” to my #. This will help prevent from jumping to the top of the page. You can of course use any text you want there.

ActiveRecord conditions with association from hash

I’m sure you all know how to use the :conditions attribute when using ActiveRecord:

User.find(:all, :conditions=>['active = ?', true])

And you may even use associations this way:

User.find(
  :all,
  :include=>[:photos],
  :conditions=>['photos.removed = ? and users.active = ?', false, true]
)

But did you know that you can do this easier through hashes?

User.find(:all, :conditions=>{:active=>true})
User.find(
  :all,
  :include=>[:photos],
  :conditions=>{'photos.removed'=>false, 'users.active'=>true}
)

Nothing special there, but I thought it was pretty cool. One thing you have to remember when using associations, is to include that model.