Rails中精彩的图表

当分析完数据库中的数据,需要将结果以某种形式展示给用户的时候,有些Web应用程序比较偏好采用报告的形式。不过,图表的优点是能够根据报告,给出直观的视觉展现,并且可以帮助开发者观察趋势以及简化数据的综合分析过程。有很多技术能够帮助开发者构建出这类应用程序,Ruby on Rails就是其中之一。在本文中,我们将会详细介绍如何使用Ruby on Rails创建图表。

\

优秀的软件能够从数据库中的数据或者报告中快速高效地生成图表。Gruff、JFreeChart、XML/SWF charts和FusionCharts是Ruby on Rails上为数不多能满足我们需求的软件。不过你得明白,并不是所有这些软件我们都试用和测试过,所以下面的信息大部分是取自产品文档。

\
类型 Ruby类 Java Class文件 Flash图表组件 Flash图表组件 Flash图表组件
操作系统平台 平*立 平*立 平*立 平*立 平*立
是否需要Adobe Flash插件 不是 不是
是否可用于高级数据分析 不可以 可以 可以 不可以 可以
是否需要安装软件或者其他插件 需要,需要安装RMagick,ImageMagick 和Gruff 需要,JDK 1.3版或更高 不需要 不需要 不需要
能否生成动态图表 不能
是否支持输出到图片 支持 支持 支持 不支持 支持
UTF8支持 文档未提及
易用性 易用,但是需要写很多代码 并不易用。对用户较友好 很复杂,太多用于配置图表的xml标记 非常易用,开发者和用户都可以轻易上手 非常易用,开发者和用户都可以轻易上手
2D/3D条形图支持
折线图支持
散点图支持
面状图支持
2D/3D饼图支持
2D/3D环状图支持 是,以环形图形式
甘特图支持 是(FusionWidgets v3的一部分)
混合图支持
仪表图支持 是(XML/SWF标准的一部分) 是(FusionWidgets v3的一部分)
实时图支持 是(FusionWidgets v3的一部分)
数据驱动地图 是(FusionWidgets v3的一部分)

本文中,我们将会研究如何使用FusionCharts和MySQL在Rails中绘制图表。FusionCharts是一个基于Flash的图表生成组件,它能够帮助开发者创建可交互的动态图表。同样,它也可以和任何一种Web脚本语言绑定,以最少的代码绘制出优秀的图表。不仅如此,FusionCharts甚至可以封装成模块并在RoR中使用。易于使用、多种图表类型支持、优秀的文档以及良好的支持是本文我们介绍FusionCharts的原因。

\

这篇文章以一个应用程序为例,描述了在RoR下FusionCharts生成图表的机制。这个应用完整运行所需要的软件有:

\

•   FusionCharts 免费版/ v3:

\

FusionCharts免费版可以从www.fusioncharts.com/free下载。而功能更全面的商业版可以从www.fusioncharts.com上下载。在本文中,我们使用的是免费版。

\

FusionCharts的安装需要将安装包中的SWF和.rb文件拷贝到合适的位置。.rb文件是在Download Package \u0026gt; Code \u0026gt; RoR \u0026gt; Libraries目录下。

\

•   Ruby 1.8.6或更高版本:

\

下载地址是http://www.rubyonrails.org/down

\

•  Rails gem 2.0.2或更高版本:

\

在RubyGems加载之后,如果要完整安装Rails以及其依赖包,你可以输入如下命令:

\

   gem install rails

\

Rails也可以从此页面下载。

\

•   MySQL 4.0或者更高版本:

\

MySQL可以从此处下载。

\

这里我们以一个计时器为例。这个程序是为员工设计的,员工需要填写每周的上班时间,并且可以查看自己的工时表。本文将会从两个部分来讨论这个应用程序。第一个部分将处理开发上的问题,包括列举、编辑、展示和删除雇员以及工时表。如果你已经有一个需要集成图表的应用程序,你可以跳过这部分,然后直接进入下一个部分,下个部分将会阐述如何将图表和应用程序集成在一起。本文假设读者有基本的Ruby on Rails的知识。

