Hotwire

Hotwire is designed to ease the pain of creating Google Wire Protocol compatible data source in Ruby. Specifically, Hotwire can make it dead simple to provide data for the Google Visualization API.

This class implements a Google Visualization API Wire Protocal Datasource, as defined here.

You can use it to wrap your custom data into a json, csv, or html output compliant with the Google Visualization APIs specifications.

Google Visualization APIs define a standard format to express tabular data with typed columns. By exposing your data in such format, you can use all the existing visualizations conforming to the specification out-of-the-box to represent and analyze your data.

You can read more about the GViz API here: http://code.google.com/apis/visualization.

It implements most of the standard with the exclusion of:

  • Query language (tq parameters)
  • Caching (sig parameters)
  • some warnings and error types in the JSON response

It supports version 0.5 of the APIs.

Basic Use

The basic use follows this pattern:

  • create an instance from the request params using the Hotwire::Request.from_paramsfactory method.
  • verify that the instance is valid
  • create a response object using the request.build_response method
  • add columns to the response using add_column.
  • add a dataset to the response using set_data.
  • invoke the body method to return the datasource formatted according to the Wire Protocol in the requested format.

Depending on the request parameters, the body method will either return a json or csv string. When an html response is requested, nothing is returned by this class itself can be passed to a view to generate the HTML report.

The dataset is expected to be a 2-dimensional array (the first index referencing the rows, the second the columns). More generally, it can be anything that supports a 2 nested iteration cycles: the first on rows, the second on columns.

The columns ordering in the dataset must match the order used to add them via the add_column method. Aka, add_column must be invoked for each column in the same order used by the dataset (and expected in the produced output).

Example

The following is a basic example of use.

class YourController < ApplicationController
    def yourmethod
        data = [
            [ Date.today, 'hello', 10 ],
            [ Date.today - 1, 'world', 20 ]
        ]
        respond_to do |wants|
            wants.json do
                if wire_request = Hotwire::Request.from_param(params)
                    wire_response = wire_request.build_response
                    wire_response.add_col('date', :id => 'A' , :label => 'Date').
                                  add_col('string', :id => 'B' , :label => 'Name').
                                  add_col('number', :id => 'C', :label => 'Count').
                                  set_data(data) if wire_request.valid?
                    wire_response.body
                else
                    data.to_json
                end
            end
        end
    end
end

Alternate Data Structures

The set_data method on a Hotwire::Response object can be called with several different data structures in order to make getting data into the response as smoothly as possible. See the documentation for Hotwire::Response.set_data for more details.

ActiveRecord

If your project includes ActiveRecord, Hotwire will automatically included an ActiveRecord mixin that allows columns to be added from a model class.

Let's say you have a Person Model:

Class Person < ActiveRecord::Base
end

Then, when you have created your response object, you can add all the columns from the Person Model:

...
wire_response.add_columns(Person)
...

Finally, you can pass a collection of People to set_data:

...
@people = Personal.find(:all)
wire_response.set_data(@people)
...

You can also set the columns manually and then still use set_data with a collection. This will only set data with columns you have already added, matching the id key of the column definition to the attribute on your objects:

...
wire_response.add_col('string', :id => 'name' , :label => 'Name').
              add_col('number', :id => 'age' , :label => 'Age')
wire_response.set_data(Person.all)
...

In that case, your wire_response.data will look like:

wire_response.data => [['Bob', 33], ['Fred', 22]]

ToDo

TODO: should be updated to handle version 0.6 of the API

Author

Les Kiger [[email protected]]

Acknowledgements

This code is heavily based on the GVis class by Riccardo Govoni [[email protected]].

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don't break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Copyright (c) 2010 Les Freeman. See LICENSE for details.