Google Charts with Rails

Posted by chris olsen on January 26, 2008

Nothing beats some nice charts to dazzle your clients. I guess this is another addition to a couple of previous posts that go over various charting tools that can be found here and here.

If you haven’t checked out Google Charts go here to check out the API. To use Google’s charts you have to encode the data with one of the three allowable formats.

  • Simple encoding uses the alphanumeric characters (A to Z, a to z, and 0 to 9) where A represents 0, B represents 1, and so on up to 9, representing 61 to provide a resolution of 62 different values. Allowing five pixels per data point, this is sufficient for line and bar charts up to about 300 pixels. Simple encoding is suitable for all other types of chart regardless of size. This type of encoding results in the shortest URL.
  • Text encoding uses floating point numbers between 0.0 and 100.0 to provide a resolution of 1,000 different values. Allowing five pixels per data point, integers (1.0, 2.0, and so on) are sufficient for line and bar charts up to about 500 pixels. Include a single decimal place (35.7 for example) if you require higher resolution. Text encoding is suitable for all other types of chart regardless of size.
  • Extended encoding uses pairs of alphanumeric (plus a few others that are discussed later) where AA represents 0, AB represents 1, and so on up to two periods (..) representing 4095 to provide a resolution of 4,096 different values. This is best suited for large charts with a large data range is required.

The good thing is, is that there are a few tools that are available for Rails that will do the work for us. I fooled around with the top two of them, although I am a little unsure of the status of either.

I had some issues with Google Charts on Rails as the labels would not render as they were supposed to. After looking at the source code it seemed that, by all the #TODO… comments that the development on this plugin may be discontinued.

With the first not working as I thought it would I moved onto the second. Google Charts worked very nicely and within a couple of minutes of reformatting the reporting data I had some pretty cool charts.

Since Google Charts works I never even bothered to test gchartb although I will put it on my to-do list.

So let’s create some graphs. First thing that we have to do is install the GoogleCharts gem

sudo gem install googlecharts

Once that is done we will have to add the following to the top of our controllers/application.rb file. You could also just add this line to the controller(s) that contain reporting methods.

require "gchart"  #the line to add
 
class ApplicationController < ActionController::Base
  ...

For the tests I created a Seller and Sale models with the following migration files.

# 001_create_sellers.rb
class CreateSellers < ActiveRecord::Migration
  def self.up
    create_table :sellers do |t|
      t.string :name
      t.timestamps
    end
  end
 
  def self.down
    drop_table :sellers
  end
end
 
# 002_create_sales.rb
class CreateSales < ActiveRecord::Migration
  def self.up
    create_table :sales do |t|
      t.decimal :amount, :precision => 10, :scale => 2
      t.date :sold_on
      t.integer :seller_id
      t.timestamps
    end
  end
 
  def self.down
    drop_table :sales
  end
end

And of course to test out the charts we will need some test data.

# 003_test_data.rb
class SampleData < ActiveRecord::Migration
  def self.up
    # create some sellers
    john = Seller.create(:name => "John")
    susan = Seller.create(:name => "Susan") 
 
    # add sales to sellers
    [john, susan].each do |seller|
      (0..20).each do |i|
        Sale.create(:amount => rand(1000), :seller_id => seller.id, :sold_on => DateTime.now + i.days)
      end
    end
  end
  def self.down
  end
end

Now we have to be able to access the data in a format that will work well with charts. To do this I created a sales_report method within the Seller model.

class Seller < ActiveRecord::Base
  def sales_report
    data = []
    labels = []
    Sale.find_all_by_seller_id(self.id).each do |s|
      labels << s.sold_on.to_date.strftime("%B %d")
      data << s.amount
    end
    {:labels => labels, :data => data}
  end
end

That is it for the dirty work, it is smooth sailing from here (said with a lot of sarcasm).

All we have to do now is access the data from the controller.

class SalesReportController < ApplicationController
  def show
    @report = Seller.find(params[:id]).sales_report
  end
end

and make sure that the routing is set up by adding the following to the router.rb file

map.resources :sales_report

To display it on the page I created a show.html.erb file with the following code.

<%= image_tag Gchart.bar(:title => 'Sales', :size => '600x200', :data => @report[:data], :custom => "chbh=6,2,0") %>

I did leave out the labels on the chart. Since I had so much data they were taking up way too much room. To add the labels you would just append :labels => @report.labels to the arguments in the bar method call.

Here is the final result.

Picture 1-1

It is pretty cool that Google provides us with the service free of charge. The downfall is that there is a daily limit of 50,000 charts that can be generated per site. I can’t think of anything that I have worked on that would exceed that number, but things can change. The other issue is that Google has control and at any time could discontinue supplying the service. Personally I can’t picture Google doing something like that, but anything can happen.

Although Google’s API and GoogleCharts make things pretty easy I hope this helps a couple of people.

Technorati Tags: ,

Custom Validation Messages

Posted by chris olsen on January 25, 2008


8 errors prohibited this account from being saved
There were problems with the following fields:

Maybe it is just me, but having error messages like the above seems a little to artificial to me. Maybe rails has standard error messages like the following so everybody changes them, who knows.

One thing I found when searching for customized error formatters is that they would only accept one model object reference, ie, they would only allow for

error_messages_for(:account)

and not

error_messages_for(:account, :user)

which is odd seeing as the overridden method does allow for many models.

Below is a custom method that will allow us to deliver the message with more of a person to person flavour.

def error_messages_for(*object_names)
  messages = []
  object_names.each do |object_name|
    object = instance_variable_get("@#{object_name}")
    if object && !object.errors.empty?      
      object.errors.full_messages.each do |message| 
        messages << %(<li>#{message}</li>) unless message =~ /is invalid/
      end
    end
  end
 
  if messages.size > 0
    content_tag(:div, 
    content_tag(:h2, "Uh-oh! We have some invalid fields.") +
    content_tag(:ul, messages),:id => 'errors')
  end
end

It will also prevent messages like “Users is invalid” (yes that is pluralized on purpose) that occur from having the validates_associated :user validator telling us that the associated model is invalid.

This isn’t groundbreaking, but I know I will be searching for this again sometime soon.

Skydiving

Posted by chris olsen on January 25, 2008

On a recent trip to Mexico I went skydiving for the first time and it was most amazing thing. I can honestly say that if I was a heroin junkie I would give it all up for a few skydives.

Here’s the video

Sky Diving from chris olsen on Vimeo.