\

创建基本的计时器程序

\

在脚手架框架(scaffolding framework)的帮助下,一些基本功能,例如列举、编辑、展示和删除雇员以及时间表,可以很简单地生成出来。生成基本的员工和时间表控制器的命令如下:(运行第一个命令后,在创建的TimeTrackerApplication目录下运行后面两个命令)

\
\rails –d mysql TimeTrackerApplication\ruby script/generate scaffold Employee name:text\ruby script/generate scaffold Timesheet log_date:datetime hours_spent:integer employee_id:integer
\

下一步,你需要修改config文件夹里面的database.yml,让其指向“timetrackerdb”,并且配置好用户名和密码。运行如下命令创建数据库:

\
\rake db:create\rake db:migrate
\

在mysql中运行如下命令,创建employees和timetables表的外键。

\
\alter table timesheets add constraint fk_employee_id foreign key fk_employee_id(employee_id) references employees(id) on delete cascade 
\

当然,创建外键的方法有多种;不过如果要深入下去,那么就超出了本文的讨论范围。你需要在模型之间创建关联才能够在Ruby on Rails中表示外键。

\

在Employee模型中,在类声明语句后,加入如下语句:

\
\has_many :timesheets 
\

而在Timesheet模型你需要加入:

\
\belongs_to :employee 
\

运行sql脚本db/sampledata.sql将样例数据插入到数据库中。至此,基本的程序和数据已经就绪,你可以增加、查看、编辑和删除雇员。

\

在应用程序中集成图表

\

当程序就绪之后,下面我将介绍如何生成一名员工的工作时间图表:

\

•   将FusionCharts文件夹(Download Package \u0026gt; Code \u0026gt; FusionCharts)拷贝到TimeTrackerApplication的public目录下。

\

•   将Download Package \u0026gt; Code \u0026gt; FusionCharts文件夹中的FusionCharts.js拷贝到public/javascripts文件夹下。

\

•   将Download Package \u0026gt; Code \u0026gt; RoR \u0026gt; Libraries文件夹中的fusioncharts_helper.rb拷贝到TimeTrackerApplication的lib目录下。

\

至此,FusionCharts的安装就已经完成了。

\

员工名单页中需要一个指向Time Tracker Chart的链接。这样,每个雇员都可以查看自己的工时图表。链接是在app/views/employees/index.html.erb文件中创建。列表1详细描述了链接创建的方法。这段代码是把这个链接加到此页面的其他链接之后。

\

列表1

\
\ \u0026lt;td\u0026gt;\u0026lt;%= link_to 'Time Chart', {:action=\u0026gt;'view_timesheet_chart',:id=\u0026gt;employee.id} %\u0026gt;\u0026lt;/td\u0026gt;
\

图1展示的是员工名单页面。在设置完数据库之后,开发者使用Ruby脚手架框架,可以很容易地实现这个功能。我们可以访问“http://yourserver:port/employees”来查看此页面。图1是屏幕部分截图。

\

图1

\

Rails中精彩的图表

\

点击“Time Chart”链接将会调用employees控制器的view_timesheet_chart动作。列表2列出了它的代码。这个动作的作用是展示选中员工的工时表。它会向Employee模型查询指定员工在“2008-12-01”至“2008-12-07”这段时间的工时表,得到结果之后,渲染“view_timelog_chart.html.erb”文件。为了简单起见,在本文中,这个时间范围是固定的。当然,现实世界中,这个时间范围应该是可由员工指定。

\

列表2

\

EmployeesController

