domingo, 30 de marzo de 2008

acts_as_node: visualize the information




Hello everyone,

Once and again, I've been occuping my time coding another plugin, this one, I got to tell, I really enjoy doing it.

Why ? Because I love the topic: "Information Visualization", and this is the very purpose for this plugin, to help you, or your clients, to see the data in graphic form in the way it can help you to inferred information using the perceptual senses.

acts_as_node plugin allows you to see the relationships between specific instances of the application models as it renders a graph drawing based on that information.

It is very straighforward to use, because you don't have to modify any model or controller, instead you just need to use the graph_drawimg helper that makes all the magic within the view.

Installation

First of all, you got to download the plugin in the following way:

./script/plugin install http://acts-as-node.googlecode.com/svn/trunk/acts_as_node

Then, the plugin needs to generate the necesesary files and assets with the generate command:

./script/generate acts_as_node

Finally, we got to define a route for the plugin, that will be used to generate an intermediate xml file which is the input for the graph drawing algorithm. The route should look like this:

#config/routes.rb
map.graph_generator '/graph_generator', :controller => "acts_as_node", :action => "generate_graph"

That's all we need to get started. Now we can just try it out!

Usage

First of all, you have to add the following line to the application's layout file (or where you want to use it):

#app/views/layout/application.rhtml
..
<%= javascript_include_tag :acts_as_node %>
..

Then, all you have to do is use the graph_drawing helper. For that matter, I am going to explain the graph_drawing helper signature to understand how it works:

graph_drawing class_name, root_id, optional_parameters

The parameters represents:
  • class_name. The model name of the root of the graph. (Eg. :ProductCategory)
  • root_id. The id of root instance that will generate the graph (Eg: 1)
There is a long list of optional_parameters, that I'll describe here:
  • depth. The depth of the graph involved, counting from the root node.
  • layout. This enable us to set which layout we want to render the graph. The available options are: :SnowflakeLayout & :ForceDirectedLayout.
  • content. The DOM elment's id that will contain the drawing. If this value is not set, it will be add to document.body object.
  • childRadius. The lenght of the edge between nodes
  • fanAngle. The angle between silbling nodes, applies only for the Snowflake layout.
  • mass. This value represents the mass of each node for the algorithm, it is used to calculate the forces & the separation between the nodes, applies only for ForceDirected layout (default value: 0.3)
  • children_accesors. This is an important parameter, it indicates which methods of the model we will invoked to inspect for children nodes. It MUST be defined as a hash like the following: { :ProductCategory => [:children, :compaines] }. In this example, the graph_drawing helper will inspect the children's and companies's collections of the ProductCategory instances that the algorithm reaches.
  • children_fields. This enables the algorithm to set the appropiate title, content & picture attributes of the nodes. For example, in the followin definition for children_fields => { :ProductCategory => { :title => :name, :content => :description, :picture => :picture_uri}, the graph_drawing helper will render all ProductCategory nodes with a tooltip that contains a title assosiated with the name field of the instance, the content of the tooltip will be the value of description field of the instance, and the node itself will be rendered with the image provided by the picture_uri field.
  • image_width. This parameter enables you to set the width of the image for the pictures of the nodes.
  • image_height. Likewise The same concept of image_width related to the height of the pictures.
The best way to get started is to see examples:

#sample.rhtml

..
<%= graph_drawing :NetworkNode, 1, :container => "$('graph_id')", :children_fields => { :NetworkNode => {:title => :name, :content => :descrption, :picture => 'picture' }} %>
..

This configuration generates an output similiar to the picture shown at the beginning of the article. In that case, the algorithm will start with the object NetworkNode.find(1), provided that the class_name parameter is :NetworkNode and the root_id is 1. The drawing will be displayed within the DOM element with 'graph_id' identificator. The values used to display the attributes of the tooltip are :name (for the header), and :content (for the body). The image is obtained by invoking the picture method of the instance. It is worth noticing that :children_accesors are not defined, this way the helper assumes :children method is present (this will be the case if the model uses acts_as_tree, acts_as_nested, among other well known plugins).

Another sample, a little bit more comlex:

<%= graph_drawing :ProductCategory, 1, :layout => :ForceDirectedLayout, :mass => 0.3, :container => "$('graph_id')", :children_accessors => { :ProductCategory => [:children, :companies, :legal_questions], :Company => [], :LegalQuestion => [:legal_categories], :LegalCategory => []}, :children_fields => { :ProductCategory => {:title => :name, :picture => 'picture', :content => :name}, :Company => {:title => :name, :content => :name}, :LegalQuestion => {:title => :title, :content => :answer_html}, :LegalCategory => {:title => :name, :content => :name}} %>

In this example, the graph will be generated starting from a ProductCategory instance (the one with id = 1). And it will be asked for the following collections: children, companies, legal_questions. Then for each instance that the algorithm reaches, it will look up in the children_accesors hash to see what collection it has to cover. For example, when it reach a LegalQuestion instance, it will only process the legal_categories collection (as it is the only collection name defined for :LegalQuestion in the hash).

Improvment & Comments

If you have any comments, questions, and/or suggestions please don’t hesitate to send me an email here.