\
\def view_timesheet_chart    \   start_date= \"2008-12-01\"    \   end_date=\"2008-12-07\"    \   @employee_id = params[:id]    \   employee_details_with_timesheets =  Employee.find_with_timesheets_in_date_range(@employee_id,start_date,end_date)\   if(!employee_details_with_timesheets.nil?)        \      @employee_details_with_timesheets =employee_details_with_timesheets[0]    \   else      \      @employee_details_with_timesheets =nil;    \   end    \   headers[\"content-type\"]=\"text/html\"\\end\
\

这个动作以员工id为参数,它向Employee模型查询指定员工在特定时间范围内的工时表。我们将在Employee模型中加入如列表3所示的函数:

\

列表3

\

Employee.rb

\
\def self.find_with_timesheets_in_date_range(id, start_date, end_date)    \   conditions=\"employees.id =? and log_date between ? and ?\"    \   employee_details_with_timesheets=self.find(:all, :include=\u0026gt;'timesheets', :conditions=\u0026gt; [conditions,id,start_date,end_date], :order=\u0026gt;'log_date asc')    \   return employee_details_with_timesheets\end
\

最后,这个动作会渲染“view_time_chart.html.erb”文件。“view_time_chart.html.erb”模板使用的布局模板是“employee.html.erb”,所有employees控制器的视图都会采用这个布局模板。

\

列表4

\

view_timesheet_chart.html.erb (在app/views/employees文件夹中)

\
\\u0026lt;%= javascript_include_tag \"FusionCharts\"%\u0026gt;\\\u0026lt;%\#xml文件内容以字符串的形式从模板生成器中得到\str_xml = render \"employees/timesheet_data\"\#创建图表 - 3D柱状图,它的数据来自于strXML\render_chart '/FusionCharts/Column3D.swf' , '' , str_xml , 'TimeChart' ,  650 ,  400 ,  false ,  false do -%\u0026gt;\\u0026lt;%  end -%\u0026gt; 
\

从上面的代码,我们知道了如何在视图页面中渲染图表:

\
  1. 引入FusionCharts.js文件。\
  2. 从生成器中获取xml数据.(生成器的数据来源是控制器)\
  3. 使用合适的参数调用render_chart函数渲染图表,\

通常情况下,图表会以甘特图的形式表示,当然FusionCharts也支持这种形式。在此,简单起见,我们使用了柱状图。

\

第二步和XML的生成有关。FusionCharts使用XML(扩展标记语言)创建和操作图表。整个FusionCharts图表都是由XML控制。例如,你可以使用XML定义图表的外观,也可以定义图表的功能。每种图表都有大量特性可供选择。FusionCharts也有自己的XML结构。现在讨论的图表是单系列图表(只有一个数据集),表示员工每周工作的天数和每天工作的时间。列表5是这个图表可以使用的一个XML例子。

\

列表5

\

单系列图表XML样例

\
\\u0026lt;graph xAxisName=\"Day\" showValues=\"1\" caption=\"Time Tracker Chart\" numberSuffix=\" hrs.\" subcaption=\"For Employee John Wilson\" yAxisName=\"Hours Spent\"\u0026gt;    \   \u0026lt;set name=\"Monday\" value=\"8\" color=\"AFD8F8\"/\u0026gt;   \   \u0026lt;set name=\"Tuesday\" value=\"6\" color=\"F6BD0F\"/\u0026gt;    \   \u0026lt;set name=\"Wednesday\" value=\"5\" color=\"8BBA00\"/\u0026gt;    \   \u0026lt;set name=\"Thursday\" value=\"9\" color=\"A66EDD\"/\u0026gt;   \   \u0026lt;set name=\"Friday\" value=\"9\" color=\"F984A1\"/\u0026gt;    \   \u0026lt;set name=\"Saturday\" value=\"8\" color=\"CCCC00\"/\u0026gt;\\\u0026lt;/graph\u0026gt;
\

要用现有的数据生成和列表5类似的xml文件,那么就需要用到生成器。

\

生成器是如何生成图表XML的呢?它会用到控制器动作中的@employee_details_with_timesheets变量来构建出图表XML。列表6展示的是这个生成器的实现。

\

列表6

\

timesheet_data.builder (在app/views/employees文件夹中)

\
\xml = Builder::XmlMarkup.new(:indent=\u0026gt;0)\options = {\   :caption=\u0026gt;'Time Tracker Chart',\   :subcaption=\u0026gt;'For Employee '+ @employee_details_with_timesheets.name   ,\   :yAxisName=\u0026gt;'Hours Spent', :xAxisName=\u0026gt;'Day', \   :showValues=\u0026gt;'1', \   :formatNumberScale=\u0026gt;'0', \   :numberSuffix=\u0026gt;' hrs.'\}\xml.graph(options) do  \   for timesheet in @employee_details_with_timesheets.timesheets do    \      log_day = timesheet.log_date.strftime('%a')     \      xml.set(\         :name=\u0026gt;log_day,\         :value=\u0026gt;timesheet.hours_spent,\         :color=\u0026gt;''+get_FC_color)  \   end\end
\

在生成器中,你首先要创建一个XMLMarkup对象,将其“indent”属性赋值为0,然后可以开始使用这个对象来构建XML了。

\

配置图表的所有选项都被放到了一个名为options的散列表中。如果希望了解更多options的信息,可以参考完整的FusionCharts文档。XML文件的root元素是“graph”,其子节点是“set”,一个“set”节点表示一个特定员工的工时表。每一个“set”元素都有name和value属性。name字段表示一周的第几天,而value字段表示的是在这一天中,当前员工的工作小时数。生成器使用控制器动作中的@employee_details_with_timesheets变量来存储这些值。

\

最后,在视图页中,render_chart函数将会渲染出图表。这个函数的参数是swf图表名称、url、width、height、debugMode和registerWithJS。它可以在lib文件夹的fusioncharts_helper模块中找到。正如列表7所示,引入application_helper.rb之后,就可以在所有的视图中使用这个函数。

\

列表7

\

application_helper.rb

\
\require 'fusioncharts_helper'include FusionChartsHelper 
\

展示图表的swf文件在public/FusionCharts文件夹中。这里我们已经使用了Column3D.swf这个文件名,但是也可以生成任意名字的其他任何单系列的图表。而且,数据不仅仅可以从dataXML方法得到,也可以从dataURL中获取。这种用法在FusionCharts的文档中有举例。

\

现在,在员工列表页面中点击一个员工的“Time Chart”链接,从01/12/2008到07/12/2008这一周的工作时间将会以图表的形式展现出来。就像图2一样。

\

图2

\

Rails中精彩的图表

\

如何生成图表概述:

\

设置完FusionCharts,并且引入了application_helper.rb中的FusionChartsHelper之后,按照如下步骤生成图表:

\
  1. 控制器动作负责从数据库中寻找所需数据。\
  2. 根据当前数据编写生成器。\
  3. 在第一步提到的控制器动作所绑定的视图中,开发者有两种方式从生成器中取得XML,一种是直接将数据传递给生成器,或者让生成器从控制器动作中获取数据。\
  4. 下一步,使用合理的参数(包括从第三步中得到的XML)调用render_chart函数。\

通过一个计时器的讲解,你已经学到了如何在Ruby on Rails中使用FusionCharts创建简单的图表。而且,只要对这个程序进行简单的修改,你就能得到一个所有员工的工时图表。或者,你甚至可以深入下去,绘制出某个特定员工的详细工时表。当然,你也可以使用Ajax来获取图表。诸如此类的改进数不胜数。FusionCharts的下载包中就包含了很多值得深入研究的例子。本文讲解的应用程序可以从这里下载。

\

关于作者:

\

本文作者有着六年的IT咨询从业经验,并且对大量的Web技术感兴趣,包括J2EE栈、Ruby on Rails和PHP。

\

查看英文原文Amazing Charts In Rails

\

感谢李明对本文的审校。

\

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至[email protected]。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。